Amazon EKSでExternalDNSを利用する

# はじめに
Amazon EKSで[ExternalDNS](https://github.com/kubernetes-sigs/external-dns)を利用する方法をまとめます。
ExternalDNSは、パブリック DNS サーバーを介して Kubernetes リソースを検出します。

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)を参考にしてください。

# Route53のホストゾーンの作成

1.  Route53のホストゾーンを作成する
    ※今回は `dev-akane.com` というドメイン名にする
    ```sh
    aws route53 create-hosted-zone \
      --name dev-akane.com \
      --caller-reference `date +%Y-%m-%d_%H-%M-%S`
    ```

# 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を取得する

    ```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-external-dns-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:default:external-dns"
                    }
                }
            }
        ]
    }')
    aws iam create-role \
      --role-name akane-dev-external-dns-role \
      --assume-role-policy-document "${ROLE_POLICY}"
    ```

1.  ポリシーを作成する
    ※[IAMポリシー](https://github.com/kubernetes-sigs/external-dns/blob/master/docs/tutorials/aws.md#iam-policy)を参考にしてください。
    ※今回はポリシー名を`akane-dev-external-dns-policy`にしています。

    ```sh
    aws iam create-policy \
        --policy-name akane-dev-external-dns-policy \
        --policy-document \
    '{
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "route53:ChangeResourceRecordSets"
          ],
          "Resource": [
            "arn:aws:route53:::hostedzone/*"
          ]
        },
        {
          "Effect": "Allow",
          "Action": [
            "route53:ListHostedZones",
            "route53:ListResourceRecordSets"
          ],
          "Resource": [
            "*"
          ]
        }
      ]
    }'
    ```

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

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

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

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

    ```sh
    aws iam get-role \
      --role-name akane-dev-external-dns-role \
      --query "Role.Arn" \
      --output text
    ```

1.  `external-dns.yaml`を作成する
    ※targetRevisionは`6.7.5`としています。
    ※IAMロールのARN、Route53のドメイン名を設定します。

    ```yaml:external-dns.yaml
    apiVersion: argoproj.io/v1alpha1
    kind: Application
    metadata:
      name: external-dns
      namespace: argocd
      finalizers:
        - resources-finalizer.argocd.argoproj.io
    spec:
      project: default
      source:
        repoURL: 'https://charts.bitnami.com/bitnami'
        targetRevision: 6.7.5
        helm:
          values: |
            aws:
              zoneType: public
            txtOwnerId: my-hostedzone-identifier
          parameters:
            - name: "rbac.serviceAccountAnnotations.eks\\.amazonaws\\.com/role-arn"
              value: "${IAMロールのARN}"
            - name: "domainFilters[0]"
              value: "${Route53のドメイン名}"
        chart: external-dns
      destination:
        server: 'https://kubernetes.default.svc'
        namespace: default
      syncPolicy:
        automated:
          prune: true
    ```

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

    ```sh
    kubectl apply -f external-dns.yaml
    ```

# クリーンアップ

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

    ```sh
    kubectl delete -f external-dns.yaml
    ```

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

    ```sh
    export ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
    aws iam detach-role-policy \
      --role-name akane-dev-external-dns-role \
      --policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/akane-dev-external-dns-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-external-dns-policy
    ```

1.  IAMロールを削除する

    ```sh
    aws iam delete-role \
      --role-name akane-dev-external-dns-role
    ```

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

1.  Route53のホストゾーンを削除する

    ```sh
    aws route53 delete-hosted-zone \
      --id $( \
        aws route53 list-hosted-zones-by-name \
          --dns-name dev-akane.com \
          --query "HostedZones[0].Id" \
          --output text \
          | sed "s#/hostedzone/##" \
      )
    ```