Kong Gatewayを入れてみる

# はじめに
オンプレ環境にAPIゲートウェイを入れる構成を作ることになったみたいです。
本来であればAWSのAPI Gatewayを入れたかったのだが、OSSであるKong GatewayとKong Ingress Controllerを使えばなんとかなるらしいのでインストールしてみることからやってみようと思います。

ドキュメントに書かれたインストール方法に従って入れるので、試しにインストールしてみたい人や入門として利用されたい方向けです。

# 必要なもの
- Kubeadm/kubectl/kubelet 1.28.4
- helm 3.13.1

※ネットワークやサーバインスタンスの構築やKubernetesのインストールは割愛します。

# やってみる
### helmインストール
```
$ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
$ chmod 700 get_helm.sh
$ ./get_helm.sh
```

### Kong Gatewayインストール
#### 名前空間の作成
```
$ kubectl create namespace kong
```

#### Kongをインストール
Kongはhelmを使ってインストールしています。
```
# Gateway APIのカスタムリソース(CRD)をインストール
$ kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.0.0/standard-install.yaml

# GatewayとGatewayClassを作成
$ echo "
---
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
 name: kong
 annotations:
   konghq.com/gatewayclass-unmanaged: 'true'

spec:
 controllerName: konghq.com/kic-gateway-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
 name: kong
spec:
 gatewayClassName: kong
 listeners:
 - name: proxy
   port: 80
   protocol: HTTP
" | kubectl apply -f -

$ helm repo add kong https://charts.konghq.com
$ helm repo update

# Kong Ingress ControllerとKong Gatewayをインストール
$ helm install kong kong/ingress -n kong --create-namespace

$  kubectl get ns
NAME              STATUS   AGE
default           Active   6m38s
kong              Active   4m48s
kube-flannel      Active   5m42s
kube-node-lease   Active   6m38s
kube-public       Active   6m39s
kube-system       Active   6m39s

$ kubectl get pods -ALL
NAMESPACE      NAME                                     READY   STATUS    RESTARTS   AGE     L
kong           kong-controller-7dcb48b75d-2hgx6         1/1     Running   0          35s     
kong           kong-gateway-697fdd75fd-8sd9n            1/1     Running   0          35s     
kube-flannel   kube-flannel-ds-lr6hv                    1/1     Running   0          5m57s   
kube-system    coredns-5dd5756b68-6w8jn                 1/1     Running   0          6m38s   
kube-system    coredns-5dd5756b68-lhqp2                 1/1     Running   0          6m38s   
kube-system    etcd-ip-10-0-100-55                      1/1     Running   0          6m52s   
kube-system    kube-apiserver-ip-10-0-100-55            1/1     Running   0          6m52s   
kube-system    kube-controller-manager-ip-10-0-100-55   1/1     Running   0          6m50s   
kube-system    kube-proxy-vpqft                         1/1     Running   0          6m38s   
kube-system    kube-scheduler-ip-10-0-100-55            1/1     Running   0          6m50s 

$ kubectl get svc -ALL -o wide
NAMESPACE     NAME                                 TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                         AGE    SELECTOR                                                                                                                                                                                            L
default       kubernetes                           ClusterIP      10.96.0.1        <none>        443/TCP                         8m6s   <none>                                                                                                                                                                                              
kong          kong-controller-validation-webhook   ClusterIP      10.105.250.203   <none>        443/TCP                         109s   app.kubernetes.io/component=app,app.kubernetes.io/instance=kong,app.kubernetes.io/managed-by=Helm,app.kubernetes.io/name=controller,app.kubernetes.io/version=3.4,helm.sh/chart=controller-2.31.0   
kong          kong-gateway-admin                   ClusterIP      None             <none>        8444/TCP                        109s   app.kubernetes.io/component=app,app.kubernetes.io/instance=kong,app.kubernetes.io/name=gateway                                                                                                      
kong          kong-gateway-manager                 NodePort       10.103.106.63    <none>        8002:31082/TCP,8445:30532/TCP   109s   app.kubernetes.io/component=app,app.kubernetes.io/instance=kong,app.kubernetes.io/name=gateway                                                                                                      
kong          kong-gateway-proxy                   LoadBalancer   10.103.60.0      <pending>     80:32115/TCP,443:32090/TCP      109s   app.kubernetes.io/component=app,app.kubernetes.io/instance=kong,app.kubernetes.io/name=gateway                                                                                                      
kube-system   kube-dns                             ClusterIP      10.96.0.10       <none>        53/UDP,53/TCP,9153/TCP          8m4s   k8s-app=kube-dns 
```

#### Kongへの接続テスト
※フロントにロードバランサがある場合は、そのIPもしくはホスト名を指定すれば良い。
```
$ curl -i http://localhost:32115/
HTTP/1.1 404 Not Found
Date: Tue, 21 Nov 2023 12:00:48 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Content-Length: 52
X-Kong-Response-Latency: 0
Server: kong/3.4.2

{
  "message":"no Route matched with those values"
}
```

#### テスト用のアプリをデプロイ
```
$ kubectl apply -f https://docs.konghq.com/assets/kubernetes-ingress-controller/examples/echo-service.yaml
service/echo created
deployment.apps/echo created
```

#### デプロイ先へのルーティング設定
```
$ echo "
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
 name: echo
 annotations:
   konghq.com/strip-path: 'true'
spec:
 parentRefs:
 - name: kong
 rules:
 - matches:
   - path:
       type: PathPrefix
       value: /echo
   backendRefs:
   - name: echo
     kind: Service
     port: 1027
" | kubectl apply -f -
httproute.gateway.networking.k8s.io/echo created
```

#### ルーティングの確認
Kong GatewayがKubernetes内部で動作するアプリケーションに正しくトラフィックをルーティングできることを確認。
```
$ curl -i http://localhost:32115/echo
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 139
Connection: keep-alive
Date: Tue, 21 Nov 2023 12:02:13 GMT
X-Kong-Upstream-Latency: 1
X-Kong-Proxy-Latency: 0
Via: kong/3.4.2

Welcome, you are connected to node ip-10-0-100-55.
Running on Pod echo-74c66b778-krftv.
In namespace default.
With IP address 10.244.0.6.
```

#### レート制限のプラグインを作成
KongPlugin定義を作成し、Kubernetesリソースにkonghq.com/pluginsアノテーションを付ける。
```
$ echo "
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
 name: rate-limit-5-min
 annotations:
   kubernetes.io/ingress.class: kong
config:
 minute: 5
 policy: local
plugin: rate-limiting
" | kubectl apply -f -
kongplugin.configuration.konghq.com/rate-limit-5-min created
```
#### プラグインをサービスorルートに関連付け
```
$ kubectl annotate service echo konghq.com/plugins=rate-limit-5-min
service/echo annotated
```

#### プラグインの動作確認
レート制限プラグインがリクエストがアップストリームサービスに到達するのを妨げていることを示している。
```
$ for i in `seq 6`; do curl -sv http://localhost:32115/echo 2>&1 | grep "< HTTP"; done
< HTTP/1.1 200 OK
< HTTP/1.1 200 OK
< HTTP/1.1 200 OK
< HTTP/1.1 200 OK
< HTTP/1.1 200 OK
< HTTP/1.1 429 Too Many Requests
```

#### Proxy-CacheのKongClusterPluginの作成
KongPluginは特定のサービスまたはルートに適用されるが、KongClusterPluginは全てのサービスに適用されるグローバルサービスです。
検証のため、GETとHEADリクエストに対するすべてのHTTP 200レスポンスを300秒間キャッシュするKongClusterPluginを作成してみる。
```
$ echo '
apiVersion: configuration.konghq.com/v1
kind: KongClusterPlugin
metadata:
 name: proxy-cache-all-endpoints
 annotations:
   kubernetes.io/ingress.class: kong
 labels:
   global: "true"
plugin: proxy-cache
config:
 response_code:
 - 200
 request_method:
 - GET
 - HEAD
 content_type:
 - text/plain; charset=utf-8
 cache_ttl: 300
 strategy: memory
' | kubectl apply -f -
kongclusterplugin.configuration.konghq.com/proxy-cache-all-endpoints created
```

#### Proxy-Cacheのプラグイン動作確認
最初のリクエストはX-Cache-Status： Missで、
リクエストがアップストリーム・サービスに送られたことを意味するとのこと。
次の4つの応答はX-Cache-Status： Hit
これはリクエストがキャッシュから送られたことを意味する。

最後に注意すべきことは、HTTP 429リクエストがrate-limitプラグインによって返されるとき、X-Cache-Statusヘッダーが表示されないということ。これは rate-limiting が proxy-cache よりも先に実行されるからの理由。

```
$ for i in `seq 6`; do curl -sv http://localhost:32115/echo 2>&1 | grep -E "(Status|< HTTP)"; done
< HTTP/1.1 200 OK
< X-Cache-Status: Miss
< HTTP/1.1 200 OK
< X-Cache-Status: Hit
< HTTP/1.1 200 OK
< X-Cache-Status: Hit
< HTTP/1.1 200 OK
< X-Cache-Status: Hit
< HTTP/1.1 200 OK
< X-Cache-Status: Hit
< HTTP/1.1 429 Too Many Requests
```

#### さっきのアプリに認証を追加する
```
#key-authプラグインを作成する
$ echo "
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
  name: key-auth
plugin: key-auth
config:
  key_names:
  - apikey
" | kubectl apply -f -
kongplugin.configuration.konghq.com/key-auth created

#echoサービスに、前回のrate-limitプラグインに加えて、key-authプラグインを適用する。
$ kubectl annotate service echo konghq.com/plugins=rate-limit-5-min,key-auth --overwrite
service/echo annotated
```

#### APIへアクセスして認証が機能していることの動作確認
キーがないので認証不可となる。
```
$ curl -i http://localhost:32115/echo
HTTP/1.1 401 Unauthorized
Date: Tue, 21 Nov 2023 12:06:31 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
WWW-Authenticate: Key realm="kong"
Content-Length: 45
X-Kong-Response-Latency: 1
Server: kong/3.4.2

{
  "message":"No API key found in request"
}
```

#### キーを作成
Kong Gatewayのキー認証は、consumerオブジェクトを使用する。
キーはconsumerに割り当てられ、クライアント・アプリケーションはリクエストの中で
キー認証をする。
キーはk8s secretとして保存され、consumerはKong Consumer CRDで管理される。
```
#kongCredType=key-authとして新しいシークレットを作成する。
$ kubectl create secret generic alex-key-auth \\
  --from-literal=kongCredType=key-auth \
  --from-literal=key=hello_world
secret/alex-key-auth created

#新しいconsumerを作成し、credentialをアタッチする。
$ echo "apiVersion: configuration.konghq.com/v1
kind: KongConsumer
metadata:
  name: alex
  annotations:
    kubernetes.io/ingress.class: kong
username: alex
credentials:
- alex-key-auth
" | kubectl apply -f -
kongconsumer.configuration.konghq.com/alex created
```

#### 動作確認
```
$ curl -H 'apikey: hello_world' http://localhost:32115/echo
Welcome, you are connected to node ip-10-0-100-55.
Running on Pod echo-74c66b778-krftv.
In namespace default.
With IP address 10.244.0.6.
```

## 参考
k8s情報
```
 kubectl get all -ALL
NAMESPACE      NAME                                         READY   STATUS    RESTARTS   AGE   L
default        pod/echo-74c66b778-krftv                     1/1     Running   0          24m   
kong           pod/kong-controller-7dcb48b75d-2hgx6         1/1     Running   0          27m   
kong           pod/kong-gateway-697fdd75fd-8sd9n            1/1     Running   0          27m   
kube-flannel   pod/kube-flannel-ds-lr6hv                    1/1     Running   0          33m   
kube-system    pod/coredns-5dd5756b68-6w8jn                 1/1     Running   0          33m   
kube-system    pod/coredns-5dd5756b68-lhqp2                 1/1     Running   0          33m   
kube-system    pod/etcd-ip-10-0-100-55                      1/1     Running   0          34m   
kube-system    pod/kube-apiserver-ip-10-0-100-55            1/1     Running   0          34m   
kube-system    pod/kube-controller-manager-ip-10-0-100-55   1/1     Running   0          34m   
kube-system    pod/kube-proxy-vpqft                         1/1     Running   0          33m   
kube-system    pod/kube-scheduler-ip-10-0-100-55            1/1     Running   0          34m   

NAMESPACE     NAME                                         TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                         AGE   L
default       service/echo                                 ClusterIP      10.110.174.199   <none>        1025/TCP,1026/TCP,1027/TCP      24m   
default       service/kubernetes                           ClusterIP      10.96.0.1        <none>        443/TCP                         34m   
kong          service/kong-controller-validation-webhook   ClusterIP      10.105.250.203   <none>        443/TCP                         27m   
kong          service/kong-gateway-admin                   ClusterIP      None             <none>        8444/TCP                        27m   
kong          service/kong-gateway-manager                 NodePort       10.103.106.63    <none>        8002:31082/TCP,8445:30532/TCP   27m   
kong          service/kong-gateway-proxy                   LoadBalancer   10.103.60.0      <pending>     80:32115/TCP,443:32090/TCP      27m   
kube-system   service/kube-dns                             ClusterIP      10.96.0.10       <none>        53/UDP,53/TCP,9153/TCP          34m   

NAMESPACE      NAME                             DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE   L
kube-flannel   daemonset.apps/kube-flannel-ds   1         1         1       1            1           <none>                   33m   
kube-system    daemonset.apps/kube-proxy        1         1         1       1            1           kubernetes.io/os=linux   34m   

NAMESPACE     NAME                              READY   UP-TO-DATE   AVAILABLE   AGE   L
default       deployment.apps/echo              1/1     1            1           24m   
kong          deployment.apps/kong-controller   1/1     1            1           27m   
kong          deployment.apps/kong-gateway      1/1     1            1           27m   
kube-system   deployment.apps/coredns           2/2     2            2           34m   

NAMESPACE     NAME                                         DESIRED   CURRENT   READY   AGE   L
default       replicaset.apps/echo-74c66b778               1         1         1       24m   
kong          replicaset.apps/kong-controller-7dcb48b75d   1         1         1       27m   
kong          replicaset.apps/kong-gateway-697fdd75fd      1         1         1       27m   
kube-system   replicaset.apps/coredns-5dd5756b68           2         2         2       33m 
```

[・Kubernetesにインストールする](https://docs.konghq.com/gateway/3.5.x/install/kubernetes/kubectl/)
[・KICをインストールする](https://docs.konghq.com/kubernetes-ingress-controller/3.0.x/get-started/)
[・helmをインストールする](https://helm.sh/docs/intro/install/)
[・Gateway](https://gateway-api.sigs.k8s.io/api-types/gateway/)
[・GatewayClass](https://gateway-api.sigs.k8s.io/api-types/gatewayclass/)
[・Gateway API](https://gateway-api.sigs.k8s.io/reference/spec/#gateway.networking.k8s.io/v1beta1.GatewayClass)
[・HTTPRoute](https://gateway-api.sigs.k8s.io/api-types/httproute/)
[・Annotations](https://docs.konghq.com/kubernetes-ingress-controller/3.0.x/reference/annotations/#kubernetesioingressclass)