Mirai Translate TECH BLOG

株式会社みらい翻訳のテックブログ

EKS on Fargate で aws-loadbalancer-controller を有効にするまで

こんにちは。みらい翻訳 SRE チームの jeff です。

現在 SRE で EKS 導入を進めています。

EKS を導入するにあたり、EKS でロードバランサー気軽に扱えるように aws-loadbalancer-controller を有効化しようとしましたが、色々とハマりポイントがあったので備忘として記事にしました。

aws-loadbalancer-controller とは

EKS で使用するロードバランサーを管理してくれるアドオンです。

Ingress リソースを apply すると自動で ALB, NLB を作ってくれます。

github.com

aws-loadbalancer-controller 導入経緯

EKS で LoadBalancer サービスを作成した場合、デフォルトでは自動で ELB の一つである CLB(Classic Load Balancer)が作られます。 現在構築している EKS は Fargate 環境を使用しています。しかし、 Fargate は CLB に対応していません。 そのため EKS が Fargate に対応したロードバランサー (具体的には ALB(Application Load Balancer) ) を作れるように aws-loadbalancer-controller を導入しようというのが今回の経緯になります。

導入の前に

確認環境

以下の環境で導入及び動作確認をしています。

  • EKS on Fargate
  • aws-loadbalancer-controller 2.4.2

前提条件の確認

AWS のマニュアルから EKS で ALB を使えるようにするための前提条件を確認します。

docs.aws.amazon.com

  • 既存の EKS があること
  • クラスタのバージョン
    • それに伴う aws-loadbalancer-controller のバージョン
  • サブネットの状態とサブネットへのタグ付け

導入手順

基本的には以下のマニュアルに沿っていけば導入を進めます。

尚、aws cli & kubectl を使用しています。

docs.aws.amazon.com

aws-loadbalancer-controller を導入する

IAM ロールを作る

aws-loadbalancer-controller のサービスアカウントが ELB を管理するために使用する IAM ロールを作成します。 また、ロールに必要な IAM OIDC プロバイダーとポリシーを作成します。

EKS クラスタ用の IAM OIDC プロバイダーを作成する

$ aws iam create-open-id-connect-provider \
  --url [クラスターのOIDCプロバイダURL] \
  --client-id-list "sts.amazonaws.com" \
  --thumbprint-list [サムプリント]
クラスターのOIDCプロバイダURL 確認

以下のコマンドで確認します。

$ aws eks describe-cluster \
  --name [EKS クラスター名] 
  --query "cluster.identity.oidc.issuer" \
  --output text
サムプリント確認

こちらの手順でサムプリントを確認します

docs.aws.amazon.com

画面から登録

サムプリントの確認が手間な場合はマネジメントコンソールからも登録できます。

  1. AWS マネジメントコンソールの IAM 管理画面で [ID プロバイダ] を開きます
  2. [プロバイダを追加] を押します
  3. プロバイダのタイプを OpenID Connect を選択します
  4. プロバイダURLに上記 クラスターのOIDCプロバイダURL 確認 で取得した URL を入力します。
  5. [サムプリント取得] を押します
  6. 対象者に sts.amazonaws.com を入力します
  7. [プロバイダを追加] を押します

ポリシーの作成

aws-loadbalancer-controller に必要なポリシーを作成します。 以下の json を使用してポリシーを作成します。

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

$ aws iam create-policy \
  --policy-name [ポリシー名] \
  --policy-document file://iam_policy.json

ロールの作成

AssumeRole Policy の用意

AWS のドキュメントに沿って AssumeRole Policy の JSON を用意します。

cat >load-balancer-role-trust-policy.json <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::111122223333:oidc-provider/oidc.eks.region-code.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "oidc.eks.region-code.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE:aud": "sts.amazonaws.com",
                    "oidc.eks.region-code.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE:sub": "system:serviceaccount:kube-system:aws-load-balancer-controller"
                }
            }
        }
    ]
}
EOF
ロールの作成&ポリシーアタッチ

こちらも AWS のドキュメントに沿ってロールの作成とポリシーアタッチを行います。

上記で作成した assume role policy の json を使用して IAM ロールを作成します。

$ aws iam create-role \
  --role-name [IAM ロール名] \
  --assume-role-policy-document file://"load-balancer-role-trust-policy.json"

「ポリシーの作成」で作ったポリシーを先ほど作成したロールにアタッチします。

$ aws iam attach-role-policy \
  --policy-arn [IAM ポリシー ARN] \
  --role-name [IAM ロール名]

EKS クラスターにサービスアカウント作成

aws-loadbalancer-controller のサービスアカウントを作成します。アノテーションに上記で作成したロールを設定してサービスアカウントが IAM ロールを使用できるようにします。

apply する yaml を用意します。

cat >aws-load-balancer-controller-service-account.yaml <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    app.kubernetes.io/component: controller
    app.kubernetes.io/name: aws-load-balancer-controller
  name: aws-load-balancer-controller
  namespace: kube-system
  annotations:
    eks.amazonaws.com/role-arn: [IAM ロール ARN]
EOF

上記で作成した yaml を apply します。

kubectl apply -f aws-load-balancer-controller-service-account.yaml

これで認証周りの準備は OK です。

eksctl を使う

ここまで IAM ロールとサービスアカウントの準備について書きましたが、eksctl を使えばワンコマンドでサクッと作ることができます(ポリシーだけは事前に作成しておきます)。

$ eksctl create iamserviceaccount \
  --cluster=[EKS クラスター名] \
  --namespace=kube-system \
  --name=aws-load-balancer-controller \
  --role-name [IAM ロール名] \
  --attach-policy-arn=[IAM ポリシー ARN] \
  --approve

aws-loadbalancer-controller インストール

認証周りの準備ができたら aws-loadbalancer-controller をインストールします。 Fargate だと cert-manager に対応していないため helm でインストールします。

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

AWS のドキュメント通りでインストールは問題ないですが、Fargate だとリージョンコードと vpcId が必須になります。

$ helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
  -n kube-system \
  --set clusterName=[EKS クラスター名] \
  --set serviceAccount.create=false \
  --set serviceAccount.name=aws-load-balancer-controller \
  --set region=[リージョン名] \
  --set vpcId=[EKS クラスターが属している VPC の Id]

これで aws-loadbalancer-controller のインストールは完了です!

動作確認

aws-loadbalancer-controller のインストールが完了しました。EKS クラスタIngress リソースを特定のアノテーションを付けて作成すると ALB を自動で作ってくれるので試してみます。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/backend-protocol-version: HTTP1
    alb.ingress.kubernetes.io/healthcheck-path: /health
    alb.ingress.kubernetes.io/success-codes: '200'
  name: test
  namespace: test
spec:
  rules:
  - host: hogehoge.com
    http:
      paths:
      - path:
        pathType: ImplementationSpecific
        backend:
          service:
            name: test
            port:
              number: 80

ALB の作成に失敗する

この yaml を apply してみましたがエラーが出てしまいました(エラーメッセージは失念してしまいました)。 pods の状況を確認したところ、pods の起動に失敗していました。

$ kubectl get pods -n kube-system
NAME                                           READY   STATUS    RESTARTS   AGE
aws-load-balancer-controller-xxxxxxxxxx-xxxxx   0/1     ImagePullBackOff   0          10m
aws-load-balancer-controller-xxxxxxxxxx-xxxxx   0/1     ImagePullBackOff   0          10m
coredns-xxxxxxxxxx-xxxxx                       0/1     ImagePullBackOff   0          10m
coredns-xxxxxxxxxx-xxxxx                       0/1     ImagePullBackOff   0          10m

STATUS をみてみるとイメージの Pull に失敗しているようなことがわかります。 さらに describe で pods の情報を見てみます。

$ kubectl describe pod aws-load-balancer-controller-xxxxxxxxxx-xxxxx -n kube-system

(中略)

Events:
  Type     Reason           Age                  From               Message
  ----     ------           ----                 ----               -------
  Warning  LoggingDisabled  6m48s                fargate-scheduler  Disabled logging because aws-logging configmap was not found. configmap "aws-logging" not found
  Normal   Scheduled        5m57s                fargate-scheduler  Successfully assigned kube-system/aws-load-balancer-controller-xxxxxxxxxx-xxxxx to fargate-ip-xxx-xxx-xxx-xxx.us-west-2.compute.internal
  Warning  Failed           3m26s                kubelet            Failed to pull image "602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon/aws-load-balancer-controller:v2.4.3": rpc error: code = Unknown desc = failed to pull and unpack image "602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon/aws-load-balancer-controller:v2.4.3": failed to resolve reference "602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon/aws-load-balancer-controller:v2.4.3": failed to do request: Head "https://602401143452.dkr.ecr.us-west-2.amazonaws.com/v2/amazon/aws-load-balancer-controller/manifests/v2.4.3": dial tcp xxx.xxx.xxx.xxx:443: i/o timeout
  Warning  Failed           40s (x2 over 3m26s)  kubelet            Error: ErrImagePull
  Warning  Failed           40s                  kubelet            Failed to pull image "602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon/aws-load-balancer-controller:v2.4.3": rpc error: code = Unknown desc = failed to pull and unpack image "602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon/aws-load-balancer-controller:v2.4.3": failed to resolve reference "602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon/aws-load-balancer-controller:v2.4.3": failed to do request: Head "https://602401143452.dkr.ecr.us-west-2.amazonaws.com/v2/amazon/aws-load-balancer-controller/manifests/v2.4.3": dial tcp xxx.xxx.xxx.xxx:443: i/o timeout
  Normal   BackOff          25s (x2 over 3m25s)  kubelet            Back-off pulling image "602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon/aws-load-balancer-controller:v2.4.3"
  Warning  Failed           25s (x2 over 3m25s)  kubelet            Error: ImagePullBackOff
  Normal   Pulling          12s (x3 over 5m57s)  kubelet            Pulling image "602401143452.dkr.ecr.us-west-2.amazonaws.com/amazon/aws-load-balancer-controller:v2.4.3"

ネットワーク設定不備

aws-load-balancer-controller のイメージ Pull に失敗する原因としては EKS を構築した VPC では ECR の VPC エンドポイントを使用していたためでした。 ECR への接続はプライベートなネットワークで行われていて、 ECR の VPC エンドポイントが EKS からの接続を許可していませんでした。そのため、ECR の VPC エンドポイントに設定しているセキュリティグループの ingress に EKS クラスタに設定しているセキュリティグループを追加したところ、イメージが Pull されるようになりました。

ただ、これだけでは解決しませんでした・・・

ALB の作成に失敗する2

ingress の apply でエラーは出なくなりましたが、なぜか ALB が作られません。再度 pods の状況を見たところ、 coredns が起動していないことに気づきました。

$ kubectl get pods -n kube-system
NAME                                           READY   STATUS    RESTARTS   AGE
aws-load-balancer-controller-xxxxxxxxxx-xxxxx   1/1     Running   0          15m
aws-load-balancer-controller-xxxxxxxxxx-xxxxx   1/1     Running   0          15m
coredns-xxxxxxxxxx-xxxxx                       0/1     ImagePullBackOff   0          15m
coredns-xxxxxxxxxx-xxxxx                       0/1     ImagePullBackOff   0          15m

aws-load-balancer-controller のログをみてみると、名前解決できていないようなエラーが出ていたので coredns が起動していないことが原因のように思われました。

$ kubectl logs aws-load-balancer-controller-xxxxxxxxxx-xxxxx -n kube-system

{"level":"error","ts":1661560470.824888,"logger":"controller-runtime.manager.controller.ingress","msg":"Reconciler error","name":"test","namespace":"test-namespace","error":"ingress: test-namespace/test: WebIdentityErr: failed to retrieve credentials\ncaused by: RequestError: send request failed\ncaused by: Post \"https://sts.us-west-2.amazonaws.com/\": dial tcp: lookup sts.us-west-2.amazonaws.com on xxx.xxx.xxx.xxx:xxx: read udp xxx.xxx.xxx.xxx:xxxxx->xxx.xxx.xxx.xxx:xxx: read: connection refused"}
{"level":"error","ts":1661560654.9364552,"logger":"controller-runtime.manager.controller.ingress","msg":"Reconciler error","name":"test","namespace":"test-namespace","error":"ingress: test-namespace/test: WebIdentityErr: failed to retrieve credentials\ncaused by: RequestError: send request failed\ncaused by: Post \"https://sts.us-west-2.amazonaws.com/\": dial tcp: lookup sts.us-west-2.amazonaws.com on xxx.xxx.xxx.xxx:xxx: read udp xxx.xxx.xxx.xxx:xxxxx->xxx.xxx.xxx.xxx:xxx: read: connection refused"}

coredns が原因を調べていたところ、coredns は Fargate だとデフォルトから設定変更が必要とのことを公式ドキュメントで確認しました。

docs.aws.amazon.com

coredns を Fargate 用に設定変更

ドキュメント通りに設定すればOKです

profile の作成

$ aws eks create-fargate-profile \
    --fargate-profile-name coredns \
    --cluster-name [EKS クラスタ名] \
    --pod-execution-role-arn [pod 実行 IAM ロール ARN] \
    --selectors namespace=kube-system,labels={k8s-app=kube-dns} \
    --subnets [サブネットID1] [サブネットID1] [サブネットID1]

アノテーションの削除

$ kubectl patch deployment coredns \
    -n kube-system \
    --type json \
    -p='[{"op": "remove", "path": "/spec/template/metadata/annotations/eks.amazonaws.com~1compute-type"}]'

設定を完了したら pod を再起動すれば OK です

$ kubectl rollout restart -n kube-system deployment coredns

動作確認2

再度 ingress を apply すると aws-loadbalancer-controller から作成した ALB が作られているのを確認できます。

$ aws elbv2 \
  describe-load-balancers \
  --query "LoadBalancers[].LoadBalancerName" \
  --region us-west-2 \
  --output text

k8s-eksmanag-test-xxxxxxxx

終わりに

今回は EKS on Fargate で aws-loadbalancer-cotroller を導入して動作確認するところまではまった点を書きました。この後 EKS の構築を terraform 化したのでそのことについて書こうと思います。

みらい翻訳ではSREを募集しています

EKS の構築・運用に興味がある方、その他運用改善など SRE に興味がある方を募集しております。 ご興味がおありの方は下記のリンクからご応募・お問い合わせをお待ちしております。

miraitranslate.com