Kubernetes 集群中的服务发现问题通常不依赖外部负载均衡器,而是由内部 DNS 组件持续维护 Service 与 Pod 的解析记录。默认安装中,CoreDNS 以 Deployment 方式运行在 kube-system 命名空间下,并通过 Service 暴露为集群 DNS 地址。Pod 内部的 resolv.conf 会把 nameserver 指向该 Service 的 ClusterIP,因此所有基于名称的访问都会先经过 CoreDNS。

理解解析链路是配置与管理 DNS 的前提。当一个应用容器请求 my-svc.my-namespace.svc.cluster.local 时,resolv.conf 中配置的搜索域会参与补全。若查询名称包含的圆点数量小于 ndots 阈值,系统会依次追加搜索域进行解析。CoreDNS 收到请求后,通过 kubernetes 插件查询 API Server 中的 Service 或 Pod 信息,并返回对应的 ClusterIP 或 Pod IP。本篇将从 Pod 级策略、CoreDNS 插件配置、Service 记录类型以及排错方法几个角度展开。
Pod DNS 策略与 resolv.conf 的生成方式
每个 Pod 的 DNS 行为由 dnsPolicy 字段控制。Kubernetes 提供四种内置策略:Default、ClusterFirst、ClusterFirstWithHostNet 和 None。其中 Default 表示从运行 Pod 的节点继承 resolv.conf,适合需要完全使用宿主机网络 DNS 配置的场景,但会丢失集群内服务发现能力。默认策略 ClusterFirst 则把集群 DNS 服务的地址写入 Pod 的 nameserver,同时保留节点上的 search 域和 options。
ClusterFirstWithHostNet 专门用于 hostNetwork: true 的 Pod。如果继续使用 ClusterFirst,在 hostNetwork 模式下 kubelet 不会改写 Pod 的 resolv.conf,可能导致集群内域名无法解析。而 None 策略下,系统不会自动生成任何 DNS 配置,必须通过 dnsConfig 字段显式提供 nameserver、search 和 options,这为特殊场景提供了最大灵活性。
以下示例展示一个使用自定义 DNS 配置的 Pod。它强制使用集群 DNS,同时把 ndots 调整为 2,以减少外部短域名解析时的搜索域补全次数:
apiVersion: v1
kind: Pod
metadata:
name: dns-custom-pod
spec:
containers:
- name: app
image: nginx:1.25
dnsPolicy: None
dnsConfig:
nameservers:
- 10.96.0.10
searches:
- default.svc.cluster.local
- svc.cluster.local
- cluster.local
options:
- name: ndots
value: "2"
需要特别说明的是,dnsConfig 的作用是在原有策略基础上追加或覆盖部分配置,而不是完全替代。例如在使用 ClusterFirst 时,仍然可以通过 dnsConfig 增加额外的 search 域,或设置 timeout 和 attempts 参数。很多团队为了降低外部域名解析延迟,会把 ndots 从默认的 5 调整为 1 或 2,这样对于不包含足够数量圆点的外部域名,系统会优先直接查询,而不是先尝试所有集群内部搜索域。
CoreDNS Corefile 的插件链与配置定制
CoreDNS 的配置集中在 kube-system 命名空间下的 ConfigMap 中,默认名称为 coredns。该 ConfigMap 的核心是一个名为 Corefile 的配置文件,通过插件链组织请求处理流程。默认插件通常包含 errors、health、ready、kubernetes、prometheus、forward、cache、loop、reload 和 loadbalance。其中 kubernetes 插件负责从 API Server 获取 Service 和 Pod 记录,forward 插件负责把非集群域名转发到上游 DNS 服务器,cache 插件则缓存解析结果以提升性能。
默认配置通常会把集群域 cluster.local 交给 kubernetes 插件处理,其他所有请求通过 forward . /etc/resolv.conf 转发到节点配置的上游 DNS。这种设计虽然简单,但在混合云、本地数据中心或需要独立子域转发时并不够用。可以通过修改 Corefile 增加存根域,将特定域名定向到指定 DNS 服务器,而不用把所有外部流量都发给默认上游。
下面是一个典型的存根域与上游服务器配置示例。它把 internal.ippipp.com 域的解析请求单独转发到 10.20.0.53,并保持其他外部域名使用默认上游:
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
}
prometheus :9153
forward . /etc/resolv.conf
cache 30
loop
reload
loadbalance
}
internal.ippipp.com:53 {
errors
cache 30
forward . 10.20.0.53
}
修改 ConfigMap 后,CoreDNS 的 reload 插件会自动检测配置变化并重新加载,但前提是 CoreDNS 容器对该 ConfigMap 有读权限,且配置文件语法正确。可以使用 kubectl get configmap coredns -n kube-system -o yaml 查看当前配置,再通过 kubectl rollout restart deployment coredns -n kube-system 手动触发滚动重启,以确保所有副本加载新配置。需要注意的是,错误配置会导致 CoreDNS 无法启动,因此生产环境建议先在测试集群验证。
Service 与 Pod 的 DNS 记录解析规则
理解 Service 的 DNS 记录类型有助于判断服务发现是否按预期工作。普通 Service 会生成一条 A 记录,将 Service 名称解析为 ClusterIP。例如在 default 命名空间下有一个名为 web 的 Service,那么 web.default.svc.cluster.local 会解析为 web 的 ClusterIP。此外,Service 还会生成 web.default.svc.cluster.local 之外的短名称记录,如 web.default.svc、web.default 等,具体取决于 Pod 的 search 域配置。
对于 headless Service,即 clusterIP: None 的服务,普通 A 记录不再存在。CoreDNS 会为该 Service 返回所有就绪 Pod 的 IP 地址,从而支持客户端自行决定负载均衡策略。StatefulSet 与 headless Service 结合使用时,还可以为每个 Pod 生成稳定域名,格式为 <pod-name>.<service-name>.<namespace>.svc.cluster.local。这种记录在数据库、消息队列等有状态应用中非常重要,因为客户端可以通过固定 DNS 名称找到特定实例。
可以通过临时调试 Pod 验证 DNS 是否正常。以下命令在 default 命名空间启动一个包含 DNS 工具的 Pod,并查询 kubernetes 服务:
kubectl run dnsutils --image=infoblox/dnstools --restart=Never --rm -it -- nslookup kubernetes.default
执行后通常会看到类似下面的输出,说明 Service A 记录已经生成:
Server: 10.96.0.10 Address: 10.96.0.10#53 Name: kubernetes.default.svc.cluster.local Address: 10.96.0.1
如果查询 headless Service 时返回多个 Pod IP,或者 StatefulSet Pod 记录缺失,通常说明 Service 选择器与 Pod 标签不匹配,或者 Pod 未处于 Ready 状态。此时应优先检查 kubectl get endpoints 是否包含正确的 Pod IP,而不是盲目重启 CoreDNS。
常见故障现象与排查路径
DNS 超时是生产环境中最常遇到的问题之一。表现为应用日志中出现 i/o timeout、could not resolve host 或连接外部 API 时多次重试。首要排查点是 Pod 的 ndots 设置。默认值为 5,意味着域名中圆点数量少于 5 时,系统会按顺序追加所有 search 域进行解析。对于外部域名如 api.github.com,它只有两个圆点,于是 Pod 会先尝试 api.github.com.default.svc.cluster.local、api.github.com.svc.cluster.local 等内部域名,最后才查询原始名称。这会造成大量无效 DNS 查询,增加延迟甚至触发上游限流。
CoreDNS 自身的性能瓶颈也会导致解析超时。如果集群规模很大,默认副本数可能不足以处理所有查询。可以通过以下命令查看 CoreDNS 的 CPU 和内存使用情况,并根据需要增加副本数或进行水平自动扩缩:
kubectl top pod -n kube-system -l k8s-app=kube-dns kubectl scale deployment coredns -n kube-system --replicas=3
另一个常见问题是上游 DNS 不可达或响应缓慢。CoreDNS 默认通过 forward . /etc/resolv.conf 使用宿主机节点的 DNS 设置,而节点 DNS 可能由云厂商或内部 DHCP 提供。如果上游 DNS 出现故障,CoreDNS 日志中会记录大量 i/o timeout 或 no such host。查看日志的命令如下:
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=200 -f
定位到根因后,可以调整 CoreDNS 的 forward 插件,显式指定多个上游 DNS 服务器,并配置超时策略。例如将上游改为 forward . 8.8.8.8 1.1.1.1,配合 policy random 分摊请求。若公司内网存在特殊域名,则应使用存根域方式定向到内网解析器,而不是把所有请求都改成外部公共 DNS。
缓存策略同样值得关注。CoreDNS 的 cache 插件默认缓存成功与失败记录,如果应用刚创建 Service 后立即查询,可能因为缓存了之前的 NXDOMAIN 结果而短暂失败。虽然默认缓存时间较短,但可以通过调整 cache 30 中的秒数来平衡性能与一致性。生产环境建议保持 30 秒以内的成功缓存,并将失败缓存设置为更短时间,例如 cache 30 5 表示成功缓存 30 秒,失败缓存 5 秒。
最后,所有修改都应在测试集群验证通过后再滚动更新到生产环境。DNS 作为集群内服务发现的底层依赖,任何错误都可能放大为全集群范围的应用故障。通过理解 Pod DNS 策略、定制 Corefile、合理设置 ndots 与缓存参数,可以显著提升 Kubernetes 集群 DNS 的稳定性和响应速度。
Kubernetes DNSCoreDNSDNS策略修改时间:2026-08-24 00:31:40