9
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

EKSとRoute 53をExternalDNSで紐付ける

Last updated at Posted at 2020-09-24

はじめに

個人で作成しているサービスにて、ExternalDNSを使って、Route 53からEKSへアクセスできるようになりましたので、ここまでの知見を記事に残します。

このアクセスができる前までは、TerraformでAWSのサービスを構築し、マニフェストファイルにてPod、LoadBalancerの作成までできていました。
このときの構成図は以下のとおりです。
WeatherReport_BeforeExternalDNS.jpg

この構成では、EKSで作成したALBが公開したURLを直接指定することで、サービスにアクセスできます。ただしそのURLは、LoadBalancerで作成されるためランダムになってしまいます。
そのためRoute 53経由でEKSへアクセスできるようにしようと考えました。

前提

以下環境で開発、構築を行っています。

  • Kubernetes: v1.16
  • Terraform: v12.28
  • Route 53でドメイン取得済み

Route 53でドメイン取得時、以下のようにNSタイプ、SQAタイプのみ存在していました。
Route53_NS_SQA_Record.png

ExternalDNSを使用してEKSとRoute 53の紐付け

こちらの記事「Kubernetesを学ぶ意味とは――AWS EKSでクラスタを構築してみよう (1/4)」、「AWS EKSでDNSレコードの作成/変更を楽にする「ExternalDNS」 (1/4)」を参考にして対応しました。
基本的には上記の記事どおりなのですが、この記事ではTerraformを使った場合や、記事公開時と異なる点に対する対応を載せて説明します。

ExternalDNSについて

ExternalDNSの公式サイトによると、ExternalDNSはKubernetesのサービスやIngressをDNSのプロバイダーと同期してくれるツールです。

ExternalDNS synchronizes exposed Kubernetes Services and Ingresses with DNS providers.

この記事でのDNSのプロバイダーはRoute 53となるため、ExternalDNSにて、前提項目でのKubernetesのLoadBalancerとRoute 53を紐付けることを可能にしてくれます。

IAMの設定

参考記事では、以下ロールやポリシーの作成やアタッチを、直接AWSマネジメントコンソール上で行っています。

  1. Route 53に対するポリシーを作成する。
  2. ExternalDNS用IAMロールの信頼関係にセットする信頼ポリシーを作成する。
    この信頼ポリシーにはEKSのワーカーノードのARNをセットする。
  3. ExternalDNS用のIAMロールを作成し、1のポリシー、2の信頼ポリシーをセットする。

ここでは上記1~3をTerraformで行った場合を載せます。なお、3でEKSのワーカーノードのARNが必要になるため、こちらもTerraformでワーカーノード用のIAMロールを作成しておきます。
ロールの作成やポリシーのアタッチについては、こちらの記事「【連載】terraform によるAWS環境構築入門 第2回 ~ 権限管理とモジュール化 ~」を参考にしました。

iam.tf
# 1の対応
resource "aws_iam_policy" "route53-external-policy" {
  name = "route53-external-policy"
  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "route53:ChangeResourceRecordSets"
      ],
      "Resource": [
        "arn:aws:route53:::hostedzone/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "route53:ListHostedZones",
        "route53:ListResourceRecordSets"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}
EOF
}

# EKSのワーカーノードのIAMロール作成
resource "aws_iam_role" "eks_node_group_iam_role" {
  name = "eks-node-group-iam-role"

  assume_role_policy = <<POLICY
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
POLICY
}

# 2の対応
data "aws_iam_policy_document" "route53-externaldns-policy" {
  statement {
    effect = "Allow"
    actions = ["sts:AssumeRole"]
    principals {
      type = "Service"
      identifiers = ["ec2.amazonaws.com"]
    }
  }

  statement {
    effect = "Allow"
    actions = ["sts:AssumeRole"]
    principals {
      type = "AWS"
      #identifiersにEKSワーカーノードのarnをセットする。
      identifiers = ["${aws_iam_role.eks_node_group_iam_role.arn}"]
    }
  }
}

# 3の対応
resource "aws_iam_role" "route53-externaldns-controller" {
  name = "route53-externaldns-controller" 
  assume_role_policy = data.aws_iam_policy_document.route53-externaldns-policy.json
}


# 3の対応
resource "aws_iam_role_policy_attachment" "route53-externaldns-attachment"{
  role = aws_iam_role.route53-externaldns-controller.name
  policy_arn = aws_iam_policy.route53-external-policy.arn
}

マニフェストファイルの作成

kube2iamとは

kube2iamの公式ページでは、「アノテーションに基づいて、IAMクレデンシャルをKubernetesのクラスター内で実行しているコンテナに与える」とあります。

Provide IAM credentials to containers running inside a kubernetes cluster based on annotations.

また参考にしている記事では、以下に記載してあるように、アプリケーションに対して必要なアクセス権を与えられるとあります。

アクセス権限が必要ないアプリケーションに対してアクセス権限が付与されることを防ぐために、クラスタ内で起動しているアプリケーションそれぞれに対して最低限必要なアクセス権限を与えられる「kube2iam」

kube2iamの作成

さっそくkube2iamのマニフェストファイルを作成します。このファイルは参考記事公式ページで紹介されているので、そのファイルを参考に作成します。

kube2iam.yaml
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: kube2iam
  namespace: kube-system
---
apiVersion: v1
items:
  - apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: kube2iam
    rules:
      - apiGroups: [""]
        resources: ["namespaces","pods"]
        verbs: ["get","watch","list"]
  - apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: kube2iam
    subjects:
    - kind: ServiceAccount
      name: kube2iam
      namespace: kube-system
    roleRef:
      kind: ClusterRole
      name: kube2iam
      apiGroup: rbac.authorization.k8s.io
kind: List
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: kube2iam
  namespace: kube-system
  labels:
    app: kube2iam
spec:
  selector:
    matchLabels:
      app: kube2iam
  template:
    metadata:
      labels:
        app: kube2iam
    spec:
      serviceAccountName: kube2iam
      hostNetwork: true
      containers:
        - name: kube2iam
          image: jtblin/kube2iam:latest
          imagePullPolicy: Always
          args:
            - "--auto-discover-base-arn"
            - "--iptables=true"
            - "--host-ip=$(HOST_IP)"
            - "--host-interface=eni+"
            - "--verbose"
          env:
            - name: HOST_IP
              valueFrom:
                fieldRef:
                  fieldPath: status.podIP
          ports:
            - containerPort: 8181
              hostPort: 8181
              name: http
          securityContext:
            privileged: true

ExternalDNSの作成

記事を参考にしたマニフェストファイルを作成した際、以下2点の問題が発生していました。

  1. DeploymentのapiVersionがapiVersion: extensions/v1beta1だが、2020/08/10時点でextensions/v1beta1は使用不可となっている。
  2. ClusterRoleにapiGroupが不足しているため、ExternalDNSのマニフェストファイルapply時にfailed to sync cache: timed out waiting for the conditionが発生する。

1については、apiVersionをapps/v1に変更し、不足しているselectorを追加します。
2については、現象発生時の実際のログは以下のとおりです。

time="2020-08-10T12:39:22Z" level=info msg="Instantiating new Kubernetes client"
time="2020-08-10T12:39:22Z" level=info msg="Using inCluster-config based on serviceaccount-token"
time="2020-08-10T12:39:22Z" level=info msg="Created Kubernetes client https://172.20.0.1:443"
time="2020-08-10T12:40:22Z" level=fatal msg="failed to sync cache: timed out waiting for the condition"

この問題についてExternalDNSにissueがあがっていました。このissueによると、endpointのリソースを持つapiGroupsを加えることで解決できるようです。

After I recreated some nodes, external-dns failed to startup again. It failed after printing the error message "failed to sync cache: timed out waiting for the condition". It seems that endpoints were added and external-dns now requires extra permissions.

Make sure you have added

  • apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get","watch","list"]
to your external-dns ClusterRole. Adding this solved the problem for me.

1、2を含めたマニフェストファイルは以下のとおりです。
また、Deploymentの```iam.amazonaws.com/role```にて、ExternalDNSのIAMロール(route53-externaldns-controller)のARNが必要になります。そのためARNを取得し```iam.amazonaws.com/role```にセットしておきます。

```external-dns-manifest.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:
  labels:
    app: external-dns
  name: external-dns
spec:
  selector:
    matchLabels:
      app: external-dns
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: external-dns
      annotations:
        iam.amazonaws.com/role: $ROUTE53_EXDNS_CONTROLLER_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=the-weather-report.com
        - --provider=aws
        - --policy=sync
        - --aws-zone-type=public
        - --registry=txt
        - --txt-owner-id=example-identifier

マニフェストファイルの適用

上記項目で作成したマニフェストファイルをapplyします。
なお、deployment.yamlはVue、Ruby on Railsを1つのpodとして作成したマニフェストファイルです。

kubectl apply -f ./kube2iam/kube2iam.yaml
kubectl apply -f ./deployment.yaml
kubectl apply -f ./external-dns-manifest.yaml

PodがRunningになっていること、ServiceのLoadBalancerにEXTERNAL-IPが割り当てられていることを確認します。

$ kubectl get pods
NAME                                         READY   STATUS      RESTARTS   AGE
external-dns-xxxxx                           1/1     Running     0          39s
・・・略・・・

$ kubectl get services
NAME                    TYPE           CLUSTER-IP       EXTERNAL-IP                                                                  PORT(S)        AGE
kubernetes              ClusterIP      xxx.xxx.xxx.xxx  <none>                                                                       443/TCP        13m
weatherreport-service   LoadBalancer   xxx.xxx.xxx.xxx ![Route53_Mosaic.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406773/ab4afb9a-a8aa-b3b7-4bd1-af51bf9f0632.jpeg)
 xxxxx.ap-northeast-1.elb.amazonaws.com   80:31619/TCP   61s

ExternalDNSのpodのログを確認し、All records are already up to dateが表示されていれば成功です。

$ kubectl logs external-dns-xxxxx
time="2020-08-11T15:59:20Z" level=info msg="Created Kubernetes client https://172.20.0.1:443"
time="2020-08-11T15:59:29Z" level=info msg="Desired change: UPSERT the-weather-report.com A [Id: /hostedzone/Z11CZOCGRGWAHV]"
time="2020-08-11T15:59:29Z" level=info msg="Desired change: UPSERT the-weather-report.com TXT [Id: /hostedzone/Z11CZOCGRGWAHV]"
time="2020-08-11T15:59:30Z" level=info msg="2 record(s) in zone the-weather-report.com. [Id: /hostedzone/Z11CZOCGRGWAHV] were successfully updated"
time="2020-08-11T16:00:29Z" level=info msg="All records are already up to date"

マネジメントコンソールでRoute 53を見てみると、取得したドメインにAレコード、TXTレコードが作成されていることが分かります。
このAレコードのドメインにアクセスするとアプリケーションが表示されます。
Route53_Mosaic.jpg

ExternalDNSを使ってRoute 53からアクセスできる構成図は以下のとおりです。
WeatherReport_AfterExternalDNS.jpg

おわりに

ExternalDNSを使用し、EKSとRoute 53を紐付ける方法を説明しました。
ExternalDNSを最初見たときには何をやっているのだろうかと疑問でしたが、実際に動かし説明を読むことで、理解が深まりました。
この記事が誰かのお役に立てれば幸いです。

9
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?