KongのKubernetes Ingress Controllerを試す

はじめに

久しぶりにKongをちょっとお勉強したい気になってきたので、ドキュメントを眺めていたらKubernetes Ingress Controllerなるものを見つけました。面白そうだったので、とりあえず、動かすまでやってみようかと思います。
Kongの基本的なところプラグインの書き方も以前まとめたので興味がある人はよかったら見てみてください。

KongのIngress Controllerについて

KubernetesIngress Controllerでクラスター内でIngressリソースとして動くKongに対して設定と管理を行います。クラスター内のスケーリング、設定の変更、エラーなどのイベントによってKongをアップデートしてくれます。
以下の2つのコンポーネントからなります。

  • Kong本体
  • Controller、Kongの設定を同期する

プラグインを反映させることももちろん可能で、Kongができることは基本的になんでもできるようです。

カスタムリソースについて

いくつかのカスタムリソースが用意されおり、Kongの宣言的な設定を用いてKongの機能を利用することができます。

  • KongPlugin: KongのPluginエンティティ相当の設定を行なうリソース
  • KongIngress: ルーティング、ロードバランシング、ヘルスチェックなど細かなルーティングの設定等を行なうためのリソース
  • KongConsumer: KongのCunsumerエンティティへのマッピング
  • TCPIngress: TCPベースのルーティングを行なうためのリソース。non-HTTPベースのサービスに対して利用可能

使ってみる

今回はMinikube(driver none)にIngressControllerをインストールして、サンプルアプリにプロキシしてみたいと思います。

環境

$ minikube version
minikube version: v1.16.0
commit: 9f1e482427589ff8451c4723b6ba53bb9742fbb1

$ kubectl version -o yaml
clientVersion:
  buildDate: "2021-01-13T13:28:09Z"
  compiler: gc
  gitCommit: faecb196815e248d3ecfb03c680a4507229c2a56
  gitTreeState: clean
  gitVersion: v1.20.2
  goVersion: go1.15.5
  major: "1"
  minor: "20"
  platform: linux/amd64
serverVersion:
  buildDate: "2020-12-08T17:51:19Z"
  compiler: gc
  gitCommit: af46c47ce925f4c4ad5cc8d1fca46c7b77d13b38
  gitTreeState: clean
  gitVersion: v1.20.0
  goVersion: go1.15.5
  major: "1"
  minor: "20"
  platform: linux/amd64

$ docker version
(クライアント略)

Server: Docker Engine - Community
 Engine:
  Version:          20.10.2
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       8891c58
  Built:            Mon Dec 28 16:15:19 2020
  OS/Arch:          linux/amd64
  Experimental:     true
 containerd:
  Version:          1.4.3
  GitCommit:        269548fa27e0089a8b8278fc4fc781d7f65a939b
 runc:
  Version:          1.0.0-rc92
  GitCommit:        ff819c7e9184c13b7c2607fe6c30ae19403a7aff
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

$ uname -srvmpio
Linux 5.4.0-62-generic #70-Ubuntu SMP Tue Jan 12 12:45:47 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

$ lsb_release -a
LSB Version:    core-11.1.0ubuntu2-noarch:security-11.1.0ubuntu2-noarch
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.1 LTS
Release:    20.04
Codename:   focal

アップストリームを作っておく

下準備として、Kongがプロキシすする先のアップストリームを作って置きます。
アップストリームはNginxを用いて2つ作成し、それぞれが自分自身のhostnameを返すようにしておきます。

--dry-runオプションと使ってリソースを作ります。

# deployment.yamlの作成
$ kubectl create deployment nginx-first --image=nginx:1.19.6 --dry-run=client -o yaml > deployment.yaml

$ echo --- >> deployment.yaml 

$ kubectl create deployment nginx-second --image=nginx:1.19.6 --dry-run=client -o yaml >> deployment.yaml


# service.yamlの作成
$ kubectl create service clusterip nginx-first --tcp=8081:80 --dry-run=client -o yaml > service.yaml

$ echo --- >> service.yaml 

$ kubectl create service clusterip nginx-second --tcp=8082:80 --dry-run -o yaml > service.yaml

それぞれできたリソースはここに置いておくので興味があれば確認してみてください。
作成したリソースをApplyしてします。

$ kubectl --context=minikube apply -f deployment.yaml

$ kubectl --context=minikube get po 
NAME                            READY   STATUS    RESTARTS   AGE
nginx-first-d6db6c668-wtmwh     1/1     Running   0          84s
nginx-second-6b8d5c9696-2cj25   1/1     Running   0          84s


$ kubectl --context=minikube apply -f service.yaml
service/nginx-first created
service/nginx-second created

$ kubectl get svc
NAME           TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
kubernetes     ClusterIP   10.96.0.1        <none>        443/TCP    92m
nginx-first    ClusterIP   10.109.148.241   <none>        8081/TCP   44s
nginx-second   ClusterIP   10.105.0.182     <none>        8082/TCP   44s

最後に、それぞれが自分自身のhostnameを返すようにしておきます。

$ kubectl --context=minikube exec -it nginx-first-d6db6c668-wtmwh -- cp /etc/hostname /usr/share/nginx/html/index.html

$ kubectl --context=minikube exec -it nginx-second-6b8d5c9696-2cj25 -- cp /etc/hostname /usr/share/nginx/html/index.html

テストのためにポートフォワードしてそれぞれのNginxにつないで見ます。

# First
$ kubectl --context=minikube port-forward service/nginx-first 8081:8081

$ curl localhost:8081
nginx-first-d6db6c668-wtmwh

# Second
$ kubectl --context=minikube port-forward service/nginx-second 8082:8082

$ curl localhost:8082
nginx-second-6b8d5c9696-2cj25

無事、それぞれのNginxがhostnameを返してくれていますね。
これで準備完了です。

Kubernetes Ingress Controllerのインストール

Minikubeで作ったクラスタにKongのKubernetes Ingress Controllerをインストールします。
ドキュメントに寄るとHelmを使った方法やここからダウンロードしたYamlファイルをアプライスル方法があるみたいです。
今回は後者の方で行こうと思います。

$ kubectl create -f https://bit.ly/k4k8s
namespace/kong created
Warning: apiextensions.k8s.io/v1beta1 CustomResourceDefinition is deprecated in v1.16+, unavailable in v1.22+; use apiextensions.k8s.io/v1 CustomResourceDefinition
customresourcedefinition.apiextensions.k8s.io/kongclusterplugins.configuration.konghq.com created
customresourcedefinition.apiextensions.k8s.io/kongconsumers.configuration.konghq.com created
customresourcedefinition.apiextensions.k8s.io/kongingresses.configuration.konghq.com created
customresourcedefinition.apiextensions.k8s.io/kongplugins.configuration.konghq.com created
customresourcedefinition.apiextensions.k8s.io/tcpingresses.configuration.konghq.com created
serviceaccount/kong-serviceaccount created
Warning: rbac.authorization.k8s.io/v1beta1 ClusterRole is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 ClusterRole
clusterrole.rbac.authorization.k8s.io/kong-ingress-clusterrole created
Warning: rbac.authorization.k8s.io/v1beta1 ClusterRoleBinding is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 ClusterRoleBinding
clusterrolebinding.rbac.authorization.k8s.io/kong-ingress-clusterrole-nisa-binding created
service/kong-proxy created
service/kong-validation-webhook created
deployment.apps/ingress-kong created

むー、Kubernetesのバージョン1.20.xを使ってると、betaじゃなくなったリソースがいくつかあって警告がいくつか出てしまうみたいですね。
まぁ、この辺は一旦気にせずに先に進めようと思います。

デプロイされたコングにアクセスするためのIPを取得して環境変数(KONG_PROXY_IP)にセットしておきます。
この、今後この環境変数を使ってKongにアクセスします。

export KONG_PROXY_IP=$(minikube service -n kong kong-proxy --url | head -1)

デプロイされたKongにアクセスしてみます。

$ curl -i $PROXY_IP
HTTP/1.1 404 Not Found
Date: Sun, 24 Jan 2021 06:20:54 GMT
Content-Type: application/json; charset=utf-8
Connection: keep-alive
Content-Length: 48
X-Kong-Response-Latency: 0
Server: kong/2.2.1

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

Kongから無事レスポンスが返ってきました。
まだなにもKongの設定を行っていないので、NotFoundを返してきますね。

プロキシの設定を記述する

プロキシの設定を記述するには、普通の(?)Ingressのリソースを記述すれば良さそうです。
以下のようなYamlを記述します。

ingress.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: demo
  annotations:
    kubernetes.io/ingress.class: kong
    konghq.com/strip-path: "true"
spec:
  rules:
  - http:
      paths:
      - path: /first
        backend:
          serviceName: nginx-first
          servicePort: 8081
      - path: /second
        backend:
          serviceName: nginx-second
          servicePort: 8082

今回ちょっとだけトリッキーなのはnginxのコンテキストルートはも問題で、/firstなどのパスがそのままアップストリームにプロキシされると困るのでkonghq.com/strip-path: "true"を付与しました。

早速、リソースをApplyしてアクセスしてみます。

$ kubectl apply -f ingress.yaml 

$ curl -i $PROXY_IP/first
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 28
Connection: keep-alive
Server: nginx/1.19.6
Date: Sun, 24 Jan 2021 07:11:05 GMT
Last-Modified: Sun, 24 Jan 2021 05:57:51 GMT
ETag: "600d0c5f-1c"
Accept-Ranges: bytes
X-Kong-Upstream-Latency: 1
X-Kong-Proxy-Latency: 0
Via: kong/2.2.1

nginx-first-d6db6c668-wtmwh

$ curl -i $PROXY_IP/second
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 30
Connection: keep-alive
Server: nginx/1.19.6
Date: Sun, 24 Jan 2021 07:11:06 GMT
Last-Modified: Sun, 24 Jan 2021 05:59:05 GMT
ETag: "600d0ca9-1e"
Accept-Ranges: bytes
X-Kong-Upstream-Latency: 1
X-Kong-Proxy-Latency: 0
Via: kong/2.2.1

nginx-second-6b8d5c9696-2cj25

それぞれ、きちんとプロキシされているようですね。