Bookinfoの内部にKong Gatewayを挟む


[Bookinfo](https://istio.io/latest/docs/examples/bookinfo/)といえばIstioのデモで有名なアプリケーションで、以下のようなマイクロサービスの構造を持っている。
![Bookinfo](https://istio.io/latest/docs/examples/bookinfo/withistio.svg)

今回、Bookinfoの仕様の確認ついでに以下の図のような感じでProductpageと他のサービスの間にKong Gatewayを挟んでみる。
![20250501120730.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/23b5e01a-e2bd-4400-8c3c-2d7680301d0e.png)

なお、Bookinfoは利用するもののIstioは利用しない。
あくまでもマイクロサービスのサンプルとして利用する。

## Bookinfoの仕様
Bookinfoは前述のようにマイクロサービスの構造を持っていて、入口にはProduct pageがあり、そこから各サービスにアクセスする形になっている。
これのソースコードは以下となる。
https://github.com/istio/istio/blob/master/samples/bookinfo/src/productpage/productpage.py

内部での各サービスへのアクセス先は以下のよう定義されている。
```python
reviews = {
    "name": "http://{0}{1}:{2}".format(reviewsHostname, servicesDomain, reviewsPort),
    "endpoint": "reviews",
    "children": [ratings]
}
```
実際にアクセスする際は以下のように値を使う。
```python
    url = reviews['name'] + "/" + reviews['endpoint'] + "/" + str(product_id)
    res = send_request(url, headers=headers, timeout=3.0)
```

サービス間の接続はhttp://`reviewsHostname`:`reviewsPort`:`servicesDomain`/`endpoint`で行われるが、ここの`reviewsHostname`、`servicesDomain`、`reviewsPort`は環境変数で上書きすることが可能である。

|環境変数|意味|デフォルト値|
|---|---|---|
|`SERVICES_DOMAIN`|共通ドメイン|なし|
|`DETAILS_HOSTNAME`|detailsへアクセスするためのホスト名|details|
|`DETAILS_SERVICE_PORT`|detailsへアクセスするためのポート番号|9080|
|`RATINGS_HOSTNAME`|ratingsへアクセスするためのホスト名|ratings|
|`RATINGS_SERVICE_PORT`|ratingsへアクセスするためのポート番号|9080|
|`REVIEWS_HOSTNAME`|reviewsへアクセスするためのホスト名|reviews|
|`REVIEWS_SERVICE_PORT`|reviewsへアクセスするためのポート番号|9080|

`endpoint`は書き換え不可でホスト名のデフォルト値が適用される。

Kong Gatewayを挟む場合は上記の環境変数を使ってData Planeを指すように設定すればやりたいことが出来るようになる。

なお、上記のコードの中で以下のようなブロックがあるが、これはProductpageから各サービスに対してAPIが発行できるようになっているためである。
![20250501130558.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/9964f932-2102-4e90-bbde-a13f8598a428.png)

このパスは前述の環境変数で指定できるパスとは別物で、仕様に関してはOpen API Specが[公開されている](https://github.com/istio/istio/blob/master/samples/bookinfo/swagger.yaml)ので気になる人はこちらを参照するとよい。

## 検証環境
今回は以下の環境で検証した。
- Kubernetes：EKS
- Ingress：Contour
- Kong Gateway：Konnect

Konnectについてはdeckでアクセスする際に使う情報を以下の環境変数で設定している。
```bash
KONNECT_TOKEN=<Konnectのトークン>
KONNECT_CP=<KonnectのControl Plane名>
```

Ingressは検証目的として以下のように雑に作成済みである。
(特に`host`に実質IPを指定しているが、ELBの仕様上変わるので長期利用は非推奨）
検証するのになくても問題ない。
```yaml:Data Plane
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: kong-dp
  namespace: kong
spec:
  rules:
  - host: dp.114.185.191.224.nip.io
    http:
      paths:
      - backend:
          service:
            name: my-kong-kong-proxy
            port:
              number: 80
        path: /
        pathType: Prefix
```
```yaml:Bookinfo
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: bookinfo
spec:
  rules:
  - host: 114.185.191.224.nip.io
    http:
      paths:
      - backend:
          service:
            name: productpage
            port:
              number: 9080
        path: /
        pathType: Prefix
```
## 検証
### Kong Gatewayを挟む
上述のように環境変数でProductpageから各サービスへのアクセス先が指定できるので以下のようにbookinfoのManifest内にあるProductpageのDeployment記載箇所に環境変数を追加する。なおオリジナルのManifestは[こちら](https://raw.githubusercontent.com/istio/istio/release-1.25/samples/bookinfo/platform/kube/bookinfo.yaml)から入手できる。
```diff
diff -u bookinfo.yaml gw-bookinfo.yaml
--- bookinfo.yaml	2025-05-01 09:56:14
+++ gw-bookinfo.yaml	2025-05-01 13:48:47
@@ -329,6 +329,21 @@
         volumeMounts:
         - name: tmp
           mountPath: /tmp
+        env:
+        - name: SERVICES_DOMAIN
+          value: "kong.svc"
+        - name: DETAILS_HOSTNAME
+          value: "my-kong-kong-proxy"
+        - name: DETAILS_SERVICE_PORT
+          value: "80/details"
+        - name: RATINGS_HOSTNAME
+          value: "my-kong-kong-proxy"
+        - name: RATINGS_SERVICE_PORT
+          value: "80/ratings"
+        - name: REVIEWS_HOSTNAME
+          value: "my-kong-kong-proxy"
+        - name: REVIEWS_SERVICE_PORT
+          value: "80/reviews"
       volumes:
       - name: tmp
         emptyDir: {}
```
共通ドメインとして`SERVICES_DOMAIN`にNamespaceである`kong.svc`を指定する。
Kong GatewayでProxyする際の都合上`http://my-kong-kong-proxy.kong.svc/<サービス名>`でサービスを分けるため、`<サービス名>_HOSTNAME`には全て`my-kong-kong-proxy`を指定する。
`<サービス名>_SERVICE_PORT`についてはポート指定部分で文字列が渡せるため、DPのポート番号80とサービス名を足した`80/<サービス名>`を指定する。
この状態でBookinfoを起動すると以下のようにエラーが出る状態で起動する。
![20250501134417.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/1399af81-b9ea-41a6-bce4-3639f1e74c9e.png)

Kong Gateway側の設定が終わっていないので、Productpageから各サービスにアクセスできないためエラーとなる。
次にKong Gatewayの設定を行う。
以下のdeck用YAMLを用意した。
```yaml:kong.yaml
_format_version: "3.0"
services:
- host: details.bookinfo
  name: details-service
  path: /
  port: 9080
  protocol: http
  routes:
  - name: details-route
    paths:
    - ~/details
- host: ratings.bookinfo
  name: ratings-service
  path: /
  port: 9080
  protocol: http
  routes:
  - name: ratings-route
    paths:
    - ~/ratings
- host: reviews.bookinfo
  name: reviews-service
  path: /
  port: 9080
  protocol: http
  routes:
  - name: reviews-service
    paths:
    - ~/reviews
```
DPからBookinfo内の`kind:Service`にアクセスするためにKong側Serviceの`host`には`<Service名>.bookinfo`を指定し、RouteのパスはBookinfoの`<サービス名>_SERVICE_PORT`で指定したパスを指定する。
これを以下のようにdeckでKong Gatewayに適用する。
```bash
deck --konnect-addr https://us.api.konghq.com --konnect-token $KONNECT_TOKEN --konnect-control-plane-name $KONNECT_CP gateway sync ../kong.yaml
```
この状態でProductpageにアクセスすると以下のように他のサービスも含めて表示される。
![20250501152108.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/f2f374d2-95dc-41af-885b-289bd4f63001.png)

ちなみにKonnectを利用しているので、適用後は各サービスのアクセス状況も可視化できる。
![20250501152405.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/e36fec05-5dd3-453f-84ec-497bd9fadd6e.png)



### Kong Gatewayのプラグインの適用
ここではBook Details部分にRate Limiting Pluginを適用して保護できるか確認する。
KonnectのWebUIから作成したRouteを選択し、PluginsからRate Limiting Pluginを追加する。
![20250501153807.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/c9ce46f1-6b54-4164-b979-a6fa79856e72.png)

なお、設定値はここでは1分辺りのリクエスト数上限を3として設定した。
設定後、ブラウザで何度かアクセスしてみる。
すると以下のようにDetails部分だけエラーとなるのが確認できる。
![20250501153929.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/d2519ca3-e32b-46b0-8a3b-48cd49ca9f76.png)

プラグインが正常に動作したようだ。
KonnectのUIからもRate Limiting Pluginが作動し429エラーが発生していることが確認できる。
![20250501154046.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/239972c5-46a9-4899-982d-aeaf95c1bf4b.png)
 
## 感想
既存のマイクロサービスの中にKong Gatewayを置くようなケースでは、Bookinfoのように環境変数で接続先が簡単に切り替えられる作りになっていないとコード修正が必要になる可能性もあり、12Factorに沿ったアプリケーション開発が重要になりそうだと感じた。