はじめに

今回は、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 ポリシーをダウンロード

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

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

1
2
3
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 ロールを割り当てます
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
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 ファイルに保存し、以下のコマンドを実行

1
eksctl create cluster -f config.yml

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

1
2
kubectl get nodes
kubectl get pods -A

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

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

1
2
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 をデプロイ

1
2
3
4
5
6
7
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 がインストールされていることを確認します

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

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

1. Namespace の作成

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

1
kubectl create namespace apps

2. Service のデプロイと検証

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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をデプロイします

1
2
3
4
5
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. 結果を確認します

1
kubectl get pod,svc -n apps

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

01
02
03
04
05
06
07
08
09
10
11
12
13
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 であること
    • ヘルスチェックのパス
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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リソースの状態を確認できます

1
kubectl get ingress -n apps

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

1
kubectl describe ingress apps-ingress -n apps

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
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. 動作確認

1
2
3
4
5
6
7
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へ外部からアクセスできることが確認できました。

1
2
3
4
5
6
error
first
first
second
error
error

まとめ

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

参考リンク