はじめに

今回は、EKSのKubernetesクラスターで実行されているアプリケーションを、AWS Load Balancer Controllerを使用して外部へ公開する方法を解説します。

AWS Load Balancer Controller (旧AWS ALB Ingress Controller)は、Kubernetesクラスターの Elastic Load Balancer の管理を支援するコントローラーです。

AWS Load Balancer Controllerで作成したALBとNLBでAmazon EKSでのアプリケーション負荷分散ネットワーク負荷分散を管理します。

AWS Load Balancer Controllerでできること

  1. NLB(Kubernetes Service)の作成
    • クラスター内で1つ以上のPodとして実行されているネットワークアプリケーションを公開します
    • Pod内で動作しているアプリケーションへクラスターの外部から到達可能なようにします
  2. ALB(Kubernetes Ingress)の作成
    • クラスター外からクラスター内ServiceへのHTTPとHTTPSのルートを公開します
    • Serviceに対して、外部疎通できるURL、負荷分散トラフィック、SSL/TLS終端の機能や、名前ベースの仮想ホスティングを提供します

→ AWS Load Balancer Controller は Kubernetes Service と Ingress 両方のコントローラーの AWS のオープンソース実装です

今回すること・ゴール

  1. EKSで作成したKubernetesクラスターのノード上で動作する、リクエスト時の引数で指定した文字列を返すアプリケーション(Pod)を作成します
  2. AWS Load Balancer Controllerで、作成したアプリケーション(Pod)へ外部からアクセスできるようにします
  3. アプリケーションURLへアクセスし、リクエスト時の引数で指定した文字列が返ってこれば成功です

※ Kubernetesの基本用語はこちらにまとめましたので、よければご確認ください
【初心者向け】Kubernetesの基本用語を調べてまとめました

手順

Kubernetes アプリケーションの公開 Part 2: AWS Load Balancer Controller を参考に進めました

0. 前提条件

  • eksctl バージョン 0.109.0 以降をインストール
  • Helm をインストール
  • AWS リージョン us-west-2 (オレゴン) を使用

1. AWS Load Balancer Controller 用の AWS IAM ポリシーの作成

IRSA を使用して、AWS Load Balancer Controller に IAM アクセス許可を提供します
(IAM ポリシーを紐付ける IAM ロールの作成はEKS クラスターの作成で行います)

1. AWS Load Balancer Controller 用の IAM ポリシーをダウンロード

curl -O https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.5.4/docs/install/iam_policy.json

2. ダウンロードしたポリシーを使用して、AWSLoadBalancerControllerIAMPolicy という名前の IAM ポリシーを作成

aws iam create-policy \
--policy-name AWSLoadBalancerControllerIAMPolicy \
--policy-document file://iam_policy.json

2. EKS クラスターの作成

  • eksctl を使用して Amazon EKS クラスターを作成します
  • OIDC IAM プロバイダーの登録、AWS Load Balancer Controller の Service Account の作成、および IAM ロールの作成も同時に行います
  • AWS Load Balancer Controller 用の kube-system 名前空間に aws-load-balancer-controller という名前のKubernetes サービスアカウントを作成し、Kubernetes サービスアカウントに IAM ロールを割り当てます
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
  name: aws-load-balancer-controller-walkthrough
  region: us-west-2
  version: '1.27'
iam:
  withOIDC: true
  serviceAccounts:
    - metadata:
        name: aws-load-balancer-controller
        namespace: kube-system
      attachPolicyARNs: 
        - arn:aws:iam::${AWS_ACCOUNT}:policy/AWSLoadBalancerControllerIAMPolicy # 1 で作成した IAM ポリシーの ARN
managedNodeGroups:
  - name: main-ng
    instanceType: m5.large
    desiredCapacity: 1
    privateNetworking: true

上記のコードを config.yml ファイルに保存し、以下のコマンドを実行

eksctl create cluster -f config.yml

クラスターが稼働していることを確認します。
以下のコマンド実行で、1つの Amazon EKS ノードと 4つの実行中の Pod が確認できます

kubectl get nodes
kubectl get pods -A

3. AWS Load Balancer Controller のインストール

1. AWS Load Balancer Controller が機能するために必要なカスタムリソース定義 (CRD) をインストール

kubectl apply -k \
    "github.com/aws/eks-charts/stable/aws-load-balancer-controller//crds?ref=master"

2. Helm を使用してAmazon EKS の EC2 ノードに AWS Load Balancer Controller をデプロイ

helm repo add eks https://aws.github.io/eks-charts

helm upgrade -i aws-load-balancer-controller eks/aws-load-balancer-controller \
    --namespace kube-system \
    --set clusterName=aws-load-balancer-controller-walkthrough \ # 2 で作成した EKSクラスター名(metadata.name)
    --set serviceAccount.create=false \
    --set serviceAccount.name=aws-load-balancer-controller # 2 で作成した Service Account (iam.serviceAccounts.metadata.name)

3. AWS Load Balancer Controller がインストールされていることを確認します

kubectl get deployment -n kube-system aws-load-balancer-controller

4. テスト用 Service のデプロイ(NLB)

1. Namespace の作成

ServiceをデプロイするNamespaceを作成します

kubectl create namespace apps

2. Service のデプロイと検証

http-echo イメージを使用する Serviceを作成します
${SERVICE_NAME} 変数で定義したService名でリクエストがあったときに、Service名を返すアプリケーション(pod)を5つデプロイします

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ${SERVICE_NAME}
  namespace: ${NS}
  labels:
    app.kubernetes.io/name: ${SERVICE_NAME}
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: ${SERVICE_NAME}
  replicas: 1
  template:
    metadata:
      labels:
        app.kubernetes.io/name: ${SERVICE_NAME}
    spec:
      terminationGracePeriodSeconds: 0
      containers:
        - name: ${SERVICE_NAME}
          image: hashicorp/http-echo
          imagePullPolicy: IfNotPresent
          args:
            - -listen=:3000
            - -text=${SERVICE_NAME}
          ports:
            - name: app-port
              containerPort: 3000
          resources:
            requests:
              cpu: 0.125
              memory: 50Mi
---
apiVersion: v1
kind: Service
metadata:
  name: ${SERVICE_NAME}
  namespace: ${NS}
  labels:
    app.kubernetes.io/name: ${SERVICE_NAME}
spec:
  type: ClusterIP
  selector:
    app.kubernetes.io/name: ${SERVICE_NAME}
  ports:
    - name: svc-port
      port: 80
      targetPort: app-port
      protocol: TCP

上記のコードを service.yml ファイルで保存し、以下のコマンドを実行して5つのServiceをデプロイします

SERVICE_NAME=first NS=apps envsubst < service.yml | kubectl apply -f -
SERVICE_NAME=second NS=apps envsubst < service.yml | kubectl apply -f -
SERVICE_NAME=third NS=apps envsubst < service.yml | kubectl apply -f -
SERVICE_NAME=fourth NS=apps envsubst < service.yml | kubectl apply -f -
SERVICE_NAME=error NS=apps envsubst < service.yml | kubectl apply -f -

3. 結果を確認します

kubectl get pod,svc -n apps

5つのアプリケーション(Pod)が確認できました

NAME                          READY   STATUS    RESTARTS   AGE
pod/error-759546fcc4-n5dqr    1/1     Running   0          60s
pod/first-788fb8b9cd-rxblp    1/1     Running   0          88s
pod/fourth-654655d556-bfh24   1/1     Running   0          64s
pod/second-75bf747744-57vws   1/1     Running   0          72s
pod/third-6b5f65fd67-tsbff    1/1     Running   0          68s

NAME             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
service/error    ClusterIP   10.100.106.108   <none>        80/TCP    59s
service/first    ClusterIP   10.100.165.147   <none>        80/TCP    88s
service/fourth   ClusterIP   10.100.131.147   <none>        80/TCP    63s
service/second   ClusterIP   10.100.249.80    <none>        80/TCP    72s
service/third    ClusterIP   10.100.253.123   <none>        80/TCP    67s

5. Ingress のデプロイ(ALB)

作成したServiceのバックエンドにあるアプリケーション(Pod)へのアクセスを提供するIngressを作成します
Podへ外部からアクセスできるようにします

1. デプロイ

  • ingressClassName を alb に設定することで、AWS Load Balancer Controller がこの Ingress リソースを処理するようにします
  • アノテーションで以下を定義します
    • AWS Load Balancer Controller が作成するロードバランサーの名前
    • ロードバランサーのターゲットタイプが ip であること (Pod の IP アドレスをターゲットとして登録します)
    • ロードバランサーが internet-facing であること
    • ヘルスチェックのパス
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ${NS}-ingress
  namespace: ${NS}
  annotations:
    alb.ingress.kubernetes.io/load-balancer-name: ${NS}-ingress
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/healthcheck-path: /healthz
spec:
  ingressClassName: alb
  defaultBackend:
    service:
      name: error
      port:
        name: svc-port
  rules:
    - http:
        paths:
          - path: /first
            pathType: Prefix
            backend:
              service:
                name: first
                port:
                  name: svc-port
          - path: /second
            pathType: Prefix
            backend:
              service:
                name: second
                port:
                  name: svc-port

AWSマネージメントコンソール → EC2 → ロードバランサーより ALB がデプロイされたのを確認できました

以下のコマンドでもIngressリソースの状態を確認できます

kubectl get ingress -n apps

以下のコマンドではIngressリソースの詳細が確認できます

kubectl describe ingress apps-ingress -n apps

podへの /first /second リスナールールが作成されていることが確認できます
ルールに該当しないものは /errorへ飛びます

Name:             apps-ingress
Labels:           <none>
Namespace:        apps
Address:          apps-ingress-xxx.{region}.elb.amazonaws.com
Ingress Class:    alb
Default backend:  error:svc-port (xxx.xxx.xxx.xx:3000)
Rules:
  Host        Path  Backends
  ----        ----  --------
  *           
              /first    first:svc-port (xxx.xxx.xxx.xx:3000)
              /second   second:svc-port (xxx.xxx.xxx.xx:3000)
Annotations:  alb.ingress.kubernetes.io/healthcheck-path: /healthz
              alb.ingress.kubernetes.io/load-balancer-name: apps-ingress
              alb.ingress.kubernetes.io/scheme: internet-facing
              alb.ingress.kubernetes.io/target-type: ip
Events:
  Type    Reason                  Age                  From     Message
  ----    ------                  ----                 ----     -------
  Normal  SuccessfullyReconciled  3m18s (x2 over 32m)  ingress  Successfully reconciled

2. 動作確認

export ALB_URL=$(kubectl get -n apps ingress/apps-ingress -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
curl ${ALB_URL}
curl ${ALB_URL}/first
curl ${ALB_URL}/first/something
curl ${ALB_URL}/second
curl ${ALB_URL}/third
curl ${ALB_URL}/something

作成したServiceへ外部からアクセスできることが確認できました。

error
first
first
second
error
error

まとめ

AWS Load Balancer Controllerでアプリケーションを公開することができました
今回はパスベースでアクセスできるよう設定しましたが、今後はホストベースでのルーティング設定や external-dnsを使用したRoute53経由での公開も見てみたいと思います

参考リンク