コンテンツにスキップ

AWS Load Balancer Controller@Ingress Controller

はじめに

本サイトにつきまして、以下をご認識のほど宜しくお願いいたします。


01. AWS Load Balancer Controllerの仕組み

アーキテクチャ

AWS Load Balancer Controller は、aws-load-balancer-controller、TargetGroupBinding、といったコンポーネントから構成されている。

aws-load-balancer-controller は、etcd 上の Ingress のマニフェストを検知し、設定値に応じた AWS ALB や AWS NLB をプロビジョニングし、これらのリスナールールごとにターゲットグループもプロビジョングする。

その後、TargetGroupBinding の設定値を経由して、ALB のターゲットグループと Ingress を紐付ける。

これらにより、Cluster 外からのリクエストを Pod へルーティングできるようにする。

aws_load_balancer_controller_architecture


Nodeをターゲットグループに登録/ドレイン

▼ 登録の流れ

AWS Load Balancer Controller は、以下の仕組みでターゲットグループに新しい Node を登録する。

  1. 対象の Cluster のサブネット内に EC2 Node が起動状態に移行すると、AWS Load Balancer Controller はそれを検知する。
  2. 検知した EC2 Node をターゲットグループに追加する。
  3. EC2 Node が正常になれば、Node へルーティングできるようになる。

▼ ドレイン処理の流れ

AWS Load Balancer Controller は、以下の仕組みでターゲットグループから EC2 Node をドレインする。

  1. EC2 Node が停止状態に移行すると、AWS Load Balancer Controller はそれを検知する。
  2. 検知した EC2 Node をターゲットグループからドレインする。
  3. 停止状態移行中の EC2 Node 以外の Node にリクエストをロードバランシングする。


Serviceタイプ

NodePort Service を使用しなければならない。

AWS Load Balancer Controller を使用しない場合は、NodePort Service のポート番号に合わせて AWS ターゲットグループを作成する必要がある。

AWS Load Balancer Controller を使用する場合は、NodePort Service のポート番号をランダムにしても、そのポート番号を指定する AWS ターゲットグループを AWS Load Balancer Controller がプロビジョニングしてくれる。

パブリックネットワーク
⬇⬆️︎
Amazon Route 53
⬇⬆️︎
# L7ロードバランサー (単一のL7ロードバランサーを作成し、異なるポートを開放する複数のL4ロードバランサーの振り分ける)
AWS Load Balancer ControllerによるAWS ALB
⬇⬆️︎
# L4ロードバランサー
NodePort Service (ポート番号はランダムでよい)
⬇⬆️︎
Pod


01-02. 設計パターン

AWS Load Balancer Controllerを使用しない場合

もし AWS CLB を作成したい場合は、AWS Load Balancer Controller を使用しない。

LoadBalancer Service を作成すると、Amazon EKS 内の cloud-controller-manager が AWS CLB を自動的にプロビジョニングする。


TargetGroupBindingパターン


02. セットアップ

AWS側

▼ 共通

AWS Load Balancer Controller は、サブネットを自動的に検出し、これに AWS ALB をプロビジョニングする。

Ingress で作成する AWS ALB をパブリックサブネットで作成する場合、kubernetes.io/role/elb というタグ (値は 1 または空文字) をすべてのパブリックサブネットに設定する。

一方で、プライベートサブネットで作成する場合、kubernetes.io/role/internal-elb というタグ (値は 1 または空文字) をプライベートサブネットに設定する。

またパブリックサブネットまたはプライベートサブネットのいずれであっても kubernetes.io/cluster/<Amazon EKS Clusterの名前> (値は、複数の Amazon EKS Cluster で共有するサブネットの場合は shared、単一の Amazon EKS Cluster の場合は owned とする) を設定する。

▼ Terraformの公式モジュールの場合

AWS Load Balancer Controller のセットアップのうち、AWS 側で必要なものをまとめる。

ここでは、Terraform の公式モジュールを使用する。

コマンド (例:eksctl コマンド) を使用してもよい。

module "iam_assumable_role_with_oidc_aws_load_balancer_controller" {

  source                        = "terraform-aws-modules/iam/aws//modules/iam-assumable-role-with-oidc"

  version                       = "<バージョン>"

  # AWS Load Balancer ControllerのPodに紐付けるIAMロール
  create_role                   = true
  role_name                     = "foo-aws-load-balancer-controller"

  # Amazon EKS ClusterのOIDCプロバイダーURLからhttpsプロトコルを除いたもの
  provider_url                  = replace(module.eks.cluster_oidc_issuer_url, "https://", "")

  # AWS IAMロールに紐付けるIAMポリシー
  role_policy_arns              = [
    aws_iam_policy.aws_load_balancer_controller.arn
  ]

  # AWS Load Balancer ControllerのPodのServiceAccount名
  # ServiceAccountは、Terraformではなく、マニフェストで定義したほうが良い
  oidc_fully_qualified_subjects = [
    "system:serviceaccount:kube-system:foo-aws-load-balancer-controller"
  ]
}

# GitHubにリクエストを送信し、IAMポリシーのファイルを取得する
data "http" "aws_load_balancer_controller_policy" {
  # チャートバージョンが提供するIAMポリシーのURLを指定する
  # (例) チャートのバージョンが 1.5.3 の場合
  # https://github.com/kubernetes-sigs/aws-load-balancer-controller/blob/v2.5.2/helm/aws-load-balancer-controller/Chart.yaml#L4
  url = "https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.5.2/docs/install/iam_policy.json"
}

# レスポンスからJSONファイルの内容を取得する
resource "aws_iam_policy" "aws_load_balancer_controller" {
  name   = "foo-aws-load-balancer-controller"
  # GitHubリポジトリが返信したIAMポリシーのjsonを設定する
  policy = data.http.aws_load_balancer_controller_policy.response_body
}

別途、AWS Load Balancer Controller の Pod に紐付ける ServiceAccount を作成し、IAM ロールの ARN を設定する。

apiVersion: v1
kind: ServiceAccount
metadata:
  name: foo-aws-load-balancer-controller
  namespace: kube-system
  annotations:
    eks.amazonaws.com/role-arn: <IAMロールのARN>

IRSA により、ServiceAccount に AWS の IAM ロールが紐づく。

aws_load_balancer_controller_irsa

awscli コマンド、eksctl コマンドの場合

AWS Load Balancer Controller のセットアップのうち、AWS 側で必要なものをまとめる。

ここではコマンドを使用しているが、IaC (例:Terraform) を使用してもよい。

(1)

ローカルマシンに IAM ポリシーの json ファイルをダウンロードする。

$ curl -L https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.4.0/docs/install/iam_policy.json -o iam_policy.json
(2)

json ファイルを使用して、ServiceAccount の IAM ロールに紐付けるための IAM ポリシーを作成する。

$ aws iam create-policy \
    --policy-name AWSLoad BalancerControllerIAMPolicy \
    --policy-document file://iam_policy.json
(3)

Amazon EKS Cluster を OIDC プロバイダーとして使用する。

これにより、Amazon EKS Cluster内で認証済みのServiceAccountにIAMロールを紐付けることができるようになる。

$ eksctl utils associate-iam-oidc-provider \
    --region=ap-northeast-1 \
    --cluster=foo-eks-cluster \
    --approve

2022-05-30 23:39:04 []  eksctl version 0.96.0
2022-05-30 23:39:04 []  using region ap-northeast-1
2022-05-30 23:39:05 []  IAM Open ID Connect provider is already associated with cluster "foo-eks-cluster" in "ap-northeast-1"


(4)

AWS Load Balancer Controller の Pod の ServiceAccount と、これに紐づく IAM ロールを作成する。

$ eksctl create iamserviceaccount \
    --cluster=foo-eks-cluster \
    -n kube-system \
    --name=aws-load-balancer-controller \
    --attach-policy-arn=arn:aws:iam::<AWSアカウントID>:policy/AWSLoad BalancerControllerIAMPolicy \
    --override-existing-serviceaccounts \
    --approve
(5)

ServiceAccount を作成できたことを確認する。IAM ロールはコンソール画面から確認する。

$ eksctl get iamserviceaccount \
    --cluster foo-eks-cluster \
    --name foo-aws-load-balancer-controller \
    --namespace kube-system

2022-06-06 13:47:33 []  eksctl version 0.96.0
2022-06-06 13:47:33 []  using region ap-northeast-1
NAMESPACE       NAME                                ROLE ARN
kube-system     foo-aws-load-balancer-controller    arn:aws:iam::<AWSアカウントID>:role/eksctl-foo-eks-cluster-addon-i-Role1-****
$ kubectl get serviceaccount -n kube-system foo-aws-load-balancer-controller -o yaml
---
# 作成されたServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::<AWSアカウントID>:role/eksctl-foo-eks-cluster-addon-i-Role1-****
  creationTimestamp: "2022-05-29T12:59:15Z"
  labels:
    app.kubernetes.io/managed-by: eksctl
  name: foo-aws-load-balancer-controller
  namespace: kube-system
  resourceVersion: "2103515"
  uid: *****
secrets:
  - name: foo-aws-load-balancer-controller-token-****


Kubernetes側

▼ Helmの場合

AWS Load Balancer Controller のセットアップのうち、Kubernetes 側で必要なものをまとめる。

(1)

指定したリージョンに AWS Load Balancer Controller をデプロイする。

この時、事前にマニフェストやeksclt create iamserviceaccountコマンドで作成したServiceAcountをALBに紐付ける。

IRSAの仕組みにより、ServiceAccountを経由してPodとAWS IAMロールが紐づく。

$ helm repo add <チャートリポジトリ名> https://aws.github.io/eks-charts

# FargateにAWS Load Balancer Controllerをデプロイする場合
$ helm install <Helmリリース名> <チャートリポジトリ名>/aws-load-balancer-controller \
    -n kube-system \
    --set clusterName=foo-eks-cluster \
    --set serviceAccount.create=false \
    --set serviceAccount.name=foo-aws-load-balancer-controller \
    --set image.repository=602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/amazon/aws-load-balancer-controller \
    --set region=ap-northeast-1 \
    --set vpcId=vpc-*****


AWS Load Balancer controller installed!
$ helm repo add <チャートリポジトリ名> https://aws.github.io/eks-charts

# EC2にAWS Load Balancer Controllerをデプロイする場合
$ helm install <Helmリリース名> <チャートリポジトリ名>/aws-load-balancer-controller \
    -n kube-system \
    --set clusterName=foo-eks-cluster \
    --set serviceAccount.create=false \
    --set serviceAccount.name=foo-aws-load-balancer-controller \
    --set image.repository=602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/amazon/aws-load-balancer-controller


AWS Load Balancer controller installed!
(2)

AWS Load Balancer Controller がデプロイされ、READY 状態になっていることを確認する。

$ helm list -n kube-system

NAME                            NAMESPACE       REVISION        UPDATED                                 STATUS          CHART                                   APP VERSION
aws-load-balancer-controller    kube-system     2               2022-01-01 00:00:00.309065 +0900 JST    deployed        aws-load-balancer-controller-1.4.2      v2.4.2
$ kubectl get deployment -n kube-system aws-load-balancer-controller

NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
aws-load-balancer-controller   2/2     2            0           22m
(3)

もし、以下のように、53 番ポートへの接続でエラーになってしまう場合は、CoreDNS による名前解決が正しくできていない。

そのため、CoreDNSが正常に稼働しているか否かを確認する。

{
  "level": "error",
  "ts": "*****.*****",
  "logger": "controller-runtime.manager.controller.ingress",
  "msg": "Reconciler error",
  "name": "foo-ingress",
  "namespace": "foo",
  "error": "ingress: foo/foo-ingress: WebIdentityErr: failed to retrieve credentials\ncaused by: RequestError: send request failed\ncaused by: Post \"https://sts.ap-northeast-1.amazonaws.com/\": dial tcp: lookup sts.ap-northeast-1.amazonaws.com on *.*.*.*:53: read udp *.*.*.*:43958->*.*.*.*:53: read: connection refused",
}
(4)

Ingress をデプロイし、Ingress から AWS ALB を自動的に作成させる。

以下の条件を満たす必要がある。


02-02. マニフェスト

マニフェストの種類

AWS Load Balancer Controller は、Deployment (aws-load-balancer-controller) 、Service (aws-load-balancer-controller-webhook-service) 、TargetGroupBinding、MutatingWebhookConfiguration などのマニフェストから構成されている。


Deployment配下のPod

▼ aws-load-balancer-controller

Deployment は、Ingress で alb の IngressClass を指定していること検知して、AWS ALB をプロビジョニングする。

apiVersion: v1
kind: Pod
metadata:
  name: aws-load-balancer-controller
  namespace: kube-system
spec:
  containers:
    - args:
        # AWS ALBでL7ロードバランシングするCluster名を設定する
        - "--cluster-name=foo-cluster"
        # Ingressに紐づけるIngressClassを設定する
        - "--ingress-class=alb"
        # AWS ALBのあるリージョンを設定する
        - "--aws-region=ap-northeast-1"
        # AWS ALBのあるVPCのIDを設定する
        - "--aws-vpc-id=vpc-*****"
      name: aws-load-balancer-controller
      image: public.ecr.aws/eks/aws-load-balancer-controller:v2.4.0
      ports:
        # 内蔵されているwebhookサーバー
        - containerPort: 9443
          name: webhook-server
          protocol: TCP
          # 内蔵されているmetricsサーバー
        - containerPort: 8080
          name: metrics-server
          protocol: TCP


MutatingWebhookConfiguration

Webhook の宛先の Service を決定する。

Service を経由して、aws-load-balancer-controller に内蔵されている webhook サーバーに Webhook リクエストを送信する。

この webhook サーバーが AdmissionResponse を作成し、これを kube-apiserver に返信する。

AdmissionResponse に応じて、kube-apiserver はマニフェストを変更する。

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: aws-load-balancer-webhook
webhooks:
  - admissionReviewVersions:
      - v1beta1
    clientConfig:
      caBundle: ...
      service:
        name: aws-load-balancer-webhook-service
        namespace: kube-system
        path: /mutate-v1-pod
        port: 443
    failurePolicy: Fail
    matchPolicy: Exact
    name: mpod.elbv2.k8s.aws
    namespaceSelector:
      matchExpressions:
        - key: elbv2.k8s.aws/pod-readiness-gate-inject
          operator: In
          values:
            - enabled
    objectSelector:
      matchExpressions:
        - key: app.kubernetes.io/name
          operator: NotIn
          values:
            - aws-load-balancer-controller
    reinvocationPolicy: Never
    rules:
      - apiGroups:
          - ""
        apiVersions:
          - v1
        operations:
          - CREATE
        resources:
          - pods
        scope: "*"
    sideEffects: None
    timeoutSeconds: 30
  - admissionReviewVersions:
      - v1beta1
    clientConfig:
      caBundle: ...
      service:
        name: aws-load-balancer-webhook-service
        namespace: kube-system
        path: /mutate-elbv2-k8s-aws-v1beta1-targetgroupbinding
        port: 443
    failurePolicy: Fail
    matchPolicy: Exact
    name: mtargetgroupbinding.elbv2.k8s.aws
    namespaceSelector: {}
    objectSelector: {}
    reinvocationPolicy: Never
    rules:
      - apiGroups:
          - elbv2.k8s.aws
        apiVersions:
          - v1beta1
        operations:
          - CREATE
          - UPDATE
        resources:
          - targetgroupbindings
        scope: "*"
    sideEffects: None
    timeoutSeconds: 30


Service

AWS Load Balancer Controller が作成された場合に、webhook サーバーに Webhook リクエストを送信する。

ここでは、ClusterIP Service を使用する。

apiVersion: v1
kind: Service
metadata:
  name: aws-load-balancer-webhook-service
  namespace: kube-system
spec:
  ports:
    - name: webhook-server
      port: 443
      protocol: TCP
      targetPort: webhook-server
  selector:
    app.kubernetes.io/instance: aws-load-balancer-controller
    app.kubernetes.io/name: aws-load-balancer-controller
  type: ClusterIP


ServiceAccount

IRSA の仕組みで、Pod と IAM ロールを紐付ける。

apiVersion: v1
kind: ServiceAccount
metadata:
  name: foo-aws-load-balancer-controller
  namespace: kube-system
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::<アカウントID>:role/foo-aws-load-balancer-controller-role
secrets:
  - name: foo-aws-load-balancer-controller-token


TargetGroupBinding

記入中...

kind: TargetGroupBinding
metadata:
  name: foo-target-group-binding
  namespace: foo
spec:
  serviceRef:
    name: foo-service
    port: 80
  targetGroupARN: <ターゲットグループのARN>

alb_targetgroupbinding