ExternalDNS 让你在 Kubernetes 里用一条 Ingress 注解就自动把域名解析记录同步到 DNS 服务商,彻底告别在控制台手动改 A 记录的繁琐。本文从工作原理讲到 Helm 部署,手把手跑通 ExternalDNS 自动管理 DNS 的完整链路,让你的服务上线即拥有正确域名。
一、为什么需要 ExternalDNS
在传统流程里,每上线一个对外服务,运维都要手动登录 DNS 控制台(阿里云 DNS、Cloudflare、Route53……)添加一条 A 记录或 CNAME,指向 Ingress 的负载均衡 IP。服务一多、集群一扩,这套手工操作就暴露出问题:
- 容易漏:新 Ingress 忘了加记录,域名迟迟不生效;
- 易出错:手填 IP 写错一位,排障半天;
- 难回收:服务下线后记录残留,时间久了谁也不敢删;
- 多集群混乱:多个集群共用一个域名空间,记录互相覆盖。
举个真实场景:一次大促前临时扩容出 3 套独立的 Ingress 域名,若全靠手工在 DNS 控制台操作,不仅耗时,还容易因手误导致局部用户访问异常。把域名写进注解后,ExternalDNS 在每分钟的对账里自动补齐,扩缩容与 DNS 彻底解耦。
Kubernetes 把“期望状态”作为一等公民,ExternalDNS 正是把这套理念延伸到 DNS:你把想要的域名写进 Ingress/Service 的注解,控制器周期性对账,自动在 DNS 服务商侧创建、更新、删除记录。
二、核心工作原理:三段式控制循环
ExternalDNS 的本质是一个持续运行的对账循环,分为 Source、Registry、Provider 三个阶段。
2.1 Source:从哪里读取期望状态
ExternalDNS 支持的来源非常丰富:Ingress、Service(LoadBalancer/NodePort)、Gateway API,甚至是自定义 CRD。常用参数是 --source=ingress。它会扫描集群内所有 Ingress 的 host 字段,作为期望的域名集合。
2.2 Registry:用 TXT 标记防止误删
这是 ExternalDNS 最容易被忽视却最关键的设计。为每一个被托管的记录,它会额外写一条同名 TXT 记录(例如 app.example.com 配套 app.example.com-txt),里面记录 owner 标识。删除时 ExternalDNS 只会清理“带有自己 owner 标记”的记录,绝不会碰你手动创建的其它记录——这就是生产环境安全感的来源。
2.3 Provider:对接不同 DNS 服务商
通过 --provider=alibabacloud 等参数对接具体服务商。不同 provider 的鉴权方式差异很大:阿里云用 RAM AccessKey,Cloudflare 用 API Token,AWS 走 IAM Role。下面的实战以阿里云为例。
三、用 Helm 快速部署 ExternalDNS
最省心的方式是 Helm。下面是一个最小可用的 values.yaml,关键点都已加注释:
provider:
name: alibabacloud
# 来源:监听集群内所有 Ingress
sources:
- ingress
# 域名后缀白名单,避免误托管内部域名
domainFilters:
- example.com
# 注册表类型:txt 记录防误删
registry: txt
# 灰度期建议 upsert-only,只增不改不删,观察无误后再切 sync
policy: upsert-only
# 多集群部署时务必改 owner id,避免互相覆盖记录
ownerId: cluster-prod-01
env:
- name: ALIBABACLOUD_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: externaldns-alibaba
key: access-key-id
- name: ALIBABACLOUD_ACCESS_KEY_SECRET
valueFrom:
secretKeyRef:
name: externaldns-alibaba
key: access-key-secret
先把 RAM 子账号的 AccessKey 存进 Secret(权限只给 DNS 域名管理,遵循最小权限),再安装:
kubectl create ns external-dns
kubectl -n external-dns create secret generic externaldns-alibaba \
--from-literal=access-key-id=$AK_ID \
--from-literal=access-key-secret=$AK_SECRET
helm repo add external-dns https://kubernetes-sigs.github.io/external-dns/
helm upgrade --install external-dns external-dns/external-dns \
-n external-dns -f values.yaml
四、实战:让 Ingress 域名自动生效
部署完成后,给你的 Ingress 加上主机名,ExternalDNS 会在下一个对账周期(默认 1 分钟)自动创建 DNS 记录:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-app
annotations:
# 指定对外暴露的域名
external-dns.alpha.kubernetes.io/hostname: app.example.com
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-app
port:
number: 80
apply 之后稍等片刻,用 dig 验证记录是否自动出现:
kubectl apply -f ingress.yaml
sleep 60
dig +short app.example.com
# 输出负载均衡公网 IP 即表示 ExternalDNS 已生效
如果你的 Ingress 前面是 Nginx 反向代理做负载均衡与 HTTPS,ExternalDNS 负责把域名指到 SLB,二者天然互补。
进阶玩法:通过注解 external-dns.alpha.kubernetes.io/ttl 可设置记录 TTL,external-dns.alpha.kubernetes.io/target 可显式指定解析目标(例如指向固定的 SLB 而非 Ingress 自动发现的 IP),在多活或蓝绿发布时非常有用。
五、主流 Provider 对比
| Provider | 鉴权方式 | 记录类型 | 适用场景 |
|---|---|---|---|
| alibabacloud | RAM AccessKey | A / CNAME / TXT | 国内业务、阿里云 SLB |
| cloudflare | API Token | A / CNAME / TXT | 海外站点、CDN 一体 |
| aws-route53 | IAM Role | A / CNAME / TXT | AWS 生态、多账号 |
| coredns | 集群内 Service | 内部解析 | 仅集群内服务发现 |
六、生产环境注意事项
- 权限最小化:RAM 子账号只授予 DNS 域名管理权限,绝不挂全账号 AccessKey。
- registry 用 txt:务必开启 TXT 注册表,这是防误删的保险丝。
- ownerId 隔离:多套集群接入同一域名空间时,每个集群配不同 ownerId,互不覆盖。
- 灰度用 upsert-only:新接入先
policy: upsert-only,观察记录无误后再切sync才允许删除。 - 监控指标:ExternalDNS 暴露 Prometheus 指标,接入 Prometheus + Grafana 监控 后,可观测记录同步成功率与延迟。
七、与 cert-manager 协同:DNS + TLS 一把梭
ExternalDNS 只管“域名指向哪里”,HTTPS 证书可以交给 cert-manager 自动签发续期。两者配合,Ingress 一创建,ExternalDNS 把域名解析到 SLB,cert-manager 基于同一域名申请并挂载证书,实现“域名生效即具备 HTTPS”的无人值守闭环——这正是现代 GitOps 交付链路里最省心的一环。
八、常见排障清单:记录没生效怎么办
ExternalDNS 是声明式控制器,排障的核心思路是“先看日志,再对期望状态”。下面按出现频率列出四类典型问题。
8.1 记录迟迟不出现
先用日志确认控制器是否扫到了你的 Ingress 与期望域名:
kubectl logs -n external-dns deploy/external-dns | grep -i app.example.com
若日志里完全没有该域名,常见原因有两个:一是 values.yaml 的 domainFilters 没包含这个后缀,被白名单拦掉;二是 Ingress 没写 host 字段。若日志显示“would create”但迟迟没落地,多半是 Provider 侧写入被限频,等一个对账周期即可。
8.2 TXT 记录冲突
同一域名被两套不同 ownerId 的 ExternalDNS 托管时会互相争抢,日志报 conflict。解决办法是每个集群配置独立 ownerId,并在 registry 里启用 txt,让控制器只认领自己标记的记录。
8.3 Provider 鉴权失败
阿里云报 Forbidden 多半是 RAM 子账号只被授予了部分域名的权限,或 AccessKey 写错。建议先用 aliyun cli 单独验证密钥是否能增删该域名的解析记录,再回填到 Secret。
8.4 与手动记录打架
ExternalDNS 默认只动带 TXT 标记的记录,你手工创建的 A 记录是安全的。但切换 provider 或首次接入时,务必先 policy: upsert-only 灰度观察几天,确认不会误删既有记录后,再切回 sync。
结语
ExternalDNS 把 DNS 记录变成 Kubernetes 里的“声明式资源”,用一条注解消除手工改解析的全部痛点。从 Source/Registry/Provider 三段式原理入手,配合 txt 注册表防误删、ownerId 多集群隔离、upsert-only 灰度三道防线,你就能在生产环境放心托管整套对外域名。把它和 cert-manager、Prometheus 串起来,DNS 与证书将彻底从运维待办里消失。




