ExternalDNS + Route 53 Auto Naming(Service Discovery)でEKSのServiceをRoute53で名前解決してみる

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、かたいなかです。

レガシーシステムをEKSに段階を踏んで移行していく際、途中の段階でレガシーシステム側からEKSのサービスにアクセスさせたいことがあります。そんなとき、名前解決をしたいが手動で管理するのも面倒だ、と悩んでいる方も多いのではないでしょうか。

そこで今回はExternalDNSというツールを用いてサービスに対して特定のFQDNを紐付けて名前解決を行えるようにする方法をご紹介します。

ExternalDNSとは

ExternalDNSはKubernetesのServiceやIngressをDNSプロバイダに対して同期してくれるツールです。これを用いることで、Kubernetesのリソースを用いてDNSのレコードをDNSプロバイダに依存しない形で動的に管理できます。

今回はExternalDNSをRoute53のAuto Namingと合わせて用いることで、ServiceをFQDNと結びつけ、VPC内でPrivate DNSによる名前解決ができるようにしていきます。

ServiceDiscoveryの準備をする

まず、Route53の設定を行っていきます。Auto Namingがマネジメントコンソールに対応していないため、AWS CLIから作業を行っていきます。

今回はオレゴンリージョンにEKSクラスタが存在することを前提に、オレゴンリージョンで作業を行っていきます。

$ export AWS_DEFAULT_REGION=us-west-2

今回は、VPC内でのPrivateなDNSとして使用するため、EKSのワーカーノードがデプロイされているVPCと結びつけたprivate-dns-namespaceを作成します。

$ aws servicediscovery create-private-dns-namespace --name eks-services.katainaka.org --vpc vpc-XXXXXX
$ aws servicediscovery list-namespaces
{
    "Namespaces": [
        {
            "Type": "DNS_PRIVATE",
            "Id": "ns-XXXXXXXXXXXXXXXX",
            "Arn": "arn:aws:servicediscovery:us-west-2:XXXXXXXXXXXX:namespace/ns-XXXXXXXXXXXXXXXX",
            "Name": "eks-services.katainaka.org"
        }
    ]
}

さらに、ExternalDNSからAレコードを登録させるサービスを作成します。 今回はnginx.eks-services.katainaka.orgという名前でIPアドレスを解決できるようにしたいので、nginxという名前を指定して以下のように作成します。

$ aws servicediscovery create-service --name nginx --dns-config 'NamespaceId=ns-XXXXXXXXXXXXXX,DnsRecords=[{Type=A,TTL=60}]'
$ aws servicediscovery list-services
{
    "Services": [
        {
            "Description": "heritage=external-dns,external-dns/owner=default,external-dns/resource=service/default/nginx",
            "Id": "srv-XXXXXXXXXXXXX",
            "Arn": "arn:aws:servicediscovery:us-west-2:XXXXXXXXXXXX:service/srv-XXXXXXXXXXXXX",
            "Name": "nginx"
        }
    ]
}

ここまでで、Route53の設定は終わりです。

EKSのワーカーノードにRoute53に対しての権限を与える

ExternalDNSがRoute53のレコードを修正できるようにするため、EKSのワーカーノードに対してRoute53のレコードを操作する権限を与える必要があります。

今回はAmazonRoute53AutoNamingFullAccessというAWS管理ポリシーをEKSのワーカーノードのIAMロールにアタッチしておきます。

ExternalDNSをデプロイする

ServiceAccount,ClusterRole,ClusterRoleBindingを準備

登録されたServiceやIngressの情報を元にRoute53にレコードを登録できるようにするため、ExternalDNSのPodにKubernetesのServiceの一覧を取得したりする権限を与える必要があります。 そのため必要になる、ServiceAccountとClusterRole、さらにそれらを組み合わせるClusterRoleBindingを先にデプロイします。

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"]
---
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

ExternalDNSをデプロイ

先に作成したServiceAccountを指定してExternalDNSをDeploymentとしてデプロイします。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: external-dns
  name: external-dns
spec:
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: external-dns
    spec:
      serviceAccountName: external-dns # 作成したServiceAccountを指定
      containers:
      - name: external-dns
        image: registry.opensource.zalan.do/teapot/external-dns:latest
        args:
        - --source=service # serviceとingressを対象にする
        - --source=ingress
        - --domain-filter=eks-services.katainaka.org #namespaceと同じ値を持つドメインの場合は登録する
        - --provider=aws-sd #Route53のServiceDiscoveryに登録
        - --aws-zone-type=private #privateのdns-namespaceを使用するため。publicのものを使用する場合はpublic
        - --txt-owner-id=my-cluster-id
        env:
        - name: AWS_REGION
          value: us-west-2

動作を確認してみる

ExternalDNSがデプロイされたところで、以下のようなServiceを作成します。

Serviceのexternal-dns.alpha.kubernetes.io/hostnameというannotationでRoute53に登録するFQDNを指定しています。

apiVersion: v1
kind: Service
metadata:
  name: nginx
  annotations:
    external-dns.alpha.kubernetes.io/hostname: nginx.eks-services.katainaka.org #service名.namespace名
spec:
  type: NodePort #LoadBalancerも可能
  ports:
  - port: 80
    name: http
    targetPort: 80
  selector:
    app: nginx
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx
spec:
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
        ports:
        - containerPort: 80
          name: http

EKSが作成されたVPCにEC2インスタンスを作成、SSHでログインして名前解決できていることを実際に確認してみます。

[ec2-user@ip-192-168-69-50 ~]$ dig nginx.eks-services.katainaka.org

; <<>> DiG 9.9.4-RedHat-9.9.4-61.amzn2.1.1 <<>> nginx.eks-services.katainaka.org
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 36647
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;nginx.eks-services.katainaka.org. IN   A

;; ANSWER SECTION:
nginx.eks-services.katainaka.org. 60 IN A   192.168.149.170
nginx.eks-services.katainaka.org. 60 IN A   192.168.86.1

;; Query time: 2 msec
;; SERVER: 192.168.0.2#53(192.168.0.2)
;; WHEN: 月 10月 01 06:55:03 UTC 2018
;; MSG SIZE  rcvd: 93

nginx.eks-services.katainaka.orgというFQDNからプライベートIPアドレスに解決できています。

また、external-dns.alpha.kubernetes.io/hostnameをアノテーションで指定したServiceを削除してみます。

$ kubectl delete service nginx

この状態で名前解決を試みると

[ec2-user@ip-192-168-69-50 ~]$ dig nginx.eks-services.katainaka.org

; <<>> DiG 9.9.4-RedHat-9.9.4-61.amzn2.1.1 <<>> nginx.eks-services.katainaka.org
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 63690
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;nginx.eks-services.katainaka.org. IN   A

;; AUTHORITY SECTION:
eks-services.katainaka.org. 57  IN  SOA ns-1536.awsdns-00.co.uk. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400

;; Query time: 0 msec
;; SERVER: 192.168.0.2#53(192.168.0.2)
;; WHEN: 月 10月 01 07:37:20 UTC 2018
;; MSG SIZE  rcvd: 148

プライベートアドレスが解決されなりました。Aレコードが削除されています。

まとめ

Route53のAuto NamingとExternalDNSを用いてVPCの中からEKSのサービスに対して特定のFQDNで名前解決できるようにできました。

今回はプライベートなnamespaceによるVPC内での名前解決のためのものでしたがパブリックなnamespaceを用いればPublicなHostedZoneに登録できますし、Auto Namingを使用せずRoute53のPublic/Private Hosted Zoneを使用することももちろん可能です。

また、Route53に限らず、Google CloudDNSやAzureDNS、CloudFlare、DigitalOcean等にも対応できるようなのでEKSに限らず便利に使えるのではないでしょうか。

参考資料