Amazon EKSにデプロイしたGitLabのALB DNS名をExternalDNSを使ってRoute53に登録する

Amazon EKSにデプロイしたGitLabのALB DNS名をExternalDNSを使ってRoute53に登録する

Clock Icon2025.02.24

Amazon EKSにHelmを使ってGitLabをインストールしました。

https://dev.classmethod.jp/articles/gitlab-eks-helm-tutorial/

検証で作成削除する機会が多く、毎回Route53レコードを手動で登録するのが面倒に感じました。

ExternalDNSを使って、GitLab公開用のALBをRoute53に登録してみました。

ExternalDNSとは?

ExternalDNSは、KubernetesのServiceやIngressのリソースを自動的にパブリックDNSプロバイダー(Route53、Cloud DNS、Azure DNSなど)に同期させるツールです。

ExternalDNS

例えば、AWS Load Balancer Controllerを利用して、ELBを作成したとします。

ELBをインターネット上に公開したいときに、DNSプロバイダーにELB用のレコードを作成する必要があります。

手動で作成することも可能ですが、マニフェスト適用時に自動でレコードも追加したいです。

ExternalDNSを使うことで、これを実現できます。

なぜExternalDNSを選択したか?

前提

ExternalDNSを使うか、TerraformでRoute53レコードを管理するか迷いました。

現状の構成では、GitLabのHelmチャートにてAWS Load Balancer Controller経由でALBが作成されるようにしていました。(TerraformでALBを管理していない)

そのため、ALBのDNS名はHelmインストール後に分かります。

terraform apply(EKSクラスター等) -> helm install という流れをとっており、helm install後にterraform apply(route53レコード)を行うのは手順が複雑に思いました。

ExternalDNSとAWS Load Balancer Controller TargetGroupBinding

以下2つの方式を検討し今回は「2.」のExternalDNSを利用しました。

  1. AWS LoadBalancer ControllerでALB関連リソースを作成・ExternalDNSを利用してRoute53レコードを作成
  2. TerraformでALB・Route53レコードを作成、AWS Load Balancer ControllerのTargetGroupBindingを利用して、Terraformで作成したTargetGroupにノードを登録する

理由はGitLabのHelmチャート側でやってくれるものはそちらに寄せたかったからです。

GitLabのHelmチャートでは、ALB関連の設定を一通りやってくれます。(ALB・リスナールール・ターゲットグループ作成、ノードのターゲットグループの紐づけ等)

Helmチャート側のアップデートでALBの設定変更が必要になった場合も、バージョンを上げるだけで済みます。

一方、TerraformでALB作成からやってしまうと、変更内容によってはTerraformへ記述の追加等が必要になります。

TargetGroupBinding - AWS Load Balancer Controller

余談: HelmのTerraform管理

Helm部分もTerraform化して、helm_releaseattributesからALBのDNS名を取得しaws_route53_recordに渡す方法も考えられます。

この方法はHelmで生成されたマニフェストから値を抜き出す必要があります。

Helmチャートのバージョンアップで、生成されるマニフェストが変わったときに対応するのが大変かもしれません。

Helmチャートを自組織で管理している場合だとハードルは下がるかもしれませんが、今回はGitLab提供のHelmチャートを使うため厳しいように思いました。

helm_release | Resources | hashicorp/helm | Terraform | Terraform Registry

やってみた

以下を参考に、ExternalDNSをセットアップします。

Setting up ExternalDNS for Services on AWS - external-dns

ExternalDNSのインストール

Helmを使うため、ExternalDNS用のvalues.yamlを用意します。

values.yaml
provider:
  name: aws
env:
  - name: AWS_DEFAULT_REGION
    value: <リージョン> # example: us-east-1

EKSクラスターにExternalDNSをインストールします。

helm repo add external-dns https://kubernetes-sigs.github.io/external-dns/
helm install external-dns external-dns/external-dns -f values.yaml -n kube-system

Deploymentexternal-dnsが作成されていたらOKです。

kubectl get deployments.apps -n kube-system | grep external-dns
出力例
external-dns         1/1     1            1           5m

インストール時に自動でexternal-dnsという名前のServiceAccountが作成されます。

kubectl get sa external-dns -o yaml -n kube-system
出力例
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    meta.helm.sh/release-name: external-dns
    meta.helm.sh/release-namespace: kube-system
  creationTimestamp: "2025-02-23T02:32:55Z"
  labels:
    app.kubernetes.io/instance: external-dns
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/name: external-dns
    app.kubernetes.io/version: 0.15.1
    helm.sh/chart: external-dns-1.15.2
  name: external-dns
  namespace: kube-system
  resourceVersion: "3338"
  uid: 3669ef20-6a59-4211-8077-00e14cb6bb80

次のステップでは、このServiceAccountに対して、Pod Identityを利用してAWSの権限を付与します。

IAM Role作成・Pod Identityの関連付けを作成

Amazon EKSクラスターとAWS間の認証はPod Identityを使います。

以下のTerraformコードでExternalDNS用のIAMポリシーとIAMロールをデプロイします。

main.tf
### External DNS
resource "aws_iam_role" "external_dns" {
  name               = "test-external-dns"
  assume_role_policy = data.aws_iam_policy_document.eks_pod_assume_role.json
}

data "aws_iam_policy_document" "eks_pod_assume_role" {
  statement {
    actions = [
      "sts:AssumeRole",
      "sts:TagSession"
    ]

    effect = "Allow"

    principals {
      type        = "Service"
      identifiers = ["pods.eks.amazonaws.com"]
    }
  }
}

resource "aws_iam_policy" "external_dns_policy" {
  name        = "test-external-dns-policy"
  description = "Policy for External DNS to manage Route 53 records"
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "route53:ChangeResourceRecordSets"
        ]
        Resource = [
          "arn:aws:route53:::hostedzone/*" # hostedzoneを指定することで、より細かな制御が可能
        ]
      },
      {
        Effect = "Allow"
        Action = [
          "route53:ListHostedZones",
          "route53:ListResourceRecordSets",
          "route53:ListTagsForResource"
        ]
        Resource = [
          "*"
        ]
      }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "external_dns" {
  role       = aws_iam_role.external_dns.name
  policy_arn = aws_iam_policy.external_dns_policy.arn
}

resource "aws_eks_pod_identity_association" "external_dns" {
  cluster_name    = module.eks.cluster_name
  namespace       = "kube-system"
  service_account = "external-dns"
  role_arn        = aws_iam_role.external_dns.arn
}

Terraformを実行してリソースを作成します。

terraform init
terraform plan
terraform apply

GitLab values.yamlを更新

GitLab側はアノテーションexternal-dns.alpha.kubernetes.io/hostnameを追加するだけです。

values.yaml
nginx-ingress:
  enabled: false
global:
  # 省略
  ingress:
    # Common annotations used by kas, registry, and webservice
    annotations:
      alb.ingress.kubernetes.io/backend-protocol: HTTP
      alb.ingress.kubernetes.io/certificate-arn: <ACM ARN>
      alb.ingress.kubernetes.io/group.name: gitlab
      alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS": 443}]'
      alb.ingress.kubernetes.io/scheme: internet-facing
      alb.ingress.kubernetes.io/target-type: ip
      kubernetes.io/ingress.class: alb
      nginx.ingress.kubernetes.io/connection-proxy-header: "keep-alive"
      # 以下を追加
      external-dns.alpha.kubernetes.io/hostname: <ドメイン名> #example: gitlab.example.com

この際、ドメイン名はgitlab.<ドメイン名>としてください。(hostnameを変えている場合は、gitlab部分も合わせて変える必要があります)

ドメイン名だけではドメイン名に対して、ALB用のレコードが作成されます。

対して、デフォルトのGitLab用のALBのリスナールールのHTTPホストヘッダーはgitlab.<ドメイン名>で作成されます。

example.comがドメイン名だとすると、以下の状況になり疎通ができません。

  • Route53レコード
    • レコード名: example.com
    • 値: ALB DNS名
  • ALBリスナールール
    -HTTPホストヘッダー: gitlab.example.com

Cursor_と_リスナーの詳細___EC2___us-east-2

values.yamlの準備ができたら、適用します。

helm upgrade gitlab gitlab/gitlab -n gitlab -f values.yaml

動作確認

PodのログからRoute53レコードの更新を確認できます。

Pod名(external-dns-645b8d7fdb-tlmpdの部分)は各自の環境に合わせて変更してください。

kubectl get pod -n kube-system # pod名の確認
kubectl logs external-dns-645b8d7fdb-tlmpd # ログの確認

冒頭のブログの手順でGitLabを構築している場合、gitlab kas registry 分のAレコードとTXTレコード作成のログが確認できるはずです。

出力例
time="2025-02-23T02:54:15Z" level=info msg="Desired change: UPSERT gitlab.example.com A" profile=default zoneID=/hostedzone/XXXXXXXXXX zoneName=example.com.
time="2025-02-23T02:54:15Z" level=info msg="Desired change: UPSERT gitlab.example.com TXT" profile=default zoneID=/hostedzone/XXXXXXXXXX zoneName=example.com.

ブラウザのアドレスバーにgitlab.<ドメイン名>を入力して、GitLabにアクセスできることも確認できました。

Cursor_と_サインインする_·_GitLab

おわりに

ExternalDNSを使うことで、簡単にRoute53レコードの管理もKubernetes側で管理できました。

AWS Load Balancer ControllerでELB管理している環境と相性が良いように思いました。

EKSを利用している場合は、IaCツールを使っていることも多いと思います。

Route53レコードをIaCツールで管理するパターンも多いと思いますが、ExternalDNS利用も選択肢の一つとして持っておきたいですね。

以上、AWS事業本部の佐藤(@chari7311)でした。)

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.