Kong Gatewayで分散アクセスを実現する

Kong Gatewayを使ってトラフィックをプロクシする場合、一般的には以下のようにKong Gateway内にRouteとServiceのエンティティを定義してトラフィックを転送する。
![20240827090859.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/e6b724e4-a923-f720-2aa9-fe0cf0f99964.png)

しかし、負荷分散のためにService AとBにアクセスしたい場合、Serviceで直接複数の宛先を指定することは出来ないため、Serviceだけでは分散構成が作れない。

![20240827091040.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/43f462eb-c699-5ec1-b216-c7c3080c9d73.png)

Kong Gatewayでは分散構成を取る場合、[Upstream](https://docs.konghq.com/gateway/latest/key-concepts/upstreams/)と呼ばれるエンティティを作成して、その中にTargetと呼ばれる実際の宛先を指定して実現する。
![20240827091809.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/8fbde818-4a5d-54ff-615a-f392c577c08a.png)

今回はこれの設定方法と挙動を確認する。

## 前提
以下があるものとして進める。
- Kong Gateway
- Kong Manager

検証で使ったKong Gateway、Kong Managerは[こちら](https://qiita.com/ipppppei/items/5749cb7f3db534651b51#kong-gateway%E3%81%AE%E7%94%A8%E6%84%8F)の方法で構築したものを利用した。
設定方法についてはAdminAPI、Deckを使ったCLIでの設定方法もあるが、ここではKong ManagerによりUIで設定する。
CLIの設定方法が気になる方は公式ドキュメントの[Load Balancing](https://docs.konghq.com/gateway/3.7.x/get-started/load-balancing/)のページが参考になると思う。

## 検証のための設定
ここでは以下の構成を取る。
![20240827092233.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/2ab548d9-27d4-3ab7-ff62-575c9af6a26a.png)

[httpbin.org](https://httpbin.org/)と[httpbun.com](https://httpbun.com/)はそれぞれリクエストのチェックに使えるWebサイトで、
[https://httpbin.org/anything](https://httpbin.org/anything)と[https://httpbun.com/anything](https://httpbun.com/anything)にアクセスすると、HTTPリクエストの内容をそのまま返してくれる。
```json:httpbin.org
  "args": {},
  "data": "",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "*/*",
    "Host": "httpbin.org",
    ：（省略）
```
```json:httpbun.com
  "method": "GET",
  "args": {},
  "headers": {
    "Accept": "*/*",
    "Accept-Encoding": "gzip",
    "Host": "httpbun.com",
    ：（省略）
```
今回は`<Kong GatewayのProxy>/apicheck`にアクセスすると、それぞれhttpbin.org、httpbun.comに分散するよう設定する。

### ServiceとRouteの作成
最初にServiceを作成する。
通常はServiceの`Upstream URL`にhttpbin.org等を設定するが、今回はUpstreamエンティティの中のTargetで設定するため、実際のホスト名を指定する必要はない。
今回は`Name`を`dummy-service`とし、`Upstream URL`を`https://dummy.vhost`とした。
![20240827100134.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/c507ed5d-4bc6-42a7-1b2d-7cb6f80c16e7.png)

次にRouteを作成する。
こちらでは先程作成したServiceに紐づけて`/apicheck`というパスにアクセスすると先程のServiceを参照するように設定する。
ここでは`Name`を`apicheck-route`とし、Pathsを`/apicheck`とした。
![20240827093608.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/b6e41e6f-c4ff-698c-b329-745a8446af14.png)

### UpstreamとTargetの作成
Upstreamエンティティは`Gateway Services`や`Routes`と同じようにKong Managerの左サイドバーから選択して作成することが出来る。
左サイドバーの`Upstream`を選択し、`Name`で先ほど作成したServiceが選択できるようになっているので選択してSaveする。
![20240827094027.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/5fb9bdaf-cd9d-2cee-80b5-9abb0abb4450.png)
なお、ここでは細かい説明は省略するが、以下のようなものも設定できるようになっている。
- 分散アルゴリズム：`Round Robin`、`Least Connections`、`Consistent Hashing`、`Latency`から選択可（デフォルトはRound Robin）
- Hashに何を使うか：`Consumer`、`IP`、`Header`、`Cookie`、`Path`から選択可
- パスの健全性チェックやサーキットブレーカーの有効・無効
- 健全性チェック時の健全・不健全の定義

詳細な情報が必要な人は[Advanced Load Balancing](https://docs.konghq.com/gateway/latest/how-kong-works/load-balancing/#advanced-load-balancing)あたりのページを参照すると良いと思う。

最後にTargetを作成してRouteとエンドポイントのサービス（httpbin.org、httpbun.com）を繋ぐ。
作成後、作成したUpstreamをクリックして、`Targets`->`New Target`を選択する。
![20240827094909.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/ae3d404f-37c0-384e-93ba-873f1c6e3f9d.png)

するとTarget Addressを入力する画面に遷移するので、httpbin.orgのパスを以下のように作成する。
- Target Address：httpbin.org:443
- Weight：100（デフォルト値）
- Tags：空（デフォルト値）

注意点としては、Target Addressではポートを指定しないとデフォルトで8000番を指してしまう点と、Weightはパーセンテージではなく数値である点は気にしておいた方がよい。
作成後、同じ要領でhttpbun.comに対してもTargetを作成する。
- Target Address：httpbun.org:443
- Weight：100（デフォルト値）
- Tags：空（デフォルト値）

Weightを同値にしているので、おおよそ同じ割合でそれぞれの宛先に分散するはずだ。
以上で設定は終了となる。

## 検証
作成したRouteに以下のようなcurlコマンドで何度かアクセスしてみる。
```sh
curl localhost:8000/apicheck/anything
```
実行するとリクエストの内容が確認できる。
```json
curl localhost:8000/apicheck/anything
{
  "args": {},
  "data": "",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "*/*",
    "Host": "httpbin.org",
    "User-Agent": "curl/8.7.1",
    "X-Amzn-Trace-Id": "Root=1-66cd259a-55717d764f2154693d29a442",
    "X-Forwarded-Host": "localhost",
    "X-Forwarded-Path": "/apicheck/anything",
    "X-Forwarded-Prefix": "/apicheck",
    "X-Kong-Request-Id": "12378b7549a1a531baea7f7e72b9bf7b"
  },
```
また、`"Host"`がhttpbin.orgになったりhttpbun.comになったりと、宛先が切り替わっていることも確認できる。
```sh 
$ for((i=0;i<10;i++)); do curl -s localhost:8000/apicheck/anything ; done | grep \"Host\"
    "Host": "httpbin.org",
    "Host": "httpbin.org",
    "Host": "httpbun.com",
    "Host": "httpbin.org",
    "Host": "httpbun.com",
    "Host": "httpbin.org",
    "Host": "httpbun.com",
    "Host": "httpbun.com",
    "Host": "httpbin.org",
    "Host": "httpbun.com",
```
リクエストが分散して転送されていることが確認できた。

## 最後に
このようにUpstreamとTargetを使うと、Pluginを使わなくても分散処理が簡単に使えることが確認できた。
ただ、カナリアリリースなど特定のユーザを特定のバージョンに流したり、一定期間後に特定のバージョンを使用不可にするような用途で使うのは難しいので、あくまでも負荷分散のために利用し、カナリアリリースのようなユースケースでは専用のPluginを使った方がよい。