AKS における External DNS の活用
External DNS は、Kubernetes クラスター内の Service や Ingress リソースを監視し、クラウドプロバイダーの DNS サービス(この場合は Azure DNS)上の DNS レコードを自動的に作成・更新・削除するためのツールです。
これにより、アプリケーションをデプロイするたびに手動で DNS レコードを設定する手間が省け、運用の自動化と効率化が図れます。
アーキテクチャと仕組み
External DNS は Kubernetes クラスター内で Pod として動作し、API サーバーを通じて Service や Ingress の変更を検知します。変更があった場合、設定された Azure DNS ゾーンに対して API 経由でレコードの同期を行います。
前提条件
- OIDC Issuer と Workload Identity が有効化された AKS クラスター
- Azure DNS ゾーン(ドメインの委任設定済みが望ましい)
kubectlおよびazCLI ツール
推奨されるセキュリティプラクティスに従い、本ドキュメントでは Azure Workload Identity を使用した認証設定について解説します。 レガシーな方法である Service Principal の利用については、ドキュメント末尾の補足を参照してください。
セットアップ手順
Workload Identity を使用したセットアップの流れは以下の通りです。
1. Azure DNS ゾーンの作成
まず、Azure DNS ゾーンを作成します。既存のものがある場合はスキップ可能です。
DNS_ZONE_NAME="example.com"
DNS_ZONE_RG="rg-azure-dns"
az group create -n $DNS_ZONE_RG -l japaneast
az network dns zone create -g $DNS_ZONE_RG -n $DNS_ZONE_NAME
2. AKS の Workload Identity 有効化
既存のクラスターで Workload Identity が有効になっていない場合は、以下のコマンドで有効化します。
AKS_CLUSTER_NAME="my-aks-cluster"
AKS_RG="my-aks-rg"
az aks update -g $AKS_RG -n $AKS_CLUSTER_NAME --enable-oidc-issuer --enable-workload-identity
3. Managed Identity の作成と権限付与
External DNS 用の User Assigned Managed Identity を作成し、Azure DNS へのアクセス権限を付与します。
IDENTITY_NAME="id-external-dns"
IDENTITY_RG=$AKS_RG # 管理しやすい任意のRG
# Managed Identity の作成
az identity create -g $IDENTITY_RG -n $IDENTITY_NAME
IDENTITY_CLIENT_ID=$(az identity show -g $IDENTITY_RG -n $IDENTITY_NAME --query clientId -o tsv)
IDENTITY_RESOURCE_ID=$(az identity show -g $IDENTITY_RG -n $IDENTITY_NAME --query id -o tsv)
# DNS ゾーンのリソース ID を取得
DNS_ZONE_ID=$(az network dns zone show -n $DNS_ZONE_NAME -g $DNS_ZONE_RG --query id -o tsv)
DNS_ZONE_RG_ID=$(az group show -g $DNS_ZONE_RG --query id -o tsv)
# 権限の付与 (DNS Zone Contributor: レコード操作, Reader: RG読み取り)
az role assignment create --role "Reader" --assignee $IDENTITY_CLIENT_ID --scope $DNS_ZONE_RG_ID
az role assignment create --role "DNS Zone Contributor" --assignee $IDENTITY_CLIENT_ID --scope $DNS_ZONE_ID
4. Federated Identity Credential の作成
Kubernetes の ServiceAccount と、作成した Managed Identity を紐づけるための連携設定を作成します。
# AKS の OIDC Issuer URL を取得
AKS_OIDC_ISSUER=$(az aks show -n $AKS_CLUSTER_NAME -g $AKS_RG --query "oidcIssuerProfile.issuerUrl" -o tsv)
# フェデレーションの作成
# namespace: external-dns, ServiceAccount名: external-dns と想定
az identity federated-credential create \
--name "fed-external-dns" \
--identity-name $IDENTITY_NAME \
--resource-group $IDENTITY_RG \
--issuer $AKS_OIDC_ISSUER \
--subject "system:serviceaccount:external-dns:external-dns"
5. External DNS のデプロイ
External DNS をデプロイします。Workload Identity を利用するための設定を含めます。
まずは設定ファイル用の Secret を作成します。
azure.json:
{
"tenantId": "YOUR_TENANT_ID",
"subscriptionId": "YOUR_SUBSCRIPTION_ID",
"resourceGroup": "rg-azure-dns",
"useWorkloadIdentityExtension": true
}
kubectl create namespace external-dns
kubectl create secret generic azure-config-file -n external-dns --from-file=azure.json
次にマニフェストを適用します。ServiceAccount の annotations と labels が重要です。
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-dns
namespace: external-dns
annotations:
# Managed Identity の Client ID を指定
azure.workload.identity/client-id: "YOUR_IDENTITY_CLIENT_ID"
labels:
azure.workload.identity/use: "true"
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: external-dns
rules:
- apiGroups: [""]
resources: ["services","endpoints","pods","nodes"]
verbs: ["get","watch","list"]
- apiGroups: ["extensions","networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get","watch","list"]
---
apiVersion: rbac.authorization.k8s.io/v1
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: external-dns
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
namespace: external-dns
spec:
strategy:
type: Recreate
selector:
matchLabels:
app: external-dns
template:
metadata:
labels:
app: external-dns
# Pod にも Workload Identity 利用のラベルを付与
azure.workload.identity/use: "true"
spec:
serviceAccountName: external-dns
containers:
- name: external-dns
image: registry.k8s.io/external-dns/external-dns:v0.13.5
args:
- --source=service
- --source=ingress
- --domain-filter=example.com
- --provider=azure
- --azure-resource-group=rg-azure-dns
volumeMounts:
- name: azure-config-file
mountPath: /etc/kubernetes
readOnly: true
volumes:
- name: azure-config-file
secret:
secretName: azure-config-file
利用方法
Gateway API での利用
Gateway API (Standard) を使用して DNS レコードを管理する場合、以下の設定追加が必要です。
必要な設定変更
External DNS の ClusterRole と Deployment 引数を更新して、Gateway API リソースを監視できるようにします。
1. ClusterRole に権限追加:
- apiGroups: ["gateway.networking.k8s.io"]
resources: ["gateways", "httproutes", "grpcroutes", "tlsroutes"]
verbs: ["get","watch","list"]
2. Deployment の args に source を追加:
args:
- --source=gateway-httproute
- --source=gateway-grpcroute
- --source=gateway-tlsroute
# 既存のソース
- --source=service
- --source=ingress
リソース定義例
HTTPRoute リソースの hostnames フィールドに指定されたドメイン名が、自動的に DNS レコードとして登録されます。特別なアノテーションは不要です。
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-http-route
namespace: default
spec:
parentRefs:
- name: my-gateway
hostnames:
- "gateway-app.example.com"
rules:
- backendRefs:
- name: my-service
port: 80
Service (LoadBalancer) での利用
Service リソースの annotations に external-dns.alpha.kubernetes.io/hostname を指定することで、自動的に A レコードが作成されます。
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
annotations:
external-dns.alpha.kubernetes.io/hostname: nginx.example.com
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 80
selector:
app: nginx
Ingress での利用
Ingress の rules にホスト名を指定するだけで、External DNS がそれを検知しレコードを作成します。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-svc
port:
number: 80
補足: Service Principal の使用について
以前は Service Principal を使用して認証を行う方法が一般的でしたが、現在は Workload Identity (Federated Identity) が推奨されています。Service Principal を使用する場合は、ID/Password を Kubernetes Secret (aadClientId, aadClientSecret) として管理する必要がありますが、Workload Identity ではシークレットをクラスター内に保持する必要がないため、よりセキュアです。