Amazon EKSでAWS Load Balancer Controllerを利用する

# はじめに
Amazon EKSで[AWS Load Balancer Controller](https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.4/)を利用する方法をまとめます。
AWS Load Balancer Controllerは、KubernetesクラスターのElastic Load Balancerの管理ができるコントローラーです。
AWS Load Balancer Controllerを利用するとALBが構築されます。

Mac環境を想定しています。

# 実行環境の準備
1.  AWS CLIの設定
[AWS CloudFormationを動かすためのAWS CLIの設定](https://qiita.com/miyabiz/items/fed11796f0ea2b7608f4)を参考にしてください。

1.  EKSクラスタの構築
[Macでeksctlを利用してAmazon EKSのクラスターを構築する](https://qiita.com/miyabiz/items/75f83237795b0b7d52bd)を参考にしてください。

1.  EKSのコンテキストの設定
[MacにてAmazon EKSの設定をする](https://qiita.com/miyabiz/items/b98d145cedb35dab9524)を参考にしてください。

1.  Helmの設定
[Amazon EKSでHelmを利用する](https://qiita.com/miyabiz/items/453361e6af64b324443c)を参考にしてください。

1.  ArgoCDの設定
[Amazon EKSでArgoCDを利用する](https://qiita.com/miyabiz/items/4022b0aba372a8f2c292)を参考にしてください。

# IAM OIDC IDプロバイダの作成
[クラスター用の IAM OIDC プロバイダーの作成](https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/enable-iam-roles-for-service-accounts.html)を参考にしてください。

1.  クラスター名を設定する

    ```sh
    CLUSTER_NAME=${クラスター名}
    ```

1.  IDプロバイダを作成する

    ```sh
    eksctl utils associate-iam-oidc-provider \
      --cluster ${CLUSTER_NAME} \
      --approve
    ```

1.  IDプロバイダのARNを取得する
1.  
    ```sh
    OIDC_ARN=$(aws iam list-open-id-connect-providers \
      --query "OpenIDConnectProviderList[*].Arn" \
      --output text \
    | grep $( \
      aws eks describe-cluster \
        --name ${CLUSTER_NAME} \
        --query "cluster.identity.oidc.issuer" \
        --output text \
        | cut -d '/' -f 5 \
      )
    )
    ```

1.  IDプロバイダ名を取得する

    ```sh
    OIDC_NAME=$(aws iam get-open-id-connect-provider \
      --query "Url" \
      --output text \
      --open-id-connect-provider-arn \
      $( \
        aws iam list-open-id-connect-providers \
          --query "OpenIDConnectProviderList[*].Arn" \
          --output text \
        | grep $( \
          aws eks describe-cluster \
            --name ${CLUSTER_NAME} \
            --query "cluster.identity.oidc.issuer" \
            --output text \
            | cut -d '/' -f 5 \
          ) \
      )
    )
    ```

# IAMロールの作成

1.  IAMロールを作成する
    ※今回はロール名を`akane-dev-aws-load-balancer-controller-role`にしています。

    ```sh
    ROLE_POLICY=$(echo -n '{
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Principal": {
                    "Federated": "'; echo -n "${OIDC_ARN}"; echo -n '"
                },
                "Action": "sts:AssumeRoleWithWebIdentity",
                "Condition": {
                    "StringEquals": {
                        "'; echo -n "${OIDC_NAME}"; echo -n ':sub": "system:serviceaccount:kube-system:aws-load-balancer-controller"
                    }
                }
            }
        ]
    }')
    aws iam create-role \
      --role-name akane-dev-aws-load-balancer-controller-role \
      --assume-role-policy-document "${ROLE_POLICY}"
    ```

1.  ポリシーを作成する
    ※[ポリシー](https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.4.4/docs/install/iam_policy.json)を参考にしてください。
    ※今回はポリシー名を`akane-dev-aws-load-balancer-controller-policy`にしています。

    ```sh
    aws iam create-policy \
        --policy-name akane-dev-aws-load-balancer-controller-policy \
        --policy-document "$(curl -s https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.4.4/docs/install/iam_policy.json)"
    ```

1.  ポリシーをロールにアタッチする

    ```sh
    export ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
    aws iam attach-role-policy \
      --role-name akane-dev-aws-load-balancer-controller-role \
      --policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/akane-dev-aws-load-balancer-controller-policy
    ```

# 環境設定
※事前にKubernetesクラスターのコンテキストの設定をします。
※ArgoCDのインストールが必要です。

1.  VPC IDを取得する

    ```sh
    aws ec2 describe-tags \
      --filter "Name=tag-value,Values=eksctl-*-cluster/VPC" \
      --query "Tags[].ResourceId" \
      --output text
    ```

1.  IAMロールのARNを取得する
    ※今回はロール名を`akane-dev-aws-load-balancer-controller-role`にしています。

    ```sh
    aws iam get-role \
      --role-name akane-dev-aws-load-balancer-controller-role \
      --query "Role.Arn" \
      --output text
    ```

1.  `aws-load-balancer-controller.yaml`を作成する
    ※targetRevisionは`1.4.4`としていますが、最新バージョンは[eks-charts/stable/aws-load-balancer-controller/](https://github.com/aws/eks-charts/tree/master/stable/aws-load-balancer-controller)で確認してください。
    ※VPCのID、クラスター名、IAMロールのARNを設定します。

    ```yaml:aws-load-balancer-controller.yaml
    apiVersion: argoproj.io/v1alpha1
    kind: Application
    metadata:
      name: aws-load-balancer-controller
      namespace: argocd
      finalizers:
        - resources-finalizer.argocd.argoproj.io
    spec:
      project: default
      source:
        repoURL: 'https://aws.github.io/eks-charts'
        targetRevision: 1.4.4
        chart: aws-load-balancer-controller
        helm:
          parameters:
            - name: region
              value: ap-northeast-1
            - name: vpcId
              value: ${VPCのID}
            - name: clusterName
              value: ${クラスター名}
          values: |
            serviceAccount:
              create: true
              name: aws-load-balancer-controller
              annotations:
                eks.amazonaws.com/role-arn: ${IAMロールのARN}
      destination:
        server: 'https://kubernetes.default.svc'
        namespace: kube-system
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
    ```

1.  クラスターに適用する

    ```sh
    kubectl apply -f aws-load-balancer-controller.yaml
    ```

# [サンプル] guestbookのマニフェスト
※今回は`akane`というNamespaceでguestbookを作成します。
※[guestbook](https://github.com/argoproj/argocd-example-apps/tree/master/guestbook)を参考にしてください。

1.  `akane-namespace.yaml` を作成する
    ```yaml:akane-namespace.yaml
    apiVersion: v1
    kind: Namespace
    metadata:
      name: akane
      labels:
        name: akane
    ```

1.  `akane-ingress.yaml` を作成する

    ```yaml:akane-ingress.yaml
    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
      name: guestbook-ui
      namespace: akane
    spec:
      rules:
      - http:
          paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: guestbook-ui
                port:
                  number: 3000

    ```

1.  `akane-service.yaml` を作成する
    ※[guestbook-ui-svc.yaml](https://raw.githubusercontent.com/argoproj/argocd-example-apps/master/guestbook/guestbook-ui-svc.yaml) を参考にする

    ```yaml:akane-service.yaml
    apiVersion: v1
    kind: Service
    metadata:
      annotations:
      labels:
        app: guestbook-ui
      name: guestbook-ui
      namespace: akane
    spec:
      ports:
      - port: 3000
        protocol: TCP
        targetPort: 80
      selector:
        app: guestbook-ui
      type: NodePort
    ```

1.  `akane-deployment.yaml` を作成する
    ※[guestbook-ui-deployment.yaml](https://raw.githubusercontent.com/argoproj/argocd-example-apps/master/guestbook/guestbook-ui-deployment.yaml) を参考にする

    ```yaml:akane-deployment.yaml
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: guestbook-ui
      namespace: akane
    spec:
      replicas: 1
      revisionHistoryLimit: 3
      selector:
        matchLabels:
          app: guestbook-ui
      template:
        metadata:
          labels:
            app: guestbook-ui
        spec:
          containers:
          - image: gcr.io/heptio-images/ks-guestbook-demo:0.2
            name: guestbook-ui
            ports:
            - containerPort: 80
    ```

1.  クラスターに適用する

    ```sh
    kubectl apply -f akane-namespace.yaml
    kubectl apply -f akane-ingress.yaml
    kubectl apply -f akane-service.yaml
    kubectl apply -f akane-deployment.yaml
    ```

1.  guestbook にアクセスする
    ※ELBが構築されるまで、数分アクセスできないことがあります。

    ```sh
    open "http://$(kubectl get ingress -n akane guestbook-ui | awk '{ print $4 }' | tail -1)"
    ```

# クリーンアップ

1.  クラスターから削除する

    ```sh
    kubectl delete -f akane-deployment.yaml
    kubectl delete -f akane-service.yaml
    kubectl delete -f akane-ingress.yaml
    kubectl delete -f akane-namespace.yaml
    kubectl delete -f aws-load-balancer-controller.yaml
    ```

1.  ポリシーをロールからデタッチする

    ```sh
    export ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
    aws iam detach-role-policy \
      --role-name akane-dev-aws-load-balancer-controller-role \
      --policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/akane-dev-aws-load-balancer-controller-policy
    ```

1.  ポリシーを削除する

    ```sh
    export ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
    aws iam delete-policy \
      --policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/akane-dev-aws-load-balancer-controller-policy
    ```

1.  IAMロールを削除する

    ```sh
    aws iam delete-role \
      --role-name akane-dev-aws-load-balancer-controller-role
    ```

1.  IAM OIDC IDを削除する
    `eksctl delete cluster`を実行するとクラスターと一緒に削除されます。

# 参考
[AWS Load Balancer Controller アドオンのインストール](https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/aws-load-balancer-controller.html)