ExternalDNS 实战:用 K8s 自动管理 DNS 解析

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鉴权方式记录类型适用场景
alibabacloudRAM AccessKeyA / CNAME / TXT国内业务、阿里云 SLB
cloudflareAPI TokenA / CNAME / TXT海外站点、CDN 一体
aws-route53IAM RoleA / CNAME / TXTAWS 生态、多账号
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 与证书将彻底从运维待办里消失。

上一篇 K9s 实战:终端面板高效管理 Kubernetes
下一篇 《Pod Evicted 排查实战:K8s 资源驱逐与磁盘压力定位》
最新评论
Docker 网络模式详解:bridge/host 生产选型 - FSData
Docker 网络模式详解:bridge/host 生产选型 - FSData
8月20日
[…] 注意最后那条:把端口绑定到 127.0.0.1,再由宿主机上的 Nginx 反代进来,比直接 -p 80:80 暴露安全得多,也方便统一做
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
Vue3 + TypeScript 工程化:类型安全的前端架构 - FSData
8月20日
[…] Pinia 的工程化用法,可以参考我们之前的实战文章 《Vue3 组合式 API + Pinia:状态管理从入门到实战》,那里覆盖了 Sto
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
2026 年最值得装的 10 个 VS Code 插件(AI/前端/后端/效率全覆盖) - FSData
8月19日
[…] 状态,点一下就能起停容器,配合我们讲过的 Docker Compose 编排 […]
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
Redis 缓存设计:穿透、击穿、雪崩与最佳实践 - FSData
8月19日
[…] 缓存三大坑的本质都是”缓存失效导致请求穿透到数据库”。记住口诀:穿透用布隆、击穿用锁、雪崩用错峰。配合合理的过期策略
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
GitHub Actions 实战:从零搭建 CI/CD 流水线 - FSData
8月19日
[…] Docker 镜像部署。结合我们之前写的 Docker Compose 一键编排实战,可以在 CI […]