3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

KongAdvent Calendar 2024

Day 8

ALB IngressとKong Ingress Controller(KIC)を繋げてALB経由でKICを利用する

Last updated at Posted at 2024-12-07

本記事は「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そのものは多段接続出来ない点である。
20241114101431.png

ただ、Kong GatewayのProxyはIngressではなくServiceで提供されることを考えると、上手くやれば以下のような構成が取れるのではないかと考えた。

20241120152135.png

実際に検証してみる。

前提

検証の前提として以下で実施する。

  • 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"

作成し終わったら、ACMに証明書をインポートする。
20241114104745.png

インポート後、証明書の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(クリックして展開):
dp-alb.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: iptype: 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.yamlproxy.serviceClusterIPを指定したからである。
実際はこのアドレスは使わず、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の仕組みを知っていないとピンと来ないかも知れないので、補足として以下の絵を足しておく。
picture 6

上記の認識通り動くか確認する。
まずブラウザでhttps://kong.eks.hogehoge.infoにアクセスするとKong Managerが表示された。
20241120164846.png

次に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

ingressClassNamekong(KIC)を指定し、hostkuard.eks.hogehoge.infoにアクセスすればkuardのServiceに届くよう指定した。
Ingress作成後、http://kuard.eks.hogehoge.infoにアクセスする。
20241120172644.png

無事にアクセスできた。
Kong ManagerからRouteを確認すると、kuard向けのRouteが適切に作成されていることも確認できる。
20241120172803.png

ということで、ALBとKICの組み合わせは上手く動いた。

おわりに

設定はかなり複雑なところがあるが、設定できてしまえばALBを使いつつKICが使えるという美味しいどこどりが出来るので、EKSでKICを使う場合はこの構成も検討してみてもいいと思う。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?