Kong Gatewayをデプロイする（Hybridモード編）

# はじめに

Kong Gatewayは、以下の4つのモードでデプロイすることができます。
* Traditionalモード
* DB-less and declarativeモード
* Hybridモード
* Kong Konnect

それぞれの方式には利点と制限事項があります。そのため、Kong Gatewayを本番環境にインストールする際には、どのモードを選択するかを慎重に判断することが重要です。

本記事では、Hybridモードの特徴、メリット、制限事項、ならびにデプロイ手順について説明します。

Kong Gatewayとは何かを知りたい場合は、以下の記事をご覧ください。

https://qiita.com/m5takeda/items/d7c74681c66692144ccc

# Hybridモードとは

Hybridモードは、Kong Gatewayをコントロールプレーン[^1]とデータプレーン[^2]に分割した構成です。
このモードでは、単一のコントロールプレーンに対して複数のデータプレーンが接続されます。データプレーンはコントロールプレーンとの接続を常に維持し、これにより最新の設定をリアルタイムで受信します。
新しいデータプレーンが作成されると、コントロールプレーンとの接続が確立され、コントロールプレーン上で設定が変更されるたびに、その情報がクラスタ内のすべてのデータプレーンへと即座に反映されます。この仕組みにより、システム全体の一貫性と効率性が向上します。

![Hybridモード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2812915/a29e1b00-939b-b682-0231-217d5b944499.png)


[^1]: すべてのデータプレーンの設定を管理する。API構成管理、ノードへの構成データの配布、ノードの監視、管理インタフェースの提供などの機能を持つ。

[^2]: APIトラフィックの処理とルーティングを提供する。トラフィックのプロキシ、モニタリング、ロギング、保護などの機能をもつ。

## 特徴
* **プラットフォーム互換性**：
Kubernetes上のKong Gatewayは、[Kong Ingress Controller](https://jp.konghq.com/products/kong-ingress-controller)の有無にかかわらず、ハイブリッドモードをサポートします。

* **コントロールプレーンとデータプレーンのバージョン互換性**：
コントロールプレーンは同じメジャーバージョンのデータプレーンからの接続のみを許可します。コントロールプレーンは、新しいマイナーバージョンのデータプレーンからの接続は許可しません。詳細なルールは[こちら](https://docs.konghq.com/gateway/latest/production/deployment-topologies/hybrid-mode/#version-compatibility)をご覧ください。

* **プラグインのバージョン互換性**：
設定されたプラグインのメジャーバージョンは、コントロールプレーンとデータプレーンで同じである必要があります。データプレーン上のプラグインのマイナーバージョンは、コントロールプレーンにインストールされたバージョンより新しくできません。
詳細なルールは[こちら](https://docs.konghq.com/gateway/latest/production/deployment-topologies/hybrid-mode/#version-compatibility)をご覧ください。

* **耐障害性**：
コントロールプレーンが停止した場合でも、データプレーンはキャッシュされた設定を使用してリクエストに対応し続けることができます。また、同時にコントロールプレーンとの通信の再確立を試みます。

* **コントロールプレーンとの切断時の動作**：
コントロールプレーンが停止している間でも、新しいデータプレーンノードを登録することが可能です。そのためには、既存のデータプレーンノードのLightning Memory-Mapped Database（LMDB）[^3]のディレクトリをコピーするか、DB-lessモード[^4]のように宣言的なYAMLファイルを設定する必要があります。同様の手順で、切断されたデータプレーンの設定を変更することも可能です。


[^3]: Key-Value Storeとして設計された組み込みDB。
[^4]: アドベントカレンダー5日目の記事をご覧ください。

* **データプレーンのキャッシュ設定　※Kong Gateway Enterpriseのみ**：
デフォルトでは、キャッシュされたデータプレーンの設定は暗号化されていないLMDBのファイルシステムに保存されます。LMDBを暗号化することもでき、その場合、データプレーンは起動時にクラスタ証明書キーを用いてLMDBを復号化します。

## メリット

* **展開の柔軟性**：
異なるデータセンター、地域、ゾーンにデータプレーンのグループを展開できます。

* **信頼性の向上**：
データベース（DB）の可用性がデータプレーンの可用性に影響しません（コントロールプレーンがダウンしても、データプレーンはローカルにキャッシュされた最新設定で機能し続ける）。

* **トラフィックの削減**：
DBへの直接接続が必要なのはコントロールプレーンだけであるため、DBへのトラフィック量を大幅に削減できます。

* **セキュリティの向上**：
ひとつのデータプレーンノードが攻撃されても他ノードに影響を与えません。

* **管理の容易さ**：
管理者はコントロールプレーンとやり取りするだけで、Kongクラスタ全体のステータスを制御および監視できます。

## 制限事項

* **設定の柔軟性不足**：
コントロールプレーンでの設定変更をスケジューリングやバッチ処理で実施できません（基本的にAdmin APIで即座に反映される）。
また、異なるデータプレーンが異なる設定を持つためには、それぞれのコントロールプレーンインスタンスが必要になります。

* **プラグインの非互換性**：
Hybridモードと互換性を持たないプラグインがあります。詳細は[こちら](https://docs.konghq.com/hub/plugins/compatibility/#plugin-compatibility)をご覧ください。

* **カスタムプラグインのインストール要件**：
カスタムプラグインはコントロールプレーンとデータプレーンの両方にインストールする必要があります。

* **接続のロードバランシングの欠如**：
コントロールプレーンとデータプレーン間の接続のロードバランシングは提供されていません。

* **Admin APIの機能制限**：
Admin APIのいくつかの読み取り専用エンドポイントは、データプレーン上のStatus API（ステータスを取得するためのAPI）に公開されています。

* **データ暗号化の制限**：
KeyingモジュールはDB内のデータを暗号化する（※Kong Gateway Enterpriseのみ）が、データプレーンノード上のデータは暗号化できません。

* **オートスケーリングの制限**：
データプレーンをオートスケーリングすることはできません。スケールアウトしたい場合は、KubernetesのHPAを利用します。

# Hybridモードのセットアップ

公式ドキュメントを参考に、Dockerを使用してKong GatewayをHybridモードでデプロイします。

https://docs.konghq.com/gateway/latest/production/deployment-topologies/hybrid-mode/setup/

## 環境
* WSL 2
* Ubuntu 22.04.1
* Docker v1.0.29

## 実施手順
### DBの準備
HybridモードではDBが必要になるため、PostgreSQLでDBを構築します。

(1) Dockerネットワークを作成します。
```bash
$ docker network create kong-net
```

(2) PostgreSQLコンテナを起動します。
```bash
$ docker run -d --name kong-database \
 --network=kong-net \
 -p 5432:5432 \
 -e "POSTGRES_USER=kong" \
 -e "POSTGRES_DB=kong" \
 -e "POSTGRES_PASSWORD=kongpass" \
 postgres:13
```
設定項目は以下の通りです。

|項目名  |説明  |設定値  |
|---|---|---|
|POSTGRES_USER|Postgresのユーザ名。Kong Gatewayから期待されるデフォルト値は`kong`。|kong|
|POSTGRES_DB|PostgresのDB名。Kong Gatewayから期待されるデフォルト値は`kong`。 |kong|
|POSTGRES_PASSSWORD|Postgresのパスワード。|kongpass|


(3) Kong DBを準備します。
```bash
$ docker run --rm --network=kong-net \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=kong-database" \
-e "KONG_PG_PASSWORD=kongpass" \
-e "KONG_PASSWORD=test" \
kong/kong-gateway:3.8.1.0 kong migrations bootstrap
```
`{IMAGE-NAME:TAG} kong migrations bootstrap`は、Postgres DBを準備するためのKong Gatewayのコマンドです。

設定項目は以下の通りです。

|項目名  |説明  |設定値  |
|---|---|---|
|KONG_DABATASE|Kongが使用するDBの種類。|postgres|
|KONG_PG_HOST|手順(2)で`kong-net`ネットワーク上で通信しているPostgres Dockerコンテナの名前。|kong-database|
|KONG_PG_PASSWORD|手順(2)でPostgresコンテナを立ち上げるときに設定したパスワード。|kongpass|
|KONG_PASSWORD <br>※Kong Gateway Enterpriseのみ|Kong GatewayのSuper Adminユーザ[^3]のデフォルトパスワード。|test|

[^3]: RBAC機能を設定するために、最初に作成するユーザ。

### 証明書と秘密鍵の生成
Hybridモードでは、コントロールプレーンノードとデータプレーンノード間の通信に、Mutual TLS (mTLS)が認証に使用されます。
そのため、Hybridモードを使用する前に、証明書と秘密鍵が必要になります。Kong Gatewayは、2つのモードで証明書と秘密鍵使用します。

* 共有モード（デフォルト）：
Kong CLIを使用して証明書と秘密鍵を生成し、そのコピーをノード間で配布します。 証明書と秘密鍵は、コントロールプレーンノードとデータプレーンノードの両方で共有されます。

* PKIモード：
認証局（CA）によって署名された証明書を使用します。 Kongは、同じCAからの証明書であるかどうかをチェックすることで、両者を検証します。

今回は「共有モード」を使用します。

(1) 証明書と秘密鍵を保存するディレクトリを作成します。
```bash
$ mkdir cluster
```

(2) Kong CLIを使用して証明書と秘密鍵を生成します。
Kong Gatewayを搭載したコンテナ上でKong CLIの`kong hybrid gen_cert`を実行することで、証明書と秘密鍵が作成されます。
作成した証明書と秘密鍵をホストマシンのclusterディレクトリにマウントすることで、保存します。
```bash
$ mkdir cluster

$ docker run --rm \
-v $(pwd)/cluster:/usr/local/kong/cluster \
kong/kong-gateway:3.8.1.0 /bin/sh -c \
"cd /usr/local/kong/cluster && kong hybrid gen_cert"

$ ls cluster
cluster.crt  cluster.key
```

### Kong Gatewayの起動

#### コントロールプレーンの起動

(1) (オプション）Kong Gateway Enterpriseのライセンスを持っている場合は、ライセンスキーを変数にエクスポートします。
※こちらのライセンスキーはダミーです。

```bash
$ export KONG_LICENSE_DATA='{"license"：{"payload": {"admin_seats": "1", "customer": "Example Company, Inc", "dataplanes": "1", "license_creation_date": "2017-07-20", "license_expiration_date"："2017-07-20","license_key":"00141000017ODj3AAG_a1V41000004wT0OEAU","product_subscription":"Konnect Enterprise","support_plan":"None"},"signature":"6985968131533a967fcc721244a979948b1066967f1e9cd65dbd8eeabe060fc32d894a2945f5e4a03c1cd2198c74e058ac63d28b045c2f1fcec95877bd790e1b","version":"1"}}'
```

(2) Kong Gatewayを搭載したコンテナをコントロールプレーンとして起動します。

```bash
$ docker run -d --name kong-cp --network=kong-net \
-e "KONG_ROLE=control_plane" \
-e "KONG_DATABASE=postgres" \
-e "KONG_PG_HOST=kong-database" \
-e "KONG_PG_USER=kong" \
-e "KONG_PG_PASSWORD=kongpass" \
-e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
-e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
-e "KONG_ADMIN_LISTEN=0.0.0.0:8001" \
-e "KONG_ADMIN_GUI_URL=http://localhost:8002" \
-e "KONG_LICENSE_DATA" \
-e "KONG_CLUSTER_CERT=/usr/local/kong/cluster/cluster.crt" \
-e "KONG_CLUSTER_CERT_KEY=/usr/local/kong/cluster/cluster.key" \
-e "KONG_CLUSTER_LISTEN=0.0.0.0:8005" \
-e "KONG_CLUSTER_TELEMETRY_LISTEN=0.0.0.0:8006" \
--mount type=bind,source="$(pwd)"/cluster,target=/usr/local/kong/cluster,readonly \
-p 8443:8443 \
-p 8001:8001 \
-p 8444:8444 \
-p 8002:8002 \
-p 8445:8445 \
kong/kong-gateway:3.8.1.0
```

設定項目は以下の通りです。

|項目名  |説明  |設定値  |
|---|---|---|
|KONG_ROLE|コントロールプレーンかデータプレーンかを設定する。|cotrol_plane|
|KONG_DATABASE|Kongが使用するDBの種類。|postgres|
|KONG_PG_HOST|Postgres Dockerコンテナの名前。 |kong-database|
|KONG_PG_USER|Postgresのユーザ名。|kong|
|KONG_PG_PASSWORD|Postgresのパスワード。|kongpass|
|すべての_LOG設定|出力するログのファイルパスを設定するか、stdoutとstderrにメッセージとエラーを出力する。|/dev/stdoutまたは/dev/stderr|
|KONG_ADMIN_LISTEN|Kong Admin APIがリクエストをリッスンするポート。|0.0.0.0:8001|
|KONG_ADMIN_GUI_URL|Kong ManagerにアクセスするためのURL。|http://localhost:8002|
|KONG_LICENSE_DATA <br>※Kong Gateway Enterpriseのみ|ライセンスファイルを環境変数として保存している場合、環境変数からライセンスを取得する。|-|
|KONG_CLUSTER_CERT|コントロールプレーンノードとデータプレーンノード間のmTLSに使用される証明書。|/usr/local/kong/cluster/cluster.crt|
|KONG_CLUSTER_CERT_KEY|コントロールプレーンノードとデータプレーンノード間のmTLSに使用される秘密鍵。|/usr/local/kong/cluster/cluster.key|
|KONG_CLUSTER_LISTEN|コントロールプレーンがデータプレーンからの接続をリッスンするアドレスとポートのリスト。このポートは常にmTLS暗号化で保護される。|0.0.0.0:8005|
|KONG_CLUSTER_TELEMETRY_LISTEN <br>※Kong Gateway Enterpriseのみ|コントロールプレーンがデータプレーンのテレメトリデータをリッスンするアドレスとポートのリスト。このポートは常にmTLS暗号化で保護される。|0.0.0.0:8006|

`--mount type=bind,source="$(pwd)"/cluster,target=/usr/local/kong/cluster,readonly`は、コンテナにホストマシンのclusterディレクトリをマウントします。
これにより、作成した証明書と秘密鍵をコントロールプレーンから取得できます。

また、各ポートの役割は以下にまとめられています。

https://docs.konghq.com/gateway/latest/production/networking/default-ports/

#### データプレーンの起動
(1) Kong Gatewayを搭載したコンテナをデータプレーンとして起動します。

```bash
$ docker run -d --name kong-dp --network=kong-net \
-e "KONG_ROLE=data_plane" \
-e "KONG_DATABASE=off" \
-e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
-e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
-e "KONG_PROXY_LISTEN=0.0.0.0:8000" \
-e "KONG_CLUSTER_CONTROL_PLANE=kong-cp:8005" \
-e "KONG_CLUSTER_TELEMETRY_ENDPOINT=kong-cp:8006" \
-e "KONG_CLUSTER_CERT=/usr/local/kong/cluster/cluster.crt" \
-e "KONG_CLUSTER_CERT_KEY=/usr/local/kong/cluster/cluster.key" \
-e "KONG_CLUSTER_DP_LABELS=deployment:local" \
--mount type=bind,source="$(pwd)"/cluster,target=/usr/local/kong/cluster,readonly \
-p 8000:8000 \
kong/kong-gateway:3.8.1.0
```

設定項目は以下の通りです。

|項目名  |説明  |設定値  |
|---|---|---|
|KONG_ROLE|コントロールプレーンかデータプレーンかを設定する。|data_plane|
|KONG_DATABASE|Kongが使用するDBの種類。|off|
|すべての_LOG設定|出力するログのファイルパスを設定するか、stdoutとstderrにメッセージとエラーを出力する。|/dev/stdoutまたは/dev/stderr|
|KONG_PROXY_LISTEN|HTTP/HTTPSトラフィックをリッスンするアドレスとポートのカンマ区切りリスト。|0.0.0.0:8001|
|KONG_CLUSTER_CERT|コントロールプレーンノードとデータプレーンノード間のmTLSに使用される証明書。|/usr/local/kong/cluster/cluster.crt|
|KONG_CLUSTER_CERT_KEY|コントロールプレーンノードとデータプレーンノード間のmTLSに使用される秘密鍵。|/usr/local/kong/cluster/cluster.key|
|KONG_CLUSTER_DP_LABELS|データプレーンラベルを設定するためのオプション設定。|deployment:local|

`--mount type=bind,source="$(pwd)"/cluster,target=/usr/local/kong/cluster,readonly`は、コンテナにホストマシンのclusterディレクトリをマウントします。
これにより、作成した証明書と秘密鍵をデータプレーンから取得できます。

また、各ポートの役割は以下にまとめられています。

https://docs.konghq.com/gateway/latest/production/networking/default-ports/

## 動作確認
(1) コントロールプレーンノードとデータプレーンノードが接続されているかどうかを確認します。
```bash
$ curl -i -X GET http://localhost:8001/clustering/data-planes
```

出力には、クラスタ内の接続されたデータプレーンインスタンスがすべて表示されます。
```bash
{"data":[{"last_seen":1732097168,"config_hash":"437f5e830e3c849ada8c49e169f7a6f1","sync_status":"normal","labels":{"deployment":"local"},"updated_at":1732097168,"id":"906bbd3e-14b9-471e-8354-56340a6df269","ttl":1149739,"version":"3.8.1.0","ip":"172.21.0.4","rpc_capabilities":[],"hostname":"41a1587f28df","cert_details":{"expiry_timestamp":1826701417}}],"next":null}
```

(2) `KONG_ADMIN_GUI_URL`で指定したURLを使ってKong Managerにアクセスし、Kong Managerが起動していることを確認します。

```
http://localhost:8002
```

![Kong Manager.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2812915/2f0c484b-5d4b-6a47-1ed0-4f1f18b2b27f.png)

# さいごに
本記事では、Kong Gatewayの一つのデプロイ方式であるHybridモードについて、その特徴、メリット、制限事項、具体的なセットアップ手順を説明しました。Hybridモードはシステムをコントロールプレーンとデータプレーンに分離し、これにより一貫性と効率性を向上させる特徴があります。

このモードの主なメリットは、異なるリージョンやゾーンにデータプレーンを柔軟に展開できること、DBの可用性がデータプレーンに影響しない信頼性の高さ、そしてトラフィック削減やセキュリティの向上にあります。また、管理の容易さも大きなメリットの一つです。
一方で、設定の柔軟性不足やプラグインの非互換性、一部機能の制限など、注意が必要な点も存在します。

Hybridモードを最大限に活用するためには、これらの特徴と制限を理解し、適切にりすることが重要です。