Kong Gatewayにはデプロイ方法が2種類用意されており、ControlPlane(CP)とDataPlane(DP)を分離せず一緒くたにデプロイするTraditionalモードと、ControlPlaneとDataPlaneを別々に展開するHybridモードがある。
冗長構成などを取りたい場合はHybridモードが推奨されており、Kubernetesで利用する場合は同一のHelmリポジトリを利用してvalues.yamlでモードを指定して別々のリリースとしてデプロイしてあげる必要がある。
このHybridモードをHelmで試そうとした時、かなりハマったので手順を残しておく。
なお、デプロイに使うHelmのvalues.yamlは以下にサンプルが大量にあるので、以下を参考にするのもオススメとなる。
https://github.com/Kong/charts/tree/main/charts/kong/example-values
今回はhybrid-cert-managerをベースにvalues.yamlを作成した。
前提
以下の環境で検証を行った。
- EKS
- cert-managerは導入済み
また、Kong GatewayのデプロイはDBありのHybridモードで行い、証明書は自己署名証明書を使い、管理はcert-managerに任せる。
Kong GatewayへのアクセスはKong Ingress Controller(KIC)経由で行いたいため、Gatewayデプロイ時にあわせてKICもデプロイする。
証明書についてはsharedモードで配布し、クラスタ用に作成した証明書を別のコンポーネントでも使い回すようにする。
なお、HybridモードだとDBが必須となる。
DBがないとCP起動時にin-memory storage can not be used when roleというエラーになるので注意。
構築手順
事前準備
最初にkongという名前のNamespaceを作成する。
kubectl create namespace kong
次に各コンポーネントで利用する証明書を発行するためのcert-managerのリソースを作成する。
cat <<EOF > ./cert.yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: selfsigned-issuer
spec:
selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: my-selfsigned-ca
namespace: kong
spec:
isCA: true
commonName: my-selfsigned-ca
secretName: root-secret
privateKey:
algorithm: ECDSA
size: 256
issuerRef:
name: selfsigned-issuer
kind: ClusterIssuer
group: cert-manager.io
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: my-ca-issuer
namespace: kong
spec:
ca:
secretName: root-secret
EOF
kubectl apply -f ./cert.yaml
自分がハマった点として、kind: ClusterIssuerだけで済まそうとやったらDataPlaneのデプロイ時に以下のようなエラーが出てデプロイに失敗した点がある。
2024/11/12 23:57:00 [error] 1336#0: *153 [lua] data_plane.lua:158: communicate(): [clustering] connection to control plane wss://kong-cp-kong-cluster.kong.svc.cluster.local:8005/v1/outlet?node_id=f40db4df-7de3-4e6c-9465-f9c202ad5ef8&node_hostname=kong-dp-kong-db6b76cfc-hpsjx&node_version=3.7.1 broken: ssl handshake failed: 18: self-signed certificate (retrying after 10 seconds) [kong-cp-kong-cluster.kong.svc.cluster.local:8005], context: ngx.timer
どうもCA証明書が必須のようで、kind: CertificateのisCA: trueがないとこれが出る模様。
cert-managerの設定を変更する場合は上記の点には気をつけた方がいい。
次にControlPlaneとDataPlaneを作成していくが、その前準備として環境変数を設定する。
# ControlPlaneのHelmのリリース名
CP_RELEASE_NAME=kong-aws-cp
# DataPlaneのHelmのリリース名
DP_RELEASE_NAME=kong-aws-dp
# Ingressでアクセスする際のドメイン
DOMAIN=eks.hogehoge.info
ControlPlaneのデプロイ
ControlPlaneのvalues.yamlを作成する。
cat <<EOF > ./cp.yaml
image:
repository: kong/kong-gateway
env:
role: control_plane
cluster_telemetry_listen: 0.0.0.0:8006
database: "postgres"
pg_database: kong
pg_host: ${CP_RELEASE_NAME}-postgresql.kong.svc
pg_user: kong
certificates:
enabled: true
issuer: "my-ca-issuer"
cluster:
enabled: true
commonName: "$DOMAIN"
dnsNames:
- "*.$DOMAIN"
ingressController:
enabled: true
env:
kong_admin_filter_tag: ingress_controller_default
kong_admin_tls_skip_verify: true
kong_admin_url: https://localhost:8444
kong_workspace: default
publish_service: kong/${DP_RELEASE_NAME}-kong-proxy
ingressClass: kong
installCRDs: false
postgresql:
enabled: true
auth:
database: kong
username: kong
migrations:
enabled: true
postUpgrade: true
preUpgrade: true
manager:
enabled: true
type: ClusterIP
annotations:
konghq.com/protocol: https
ingress:
enabled: true
ingressClassName: kong
tls: ${CP_RELEASE_NAME}-kong-cluster-cert
hostname: kong.$DOMAIN
admin:
annotations:
konghq.com/protocol: https
enabled: true
type: ClusterIP
ingress:
annotations:
konghq.com/https-redirect-status-code: "301"
konghq.com/protocols: https
konghq.com/strip-path: "true"
enabled: true
ingressClassName: kong
tls: ${CP_RELEASE_NAME}-kong-cluster-cert
hostname: kong.$DOMAIN
path: /api
enterprise:
enabled: true
cluster:
enabled: true
tls:
enabled: true
servicePort: 8005
containerPort: 8005
clustertelemetry:
enabled: true
tls:
enabled: true
servicePort: 8006
containerPort: 8006
proxy:
enabled: false
portal:
enabled: false
portalapi:
enabled: false
status:
enabled: true
http:
enabled: true
containerPort: 8100
EOF
設定内容について、各ブロックを解説する。
image:
repository: kong/kong-gateway
上記はkong/kong-gatewayイメージを使うことでOSS版ではなくEnterprise版のイメージを使うことを明示している。
env:
role: control_plane
cluster_telemetry_listen: 0.0.0.0:8006
database: "postgres"
pg_database: kong
pg_host: ${CP_RELEASE_NAME}-postgresql.kong.svc
pg_user: kong
上記はKongのパラメータを指定する。
roleにcontrol_planeを指定することでHybridモード、かつデプロイするKongはControlPlaneとして動作することを指示している。
またdatabase: "postgres"でDBありモードでデプロイすることを指示している。
certificates:
enabled: true
issuer: "my-ca-issuer"
cluster:
enabled: true
commonName: "$DOMAIN"
dnsNames:
- "*.$DOMAIN"
上記は証明書をcert-managerに管理させる場合に指定する項目となる。
issuerにcert-managerのIssuerを指定し、cluster.enabledでクラスタに対する証明書を発行している。
ここはcluster以外にもadminやmanagerなどの各コンポーネントごとに証明書の設定を書くことが出来るが、sharedモードで動かす場合は証明書は1つで良いのでclusterのみ指定している。
ingressController:
enabled: true
env:
kong_admin_filter_tag: ingress_controller_default
kong_admin_tls_skip_verify: true
kong_admin_url: https://localhost:8444
kong_workspace: default
publish_service: kong/${DP_RELEASE_NAME}
ingressClass: kong
installCRDs: false
上記はKICをあわせてデプロイするかどうかの設定であり、今回はありでデプロイしている。
ポイントとしてはpublish_service: kong/${DP_RELEASE_NAME}で、どのDPのプロキシを介してKICを利用するかを設定する箇所であり、ここにDPのProxyのServiceを指定する。
今回のデプロイ順序はCP→DPであり、CPのデプロイ直後はDPがデプロイされておらず、Ingressはこのpublish_serviceのServiceが見つからず機能しない。
DPをデプロイした後に初めてIngressは使えるようになる。
installCRDs: falseについては人によってはtrueにした方がいいかもしれない。
(自分の環境は既に入っていたようで、trueにするとエラーが出る)
postgresql:
enabled: true
auth:
database: kong
username: kong
migrations:
enabled: true
postUpgrade: true
preUpgrade: true
上記はPostgresをあわせてデプロイして初期化する設定となる。
Postgresを別で用意する場合はfalseで問題ない。
manager:
enabled: true
type: ClusterIP
annotations:
konghq.com/protocol: https
ingress:
enabled: true
ingressClassName: kong
tls: ${CP_RELEASE_NAME}-kong-cluster-cert
hostname: kong.$DOMAIN
上記はKong Managerのデプロイ設定となる。
ポイントとしてはIngressで使う証明書をtls: ${CP_RELEASE_NAME}-kong-cluster-certと指定しており、これはクラスタ向けにcert-managaerが払い出す証明書を流用している。
admin:
annotations:
konghq.com/protocol: https
enabled: true
type: ClusterIP
ingress:
annotations:
konghq.com/https-redirect-status-code: "301"
konghq.com/protocols: https
konghq.com/strip-path: "true"
enabled: true
ingressClassName: kong
tls: ${CP_RELEASE_NAME}-kong-cluster-cert
hostname: kong.$DOMAIN
path: /api
上記はAdminAPIのデプロイ設定となる。
こちらもKong Managerと同様に証明書を流用している。
AdminAPIについては注意点がいくつかある。
まず、KongManagerアクセス時、AdminAPIを呼び出す関係でドメインが異なるとクロスオリジン的なものになり、自己証明書だとAdminAPIへのアクセスに失敗してWorkspacesの表示が"Data cannot be displayed due to an error."となって表示できない。
そのため、ここではドメインをManagerと統一している。
(設定次第で回避できるっぽいけど、ここでは割愛)
また、ドメインを統一した都合上、パスでアクセス先を変える必要があり、path: /apiを指定し、更に使い勝手を同じようにするためにKICのAnnotationにkonghq.com/strip-path: "true"を追加してAdminAPIアクセス時にAdminAPI側に/apiが渡らないようにしている。
enterprise:
enabled: true
上記はEnterprise版の機能を利用する際に有効化する。
使わない場合はfalseでも問題ない。
cluster:
enabled: true
tls:
enabled: true
servicePort: 8005
containerPort: 8005
clustertelemetry:
enabled: true
tls:
enabled: true
servicePort: 8006
containerPort: 8006
上記はHybridモード固有で、このCPに繋ぐためのエンドポイント公開のために必要な設定となる。
clustertelemetryは無効化しても問題ない。
proxy:
enabled: false
ProxyはDP側で有効化するため明示的にfalseにする。
portal:
enabled: false
portalapi:
enabled: false
status:
enabled: true
http:
enabled: true
containerPort: 8100
その他、不要そうなものは明示的にfalseにした。
statusについてはReadiness/Liveness Proveで利用しており、これを無効化するとPodが1/2のままで止まってしまうので有効化している。
(サンプルを見ているとなくても1/2にならないようなので、設定次第では不要かもしれない)
ControlPlaneをデプロイする。
helm upgrade -i $CP_RELEASE_NAME kong/kong -f ./cp.yaml -n kong --wait
DataPlaneのデプロイ
DataPlaneのvalues.yamlを作成する。
cat <<EOF > ./dp.yaml
image:
repository: kong/kong-gateway
env:
role: data_plane
database: "off"
cluster_control_plane: ${CP_RELEASE_NAME}-kong-cluster.kong.svc:8005
cluster_telemetry_endpoint: ${CP_RELEASE_NAME}-kong-clustertelemetry.kong.svc.cluster.local:8006
cluster:
enabled: true
tls:
enabled: true
certificates:
enabled: true
issuer: "my-ca-issuer"
cluster:
enabled: true
commonName: "$DOMAIN"
dnsNames:
- "*.$DOMAIN"
proxy:
enabled: true
http:
enabled: true
ingress:
enabled: false
tls:
enabled: true
type: LoadBalancer
ingressController:
enabled: false
postgresql:
enabled: false
migrations:
enabled: false
manager:
enabled: false
admin:
enabled: false
EOF
こちらでも各ブロックを解説する。
env:
role: data_plane
database: "off"
cluster_control_plane: ${CP_RELEASE_NAME}-kong-cluster.kong.svc:8005
cluster_telemetry_endpoint: ${CP_RELEASE_NAME}-kong-clustertelemetry.kong.svc.cluster.local:8006
HybridモードでDataPlaneをデプロイする場合はroleにdata_planeを指定する。
またcluster_control_planeは必須で、cluster_telemetry_endpointはなくても動きそうだがエラーが出るので先程のControlPlaneのServiceを宛先として指定する。
cluster:
enabled: true
tls:
enabled: true
certificates:
enabled: true
issuer: "my-ca-issuer"
cluster:
enabled: true
commonName: "eks.$DOMAIN"
dnsNames:
- "*.$DOMAIN"
ControlPlaneと同じように証明書を作成する。
proxy:
enabled: true
http:
enabled: true
ingress:
enabled: false
tls:
enabled: true
type: LoadBalancer
Proxyの設定になるが、ここではhttp,https両方でアクセスできるようにし、かつtype: LoadBalancerでProxyを外部に公開する。
KICはこの公開されたアドレスを使ってIngress処理を行う。
こちらもhelmコマンドで以下のようにデプロイする。
helm upgrade -i kong-aws-dp kong/kong -f ./dp.yaml -n kong
動作確認
DPのデプロイ後、必要に応じてtype: LoadBalancerで公開されたアドレスをDNS登録する。
変更が浸透したらアクセスしてみる。
curl kong.$DOMAIN/api -L -k
問題なければAdminAPIが出力したJSONが表示されるはずだ。
問題なく表示された。
なお、最初からServiceなどが複数あるのはKICが入っていて、勝手にGatewayに追加してくれてるからである。
ということで、ドキュメントだけでvalues.yamlを書くのは大変なので、Hybridモードで構築しようとする人の助けになれば幸いである。
