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

配置 Amazon Route53 作为外部访问微服务的 DNS 提供商
发布时间:2023 年 10 月 3 日
外部 DNS
EKS 集群设置
EKS
Kubernetes
教程
亚马逊云科技
Olawale Olaleye
亚马逊云科技使用经验
200 - 中级
完成所需时间
30 分钟
前提条件

注册 / 登录 亚马逊云科技账户

上次更新时间
2023 年 10 月 3 日
相关产品

在现代云原生环境中,微服务通常分布在不同的集群中,不仅动态扩缩,还会由于编排频繁地移动。手动更新每项微服务实例的 DNS 记录变得不切实际、耗时且容易出错。DNS 记录配置不当会导致服务中断,从而直接影响用户体验,并可能造成经济损失。解决这些错误需投入大量时间和资源,这样一来,运维成本随之上升,同时也会暴露潜在的安全漏洞。

对于在 Kubernetes 集群上托管的微服务来说。自动执行 DNS 记录管理是至关重要的。这样的自动化让应用程序的访问更便捷,同时保持了运维效率。在部署了 ExternalDNS 的情况下,微服务便不再需要手动干预。ExternalDNS 能使域名始终保持最新,并准确地指向运行中的 Kubernetes 服务的地址。这不仅通过允许客户端使用可读取的 URL 访问 Kubernetes 服务简化了用户体验,而且还提高了容错能力和弹性。通过自动化的 DNS 管理,定位微服务可以快速部署、扩展和重新,并且不会影响可访问性。ExternalDNS 附加组件使用支持的 DNS 提供程序自动创建和管理对外公开服务的 DNS 记录。该附加组件通过将服务的主机名解析为 Kubernetes 集群的外部 IP 地址,使外部客户端能够访问在集群内运行的服务。

本教程以本系列第 1 部分中的 Amazon EKS 集群为基础,详细介绍了如何设置 ExternalDNS 附加组件。在上次教程的集群配置中,包括用于 ExternalDNS 附加组件和 OpenID Connect (OIDC) 端点的服务账户的 IAM 角色 (IRSA)。对于本系列的第一部分,请参阅为运行高流量微服务构建预配置的 Amazon EKS 集群。或者,要使用本教程所需的组件设置现有集群,请使用 EKS 官方文档中的创建 IAM OpenID Connect (OIDC) 端点和 Re:Post 中的创建服务账户的 IAM 角色 (IRSA) 中的说明。

在本教程中,您将在 Amazon EKS 集群上配置 ExternalDNS 附加组件。这将涉及创建私有托管区域,安装利用服务账户的 IAM 角色 (IRSA) 来管理 Amazon Route53 DNS 记录的 ExternalDNS 附加组件。此外,我们还将说明如何验证用户友好的 URL 来访问这些 Kubernetes 服务。这种方法增强了容错能力,并保证了这些 Kubernetes 服务的高可访问性。

前提条件

  • 安装最新版本的 kubectl。运行以下命令检查您的 kubectl 版本:kubectl version --short。
  • 安装最新版本的 eksctl。运行以下命令检查您的 eksctl 版本:eksctl info。
  • 安装最新版本的 Helm。运行以下命令检查您的 Helm 版本:helm version。
  • 安装最新版本的 Amazon CLI。运行以下命令检查您的 Amazon CLI 版本:aws --version。

概述

本教程是使用 Amazon EKS 管理高流量微服务平台系列教程的第二部分,专门介绍如何设置和配置 ExternalDNS 附加组件。本教程还概述了创建私有托管区域的过程,并介绍了使用服务账户的 IAM 角色 (IRSA) 进行身份验证和授权来管理 Amazon Route53 DNS 记录的实施过程。

  • 身份验证:将服务账户的 IAM 角色 (IRSA) 用于具有 OpenID Connect (OIDC) 端点的 ExternalDNS 附加组件,以确保 Kubernetes Pod 和 亚马逊云科技服务之间的安全通信。
  • 创建 Route53 托管区域:创建一个私有托管区域,用于保存 Kubernetes 服务的 DNS 记录。此托管区域将用作与您的 Kubernetes 服务相关的所有 DNS 记录的容器。
  • 设置 ExternalDNS 附加组件:在 Amazon EKS 集群上部署 ExternalDNS 附加组件,并将其配置为将 Kubernetes 服务 DNS 记录与 Route 53 域同步。
  • 部署示例应用程序:我们会通过一个实际案例来展示在端口 80 上构建并公开“2048 游戏示例应用程序”的所有步骤。为方便起见,我们将利用 ExternalDNS 的自定义注解,特别是 hostname 注解,它指示 ExternalDNS 控制器如何通过指定的 HTTP 路径访问 Kubernetes 服务。有关更多注释,请参阅为亚马逊云科技上的服务设置 ExternalDNS
 
请注意,即便您仍在享受最初 12 个月的亚马逊云科技免费套餐,Route 53 托管区域也不属于亚马逊云科技免费套餐。因此,使用可能会导致额外费用。
ExternalDNS 附加组件可进行自我管理,客户需要负责对其其生命周期和维护进行监督。
 

步骤 1:配置集群环境变量

在使用 Helm 或其他命令行工具与 Amazon 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"
  • 在 Amazon 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": "https://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"
 }
}
  • 检索您在 Amazon 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.amazon.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"
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 文件。您应当会看到以下内容。
请注意,对于公共域名,您可以直接从浏览器访问 URL。

清理资源

完成本教程后,为了更好地管理资源,可能需要删除创建的特定资源。

# 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}

结论

完成本教程后,您就已经在 Amazon EKS 集群上有效地部署了 ExternalDNS 附加组件,为 Kubernetes 服务启用了自动 DNS 管理。通过与 Amazon Route 53 等 DNS 提供程序集成,您可以轻松进行域名解析,完全符合行业标准。本教程已引导您完成 ExternalDNS 附加组件的初始设置、环境变量(如 HOSTED_ZONE_ID 和 AWS_ROUTE53_DOMAIN)的配置以及域注册步骤。您还深入了解了外部客户端的 URL 导航的细节。

若要继续使用真实域名,您需要 通过 Amazon Route 53 注册域名。在通过 Amazon Route 53 注册域名后,用已注册的域设置 HOSTED_ZONE_ID 和 AWS_ROUTE53_DOMAIN 变量,然后再看本教程的步骤。这样一来,您将能够通过导航到 <SUB_DOMAIN>.<AWS_ROUTE53_DOMAIN> 直接从浏览器访问该服务。这样的设置确保了全面、完全可操作的环境,为内部和外部服务的可访问性做好了准备。

若要了解有关 ExternalDNS 的更多信息,请观看此视频或阅读 Kubernetes 文档