CDKでEKSクラスタにExternalDNSをインストールしてServiceやIngressで指定したホストをRoute53に登録する

awskubernetes

ExternalDNS は Kubernetes の Service や Ingress の host と外部の DNS のレコードを同期するアプリケーション。Route53 のレコードの作成は CDK で行うこともできるが cluster.addManifest() してから非同期に作られ、Controller によって管理される ELB の扱いが難しい。

まずは Route53 のレコードを編集するための Role を作成する。

const externalDNSRole = new iam.Role(scope, 'ExternalDNSRole', {
  roleName: `ExternalDNSRole-${cluster.clusterName}`,
  assumedBy: new iam.WebIdentityPrincipal(
    cluster.openIdConnectProvider.openIdConnectProviderArn,
    {
      "StringEquals": new cdk.CfnJson(scope, 'ExternalDNSRoleStringEquals', { value: {
        [`${cluster.clusterOpenIdConnectIssuer}:aud`]: 'sts.amazonaws.com',
        [`${cluster.clusterOpenIdConnectIssuer}:sub`]: 'system:serviceaccount:external-dns:external-dns-sa'
      }}),
    },
  ),
  inlinePolicies: {
    'Route53': new iam.PolicyDocument({
      statements: [
        new iam.PolicyStatement({
          actions: [
            'route53:ChangeResourceRecordSets',
          ],
          resources: [
            'arn:aws:route53:::hostedzone/*'
          ]
        }),
        new iam.PolicyStatement({
          actions: [
            'route53:ListHostedZones',
            'route53:ListResourceRecordSets',
            'route53:ListTagsForResource'
          ],
          resources: [
            '*'
          ]
        })
      ]
    })
  }
})

既存のゾーンでの挙動を確認するためレコードをいくつか登録した。

Helm Chart で 対象のゾーンを domainFilters で指定して ExternalDNS をインストールする。複数の ExternalDNS が同じドメインを参照する場合、txtOwnerId で変更するレコードが選択されるため、重複しない値にしないと相互にレコードを削除しようとしてしまう。

const externalDns = cluster.addHelmChart('ExternalDNS', {
  release: 'external-dns',
  chart: 'external-dns',
  repository: 'https://kubernetes-sigs.github.io/external-dns',
  version: '1.14.3',
  namespace: 'external-dns',
  createNamespace: true,
  values: {
    serviceAccount: {
      name: "external-dns-sa",
      annotations: {
        'eks.amazonaws.com/role-arn': externalDNSRole.roleArn
      }
    },
    sources: [
      'service',
      'ingress'
    ],
    domainFilters: ['externaldns.test'],
    provider: 'aws',
    policy: 'sync', // or 'upsert-only'
    awsZoneType: 'public',
    registry: 'txt', // Specify the registry for storing ownership and labels
    txtOwnerId: 'external-dns', // Specify an identifier for this instance of ExternalDNS
  },
  wait: true,
})

if (cluster.albController) {
  externalDns.node.addDependency(cluster.albController)
}

Service の external-dns.alpha.kubernetes.io/hostname annotations や Ingress の rule.host を指定すると

apiVersion: v1
kind: Service
metadata:
  name: nginx
  annotations:
	  service.beta.kubernetes.io/aws-load-balancer-type: external
    external-dns.alpha.kubernetes.io/hostname: cccc.externaldns.test
spec:
  type: LoadBalancer
  ports:
  - port: 80
    name: http
    targetPort: 80
  selector:
    app: nginx

対応する A レコードと “heritage=external-dns,external-dns/owner=external-dns,external-dns/resource=service/default/nginx” のような TXT レコードが作られる。

Desired change: CREATE cccc.externaldns.test A [Id: /hostedzone/****]
Desired change: CREATE cccc.externaldns.test TXT [Id: /hostedzone/****]
Desired change: CREATE cname-cccc.externaldns.test TXT [Id: /hostedzone/****]
3 record(s) in zone externaldns.test. [Id: /hostedzone/****] were successfully updated
Applying provider record filter for domains: [externaldns.test. .externaldns.test.]
All records are already up to date

Load Balancer Controller の ALB は Host ヘッダーによるルーティングを行う。

EKSクラスタにAWS Load Balancer ControllerをインストールしてALBのIngressを立てる - sambaiz-net

policyがsyncの場合、リソースを削除すると作られたレコードは削除されるが、既存のレコードは残る。

Desired change: DELETE cccc.externaldns.test A [Id: /hostedzone/****]
Desired change: DELETE cccc.externaldns.test TXT [Id: /hostedzone/****]
Desired change: DELETE cname-cccc.externaldns.test TXT [Id: /hostedzone/****]
3 record(s) in zone externaldns.test. [Id: /hostedzone/****] were successfully updated
All records are already up to date

同名のレコードを登録するには external-dns.alpha.kubernetes.io/set-identifier と併せて external-dns.alpha.kubernetes.io/aws-weight などを設定する必要がある

既存のレコードとルーティングポリシーが異なる場合エラーになる。

Failure in zone externaldns.test. [Id: /hostedzone/*****] when submitting change batch: InvalidChangeBatch: [RRSet with DNS name bbbb.externaldns.test., type A, SetIdentifier nginx cannot be created as a non-weighted set exists with the same name and type.]\n\tstatus code: 400