Kubernetes服务发现:CoreDNS如何改写DNS规则
CoreDNS:如何把 Kubernetes 服务发现写进 DNS
核心观点:CoreDNS 是 CNCF 毕业的插件化权威 DNS 服务器。在 Kubernetes 中,它通过
kubernetes插件监听 API Server,将 Service / EndpointSlice 动态合成为 DNS 记录,承担集群内服务发现的首跳解析;流量转发与治理由 kube-proxy / CNI / Gateway / Mesh 承接。
0. 核心结论
| 维度 | 结论 |
|---|---|
| 范式定位 | 声明式平台发现(Service + CoreDNS)替代集群内应用级注册中心;非简单换地址 |
| 定义 | Go 实现的插件化权威 DNS;CNCF 毕业项目;Kubernetes 默认集群 DNS(≥1.13) |
| 控制面职责 | kubernetes 插件 watch API,将 Service / EndpointSlice 合成 A/AAAA/SRV 记录 |
| 数据面衔接 | 解析结果交付 kube-proxy / CNI,完成 ClusterIP → Pod 的流量转发 |
| 标准调用 | <service>.<namespace>.svc.cluster.local |
| 跨 NS 短名 | <service>.<namespace>(依赖 search 域补全) |
| 排障路径 | 应用配置 → resolv.conf → 解析验证 → Service/EndpointSlice → CoreDNS |
| 非目标 | 服务注册 · 流量治理 · 灰度路由 · 业务级健康裁决 |
1. 背景与问题定义
1.1 典型迁移场景
工作负载由 VM 迁至容器时,常见第一步并非拆分微服务,而是将 Web 与 API 分别容器化,例如 portal-web 与 order-api。
| 环境 | 服务定位方式 | 核心矛盾 |
|---|---|---|
| VM | 静态 IP 或反向代理转发 | 实例地址相对稳定 |
| Kubernetes | 工作负载映射为 Pod | Pod IP 随重建、扩缩容、调度而漂移 |
待解问题:portal-web 如何以稳定方式定位 order-api?
传统路径中,服务实例向 Eureka / Nacos / Consul 注册,调用方通过 SDK 完成发现。迁移至 Kubernetes 后,若继续固化 IP 或强绑定注册中心 SDK,将与平台原生的声明式服务发现机制产生冲突——二者解决的是同一类问题,但架构哲学不同(详见 §3)。
职责划分:Service 声明稳定入口;CoreDNS 解析服务标识;kube-proxy/CNI 完成负载均衡——三者组合,在集群内覆盖注册中心的基础发现能力。
1.2 文档结构
| # | 章节 | 阅读收益 |
|---|---|---|
| §3 | 范式对比 | 注册中心 vs Service + CoreDNS 能力边界与选型 |
| §4 | CoreDNS 本体 | 架构、插件模型、与 kube-dns 差异 |
| §5–7 | 解析机制 | 链路、记录类型、Pod 侧查询 |
| §8–10 | 迁移与运维 | 注册中心剥离 · 排障 · 容量 |
2. 对象模型
阅读前提:先厘清 Kubernetes 控制面对象及其与 DNS 的映射关系,再深入 CoreDNS 实现。
| 对象 | 职责 | DNS 层关联 |
|---|---|---|
| Service | 声明 ClusterIP 或 Headless 语义 | 决定 FQDN 结构与记录类型 |
| EndpointSlice | 维护后端 Pod IP:Port 集合 | Headless / SRV 记录的数据源 |
| CoreDNS | 插件化 DNS 服务进程 | kubernetes 插件监听并合成应答 |
| kube-proxy / CNI | 执行四层转发 | 承接 DNS 解析之后的数据路径 |
说明:EndpointSlice 已成为 Service 后端发现的主路径;CoreDNS 通过 Kubernetes API 按配置 watch Service、EndpointSlice、Namespace、Pod 等对象,按 DNS 规范合成应答。
3. 服务发现范式对比
本章目标:厘清注册中心与 Service + CoreDNS 是否解决同一问题、能力如何拆分、何时保留注册中心、何时应迁移。
行业共识(Kubernetes 官方文档、CNCF 实践):在 Kubernetes 集群内,Service + CoreDNS 是默认且推荐的服务发现基座;引入 Eureka / Nacos / Consul 并行运行,往往意味着与平台能力重复,增加运维负担。高级治理应叠加 Gateway 或 Service Mesh,而非用注册中心替代 DNS。(参考:NILUS、Uplatz 架构分析、HashiCorp Consul 社区讨论)
3.1 范式转变:从「实例注册」到「平台调和」
| 维度 | 注册中心范式 | Kubernetes 范式 |
|---|---|---|
| 哲学 | 命令式:实例主动上报「我在哪」 | 声明式:平台声明「我要什么」,控制器持续调和 |
| 发现单元 | 实例(IP:Port) | 服务(Service 身份) |
| 状态来源 | 应用心跳 + 注册 API | API Server + EndpointSlice 控制器 |
| 消费方式 | SDK / HTTP API 查实例列表 | 标准 DNS 解析服务名 |
| 负载均衡 | 客户端(Ribbon、自研) | 服务端(kube-proxy / IPVS / eBPF) |
| 耦合度 | 业务代码依赖注册中心客户端 | 业务仅依赖主机名,与平台解耦 |
本质变化:发现责任从应用层下沉到编排平台——这不是「把 Nacos 地址换成 CoreDNS 地址」,而是服务发现模型的替换。
3.2 双路径架构对照
3.3 注册中心完整链路(Eureka / Nacos / Consul)
以电商 order-service 调用 payment-service 为例:
| 阶段 | 参与方 | 动作 |
|---|---|---|
| 1 启动 | payment-service | 向注册中心上报 10.10.8.21:8080、元数据、健康状态 |
| 2 续约 | payment-service | 周期性心跳,维持租约 |
| 3 发现 | order-service | 通过 Nacos SDK 查询 payment-service 实例列表 |
| 4 选路 | order-service | Ribbon / 自研策略在实例间负载均衡 |
| 5 调用 | order-service | 直连选中实例 IP |
| 6 剔除 | 注册中心 | 心跳超时后移除实例 |
注册中心额外能力(超出基础 DNS 的部分):
| 能力 | 说明 | 典型产品 |
|---|---|---|
| 动态配置 | 配置中心、热推送 | Nacos Config |
| 元数据路由 | 版本、权重、标签路由 | Nacos / Consul |
| 跨环境统一目录 | VM + K8s + 多云实例同一视图 | Consul / Nacos |
| 显式健康模型 | 应用自定义健康检查逻辑 | Eureka / Consul |
| CAP 取向 | Eureka 偏 AP;Consul 偏 CP | 架构选型相关 |
3.4 Service + CoreDNS 完整链路
同等场景,Kubernetes 原生路径:
| 阶段 | 参与方 | 动作 |
|---|---|---|
| 1 声明 | 平台工程师 | 创建 payment-api Service(selector 关联 Pod) |
| 2 调和 | EndpointSlice 控制器 | 根据 Ready Pod 自动维护后端地址集合 |
| 3 合成 | CoreDNS kubernetes 插件 |
watch API,生成 payment-api.payment.svc.cluster.local → ClusterIP |
| 4 解析 | order-service | 标准 DNS 查询(无需 SDK) |
| 5 转发 | kube-proxy / CNI | ClusterIP 负载均衡至就绪 Pod |
| 6 剔除 | Readiness 探针 | 不健康 Pod 自动移出 EndpointSlice,DNS/转发同步更新 |
关键差异:应用无需注册代码、无需发现 SDK、无需客户端负载均衡库——平台已内建。
3.5 多维能力对比
| 维度 | 注册中心 | Service + CoreDNS | 说明 |
|---|---|---|---|
| 集群内基础发现 | ○ | ● | K8s 原生方案足够覆盖 |
| 语言 / 框架无关 | △ | ● | DNS 为标准协议;SDK 绑定语言 |
| 应用零侵入 | ✗ | ● | 注册中心需 SDK 或 Sidecar |
| 实例级元数据 | ● | △ | DNS 仅返回 IP;元数据需 Mesh/标签 |
| 权重 / 灰度路由 | ● | ✗ | 需 Gateway / Istio / Argo Rollouts |
| 客户端负载均衡 | ● | ✗(服务端 LB) | K8s 默认服务端均衡 |
| 跨集群统一目录 | ● | △ | 单集群 DNS 足够;多集群用 MCS/Mesh |
| VM + K8s 混合 | ● | △ | 外部依赖用 ExternalName / 注册中心桥接 |
| 配置中心一体化 | ●(Nacos) | △ | K8s 用 ConfigMap / Secret |
| 运维组件数 | +1 集群 | 0(平台内置) | 并行注册中心增加 HA、升级、监控成本 |
| 一致性模型 | 最终一致(AP)或 CP | 控制面调和(声明式) | 依赖控制器而非心跳 |
| TTL / 缓存延迟 | 秒级(SDK 缓存) | 秒级(DNS TTL + 客户端缓存) | 二者均有传播延迟 |
| 健康检查 | 应用自定义 | Readiness 探针 + Endpoint | 语义不同:基础设施 vs 业务 |
| Spring Cloud 集成 | 原生 | 需改造(去 Eureka/Ribbon) | 见 §8 迁移框架 |
图例:● 强项 · ○ 可覆盖 · △ 部分覆盖 / 需补充 · ✗ 非职责
3.6 能力归属:谁解决什么问题
| 需求 | 注册中心方案 | K8s 原生方案 | 推荐叠加 |
|---|---|---|---|
| 集群内服务互调 | SDK + 注册 | Service DNS + CoreDNS | — |
| 负载均衡 | 客户端 Ribbon | kube-proxy / IPVS / eBPF | — |
| 实例健康剔除 | 心跳超时 | Readiness + EndpointSlice | — |
| 金丝雀 / 权重发布 | 元数据路由 | ✗ | Argo Rollouts / Mesh |
| 熔断 / 重试 / 超时 | Hystrix / Sentinel | ✗ | Mesh / 应用框架 |
| mTLS / 零信任 | Consul Connect | ✗ | Istio / Linkerd |
| 跨命名空间隔离 | 逻辑服务名 | Namespace + DNS | NetworkPolicy |
| VM 遗留系统互通 | 统一注册 | ExternalName / Ingress | 注册中心桥接(过渡期) |
| 多集群服务发现 | 全局注册 | 单集群 DNS | MCS-API / 多集群 Mesh |
| 动态配置 | Nacos Config | ConfigMap + 控制器 | Nacos / Vault(按需) |
结论:注册中心是应用级服务目录 + 治理平面;Service + CoreDNS 是基础设施级寻址层。二者层级不同,不应在同一维度简单类比「谁更好」。
3.7 DNS 方案的局限与 Kubernetes 如何补足
纯 DNS 发现的已知局限(行业文献共识)及 K8s 侧的补偿机制:
| DNS 局限 | 传统 DNS 环境 | Kubernetes 环境中的补偿 |
|---|---|---|
| TTL 导致变更传播延迟 | 客户端缓存过期前仍访问旧 IP | EndpointSlice 更新 + 短 TTL(5–30s);ClusterIP 屏蔽 Pod IP 变化 |
| DNS 不做健康检查 | 死实例仍在记录中 | Readiness 探针 → 不健康 Pod 移出 Endpoint |
| 客户端缓存不可控 | JVM / libc / 语言运行时缓存策略不一 | 统一运行时 DNS 策略;压测中验证连接池与缓存刷新 |
| 无实例元数据 | 仅返回 IP | 需 Mesh 或 Gateway 提供版本/权重路由 |
| 负载均衡能力弱 | DNS 轮询 | kube-proxy / CNI 在 Service 层做均衡 |
| 查询放大 | search 域补全 | autopath / NodeLocal DNSCache / FQDN 规范 |
注意:CoreDNS 解决的是名字 → 入口地址;入口 → 健康后端由 EndpointSlice + 探针保证;流量策略不属于 DNS 层。
3.8 注册中心仍不可替代的场景
| 场景 | 原因 | 建议架构 |
|---|---|---|
| VM + K8s 混合部署 | 需要跨运行时统一服务目录 | 注册中心桥接 + K8s Service 同步(过渡期) |
| 多集群无 Mesh | 单集群 DNS 无法跨集群 | Consul / Nacos 全局视图,或 MCS-API |
| Spring Cloud / Dubbo 存量 | 框架深度绑定注册中心 | 分阶段剥离 SDK,见 §8 |
| 配置 + 发现一体化 | Nacos 同时承担配置中心 | 发现迁 K8s DNS;配置保留 Nacos 或迁 ConfigMap |
| 非 HTTP RPC 治理 | Dubbo 路由依赖注册元数据 | 保留 Nacos 或迁 Mesh |
| 合规审计级路由 | 需显式可查询的服务目录 | 注册中心或 Mesh 控制面 |
行业建议(HashiCorp、NILUS):Kubernetes 集群内默认不引入并行注册中心;异构或多集群场景再评估 Consul / Nacos 作为补充层,而非替代 CoreDNS。
3.9 三种发现模式定位
| 模式 | 定位 | 适用 |
|---|---|---|
| Registry(注册中心) | 应用级目录 + 跨运行时治理 | 混合云、多集群、框架绑定 |
| DNS(Service + CoreDNS) | 平台级寻址,集群内默认 | 东西向流量、无状态微服务 |
| Mesh(Istio / Linkerd) | 发现 + 流量策略 + 安全 | 金丝雀、mTLS、全链路治理 |
选型原则:集群内从 DNS 开始;治理需求叠加 Mesh/Gateway;异构环境注册中心作桥接——避免三套并行。
3.10 选型决策
| 结论 | 条件 |
|---|---|
| 首选 Service + CoreDNS | 同集群东西向、无状态服务、标准 HTTP/gRPC |
| DNS + Mesh | 需版本路由、mTLS、重试熔断,且愿承担 Mesh 运维成本 |
| 保留注册中心 | VM 混合、多集群无 Mesh、Dubbo/Spring Cloud 存量 |
| 反模式 | K8s 集群内同时跑 Nacos + CoreDNS 做同一层发现 |
4. CoreDNS 深度解析
本章目标:阐明 CoreDNS 的定义、演进路径、插件架构及在 Kubernetes 中的部署与配置模型。
4.1 定义与定位
CoreDNS 是基于 Go 实现的插件化权威 DNS 服务器(Authoritative DNS Server),具备双重定位:
| 定位 | 说明 |
|---|---|
| Kubernetes 集群 DNS | 自 1.13 起为默认方案,全面替代 kube-dns |
| 通用 DNS 引擎 | 可独立部署,后端支持 etcd、Consul、Zone 文件及上游转发 |
| CNCF 毕业项目 | 2017 纳入 → 2018 Incubating → 2019 Graduated |
权威 DNS:对指定 Zone 拥有最终应答权。*.svc.cluster.local 由 CoreDNS 权威解析;集群外域名经 forward 插件转交上游递归解析器。
设计原则:相较 BIND、Knot、PowerDNS 等单体架构,CoreDNS 以插件链组合能力——单二进制、可编排、可扩展。
4.2 技术演进
2014 SkyDNS2 基于 etcd 的服务发现 DNS(Kubernetes 早期)
↓ 扩展性与可观测性不足
2016 CoreDNS SkyDNS 继任者;插件链架构(Miek Gieben)
↓ CNCF 孵化;kubernetes 插件成熟
2017 kube-dns 三容器架构(kube-dns + dnsmasq + sidecar)
↓ 1.11 可选 CoreDNS;1.13 默认;kube-dns 退役
2019 CoreDNS CNCF 毕业;成为 Kubernetes 默认集群 DNS 标准方案
| 代际 | 代表方案 | 架构特征 | 主要局限 |
|---|---|---|---|
| 第一代 | SkyDNS | etcd 后端服务发现 | 难以集成监控、缓存与多后端 |
| 第二代 | kube-dns | 三容器 Sidecar 模式 | 架构复杂、缓存隐患、定制困难 |
| 当前 | CoreDNS | 单进程 + 插件链 | 需掌握 Corefile 与插件语义 |
4.3 插件架构:Server Block 与 Plugin Chain
配置基本单元为 Server Block(监听地址 + Zone 集合),每个 Server Block 挂载一条 Plugin Chain。
| 机制 | 行为描述 | 工程含义 |
|---|---|---|
| Server Block 匹配 | 按最长 Zone 后缀选择配置块 | cluster.local 与 . 可拥有不同处理链 |
| 插件启用 | Corefile 声明某插件及参数 | 未写入 Corefile 的插件不会参与本 Server Block |
| 插件执行顺序 | 由 CoreDNS 编译时插件顺序决定 | Corefile 行顺序主要表达配置,不等同于精确执行顺序 |
| 终止应答 | 插件生成响应并结束链路 | kubernetes、hosts、file |
| Fallthrough | 插件在指定未命中场景继续下游 | kubernetes 处理反向域、Pod 域常用 |
| 旁路观测 | 插件记录指标、日志或健康状态 | prometheus、errors、health |
要点:Corefile 决定「启用什么、参数是什么」;编译时插件链决定「相对执行顺序」。因此生产调优应关注插件语义与参数,不应依赖移动 Corefile 行位置来改变链路行为。
4.4 常用内置插件
CoreDNS 默认内置约 30+ 插件。Kubernetes 生产环境高频使用:
| 插件 | 分类 | 功能说明 |
|---|---|---|
kubernetes |
数据源 | 监听 API Server,合成 Service / Pod 记录 |
forward |
转发 | 非集群域查询转上游 DNS |
cache |
性能 | 应答缓存,降低重复查询负载 |
errors |
可观测 | 错误级日志输出 |
health |
运维 | Liveness 端点(:8080/health) |
ready |
运维 | Readiness;informer 同步完成后置位 |
prometheus |
可观测 | 指标暴露(:9153/metrics) |
reload |
运维 | Corefile 热重载 |
loop |
安全 | 转发环路检测 |
loadbalance |
性能 | A 记录随机打散,均衡客户端选路 |
autopath |
性能 | 服务端 search 补全,抑制查询放大 |
rewrite |
策略 | 查询名 / 应答重写 |
hosts / file |
数据源 | 静态记录(类 hosts / Zone 文件) |
etcd |
数据源 | etcd 后端(兼容 SkyDNS 模式) |
扩展:支持编译第三方插件,如 multicluster(基于 MCS-API 的多集群 DNS)。
4.5 与 kube-dns 对照
| 维度 | kube-dns(已退役) | CoreDNS |
|---|---|---|
| 运行时 | 三容器 Pod | 单进程 |
| 配置模型 | 多段 ConfigMap | 统一 Corefile DSL |
| 扩展机制 | 几乎不可扩展 | 插件链可组合 |
| 缓存实现 | dnsmasq | cache 插件 |
| 可观测性 | 有限 | prometheus 指标完备 |
| Stub 域 | stubDomains |
独立 Server Block + forward |
| 默认版本 | ≤1.12 | ≥1.13 |
| Service 命名 | kube-dns |
保留 kube-dns(向后兼容) |
4.6 Kubernetes 部署拓扑
通常作为集群 DNS add-on 部署于 kube-system。不同发行版可能由 kubeadm、托管云控制面或平台 Addon Manager 管理,但核心资源形态一致:
| 资源类型 | 名称 | 职责 |
|---|---|---|
| Deployment | coredns |
多副本运行;监听 :53 UDP/TCP |
| Service | kube-dns |
ClusterIP;Pod nameserver 指向此处 |
| ConfigMap | coredns |
承载 Corefile |
| ServiceAccount | coredns |
访问 API Server 的 RBAC 主体 |
kubelet 注入参数(决定 Pod DNS 行为):
| 参数 | 默认值 | 含义 |
|---|---|---|
--cluster-dns |
kube-dns ClusterIP |
Pod nameserver |
--cluster-domain |
cluster.local |
集群 DNS 后缀 |
kubectl -n kube-system get deploy,svc,cm,sa -l k8s-app=kube-dns
kubectl -n kube-system get cm coredns -o jsonpath='{.data.Corefile}'
4.7 Corefile 配置模型
<zone>[:<port>] {
<plugin> [<args>]
...
}
官方默认配置:
.:53 {
errors
health { lameduck 5s }
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
| 指令 | 语义 |
|---|---|
.:53 |
全接口监听 53 端口 |
kubernetes cluster.local ... |
对集群域及反向域权威解析 |
pods insecure |
启用 Pod IP 形式的正向查询(兼容 kube-dns) |
fallthrough ... |
反向域未命中时透传下游 |
forward . /etc/resolv.conf |
非集群域转发至节点上游 |
cache 30 |
插件层缓存 30s |
loadbalance |
多条 A 记录随机排序 |
reload |
ConfigMap 变更自动生效 |
loop |
环路检测与阻断 |
冷启动:启动后 informer 同步窗口默认 5s;未同步记录可能返回 SERVFAIL。ready 插件在缓存就绪后报告 Ready;大规模集群可显式调大 startup_timeout。
4.8 Stub Domain 与上游解析
通过追加 Server Block 实现域级转发(等价于 kube-dns stubDomains):
.:53 {
kubernetes cluster.local in-addr.arpa ip6.arpa { ... }
forward . 172.16.0.1
...
}
consul.local:53 {
errors
cache 30
forward . 10.150.0.1
}
| 需求 | 配置策略 |
|---|---|
| 指定全局上游 | forward . <ip> |
| 域级专用解析器 | 独立 Server Block |
| 阻断特定域 | template / rewrite 返回 NXDOMAIN |
约束:forward 上游不支持 FQDN 形式(如 ns.example.com 将被忽略)。
4.9 能力边界
| 职责范围内 | 职责范围外 |
|---|---|
| 集群内外 DNS 解析与转发 | 服务注册与心跳维护 |
| 基于 K8s 对象的动态记录合成 | 灰度 / 金丝雀 / 熔断 / 限流 |
| 缓存、指标、健康探测 | 替代 Nacos / Consul 治理平面 |
| Stub 域与记录重写 | HTTP 级业务健康裁决 |
| DNS over TLS / HTTPS | 替代 kube-proxy 负载均衡 |
4.10 Corefile 变更工作流
原则:CoreDNS 是全集群关键路径组件;Corefile 变更必须像控制面变更一样灰度、可回滚、可观测。
| 阶段 | 动作 | 命令 / 检查 |
|---|---|---|
| 1 备份 | 保存当前 Corefile 与 Deployment | kubectl -n kube-system get cm coredns -o yaml > coredns.cm.bak.yaml |
| 2 校验 | 在测试集群或临时 Pod 中验证语法 | coredns -conf Corefile -dns.port=1053 |
| 3 小步发布 | 仅修改必要插件参数 | 优先改 cache、forward、kubernetes 子参数 |
| 4 观察 | 关注 SERVFAIL、NXDOMAIN、延迟、CoreDNS 重启 | Prometheus + kubectl -n kube-system logs deploy/coredns |
| 5 回滚 | 恢复 ConfigMap 并触发重载 | kubectl apply -f coredns.cm.bak.yaml |
推荐:在生产集群中使用 GitOps 管理 Corefile,所有变更带审阅、Diff、回滚记录;禁止直接手工编辑 ConfigMap 后离开现场。
5. 解析链路与生产实践
5.1 端到端时序
5.2 分层模型
| 层级 | 组件 | 职责 |
|---|---|---|
| L1 | /etc/resolv.conf |
kubelet 按 dnsPolicy 注入解析配置 |
| L2 | kube-dns Service |
集群 DNS 统一入口(后端为 CoreDNS) |
| L3 | CoreDNS | 集群内 kubernetes;集群外 forward |
| L4 | API Server | Service / EndpointSlice 数据源 |
5.3 生产增强手段
| 手段 | 机制 | 适用场景 |
|---|---|---|
| NodeLocal DNSCache | 节点级 DNS 缓存 DaemonSet | 大规模集群、高 QPS |
| autopath | 服务端 search 优化 | 需 pods verified;抑制查询放大 |
| 水平扩容 | 增加副本 + 反亲和 | CoreDNS CPU 或延迟接近阈值 |
| Cluster Proportional Autoscaler | 按节点数 / Core 数调整副本 | 集群规模变化频繁 |
| Pod 反亲和 | 跨节点分布 | 规避单点故障 |
| PDB | 升级过程最低可用保障 | 生产环境标配 |
| forward 并发控制 | max_concurrent 限制上游并发 |
上游 DNS 抖动或外部域洪峰 |
| 上游隔离 | 内外域拆分 Server Block | 企业 DNS、Consul、私有域混合 |
5.4 查询放大与 NodeLocal DNSCache
默认 ndots:5 会让短名和多数外部域先走 search 域补全。例如容器内查询 api.example.com 时,可能先尝试:
api.example.com.<ns>.svc.cluster.local
api.example.com.svc.cluster.local
api.example.com.cluster.local
api.example.com
| 问题 | 影响 | 处理 |
|---|---|---|
| search 补全过多 | 单次业务解析放大为多次 DNS 查询 | 外部域使用尾点:api.example.com. |
| 节点到 CoreDNS 跨节点访问 | 网络抖动放大 DNS 延迟 | 部署 NodeLocal DNSCache |
| UDP conntrack 压力 | 高 QPS 下丢包或超时 | NodeLocal DNSCache 将本地查询转为 TCP/本地缓存 |
| 重复 NXDOMAIN | 无效短名持续冲击 CoreDNS | 规范配置名;监控 NXDOMAIN TopN |
实践建议:大规模集群优先启用 NodeLocal DNSCache;应用侧将外部域名写成带尾点 FQDN,跨命名空间服务写成 <svc>.<ns> 或完整 FQDN。
5.5 解析链路中的缓存层
| 层级 | 缓存主体 | 可控性 | 关注点 |
|---|---|---|---|
| L1 | 应用 / 语言运行时 | 差异较大 | JVM、Go、glibc、连接池各有行为 |
| L2 | NodeLocal DNSCache | 平台可控 | 节点级命中率、DaemonSet 健康 |
| L3 | CoreDNS cache 插件 |
平台可控 | 正向 / 负向缓存 TTL、命中率 |
| L4 | 上游企业 DNS | 外部依赖 | 超时、递归策略、企业域分流 |
注意:DNS TTL 只约束 DNS 缓存,不会自动刷新应用连接池中已建立的 TCP 连接。服务下线、灰度切换、证书轮转等场景必须同时考虑连接池生命周期。
6. DNS 记录与命名规范
6.1 FQDN 结构
order-api . order . svc . cluster.local
└─ Svc ─┘ └─ NS ┘ └┘ └──── cluster-domain(默认 cluster.local)────┘
6.2 记录类型(DNS-Based Service Discovery 规范)
| 类型 | 应答行为 | 典型场景 |
|---|---|---|
| ClusterIP Service | A/AAAA → ClusterIP | 无状态微服务互调 |
| Headless Service | A/AAAA → 就绪 Pod IP 列表 | StatefulSet 成员发现 |
| ExternalName Service | CNAME → 外部主机名 | VM / 外部依赖过渡 |
| SRV | _port._proto.<svc>.<ns>.svc.<domain> |
命名端口发现 |
| Pod IP | 10-244-1-5.<ns>.pod.cluster.local |
调试(pods 非 disabled) |
| Pod hostname | <hostname>.<subdomain>.<ns>.svc.<domain> |
Headless + subdomain |
StatefulSet 稳定标识:
<pod-name>.<service-name>.<namespace>.svc.cluster.local
示例:mysql-0.mysql.data.svc.cluster.local → 固定路由至 mysql-0
6.3 Headless + StatefulSet 参考配置
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: data
spec:
clusterIP: None
selector:
app: mysql
ports:
- name: mysql
port: 3306
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
namespace: data
spec:
serviceName: mysql
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
| Pod | 稳定 FQDN |
|---|---|
mysql-0 |
mysql-0.mysql.data.svc.cluster.local |
mysql-1 |
mysql-1.mysql.data.svc.cluster.local |
约束:Pod 处于 Ready 状态方生成 A 记录;publishNotReadyAddresses: true 可覆盖此行为。
6.4 kubernetes 插件参数
| 参数 | 语义 | 默认 / 建议 |
|---|---|---|
pods disabled |
拒绝 Pod IP 类查询 | 插件默认 |
pods insecure |
无校验返回 A 记录 | 官方默认 Corefile |
pods verified |
校验 Pod 存在性 | autopath 前置;内存开销较高 |
endpoint_pod_names |
A 记录优先使用 Pod 名 | StatefulSet 场景推荐 |
ignore empty_service |
无就绪 Endpoint 时 NXDOMAIN | 允许 search 继续匹配 |
ttl N |
记录 TTL(0–3600s) | 默认 5s;生产常设 30s |
namespaces |
限定暴露的命名空间 | 多租户隔离 |
labels |
按标签过滤资源 | 精细化 DNS 暴露 |
6.5 ClusterIP Service 参考
apiVersion: v1
kind: Service
metadata:
name: payment-api
namespace: payment
spec:
selector:
app: payment-api
ports:
- name: http
port: 8080
targetPort: 8080
| 调用上下文 | 推荐地址 |
|---|---|
| 同命名空间 | http://payment-api:8080 |
| 跨命名空间 | http://payment-api.payment.svc.cluster.local:8080 |
kubectl run dnsutils --image=registry.k8s.io/e2e-test-images/agnhost:2.39 \
--restart=Never -- sleep 3600
kubectl exec dnsutils -- nslookup payment-api.payment.svc.cluster.local
7. Pod 侧解析机制
7.1 resolv.conf 结构
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
| 字段 | 语义 |
|---|---|
nameserver |
指向 kube-dns ClusterIP |
search |
短名补全域列表(≤32 条,总长 ≤2048) |
ndots:5 |
标签内 . 少于 5 且无尾点时,优先 search 补全 |
7.2 解析规则(官方语义)
| 规则 | 示例(Pod 位于 test NS) |
结果 |
|---|---|---|
| R1 | 查询 data(服务在 prod) |
限定 test NS → 未命中 |
| R2 | 查询 data.prod |
search 补全 → 命中 prod/data |
| R3 | 查询 payment-api |
依次 search 补全 → 命中 |
7.3 命名书写规范
| 场景 | 推荐格式 |
|---|---|
| 同命名空间 | payment-api |
| 跨命名空间 | payment-api.payment 或完整 FQDN |
| 外部域名 | api.partner.example.com.(FQDN + 尾点) |
统一约定:业务配置文件中优先使用完整 FQDN,代码示例和同命名空间临时调用可使用短名。这样可以减少跨命名空间迁移、灰度环境复制和 search 域差异带来的误解析。
7.4 dnsPolicy 策略
| 策略 | 行为 | 适用 |
|---|---|---|
ClusterFirst(默认) |
优先集群 DNS;外部域 forward | 常规工作负载 |
ClusterFirstWithHostNet |
HostNetwork 下仍走集群 DNS | hostNetwork: true 必配 |
Default |
继承节点 resolv.conf | 特殊网络需求 |
None + dnsConfig |
完全自定义 | 精细化 DNS 控制 |
注:Windows 节点解析语义与 Linux 存在差异,混合集群需独立验证。
7.5 dnsConfig 示例
需要缩短外部域解析路径或注入企业内网搜索域时,可显式配置 dnsConfig:
apiVersion: v1
kind: Pod
metadata:
name: dns-demo
spec:
dnsPolicy: ClusterFirst
dnsConfig:
options:
- name: ndots
value: "2"
- name: timeout
value: "2"
- name: attempts
value: "2"
searches:
- corp.example.com
containers:
- name: app
image: registry.k8s.io/e2e-test-images/agnhost:2.39
| 参数 | 作用 | 风险 |
|---|---|---|
ndots |
控制何时先按绝对域名查询 | 过低可能改变短名优先级 |
timeout |
单次 DNS 查询超时 | 过低会放大瞬时抖动 |
attempts |
重试次数 | 过高会放大故障流量 |
searches |
追加搜索域 | 过多会增加查询次数 |
7.6 客户端运行时注意事项
| 运行时 / 组件 | 关注点 | 建议 |
|---|---|---|
| JVM | DNS 缓存受 networkaddress.cache.ttl 影响 |
显式设置合理 TTL,避免永久缓存 |
| Go | 默认使用系统解析器或 Go 解析器 | 容器内压测验证 GODEBUG=netdns 行为 |
| glibc | 遵循 resolv.conf,受 ndots/search 影响明显 |
外部域使用尾点 |
| HTTP 连接池 | DNS 变更不等于连接立即重建 | 配置连接最大生存期与空闲回收 |
| gRPC | 长连接会隐藏后端变更 | 配置 keepalive、连接重建策略 |
8. 迁移实践
前提:迁移目标不是「停用 Nacos」,而是将发现层从应用级注册中心切换为平台级 Service DNS(见 §3)。
8.1 三阶段迁移框架(注册中心 → K8s DNS)
| 阶段 | 名称 | 动作 | 注册中心状态 |
|---|---|---|---|
| P1 | 并行运行 | K8s 内创建 Service;应用仍走 Nacos,验证 DNS 可达 | 保留,作兜底 |
| P2 | 切换发现 | 调用方改 Service DNS;移除 @EnableDiscoveryClient、Ribbon |
只保留配置/治理(可选) |
| P3 | 下线注册 | 剥离 SDK 依赖;Nacos 仅保留跨集群/VM 桥接(若需要) | 缩减或退役 |
8.2 四层改造清单
| 层级 | 注册中心时代 | K8s 时代 | 改造动作 |
|---|---|---|---|
| L1 发现 | Nacos / Eureka 查实例 | Service DNS | 删除注册代码 · 改主机名 |
| L2 负载均衡 | Ribbon 客户端 LB | Service ClusterIP | 移除 Ribbon 依赖 |
| L3 配置 | Nacos Config | ConfigMap / Secret | 按需保留或迁移 |
| L4 治理 | Sentinel / Hystrix | Gateway / Mesh | 评估是否叠加 Mesh |
8.3 Spring Cloud 对照改造
| Spring Cloud 组件 | Kubernetes 替代 | 迁移要点 |
|---|---|---|
@EnableDiscoveryClient |
无需注解 | 删除;直接 HTTP 调用 Service 名 |
spring-cloud-starter-netflix-eureka-client |
CoreDNS + Service | 移除依赖 |
Ribbon / LoadBalancerClient |
kube-proxy 服务端 LB | 移除;单地址 http://svc:port |
spring-cloud-starter-alibaba-nacos-discovery |
Service DNS | 发现剥离;配置可暂留 Nacos |
Spring Cloud Gateway + Eureka |
Ingress / Gateway API | 入口层独立迁移 |
Feign + 服务名 |
Feign + K8s DNS 名 | URL 从逻辑名改为 http://order-api.order:8080 |
改造前(Eureka + Ribbon):
@FeignClient(name = "payment-service")
public interface PaymentClient { ... }
// 底层:查 Eureka → 选实例 → 直连 IP
改造后(K8s DNS):
@FeignClient(name = "payment-api", url = "http://payment-api.payment:8080")
public interface PaymentClient { ... }
// 底层:DNS 解析 → ClusterIP → kube-proxy 转发
或配置化:
# application.yml
payment:
url: http://payment-api.payment.svc.cluster.local:8080
8.4 参考案例:VM → Kubernetes
portal-web → user-service → order-service → mysql
| 遗留配置 | 目标地址 | 对应改造 |
|---|---|---|
http://user-service.prod.company.local |
user-api.user.svc.cluster.local |
建 Service · 改 URL |
http://10.10.8.21:8080 |
order-api.order.svc.cluster.local |
建 Service · 去 IP |
MYSQL_HOST=10.10.12.30 |
mysql-0.mysql.data.svc.cluster.local |
Headless + StatefulSet |
nacos://payment-service |
payment-api.payment.svc.cluster.local |
发现走 DNS;治理走 Mesh |
8.5 依赖映射
| 旧依赖 | 新入口 | 动作 |
|---|---|---|
http://10.10.8.21:8080 |
order-api.order.svc.cluster.local:8080 |
建 SVC · 改配置 · 删注册 |
nacos://payment-service |
payment-api.payment.svc.cluster.local |
剥离 discovery SDK |
eureka://order-service |
order-api.order.svc.cluster.local |
移除 Eureka 客户端 |
8.6 ExternalName 过渡(VM 遗留)
apiVersion: v1
kind: Service
metadata:
name: legacy-user
namespace: user
spec:
type: ExternalName
externalName: user-service.prod.company.local
应答类型为 CNAME。限制:仅主机名 · 无端口映射 · 部分客户端兼容性受限。适用于 P1 并行阶段。
8.7 协作分工
| 角色 | 职责 |
|---|---|
| 平台工程 | DNS 命名规范 · CoreDNS 运维 · 迁移节奏把控 |
| 应用团队 | 剥离 SDK · 配置改用 Service 名 |
| 架构 | 明确发现/配置/治理三层归属(见 §3.6) |
| SRE | 监控 SERVFAIL / NXDOMAIN · 迁移期双轨验证 |
9. 场景决策
完整选型逻辑见 §3.10。本章为速查摘要。
| 场景 | 匹配度 | 关键实践 |
|---|---|---|
| 同集群无状态微服务 | 高度匹配 | Service DNS,无需注册中心 |
| StatefulSet | 匹配 | Headless + Pod FQDN |
| VM + K8s 混合 | 分阶段 | 注册中心桥接 → 逐步 DNS |
| Spring Cloud 存量 | 分阶段 | P1 并行 → P2 剥离 SDK → P3 下线 |
| 流量治理 | 仅基础层 | DNS + Gateway / Mesh,非 Nacos 路由 |
| 多集群 | 按需 | MCS / Mesh / 全局注册中心 |
10. 运维与排障
10.1 容量规划
| 维度 | 建议基线 |
|---|---|
| 副本 | ≥2;按 QPS、P99 延迟、CPU 饱和度水平扩展 |
| 资源 | 小型集群 100–300m CPU / 128–256Mi;大型集群按压测上调 |
| 拓扑 | podAntiAffinity 跨节点分布 |
| 可用性 | PDB:minAvailable: 1 |
| API 压力 | 大规模集群调优 apiserver_qps / apiserver_burst |
| 节点本地缓存 | 高 QPS 集群启用 NodeLocal DNSCache |
容量判断信号:
| 信号 | 含义 | 动作 |
|---|---|---|
| CPU 持续 > 70% | CoreDNS 处理能力接近瓶颈 | 增加副本或启用 NodeLocal DNSCache |
| P99 延迟升高 | 上游慢、缓存低命中或 CoreDNS 饱和 | 拆分内外域、调缓存、检查上游 |
| SERVFAIL 增长 | API / 上游 / CoreDNS 自身异常 | 先看 CoreDNS 日志与 forward 指标 |
| NXDOMAIN 增长 | 错误域名、search 放大、服务缺失 | 统计 Top 查询名,回查应用配置 |
| Pod 重启 / OOM | watch 对象过多或资源不足 | 调资源、减少 pods verified、扩容 |
10.2 观测指标
| 类别 | 指标 / 端点 | 关注点 |
|---|---|---|
| 延迟 | coredns_dns_request_duration_seconds |
P95/P99 与上游波动 |
| 吞吐 | coredns_dns_requests_total |
按 type、rcode、zone 分组 |
| 错误码 | SERVFAIL、NXDOMAIN、REFUSED |
区分系统异常与业务误配 |
| API 客户端 | coredns_kubernetes_rest_client_requests_total |
API Server 访问失败、限流 |
| 缓存 | coredns_cache_hits_total / coredns_cache_misses_total |
命中率、负向缓存 |
| 转发 | coredns_proxy_request_duration_seconds{proxy_name="forward"}、coredns_forward_max_concurrent_rejects_total |
上游 DNS 延迟、错误与并发拒绝 |
| 记录编程 | coredns_kubernetes_dns_programming_duration_seconds |
Service 变更到 DNS 可见的延迟 |
| 进程 | process_cpu_seconds_total、process_resident_memory_bytes |
CPU / 内存容量 |
| 健康 | :8080/health、:8181/ready |
存活与 informer 同步状态 |
PromQL 参考:
# CoreDNS QPS
sum(rate(coredns_dns_requests_total[5m]))
# SERVFAIL 比例
sum(rate(coredns_dns_responses_total{rcode="SERVFAIL"}[5m]))
/
sum(rate(coredns_dns_responses_total[5m]))
# P99 解析延迟
histogram_quantile(0.99, sum(rate(coredns_dns_request_duration_seconds_bucket[5m])) by (le))
10.3 故障定位流程
「服务不可达」→ 按序排查,避免跳步
| 步 | 检查项 | 操作 | 期望 |
|---|---|---|---|
| 1 | 目标主机名 | 审查环境变量 / 配置 | 使用 <svc>.<ns> 或完整 FQDN |
| 2 | DNS 配置 | kubectl exec <pod> -- cat /etc/resolv.conf |
nameserver 指向集群 DNS |
| 3 | 解析结果 | kubectl exec <pod> -- nslookup <fqdn> |
返回 ClusterIP / Pod IP / CNAME |
| 4 | Service | kubectl get svc -n <ns> <svc> -o wide |
Service 存在,端口正确 |
| 5 | EndpointSlice | kubectl get endpointslice -n <ns> -l kubernetes.io/service-name=<svc> |
存在就绪 endpoint |
| 6 | CoreDNS Pod | kubectl -n kube-system get pod -l k8s-app=kube-dns -o wide |
多副本 Running / Ready |
| 7 | CoreDNS 日志 | kubectl -n kube-system logs deploy/coredns --tail=100 |
无 loop、panic、API 错误 |
| 8 | Corefile | kubectl -n kube-system get cm coredns -o yaml |
Corefile 符合预期 |
| 9 | 指标 | kubectl -n kube-system port-forward deploy/coredns 9153 |
查看 QPS、rcode、延迟 |
分叉判断:
| 现象 | 定位方向 |
|---|---|
| 解析失败 | DNS 链路(本章) |
| 解析成功、连接超时 | Endpoint · NetworkPolicy · 数据面 |
10.4 典型故障模式
| 现象 | 根因倾向 | 处置 |
|---|---|---|
| SERVFAIL 激增 | 冷启动 / API 不可达 | 检查 API 连通性 · 调整 startup_timeout |
| NXDOMAIN 激增 | 命名错误或 Service 缺失 | 核对配置与对象存在性 |
| 外部域解析慢 | ndots 放大 · 缓存不足 | FQDN 尾点 · 调优 cache |
| CoreDNS OOM | pods verified + 大规模 watch |
评估 pods insecure 或扩容 |
| 变更后 DNS 中断 | Corefile 语法错误 | 变更前用 coredns -conf Corefile -dns.port=1053 试启动 |
| CoreDNS CrashLoop | loop 检测到转发环路 |
检查 forward . /etc/resolv.conf 是否指回集群 DNS |
| 仅外部域失败 | 上游 DNS 不可达或企业域分流错误 | 检查 forward 上游、NetworkPolicy、防火墙 |
| 仅某命名空间失败 | Service / EndpointSlice 不存在或无 Ready Pod | 检查 selector、Readiness、命名空间 |
| 随机超时 | UDP 丢包、conntrack、节点网络抖动 | 启用 NodeLocal DNSCache,检查节点网络 |
| Headless 返回不完整 | Pod 未 Ready 或未设置 subdomain | 检查 publishNotReadyAddresses 与 StatefulSet 配置 |
10.5 常用诊断命令
# 查看 CoreDNS 资源
kubectl -n kube-system get deploy,svc,cm,pod -l k8s-app=kube-dns -o wide
# 从业务 Pod 内验证解析
kubectl exec -n <app-ns> <pod> -- cat /etc/resolv.conf
kubectl exec -n <app-ns> <pod> -- nslookup <svc>.<ns>.svc.cluster.local
# 临时启动调试 Pod
kubectl run dnsutils --image=registry.k8s.io/e2e-test-images/agnhost:2.39 \
--restart=Never -- sleep 3600
kubectl exec dnsutils -- nslookup kubernetes.default.svc.cluster.local
# 查看 Service 与 EndpointSlice
kubectl get svc -n <ns> <svc> -o yaml
kubectl get endpointslice -n <ns> -l kubernetes.io/service-name=<svc> -o wide
# 查看 CoreDNS Corefile 与日志
kubectl -n kube-system get cm coredns -o jsonpath='{.data.Corefile}'
kubectl -n kube-system logs deploy/coredns --tail=200
# 暴露指标
kubectl -n kube-system port-forward deploy/coredns 9153:9153
11. 快速参考
核心定义
CoreDNS = Go · 插件化权威 DNS · CNCF Graduated · K8s 默认集群 DNS
配置载体:Corefile | 集群内:kubernetes | 集群外:forward
FQDN 模板
<svc>.<ns>.svc.cluster.local # ClusterIP
<svc>.<ns> # 跨 NS 短名
<pod>.<svc>.<ns>.svc.cluster.local # StatefulSet
_<port>._<proto>.<svc>.<ns>.svc... # SRV
常用命令
kubectl -n kube-system get deploy,svc,cm -l k8s-app=kube-dns
kubectl -n kube-system logs deploy/coredns --tail=100
kubectl -n kube-system get cm coredns -o jsonpath='{.data.Corefile}'
kubectl exec <pod> -- cat /etc/resolv.conf
kubectl exec <pod> -- nslookup <fqdn>
范式对比速记
注册中心 = 应用级目录 + SDK + 客户端 LB + 治理
K8s DNS = 平台级寻址 + 零侵入 + 服务端 LB(kube-proxy)
治理需求 = Gateway / Mesh(不是 Nacos 替代 CoreDNS 的理由)
反模式
| 反模式 | 风险 |
|---|---|
| K8s 内 Nacos 与 CoreDNS 做同层发现 | 重复运维、与平台对抗 |
| 只改地址不删 SDK | 双轨发现、排障困难 |
| 用注册中心做金丝雀 | 应使用 Mesh / Rollouts |
| 未验证即修改 Corefile | 全集群 DNS 不可用 |
| 固化 Pod IP | 与 Pod 生命周期冲突 |
| 跨 NS 仅使用短服务名 | 解析至错误命名空间 |
| 将 CoreDNS 用作治理平面 | 超出组件职责边界 |
附录 · 参考资料
| 资源 | 链接 |
|---|---|
| CoreDNS 官网 | https://coredns.io/ |
| CoreDNS Manual | https://coredns.io/manual/toc/ |
| CNCF CoreDNS | https://www.cncf.io/projects/coredns/ |
| kubernetes 插件 | https://coredns.io/plugins/kubernetes/ |
| 插件目录 | https://coredns.io/plugins/ |
| K8s DNS 概念 | https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/ |
| 自定义 DNS | https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/ |
| DNS 发现规范 | https://github.com/kubernetes/dns/blob/master/docs/specification.md |
| DNS 调试 | https://kubernetes.io/docs/tasks/administer-cluster/dns-debugging-resolution/ |
| NodeLocal DNSCache | https://kubernetes.io/docs/tasks/administer-cluster/nodelocaldns/ |
| 部署清单 | https://github.com/coredns/deployment/tree/master/kubernetes |
| 服务发现范式对比(NILUS) | https://www.nilus.be/blog/service_discovery_patterns_compared_in_kubernetes/ |
| Consul 在 K8s 中的用例 | https://discuss.hashicorp.com/t/what-are-advantages-use-consul-in-kubernetes-use-cases-without-service-mesh/9901 |
| Spring Cloud → K8s 迁移 | https://elamranioussama.me/blog/spring-cloud-to-kubernetes/ |
| DNS vs Consul/Eureka 架构分析 | https://uplatz.com/blog/an-architectural-analysis-of-service-discovery-patterns-a-comparative-study-of-consul-eureka-and-dns-based-implementations/ |
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)