本記事は「Kong Advent Calendar 2024」の8日目のエントリとして、ALB IngressとKong Ingress Controllerを組み合わせて利用する方法について解説する。
EKSを使っている多くの人はALB Ingress Controllerを使っているのではないかと思う。
これを使うとIngress作成時にALBが自動で作成され、IP制限やACMとの証明書連携などが簡単に出来る優れモノである。
一方で、Kong Gatewayを使っている場合、Kong Ingress Controller(KIC)も非常に便利である。
KICの場合はIngressを作成するとKong Gateway側にService/Routeを自動作成してくれるので、Ingressで公開するサービスのレート制限などが簡単に行えるようになる。
比較するとおおよそ以下のような感じになると思う。
ALB Ingress | KIC | |
---|---|---|
主目的 | Ingress公開時にALBのプロビジョニングも行う | KubernetesリソースとしてKongのエンティティを管理 |
サービスの公開方法 | 個々のALBのエンドポイント | DPのエンドポイント(type:LoadBalancer) |
設定の反映先 | ALB | Kong Gateway |
設定方法 | Annotation | Annotation, CRD(KongIngress) |
設定出来ること | IP制限、ALBの向き先、etc | Plugin、strip-path、プロトコル、etc |
証明書利用の推奨方法 | ACM | cert-manager |
利用シーン | クラスタのアクセスにALBが必要な場合 | Kong Gatewayの設定をKubernetesネイティブに行いたい場合 |
ALB使いたいしKong Gatewayも使いたい、ってなった場合にネックになるのが、Ingressそのものは多段接続出来ない点である。
ただ、Kong GatewayのProxyはIngressではなくServiceで提供されることを考えると、上手くやれば以下のような構成が取れるのではないかと考えた。
実際に検証してみる。
前提
検証の前提として以下で実施する。
- EKSを利用
- ALB Ingress Controllerは導入済み
- Proxyに使うIngressはLet's Encryptの証明書を利用する
- ALB IngressはAWS Certificate Manager(ACM)から証明書を取得する
- Proxyが使うドメインとKICで提供するサービスのドメインは同一のドメインとする(サブドメインは異なる)
ProxyのドメインとKICで提供するサービスのドメインを共通化しているのは、証明書やIngressの用意が大変になるからである。
ドメインを分けたい場合はここでの作成手順で作った後、Ingressをコピーして別ドメイン用にカスタマイズして提供すれば上手くいくんじゃないかと思う。
検証
検証するにあたり、環境によって変更するような値を環境変数で定義しておく。
# ControlPlaneのHelmのリリース名
CP_RELEASE_NAME=kong-aws-cp
# DataPlaneのHelmのリリース名
DP_RELEASE_NAME=kong-aws-dp
# Ingressでアクセスする際のドメイン
DOMAIN=eks.hogehoge.info
証明書の払い出しとACMへの登録
ここでの証明書の払い出しはあくまでも一例であり、これでなくてはならない、というものでもないので参考程度に見て欲しい。
以下、certbot
コマンドを使ってワイルドカード証明書として証明書を作成する。
EMAIL=mymail@hogehoge.info
sudo certbot certonly --manual \
--preferred-challenges dns-01 \
--server https://acme-v02.api.letsencrypt.org/directory \
-m $EMAIL \
-d "*.$DOMAIN"
インポート後、証明書のARNを後で使うので、環境変数に設定する。
CERT_ARN="arn:aws:acm:us-east-1:xxxx:certificate/9d8bbc9b-142c-4329-96d0-xxxx"
ControlPlaneのデプロイ
次にControlPlaneをデプロイする。
ÇontrolPlaneのデプロイは以前紹介した「Kong GatewayをKubernetes上でHybridモードで構築する(cert-manager使用)」の手順でデプロイする。
全く同じ手順なのでvalues.yaml
やその説明については割愛する。
ControlPlane構築後、Ingress Controllerが展開されるがDataPlaneはまだ展開されていないのでProxyが利用できないところも同じとなる。
DataPlaneのデプロイ
DataPlaneをデプロイする。
今回デプロイに使ったvalues.yamlは以下となる。
values.yaml(クリックして展開):
cat <<EOF > ./dp-alb.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
type: ClusterIP
http:
enabled: true
tls:
enabled: true
ingress:
enabled: true
ingressClassName: alb
hostname: "*.$DOMAIN"
path: /*
pathType: ImplementationSpecific
tls:
- hosts:
- proxy.$DOMAIN
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/backend-protocol: HTTPS
alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS": 443},{"HTTP": 80}]'
ingressController:
enabled: false
postgresql:
enabled: false
migrations:
enabled: false
manager:
enabled: false
admin:
enabled: false
EOF
基本的な解説は以前の記事で紹介済みなので、ここではProxyのブロックだけ解説する。
proxy:
enabled: true
type: ClusterIP
ここは従来だとtype: LoadBalancer
だがここではtype: ClusterIP
を指定している。
Proxyとしての公開先はALB Ingressのみであり、クラスタ内通信で十分なのでClusterIP
とした。
なお、当初はALB Ingressの使用で最初のアーキテクチャ図に記載したようにNodePort
で検討していたが、alb.ingress.kubernetes.io/target-type: ip
を指定すればClusterIP
で問題ないのでNodePort
は使用しなかった。
(多分NodePort
でも利用できるが、NodePort
の仕組み上性能面ではClusterIP
に劣る上、セキュリティ観点やポート消費の観点からClusterIP
の方が望ましい)
ingress:
enabled: true
ingressClassName: alb
hostname: "*.$DOMAIN"
ここが一番の肝となる。
Ingressを有効化し、Ingress Controllerにalb
を指定してALB Ingressを利用する。
またhostname
がワイルドカードになっているのも重要で、KICで公開するホスト名がこのhostnameと一致する必要がある。
これが一致しないとIngressがそもそもProxyに転送してくれず、以下のようなエラーを見続けることになる(検証時にハマった)。
HTTP/1.1 404 Not Found
Server: awselb/2.0
次にTLS箇所を見る
tls:
- hosts:
- proxy.$DOMAIN
これを指定しないと443で待ってくれないので指定する。
ワイルドカード証明書を使うようにしていれば、hosts
に書くホスト名は1つあれば十分だが、ワイルドカード証明書を使わない場合はKICで使うホスト名を列挙する感じになると思う。
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/backend-protocol: HTTPS
alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS": 443},{"HTTP": 80}]'
Annotationについては以下の目的で設定している。
-
alb.ingress.kubernetes.io/scheme: internet-facing
:インターネット経由で利用したいのでinternet-facing
を指定 -
alb.ingress.kubernetes.io/backend-protocol: HTTPS
:ProxyにHTTPSで転送するのでHTTPS
を指定 -
alb.ingress.kubernetes.io/certificate-arn: $CERT_ARN
:ACMのARNを指定し、ACM上の証明書を利用 -
alb.ingress.kubernetes.io/target-type: ip
:type: ClusterIP
を利用するために指定 -
alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS": 443},{"HTTP": 80}]'
:ALB側(≠Ingress側)で待ち受けるポートの指定。HTTPでの待ち受けも許可するためにHTTP:80
を指定
ALB側でIP制限をしたい場合などはここに設定を追加する。
values.yaml
による設定が終わったら、DataPlaneをデプロイする。
helm upgrade -i kong-aws-dp kong/kong -f ./dp-alb.yaml -n kong --wait
デプロイ後、Ingressは以下のような感じになる。
$ kubectl get ing -n
NAME CLASS HOSTS ADDRESS PORTS AGE
kong-aws-cp-kong-admin kong kong.eks.hogehoge.info 10.100.73.101 80, 443 6d
kong-aws-cp-kong-manager kong kong.eks.hogehoge.info 10.100.73.101 80, 443 6d
kong-aws-dp-kong-proxy alb *.eks.hogehoge.info k8s-kong-kongawsd-c997c9b60c-xxxx.us-east-1.elb.amazonaws.com 80, 443 37m
KICを使っているKong ManagerとAdminAPIのHOSTS
はDPのProxyのServiceのIPを指している。
これは先程values.yaml
でproxy.service
にClusterIP
を指定したからである。
実際はこのアドレスは使わず、DNS側で宛先を指定する。
ということで、DataPlaneのデプロイが終わったらDNSで*.eks.hogehoge.info
参照時はALBのホストとなるk8s-kong-kongawsd-c997c9b...
を使うよう設定する。
動作確認
他にKICを使っているものがなければ、デプロイ後は以下のように動作するはずだ。
-
https://kong.eks.hogehoge.info
:Kong Managerへのアクセス -
https://kong.eks.hogehoge.info/api
:Admin APIへのアクセス -
https://<kong以外>.eks.hogehoge.info
:Proxyへのアクセス
KICの仕組みを知っていないとピンと来ないかも知れないので、補足として以下の絵を足しておく。
上記の認識通り動くか確認する。
まずブラウザでhttps://kong.eks.hogehoge.info
にアクセスするとKong Managerが表示された。
次にcurlでhttps://kong.eks.hogehoge.info/api
にアクセスすると、Kong Gatewayの設定値が表示された。
$ curl https://kong.eks.hogehoge.info/api
{"version":"3.7.1.2","plugins":{"disabled_on_server":{},"enabled_in_cluster":["prometheus"],"available_on_server":{"statsd":{"version":"3.7.1","priority":11},"bot-detection":{"version":"3.7.1","priority":2500},"aws-lambda":{"version":"3.7.1","priority":750},"request-termination":
:(省略)
Admin APIは/
にアクセスすると設定値を表示するのでこちらも問題なさそうだ。
最後にProxyも確認する。
$ curl https://proxy.eks.hogehoge.info/
{
"message":"no Route matched with those values",
"request_id":"5a60e547d6d64e34fc22783ab4b1d210"
}
こちらも問題なさそうだ。
ここでKIC経由でKong Gatewayを正常に操作出来るか確認するために、ServiceやRouteを足してみる。ここではKongがホスティングしているhttpbinを使って確認する。
curl -s -X POST https://kong.eks.hogehoge.info/api/services \
-H "Content-Type: application/json" \
-d '{
"name":"httpbin-svc",
"url":"https://httpbin.konghq.com"
}'
curl -s -X POST https://kong.eks.hogehoge.info/api/services/httpbin-svc/routes \
-H "Content-Type: application/json" \
-d '{
"name":"httpbin-rt",
"paths":["/httpbin"]
}'
設定後、アクセスすると正常に使えることが分かる。
$ curl https://proxy.eks.hogehoge.info/test/user-agent
{
"user-agent": "curl/8.7.1"
}
KICも使ってみる。Ingressの確認では定番のkuardを利用する。
以下でNamespace、Service、Deploymentを作成する。
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
kuardにアクセスするためのIngressを作成する。
cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kuard
namespace: demo
spec:
ingressClassName: kong
rules:
- host: kuard.$DOMAIN
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kuard
port:
number: 80
EOF
ingressClassName
でkong
(KIC)を指定し、host
でkuard.eks.hogehoge.info
にアクセスすればkuardのServiceに届くよう指定した。
Ingress作成後、http://kuard.eks.hogehoge.info
にアクセスする。
無事にアクセスできた。
Kong ManagerからRouteを確認すると、kuard向けのRouteが適切に作成されていることも確認できる。
ということで、ALBとKICの組み合わせは上手く動いた。
おわりに
設定はかなり複雑なところがあるが、設定できてしまえばALBを使いつつKICが使えるという美味しいどこどりが出来るので、EKSでKICを使う場合はこの構成も検討してみてもいいと思う。