Kong KonnectのControl Plane Groupを使ってみる

Kong KonnectではControl Plane(CP)という機能でData Plane(DP)の設定を管理しており、基本的にはこの単位でマルチテナント的な分離を実現している。
通常はCPを複数作った場合、それぞれでData Planeを用意してProxy機能を提供する。
一方でControl Plane Groupという概念があり、これを使うとControl Plane間でData Planeを共有することが出来る。
今回はこれを検証する。

## Control Plane Groupの概要
Control Plane GroupはControl Planeを束ねて抽象化し、Data PlaneをControl Plane間で共有してアクセスすることが出来るようになる機能である。

![20250124160540.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/956beef4-cdc3-1da8-824a-1d7cfed806b2.png)
([Control Plane Group](https://docs.jp.konghq.com/konnect/gateway-manager/control-plane-groups/)より引用)

例えば組織レベルの分割ではなく、開発チームA、開発チームBといった組織内の小さな単位で分離をしたい場合、それぞれでData Planeノード用のリソースを用意するのは大変なので、この機能があればリソース共有しつつKong Gatewayの抽象化レイヤーではしっかり分離して使うようなことが出来る。

ただいくつか注意点がある。
- エンティティの名前を他のCP内の名前と被らないようにする
- 一部のエンティティは自動的に他のCPに共有される（Consumer、Globalスコープのプラグイン等）

詳細は[この辺り](https://docs.jp.konghq.com/konnect/gateway-manager/control-plane-groups/#configuring-core-entities)で確認して欲しい。

## 検証
### DPの共有の確認
最初に主たる機能であるDPの共有を確認する。
KonnectのUIからCPを作成する。
![20250124145901.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/5d45cfb3-8567-2818-f080-078e46981f4f.png)

ここでは以下のような構成とする。
- CP1: CP-Blue \
DP: なし
- CP2: CP-Red \
DP: なし

Data Planeを繋いでいると、Control Plane Group作成時に以下のエラーが表示されるため、Data Planeがない状態でControl Planeを用意する。

:::note alert
Error 400 8f21cca0-9637-4ff8-9eb9-13cffe123bd1: Control Plane has some data plane nodes connected. These must be disconnected before the Control Plane can join a Control Plane Group.
:::

次に以下のようにServiceとRouteを作成する。
- CP1:
  - Service名: `cp-blue-httpbin-svc` \
URL: `https://httpbin.org/anything`
  - Route名: `cp-red-httpbin-rt` \
Path: `/cp-blue-httpbin`
- CP2:
  - Service名: `cp-red-httpbin-svc` \
URL: `https://httpbin.konghq.com/anything`
  - Route名: `cp-red-httpbin-rt` \
Path: `/cp-red-httpbin`

`httpbin.org`と`httpbin.konghq.com`はホストが違うだけで同一のサービスであり、/anythingにアクセスすると送ったリクエストの詳細を返してくれる。

次にControl Plane Groupを作成する。
Gateway Managerの画面で`+ Create a gateway`の横の︙をクリックすると`Create a Control Plane Group`が表示されるのでこれをクリックする。
![20250124151305.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/5a7b8865-02f0-e8dd-404b-5301c85bda1f.png)

作成画面で名前とGroupに取り込むControl Planeを選択してSaveする。
![20250124151452.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/fe463c42-0ed8-8588-7693-7c2ea73c3ecd.png)

すると今度は正常に追加され以下のようにUIから確認することが出来るようになる。
![20250124151856.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/0ca2f925-d2d7-fe7c-5176-d0a8fe1b8b61.png)

この状態では、各Control PlaneではData Planeが追加できなくなっており、Control Plane GroupからData Planeを追加する。
追加後、それぞれのCPで作成したRouteのパスにアクセスする。

```sh
$ curl localhost:8000/cp-blue-httpbin
{
  "args": {},
  "data": "",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "*/*",
    "Host": "httpbin.org",
：（省略）
```
```sh
$ curl localhost:8000/cp-red-httpbin
{
  "args": {},
  "data": "",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "*/*",
    "Connection": "keep-alive",
    "Host": "httpbin.konghq.com",
：（省略）
```
`headers.Host`を見ると、それぞれ意図したホストのサービスにアクセスしている事が分かる。
これによりData Planeが1つしかないにも関わらず、それぞれのCPで定義したパスにアクセスできた。

## エンティティ名の競合の確認
注意点にあげたエンティティ名の競合を確認してみる。
CP-redで`cp-blue-httpbin-svc`というCP-blue側にあるServiceと同名のServiceをホスト`https://httpinb.konghq.com/user-agent`で作成してみる。
![20250124162543.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/869334bf-7012-8240-34c1-bad2576cfb0c.png)

作成は問題なく成功する。
ただし、CPのOverviewで警告が出るようになった。
![20250124162634.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/6cd766da-5050-9675-592c-fa03d97c0bb9.png)

Gateway Managerのトップでも競合が起きていると表示される。
![20250124162730.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/ef976734-63f0-00a6-0a7a-10564184690e.png)

エンティティ名が重複した場合、エンティティは作成できるがこのように警告が出ることが確認できた。
なお、先程作成した重複したServiceにRouteを関連付けて作成し、アクセスしても`"no Route matched with those values"`とエラーになったため、動作も正常動作しない模様。
スクリプトで自動構築している場合は警告画面に気が付かない可能性があるので、動作確認も含めてスクリプト化した方がよさそうだ。
（なお、重複した状態で`/cp-blue-httpbin`へのアクセスは問題なく行えた。）

## Globalスコープのプラグインの共有
注意点にあげたあるCPで設定したGlobalスコープのプラグインが別のCPに共有される点も確認してみる。
今CP-blueにRate Limiting Pluginを設定した。
![20250124160031.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/b45d5bdb-6193-f6cb-b6d1-f9232fcb3247.png)

この状態で`/bp-blue-httpbin`に5回アクセスする。
```sh
for((i=0;i<5;i++)); do curl -o /dev/null -s -w "%{http_code}\n" localhost:8000/cp-blue-httpbin; done
```
結果は以下のようになった。
```sh
200
200
200
429
429
```
同じように`/cp-red-httpbin`にもアクセスする。CP-redにはRate Limiting Pluginは設定していない。
```
for((i=0;i<5;i++)); do curl -o /dev/null -s -w "%{http_code}\n" localhost:8000/cp-red-httpbin; done
```
結果は以下のように、設定していないにも関わらずRate Limiting Pluginが効いた結果となった。
```
200
200
200
429
429
```

ということでGlobalスコープのプラグインの利用は他のCPに迷惑がかかるため、注意が必要となる。
CPの管理者間でコミュニケーションが取れないようなケース（CPをテナントと見立ててのテナント貸し）等では事故にも繋がりかねないので、そのようなユースケースでは使わない方が良さそうだ。
逆にガバナンスを効かせるためにまとめてプラグインを全CPに適用したいようなケースでは使えることもあるかもしれない。