【EKS】externalDNSの導入手順

### externalDNSとは？

・Ingressに紐づくドメインに**外部からアクセスできるようRoute53にAレコード(ドメイン--ALB)を追加**してくれる。

全体像
![スクリーンショット 2021-12-23 16.57.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/faa42386-189c-3f52-8aad-ec60c0fc9c30.png)

---
### 導入手順

#### 作るもの
・IAMロール/IAMポリシー（PodからRoute53を操作する権限）
・ServiceAccount(Pod内からPod外のリソースにアクセスするためのロール)
・Pod(externalDNS)

#### 前提
・Route53等でドメインが作成済であること
・Route53のホストゾーンが作成済であること
・AWS Load Balancer ControllerでIngressに紐づくALBが作成できる/されていること

https://qiita.com/crml1206/items/3f5ceeaae27bba033bb1

---
#### 手順１：IAMポリシー作成

・コマンド

```
aws iam create-policy --policy-name externalDNSPolicy --policy-document file://externalDNSPolicy.json
```

・route53を操作する権限付きのポリシー

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

・作成されたことを確認

```
aws iam list policies
```

---
#### 手順２：ServiceAccount＆IAMロールの作成

・「eksctl create iamserviceaccount」で 
　　①k8s内のServiceAccount
　　②ServiceAccountに紐づかせるIAMロール
　の両方を作成できる

※注意点：name、roleName共にキャメルケースはNG。スネークケースで。

```
eksctl create iamserviceaccount
  --cluster <★clusterName★>
  --namespace kube-system
  --name <★serviceaccountname★>
  --role-name <★IAMロール名★>
  --override-existing-serviceaccounts
  --attach-policy-arn <★手順1で作成したIAMポリシーのARN★>
  --approve
```

---
#### 手順３：Pod(externalDNS)作成

・下記のYAMLでPod(externalDNS)を構築する

```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: external-dns
  namespace: kube-system
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/v1
kind: ClusterRoleBinding
metadata:
  name: external-dns-viewer
  namespace: kube-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: external-dns
subjects:
  - kind: ServiceAccount
    name: <★手順２で作成したServiceAccount名★>
    namespace: kube-system
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
  namespace: kube-system
spec:
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: external-dns
  template:
    metadata:
      labels:
        app: external-dns
    spec:
      serviceAccountName: external-dns-sa
      containers:
        - name: external-dns
          image: k8s.gcr.io/external-dns/external-dns:v0.7.6
          args:
            - --source=service
            - --source=ingress
            - --domain-filter=<★ドメイン名★> # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
            - --provider=aws
            - --policy=upsert-only # would prevent ExternalDNS from deleting any records, omit to enable full synchronization
            - --aws-zone-type=public # only look at public hosted zones (valid values are public, private or no value for both)
            - --registry=txt
            - --txt-owner-id=my-hostedzone-identifier
      securityContext:
        fsGroup: 65534 # For ExternalDNS to be able to read Kubernetes and AWS token files
```

---
### Ingressリソース

・[spec.rules.host]に指定されたドメインをexternalDNSが検知してRoute53にレコード追加してくれる。

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hogeapp-back-ingress
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP":80,"HTTPS": 443}]'
    alb.ingress.kubernetes.io/actions.response-404: >
      {"type":"fixed-response","fixedResponseConfig":{"contentType":"text/plain","statusCode":"404","messageBody":"not found"}}

  labels:
    app: nginx-ingress
spec:
  rules:
    - host: <★ドメイン名★>
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: nginx-service
                port:
                  number: 8085

```