KonnectでKong Ingress Controllerを使う

本記事は「Kong Advent Calendar 2024」の3日目のエントリとして、KonnectでKong Ingress Controllerを利用する方法について解説する。

[Kong Ingress Controller](https://docs.konghq.com/kubernetes-ingress-controller/latest/)(KIC)はKong Gatewayの各種エンティティ（ServiceやRouteなど）をKubernetesのIngressと融合して使うような事が出来るIngress Controllerであり、例えばIngressでサービスを公開すると、Ingress内の`rules`の記載に基づいて自動的にKong Gateway内にRouteやServiceを作成してくれる。
![20241125102244.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/01fbd19c-7d90-2129-fe5a-36fa1965f4d9.png)

このKIC、デプロイモードとして[DBありモード](https://docs.konghq.com/kubernetes-ingress-controller/3.3.x/production/deployment-topologies/db-backed/)と[DBなしモード](https://docs.konghq.com/kubernetes-ingress-controller/3.3.x/production/deployment-topologies/gateway-discovery/)が存在する。
ざっくりとした違いは以下になる。

||DBありモード|DBなしモード|
|---|---|---|
|構成の保存先|DB|クラスタのetcd|
|宣言的アプローチでの変更|deckを使用|Kubernetes Manifestを使用|
|構成|何でもあり|Hybridモード不可|

DBなしモードの場合はDBのメンテナンスは不要になる一方でアーキテクチャの制約が強く、完全に新規で利用する場合に検討候補となる構成となる。
話が少しそれたが、言いたかったのはどちらの構成もデータをクラスタに持たせる構成になっている。
ではクラウド側にデータを持っているKonnectを使った場合はどうなるか、が本日の趣旨となる。
公式ドキュメントでは[Kong Ingress Controller for Kubernetes Association](https://docs.konghq.com/konnect/gateway-manager/kic/)で説明しているあたりになる。
## KonnectでのKICの利用
KonnectではGateway Manager（ControlPlane）の作成時にKICを選択することでKonnect＋KICの構成が組めるようになっている。
特徴としては以下となる。
- Konnectは可視化用のダッシュボードとなり、ServiceやRouteなどの作成が出来ない
- ServiceやRouteはKubernetesのIngressリソースを介して作成する
- DataPlaneや通信の状態をダッシュボードから可視化可能

3番目の特徴はKICでなくても見れるので特筆する程のものでもないのだが、Kubernetes利用時に可視化を行おうとするとPrometheus/Grafanaのようなものの導入がデファクトとなるため、その点は少し楽が出来ると言える。


## 検証
### 構築から疎通確認
ここでは検証としてEKS上にKICを構築してKonnectと繋いでみる。

Konnectにログインし、`Gateway Manager`から`New Gateway`を選択する。
すると選択肢にKubernetes Ingress Controllerがあるのでこれを選択してGatewayを作成する。
![20241125103737.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/eaed74bf-68ff-18ac-c285-f69609f9dbc2.png)


次にDataPlaneのデプロイ画面に遷移するのでDataPlanを作成する。
検証目的なのでここでは特に値などは変更せず、表示されたコマンドをそのまま実行する。
```sh
kubectl create namespace kong
helm repo add kong https://charts.konghq.com
helm repo update
kubectl create secret tls konnect-client-tls -n kong --cert=/tmp/tls.crt --key=/tmp/tls.key
helm install kong kong/ingress -n kong --values /tmp/values.yaml
```
コマンドを実行すると以下のような状態確認の画面になる。
![20241125104355.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/229bb1c1-9755-2e5b-632a-3f25f3f0f3cd.png)

自動で画面上の状態がアップデートされないようなので、デプロイが終わったらDataPlaneのページからConnectedになっていることを確認する。
![20241125104455.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/00bbd500-cc06-8e36-37da-fcd95fd3aac2.png)

なおOverviewでは以下のようにFully Operationalと表示されていればOK。
![20241125105732.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/72d86996-8b11-a048-c25d-8c0625af0309.png)


クラスタ側では2つのPodがデプロイされる。
```sh
$ kubectl get pod -n kong
NAME                              READY   STATUS    RESTARTS   AGE
kong-controller-f745fd46f-s8hlp   1/1     Running   0          22m
kong-gateway-7bcccc668-t2q2r      1/1     Running   0          22m
```
KIC本体とProxyがデプロイされており、ProxyのServiceは`type: LoadBalancer`によって公開され、Ingress使用時はこのIPを利用することになる。
```sh
$ kubectl get svc -n kong
NAME                                 TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                         AGE
kong-controller-validation-webhook   ClusterIP      10.100.248.128   <none>        443/TCP                         23m
kong-gateway-admin                   ClusterIP      None             <none>        8444/TCP                        23m
kong-gateway-manager                 NodePort       10.100.170.74    <none>        8002:30662/TCP,8445:32409/TCP   23m
kong-gateway-proxy                   LoadBalancer   10.100.154.216   a3904a8cc45d7413f895dfe87162cd5f-xxxxx.us-east-1.elb.amazonaws.com     80:31015/TCP,443:31328/TCP      23m
```
KICは正常にデプロイで来たので、適当にIngressをデプロイしてみる。
ここでは[Kuard](https://github.com/kubernetes-up-and-running/kuard)を利用してデプロイする。
最初にNamespace、Service、Deploymentをデプロイする。
```yaml
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
  name: demo
---
apiVersion: v1
kind: Service
metadata:
  name: kuard
  namespace: demo
spec:
  ports:
    - port: 80
      targetPort: 8080
      protocol: TCP
  selector:
    app: kuard
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kuard
  namespace: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kuard
  template:
    metadata:
      labels:
        app: kuard
    spec:
      containers:
        - name: kuard
          image: gcr.io/kuar-demo/kuard-amd64:1
          ports:
            - containerPort: 8080
EOF
```
次にIngressをデプロイする。`IngressClass`はKIC展開時にあわせて`kong`という名前でデプロイ済みであるため、これを指定する。
```yaml
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: kuard
  namespace: demo
spec:
  ingressClassName: kong
  rules:
    - host: kuard.hogehoge.info
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: kuard
                port:
                  number: 80
EOF
```
デプロイするとKonnectの方でServiceとRouteが作成されたことが確認できる。
![20241125111333.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/a3c8c3e8-52d6-a80f-0281-98502cd53cdb.png)

kuardにアクセスすると問題なく利用できる。
![20241125111401.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/62c20f39-bdc0-ea67-50ab-33367a4d6c9e.png)

kuardにアクセス後、KonnectのRouteやServiceの画面からアクセス状況が可視化できていることも確認できる。
![20241125111513.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/3c404d7a-ed98-cb1f-0f03-bf816b61ff7e.png)

なお、Ingressの設定が適切でなくIngress経由で上手く通信できなくなると、以下のようにOverviewのKIC Detailsが警告っぽい画面になる。
![20241125104634.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/f445b462-ae61-c8e8-b0b5-1182553176a7.png)

この画面は例えば今作成したIngressの`spec.rules.http.paths.backend.service.name`を`kuard`から`kuard-wrong`のように存在しないServiceを指定すると簡単に確認できる。

### deckのYAMLを使ってIngressを作成
Kong Gatewayをある程度使っている人は[`deck`](https://docs.konghq.com/deck/latest/)というCLIで構築や設定をYAMLで書いていることも多いと思う。
KICの場合、Ingressリソースに置き換える必要があるが、既存のdeckのYAMLをKubernetesのManifestに変換する方法が用意されているのでこれも確かめておく。

以下にサンプルのdeck用YAMLを用意した。
```yaml:nginx-deck.yaml
_format_version: "3.0"
_konnect:
  control_plane_name: simple-nginx
services:
- host: nginx.hogehoge.info
  name: simple-nginx
  path: /
  port: 80
  protocol: http
  routes:
  - name: route_route
    methods:
    - GET
    paths:
    - /simple-nginx
```
これをKICに持ち込んでみる。
deckには[`deck file kong2kic`](https://docs.konghq.com/deck/latest/reference/deck_file_kong2kic/)という変換コマンドがあるのでこれを利用する。
```sh
deck file kong2kic -s /tmp/nginx-deck.yaml -o /tmp/nginx-ingress.yaml --ingress
```
引数に`--ingress`をつけないとGateway APIの`kind: HTTPRoute`に変換されるので注意。
変換後のIngressのManifestのspecは以下のようになった。
```yaml
spec:
  ingressClassName: kong
  rules:
  - http:
      paths:
：（省略）
---
```
ホスト名が欠落していたため、元のdeck用YAMLのRouteにもホストを指定するよう修正して再実行した。
```yaml
  routes:
  - name: route_route
    hosts:
    - nginx.hogehoge.info
    methods:
    - GET
    paths:
    - /simple-nginx
```
確認したところ、今度はホスト名が反映された。
```yaml
spec:
  ingressClassName: kong
  rules:
  - host: nginx.hogehoge.info
    http:
      paths:
：（省略）
```
変換したManifestをデプロイする。
```sh
kubectl apply -f /tmp/nginx-ingress.yaml -n default
```
Ingressは作成され、Konnectでも確認できた。
![20241125115018.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547981/26c7b204-c781-c54d-68aa-af9cc74f6426.png)



## 所感
KIC+Konnectを使うとOut of Boxでメトリクス等を可視化出来るのは嬉しいし、Ingressが利用可能かの状態を見てくれるのも嬉しい。
特にIngressの状態は`kubectl get ing`とかでは問題が起きているかどうか確認出来ないので、これはKIC+Konnectの強みだと思う。
一方で、UIから操作が出来ないことやdeckが使えないことはかなり辛い。
deckについては今回紹介した`deck file kong2kic`でカバー出来る部分はあるが、検証時にホスト名を書き換えたりしたなど、変換後のManifestを確認せずに使用するには少しリスクがありそうだ。
自身の組織でどのようにKong Gatewayを使うかによって合う合わないが大きそうなので、利用する際はユースケース等を整理してから使うのが良さそうである。