Amazon EKS　ExternalDNSでサービスを公開する

# はじめに
本記事はAmazon EKSでExternalDNSのデプロイを行ない、サービスを公開する方法について記載しています。

Amazon EKSでクラスターを作成し、ドメインを取得してアプリケーションを外部に公開するためには、ドメインとAmazon EKSを紐付ける作業が必要になります。

ExternalDNSを用いてデプロイすることで動的にServiceとドメインの紐付けができます。また、サブドメインの管理も柔軟に行うことができます。

## ExternalDNSの構築
ExternalDNSを使用するためにはドメインの取得が必要です。

ドメインはお名前.comなどのドメイン登録サービスか、Amazon Web Services（AWS）のRoute53で取得します。なお、お名前.com等外部のドメイン登録サービスを利用する場合は、Route53で管理するためネームサーバの設定が必要になります。

本記事ではお名前.comで取得したドメインを、Route53で管理する場合を例に解説します。

### Route53
はじめにAWSコンソール画面からRoute53を選択します。
画面左ペインの「ホストゾーン」を選択し、「ホストゾーンの作成」をクリックします。

![スクリーンショット 2020-07-29 20.21.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/3ef840ed-08f1-e58e-ed28-ad1c45ef29a3.png)

ドメイン名に取得したドメインの名前を入力し、タイプはパブリックホストゾーンを選択して「ホストゾーンの作成」をクリックします。

![スクリーンショット 2020-07-29 20.21.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/f48f145b-172b-411e-81d7-4dab4af6e3d7.png)

作成されたネームサーバの情報は、ドメインを取得したお名前.comなどのドメイン登録サービスのネームサーバ情報に設定します。

![スクリーンショット 2020-07-29 20.22.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/1df444bd-c85e-a03f-2432-708b2a7f8512.png)

### IAMのポリシー設定
ExternalDNSからRoute53を操作するための権限を、IAMのポリシーに設定します。

AWSコンソール画面からIAMを選択します。
画面左ペインのポリシーを選択し、「ポリシーの作成」をクリックします。

![スクリーンショット 2020-07-29 20.25.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/f7d41d79-a874-c27b-d3d0-313b52864051.png)

- ポリシーの作成

```json
{
"Version": "2012-10-17",
 "Statement": [
   {
     "Effect": "Allow",
     "Action": [
       "route53:ChangeResourceRecordSets"
     ],
     "Resource": [
       "arn:aws:route53:::hostedzone/*"
     ]
   },
   {
     "Effect": "Allow",
     "Action": [
       "route53:ListHostedZones",
       "route53:ListResourceRecordSets"
     ],
     "Resource": [
       "*"
     ]
   }
 ]
}
```

### ExternalDNSのIAMロールの作成
ExternalDNS用のIAMロールを作成して、先ほど作成したポリシーとAmazon EKSのノードとの紐付けを行います。

AWSコンソール画面からIAMを選択します。
画面左ペインのロールを選択し、「ロールの作成」をクリックします。

先ほど作成したポリシーをアタッチし、信頼関係を編集します。
信頼関係は以下のアクセスコントロールポリシードキュメントを編集します。
ポリシードキュメントの＜EKSワーカーノードのARN＞は、使用するAmazon EKSノードのロールARNの文字列を記載します。

![スクリーンショット 2020-07-29 20.29.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/a33df4eb-d2be-52bd-9bc4-91dab835d883.png)

- 信頼関係の編集

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    },
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": ＜EKSワーカーノードのARN＞
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```

### マニフェストファイルの作成
デプロイするためのマニフェストファイルを作成します。
annotationsの＜EKSワーカーノードのARN＞は、使用するAmazon EKSノードのロールARNの文字列を、domain-filterの＜取得したドメイン名＞は、取得したドメイン名を記載します。

```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: external-dns
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: external-dns
rules:
- apiGroups: [""]
  resources: ["services"]
  verbs: ["get","watch","list"]
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get","watch","list"]
- apiGroups: ["extensions"]
  resources: ["ingresses"]
  verbs: ["get","watch","list"]
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["list","watch"]
- apiGroups: [""]
  resources: ["endpoints"]
  verbs: ["get","watch","list"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: external-dns-viewer
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: external-dns
subjects:
- kind: ServiceAccount
  name: external-dns
  namespace: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: external-dns
  template:
    metadata:
      labels:
        app: external-dns
      annotations:
        iam.amazonaws.com/role: ＜EKSワーカーノードのARN＞
    spec:
      serviceAccountName: external-dns
      containers:
      - name: external-dns
        image: registry.opensource.zalan.do/teapot/external-dns:latest
        args:
        - --source=service
        - --source=ingress
        - --domain-filter=＜取得したドメイン名＞
        - --provider=aws
        - --policy=sync
        - --aws-zone-type=public
        - --registry=txt
        - --txt-owner-id=example-identifier
```

## ExternalDNSのデプロイ
マニフェストファイルの作成後、kubectlコマンドでデプロイします。
以下のコマンドを実行し、ExternalDNSのデプロイを行ないます。

`$ kubectl apply -f external-dns-manifest.yaml`

以下はサンプルのデプロイメントとサービスになります。
Serviceとドメインが紐づいているのでServiceを介してNginxにアクセスできます。

- sample-deployment.yaml 

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
spec:
  selector:
    matchLabels:
      app: sample-app
  replicas: 2
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
      - name: nginx-container
        image: nginx:latest
        ports:
        - containerPort: 80
```

- sample-service.yaml 

```yaml
apiVersion: v1
kind: Service
metadata:
  name: sample-service
  annotations:
    external-dns.alpha.kubernetes.io/hostname: test.＜取得したドメイン＞
spec:
  type: LoadBalancer
  selector:
    app: sample-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
```

以下のコマンドを実行し、デプロイします。

`$ kubectl apply -f sample-deployment.yaml`
`$ kubectl apply -f sample-service.yaml`

デプロイ後、ブラウザからtest.＜取得したドメイン名＞にアクセスするとNginxのWelcome画面が表示されます。

![スクリーンショット 2020-08-25 1.16.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/23ea6523-4cd0-4ab5-90a8-6dd6180914aa.png)

以下のコマンドを実行し、リアルタイムでドメインの反映を確認できます。
ドメインは約1分程度で反映されます。

`$ while true; do dig test.＜取得したドメイン名＞; sleep 1;done`

# おわりに
以上、Amazon EKSでExternalDNSを用いてデプロイする方法でした。
ExternalDNSを使用することで高速デプロイを実現しますが、HTTPSなどは別途考慮が必要です。