使用 ExternalDNS 自动管理 HAQM EKS 中微服务的 DNS 记录




在现代云原生环境中,微服务通常分布在不同的集群中,不仅动态扩缩,还会由于编排频繁地移动。手动更新每项微服务实例的 DNS 记录变得不切实际、耗时且容易出错。DNS 记录配置不当会导致服务中断,从而直接影响用户体验,并可能造成经济损失。解决这些错误需投入大量时间和资源,这样一来,运维成本随之上升,同时也会暴露潜在的安全漏洞。
对于在 Kubernetes 集群上托管的微服务来说。自动执行 DNS 记录管理是至关重要的。这样的自动化让应用程序的访问更便捷,同时保持了运维效率。在部署了 ExternalDNS 的情况下,微服务便不再需要手动干预。ExternalDNS 能使域名始终保持最新,并准确地指向运行中的 Kubernetes 服务的地址。这不仅通过允许客户端使用可读取的 URL 访问 Kubernetes 服务简化了用户体验,而且还提高了容错能力和弹性。通过自动化的 DNS 管理,定位微服务可以快速部署、扩展和重新,并且不会影响可访问性。ExternalDNS 附加组件使用支持的 DNS 提供程序自动创建和管理对外公开服务的 DNS 记录。该附加组件通过将服务的主机名解析为 Kubernetes 集群的外部 IP 地址,使外部客户端能够访问在集群内运行的服务。
本教程以本系列第 1 部分中的 HAQM EKS 集群为基础,详细介绍了如何设置 ExternalDNS 附加组件。在上次教程的集群配置中,包括用于 ExternalDNS 附加组件和 OpenID Connect (OIDC) 端点的服务账户的 IAM 角色 (IRSA)。对于本系列的第一部分,请参阅为运行高流量微服务构建预配置的 HAQM EKS 集群。或者,要使用本教程所需的组件设置现有集群,请使用 EKS 官方文档中的创建 IAM OpenID Connect (OIDC) 端点和 Re:Post 中的创建服务账户的 IAM 角色 (IRSA) 中的说明。
在本教程中,您将在 HAQM EKS 集群上配置 ExternalDNS 附加组件。这将涉及创建私有托管区域,安装利用服务账户的 IAM 角色 (IRSA) 来管理 HAQM Route53 DNS 记录的 ExternalDNS 附加组件。此外,我们还将说明如何验证用户友好的 URL 来访问这些 Kubernetes 服务。这种方法增强了容错能力,并保证了这些 Kubernetes 服务的高可访问性。

前提条件
概述
本教程是使用 HAQM EKS 管理高流量微服务平台系列教程的第二部分,专门介绍如何设置和配置 ExternalDNS 附加组件。本教程还概述了创建私有托管区域的过程,并介绍了使用服务账户的 IAM 角色 (IRSA) 进行身份验证和授权来管理 HAQM Route53 DNS 记录的实施过程。
- 身份验证:将服务账户的 IAM 角色 (IRSA) 用于具有 OpenID Connect (OIDC) 端点的 ExternalDNS 附加组件,以确保 Kubernetes Pod 和 亚马逊云科技服务之间的安全通信。
- 创建 Route53 托管区域:创建一个私有托管区域,用于保存 Kubernetes 服务的 DNS 记录。此托管区域将用作与您的 Kubernetes 服务相关的所有 DNS 记录的容器。
- 设置 ExternalDNS 附加组件:在 HAQM EKS 集群上部署 ExternalDNS 附加组件,并将其配置为将 Kubernetes 服务 DNS 记录与 Route 53 域同步。
- 部署示例应用程序:我们会通过一个实际案例来展示在端口 80 上构建并公开“2048 游戏示例应用程序”的所有步骤。为方便起见,我们将利用 ExternalDNS 的自定义注解,特别是 hostname 注解,它指示 ExternalDNS 控制器如何通过指定的 HTTP 路径访问 Kubernetes 服务。有关更多注释,请参阅为亚马逊云科技上的服务设置 ExternalDNS。
请注意,即便您仍在享受最初 12 个月的亚马逊云科技免费套餐,Route 53 托管区域也不属于亚马逊云科技免费套餐。因此,使用可能会导致额外费用。
ExternalDNS 附加组件可进行自我管理,客户需要负责对其其生命周期和维护进行监督。
步骤 1:配置集群环境变量
在使用 Helm 或其他命令行工具与 HAQM EKS 集群互动之前,必须定义封装集群详细信息的特定环境变量。这些变量将在后续命令中使用,确保它们以正确的集群和资源为目标。
- 首先,请确认您是在正确的集群上下文中进行操作。这样可确保将任何后续命令可以发送到预期的 Kubernetes 集群。您可以通过执行以下命令来验证当前上下文:
kubectl config current-context
- 定义 CLUSTER_ACCOUNT 环境变量以存储您的亚马逊云科技账户 ID。
export CLUSTER_ACCOUNT=$(aws sts get-caller-identity --query Account --o text)
- 为 EKS 集群定义 CLUSTER_NAME 环境变量。
export CLUSTER_NAME="managednodes-quickstart"
- 为 EKS 集群定义 CLUSTER_REGION 环境变量。
export CLUSTER_REGION="us-east-2"
- 为 EKS 集群定义 CLUSTER_VPC 环境变量。
export CLUSTER_VPC=$(aws eks describe-cluster --name ${CLUSTER_NAME} --region ${CLUSTER_REGION} --query "cluster.resourcesVpcConfig.vpcId" --output text)
步骤 2:创建 Route 53 域
在这一步中,我们将创建一个名为“my-externaldns-demo.com”的 Route53 私有托管区域。如果您已经拥有一个公共域名,便可直接使用。
- 定义 AWS_ROUTE53_DOMAIN 环境变量来存储您的 Route 53 域名。
export AWS_ROUTE53_DOMAIN="my-externaldns-demo.com"
- 在 HAQM Route 53 中创建新的托管区域。
aws route53 create-hosted-zone --name "${AWS_ROUTE53_DOMAIN}." --vpc VPCRegion=${CLUSTER_REGION},VPCId=${CLUSTER_VPC} --caller-reference "my-externaldns-demo-$(date +%s)"
- 输出结果应该如下所示:
{
"Location": "http://route53.amazonaws.com/2013-04-01/hostedzone/Z0116663IIIIIIVJ3D",
"HostedZone": {
"Id": "/hostedzone/Z0116663IIIIIIVJ3D",
"Name": "my-externaldns-demo.com.",
"CallerReference": "my-externaldns-demo-1691704176",
"Config": {
"PrivateZone": true
},
"ResourceRecordSetCount": 2
},
"ChangeInfo": {
"Id": "/change/C02072961XZD56YBSLPL",
"Status": "PENDING",
"SubmittedAt": "2023-08-10T21:49:38.828000+00:00"
},
"VPC": {
"VPCRegion": "us-east-1",
"VPCId": "vpc-0c508g5678242g7g"
}
}
- 检索您在 HAQM Route 53 中创建的托管区域的 ID。
export HOSTED_ZONE_ID=$(aws route53 list-hosted-zones-by-name --dns-name "${AWS_ROUTE53_DOMAIN}." --query 'HostedZones[0].Id' --o text | awk -F "/" {'print $NF'})
- 验证是否已成功创建 Route53 托管区域。
aws route53 list-resource-record-sets --hosted-zone-id ${HOSTED_ZONE_ID} --query "ResourceRecordSets[?Name == '${AWS_ROUTE53_DOMAIN}.']"
输出结果应该如下所示:
[
{
"Name": "my-externaldns-demo.com.",
"Type": "NS",
"TTL": 172800,
"ResourceRecords": [
{
"Value": "ns-1536.awsdns-00.co.uk."
},
{
"Value": "ns-0.awsdns-00.com."
},
{
"Value": "ns-1024.awsdns-00.org."
},
{
"Value": "ns-512.awsdns-00.net."
}
]
},
{
"Name": "my-externaldns-demo.com.",
"Type": "SOA",
"TTL": 900,
"ResourceRecords": [
{
"Value": "ns-1536.awsdns-00.co.uk. awsdns-hostmaster.haqm.com. 1 5600 500 1209600 86400"
}
]
}
]
步骤 3:验证或创建服务账户的 IAM 角色
确保集群的“kube-system”命名空间中的“external-dns”服务账户设置正确。
kubectl get sa external-dns -n kube-system -o yaml
apiVersion: v1
kind: ServiceAccount
metadata:
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::#########:role/eksctl-managednodes-quickstart-addon-iamserv-Role1-U0CHMZQX2WC4
creationTimestamp: "2023-08-31T16:43:32Z"
labels:
app.kubernetes.io/managed-by: eksctl
name: external-dns
namespace: kube-system
resourceVersion: "1469"
uid: 68923daf-7865-4ffe-dfgd-01d98a00a01a
另外,如果您尚未设置 IAM 角色,或者收到错误,则以下命令将创建 IAM 角色以及服务账户名称“external-dns”。请注意,在运行这些命令之前,您必须具有与集群相关联的 OpenID Connect (OIDC) 端点。
- 配置 IAM 权限以允许 ExternalDNS Pod 管理您的亚马逊云科技账户中的 Route 53 记录。
cat << EOF > external-dns-policy.json
{ "Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"route53:ChangeResourceRecordSets"
],
"Resource": [
"arn:aws:route53:::hostedzone/${HOSTED_ZONE_ID}"
]
},
{
"Effect": "Allow",
"Action": [
"route53:ListHostedZones",
"route53:ListResourceRecordSets"
],
"Resource": [
"*"
]
}
]
}
EOF
- 创建策略以授予 ExternalDNS 用于与 Route 53 交互所需的权限。
aws iam create-policy --policy-name "ExternalDNSUpdatesPolicy" --policy-document file://external-dns-policy.json
- 使用该策略创建服务账户的 IAM 角色。该服务账户会被 ExternalDNS Pod 用来管理 Route53 托管区域中的记录。
eksctl create iamserviceaccount --name external-dns --namespace kube-system --cluster ${CLUSTER_NAME} --attach-policy-arn arn:aws:iam::${AWS_CURRENT_ACCOUNT}:policy/ExternalDNSUpdatesPolicy --approve --override-existing-serviceaccounts --region ${CLUSTER_REGION}
步骤 4:安装 ExternalDNS 附加组件
在这一步中,您将在 EKS 集群中部署 ExternalDNS 附加组件,具体是部署在“kube-system”命名空间中。您将进一步配置该附加组件以同步 my-externaldns-demo.com 托管区域的 DNS 记录。此配置使 ExternalDNS 附加组件能够自动管理 Kubernetes 集群中运行的服务的 DNS 记录,确保这些服务可以通过域名访问。若要了解详细信息,请参阅 ExternalDNS 参数。
- 更新 kubeconfig 文件以将上下文设置为当前 EKS 集群。
aws eks update-kubeconfig --name ${CLUSTER_NAME} --region ${CLUSTER_REGION}
- 运行以下 Helm 命令,以在 EKS 集群上安装 ExternalDNS 附加组件。此命令将配置 ExternalDNS 附加组件来管理指定域的 DNS 记录。
helm upgrade --wait --timeout 900s --install externaldns-release \
--set provider=aws \
--set aws.region=${CLUSTER_REGION} \
--set txtOwnerId=${HOSTED_ZONE_ID} \
--set domainFilters\[0\]="${AWS_ROUTE53_DOMAIN}" \
--set serviceAccount.name=external-dns \
--set serviceAccount.create=false \
--set policy=sync \
oci://registry-1.docker.io/bitnamicharts/external-dns --namespace kube-system
输出结果应该如下所示:
Release "externaldns-release" does not exist. Installing it now.
Pulled: registry-1.docker.io/bitnamicharts/external-dns:6.23.3
Digest: sha256:79479fb62f8955c37c4994ac208f2f367aba22559473d5ceb4d07531a9c2dd6e
NAME: externaldns-release
LAST DEPLOYED: Thu Aug 10 12:15:45 2023
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
CHART NAME: external-dns
CHART VERSION: 6.23.3
APP VERSION: 0.13.5
** Please be patient while the chart is being deployed **
To verify that external-dns has started, run:
kubectl --namespace=kube-system get pods -l "app.kubernetes.io/name=external-dns,app.kubernetes.io/instance=externaldns-release"
步骤 5:验证 ExternalDNS 的功能
现在,我们已经设置了 ExternalDNS,我们可以允许集群以外的用户使用人类可读的 URL 访问容器化应用程序。在这一步中,我们将部署流行的“2048 游戏”作为集群中的示例应用程序。我们提供的清单包括 ExternalDNS 的自定义注解,具体来说就是是主机名的注解。这些注解与 ExternalDNS 控制器协同工作,通过 HTTP 路径访问 Kubernetes 服务。有关更多注解,请参阅 ExternalDNS 文档。
- 定义 SUB_DOMAIN 环境变量。
export SUB_DOMAIN="game-2048"
- 使用 ExternalDNS 注解创建命名空间、部署和服务。要了解有关这些组件的更多信息,请参阅以下资源:部署、服务、负载均衡和联网以及 ExternalDNS。
cat << EOF > game-2048.yaml
---
apiVersion: v1
kind: Namespace
metadata:
name: game-2048
---
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: game-2048
name: deployment-2048
spec:
selector:
matchLabels:
app.kubernetes.io/name: app-2048
replicas: 5
template:
metadata:
labels:
app.kubernetes.io/name: app-2048
spec:
containers:
- image: public.ecr.aws/l6m2t8p7/docker-2048:latest
imagePullPolicy: Always
name: app-2048
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
namespace: game-2048
name: service-2048
annotations:
external-dns.alpha.kubernetes.io/hostname: ${SUB_DOMAIN}.${AWS_ROUTE53_DOMAIN}
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
type: LoadBalancer
selector:
app.kubernetes.io/name: app-2048
EOF
- 通过将配置文件应用于 Kubernetes API 服务器来创建 Kubernetes 资源。
kubectl apply -f game-2048.yaml`
namespace/game-2048 created
deployment.apps/deployment-2048 created
service/service-2048 created
- 您可以使用以下命令验证日志。请注意,更新条目可能需要几秒钟。
kubectl logs --namespace=kube-system -l "app.kubernetes.io/name=external-dns,app.kubernetes.io/instance=externaldns-release
输出结果应该如下所示:
time="2023-08-31T20:37:38Z" level=info msg="Applying provider record filter for domains: [my-externaldns-demo.com. .my-externaldns-demo.com.]"
time="2023-08-31T20:37:38Z" level=info msg="Desired change: CREATE cname-game-2048.my-externaldns-demo.com TXT [Id: /hostedzone/Z0116663IIIIIIVJ3D]"
time="2023-08-31T20:37:38Z" level=info msg="Desired change: CREATE game-2048.my-externaldns-demo.com A [Id: /hostedzone/Z0116663IIIIIIVJ3D]"
time="2023-08-31T20:37:38Z" level=info msg="Desired change: CREATE game-2048.my-externaldns-demo.com TXT [Id: /hostedzone/Z0116663IIIIIIVJ3D]"
time="2023-08-31T20:37:38Z" level=info msg="3 record(s) in zone game-2048.my-externaldns-demo.com. [Id: /hostedzone/Z0116663IIIIIIVJ3D] were successfully updated"
- 您可以通过运行以下命令来验证新创建的 DNS 记录,这些记录指向私有托管区域内的 game-2048 服务:
aws route53 list-resource-record-sets --hosted-zone-id ${HOSTED_ZONE_ID} --query "ResourceRecordSets[?Name == '${SUB_DOMAIN}.${AWS_ROUTE53_DOMAIN}.']
输出结果应该如下所示:
[
{
"Name": "game-2048.my-externaldns-demo.com.",
"Type": "A",
"AliasTarget": {
"HostedZoneId": "Z0116663IIIIIIVJ3D",
"DNSName": "g23456d6180ec4a76540b9a1ecdb17d-1018765421.us-east-1.elb.amazonaws.com.",
"EvaluateTargetHealth": true
}
},
{
"Name": "game-2048.my-externaldns-demo.com.",
"Type": "TXT",
"TTL": 300,
"ResourceRecords": [
{
"Value": "\"heritage=external-dns,external-dns/owner=/hostedzone/Z0116663IIIIIIVJ3D,external-dns/resource=service/game-2048/service-2048\""
}
]
}
]
- 由于托管域是私有的,因此您可以使用访问便捷的 URL game-2048.my-externaldns-demo.com 从 Pod 中访问服务game-2048。我们将创建一个测试 Pod 并运行 curl 命令来验证设置。
kubectl run nginx-test-conn --image=nginx -n game-2048&& sleep 5&& kubectl exec -it nginx-test-conn -n game-2048 -- sh -c "curl http://${SUB_DOMAIN}.${AWS_ROUTE53_DOMAIN}"> test.html
- 双击之前命令创建的 test.html 文件。您应当会看到以下内容。
清理资源
完成本教程后,为了更好地管理资源,可能需要删除创建的特定资源。
# Delete the Deployments, Services by deleting namespace
kubectl delete namespace game-2048
# Delete the ExternalDNS add-on
helm delete externaldns-release -n kube-system
# Remove Route53 Domain
aws route53 delete-hosted-zone --id ${HOSTED_ZONE_ID}
结论
完成本教程后,您就已经在 HAQM EKS 集群上有效地部署了 ExternalDNS 附加组件,为 Kubernetes 服务启用了自动 DNS 管理。通过与 HAQM Route 53 等 DNS 提供程序集成,您可以轻松进行域名解析,完全符合行业标准。本教程已引导您完成 ExternalDNS 附加组件的初始设置、环境变量(如 HOSTED_ZONE_ID 和 AWS_ROUTE53_DOMAIN)的配置以及域注册步骤。您还深入了解了外部客户端的 URL 导航的细节。
若要继续使用真实域名,您需要 通过 HAQM Route 53 注册域名。在通过 HAQM Route 53 注册域名后,用已注册的域设置 HOSTED_ZONE_ID 和 AWS_ROUTE53_DOMAIN 变量,然后再看本教程的步骤。这样一来,您将能够通过导航到 <SUB_DOMAIN>.<AWS_ROUTE53_DOMAIN> 直接从浏览器访问该服务。这样的设置确保了全面、完全可操作的环境,为内部和外部服务的可访问性做好了准备。