ExternalDNSでPrivate Hosted ZoneとPublic Hosted Zoneにレコードを出し分ける

2018.11.24

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

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

ExternalDNSを使用するとKubernetesのYAML内でドメイン名を指定するだけで、Route53のHosted Zoneにレコードが登録でき便利です。

実際にExternalDNSを使用していたのですが、外部向けELBのエンドポイントはPublic Hosted Zoneに、内部向けELBのエンドポイントはPriate Hosted Zoneにレコードを登録させたい状況が発生しました。

その際、ExternalDNSの--annotation-filterというオプションを指定して適切なHosted Zoneへレコードを登録させるようにしたので、その際の内容を備忘録として記事にまとめます。

環境

  • Kubernetes バージョン 1.10
  • EKS プラットフォームバージョン eks.2
  • kube2iam 0.10.4
    • ワーカーノードとなるEC2インスタンスのロールが汚れるのが嫌なので先に導入しています。 EKSへの導入手順はこちらの記事を参考にしました。
  • ExternalDNS 0.5.8

今回やりたいこと

図のように、LoadBalancer Serviceが作成するELBのエンドポイントに対して、

  • 内部向けELBはエンドポイントをPrivate Hosted ZoneにALIASレコードとして登録します。
  • 外部向けのELBはエンドポイントをPublic Hostex ZoneにALIASレコードとして登録します。

Hosted Zoneの作成

Record Setの登録先となるHosted Zoneを先に作成しておきます。下のようなコマンドを使用して作成しました。

$ aws route53 create-hosted-zone \
    --name <ドメイン名> \
    --caller-reference <任意の値> # public hosted zoneの作成
$ aws route53 create-hosted-zone \
    --name <ドメイン名> \
    --vpc 'VPCRegion=<region>,VPCId=<vpc-id>' \
    --caller-reference <任意の値> # private hosted zoneの作成

ExternalDNSのデプロイ

次にExternalDNSをデプロイしていきます。

基本的には、こちらのExternalDNSのドキュメントに沿って進めましたが、Hosted Zoneの振り分けおよびkube2iamを使用するため、2点変更を行いました。

  • External DNS用IAMロールをkube2iamで使用できるようにするため、信頼関係のポリシーを少し変更しました。
  • Deploymentについては、以下で紹介する設定のように--annotation-filterというオプションを使用してPublic用とPrivate用の2つを作成するようにしました。

変更した点に絞って設定内容を紹介します。

External DNS用IAMロールの作成

ExternalDNSに必要な権限を持ち、kube2iamでExternalDNSのPodに割り当てられるようにロールを作成していきます。

IAMロールには、こちらのExternalDNSのドキュメントに従い以下のようなポリシードキュメントで表されるような権限をもつIAM Policyをアタッチしました。

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

また、このロールをkube2iamでPodに割り当てることができるようにするため、Trust Policyはこちらの記事を参考に以下のようにしました。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    },
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "<ワーカーノードインスタンスのロールARN>"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Public Hosted Zoneに外部向けELBのRecord Setを登録させる設定

ExternalDNSのDeploymentの設定です。振り分けを行うため、2つ作成するDeploymentのうちのPublic Hosted Zone用の設定です。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: external-dns-public
spec:
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: external-dns-private
      annotations:
        iam.amazonaws.com/role:  <先に作成しておいたExternalDNSに必要な権限を持ったIAMロール名 (not 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=<Hosted Zoneのドメイン名>
        - --provider=aws
        - --policy=upsert-only
        - --aws-zone-type=public # Public Hosted Zoneに登録
        - --registry=txt
        - --annotation-filter=service.beta.kubernetes.io/aws-load-balancer-internal=false # 外部向けELBのみ対象
        - --txt-owner-id=my-identifier

Public Hosted Zone用のDeploymentではExternalDNSのオプションとして以下のものを指定しました。

  • --aws-zone-type=privateと指定し、Public Hosted Zoneに登録
  • --annotation-filter=service.beta.kubernetes.io/aws-load-balancer-internal=falseと指定し、外部向けELBのみ対象
    • annotation-filterでこのように指定したため、外部向けELBを作成するLoadBalancer Serviceでは、 アノテーションで、service.beta.kubernetes.io/aws-load-balancer-internal:"false"と明示的に外部向けLBであることを指定する必要があります。
    • 今回はLoadBalancer Serviceが対象のため、service.beta.kubernetes.io/aws-load-balancer-internalというアノテーションを指定していますが、Ingressの場合はkubernetes.io/ingress.classというアノテーションを指定すると良いようです(参考)。

また、Deploymentのannotationでiam.amazonaws.com/role: external-dns-iam-roleのように指定し、ExternalDNSに必要な権限を持ったIAMロールをPodに割り当てるようにしました。

Private Hosted Zoneに内部向けELBのRecord Setを登録させる設定

振り分けを行うため、2つ作成するDeploymentのうちのPrivate Hosted Zone用の設定です。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: external-dns-private
spec:
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: external-dns-private
      annotations:
        iam.amazonaws.com/role: <先に作成しておいたExternalDNSに必要な権限を持ったIAMロール名 (not 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=<Hosted Zoneのドメイン名>
        - --provider=aws
        - --policy=upsert-only
        - --aws-zone-type=private # Private Bosted Zoneに登録
        - --registry=txt
        - --annotation-filter=service.beta.kubernetes.io/aws-load-balancer-internal=true # 内部向けELBのみ対象
        - --txt-owner-id=my-identifier

Private Hosted Zone用のDeploymentではExternalDNSのオプションとしてPublic Hosted Zone用のときと同様に以下のものを指定しました。

  • --aws-zone-type=privateと指定し、Private Hosted Zoneに登録
  • --annotation-filter=service.beta.kubernetes.io/aws-load-balancer-internal=trueと指定し、内部向けELBのみ対象に

IAMロールに関してもPublic Hosted Zone用の設定と同様です。

ここまで紹介した変更点以外は、こちらのExternalDNSのドキュメントに載っている設定をそのまま使用します。

動作確認

ここまででExternalDNSの準備はできたので実際に外部向け/内部向けロードバランサーを作成し、動作確認してみましょう。

外部向けELBを作成するLoadBalancer Serviceを作成してみる

apiVersion: v1
kind: Service
metadata:
  name: external-lb
  annotations:
    external-dns.alpha.kubernetes.io/hostname: external-app.katainaka.org
    service.beta.kubernetes.io/aws-load-balancer-internal: "false"
spec:
  type: LoadBalancer
  selector:
    app: external-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

上のようなYAMLで外部向けELBを作成するServiceを作成すると、以下の画像のように指定したドメインのPublic Hosted Zoneに、Serviceから作成されたELBに結びついたALIASレコードが登録されました。

内部向けELBを作成するLoadBalancer Serviceを作成してみる

apiVersion: v1
kind: Service
metadata:
  name: internal-lb
  annotations:
    external-dns.alpha.kubernetes.io/hostname: internal-app.katainaka.org
    service.beta.kubernetes.io/aws-load-balancer-internal: "true"
spec:
  type: LoadBalancer
  selector:
    app: internal-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080

上のようなYAMLで内部向けELBを作成するServiceを作成すると、以下の画像のように指定したドメインのPrivate Hosted Zoneに、Serviceから作成されたELBに結びついたALIASレコードが登録されました。

まとめ

ExternalDNSの--annotation-filterというオプションを使用することで、アノテーションの値に基づいてどのHosted Zoneにレコードを登録するかを振り分けることができました。今回はLoadBalancer Serviceで試しましたがExternalDNSはIngressにも対応しているためIngressでも同様のことを行うことが可能です。

参考資料