在Kubernetes集群中,Pod的生命周期是高度动态的。一个Deployment滚动更新时,旧Pod会被终止,新Pod会被调度到任意节点上,并获得一个全新的IP地址。如果应用之间通过硬编码IP互相访问,每一次Pod重启都会导致连接失败。Kubernetes为了解决这个问题,引入了Service资源作为服务发现的抽象层。Service拥有一个稳定的ClusterIP,并且通过标签选择器关联到一组Pod。但Service的ClusterIP只是一个虚拟IP,客户端应用如何得知自己要访问的服务对应的ClusterIP是多少?答案就是集群DNS。Kubernetes默认部署CoreDNS作为集群内部的DNS服务器,它会自动为每个Service创建DNS记录,Pod内的应用只需要通过服务名,比如my-service.default.svc.cluster.local,就能解析到对应的ClusterIP,从而完成服务发现。

这一整套机制听起来简单,但背后涉及多个组件之间的协作:kube-apiserver存储Service和Endpoints对象,kube-proxy负责将ClusterIP的流量转发到后端Pod,而CoreDNS则负责把服务名翻译成IP地址。任何一个环节出错,都可能导致服务间调用失败。本文将从Service资源本身的机制讲起,深入分析CoreDNS的配置与数据同步方式,最后结合Pod内的DNS解析流程,梳理出排查问题的完整思路。
Service如何成为服务发现的稳定锚点
Kubernetes中的Service并不是一个运行中的进程,它只是一条存储在etcd中的API对象。当用户创建Service时,kube-apiserver会为该Service分配一个ClusterIP,这个IP来自集群配置的Service网段,例如10.96.0.0/12。同时,与Service选择器匹配的所有Pod会被收集起来,形成一个Endpoints对象。Endpoints里面记录了每个后端Pod的IP和端口。kube-proxy在每个节点上运行,监听API Server中Service和Endpoints的变化,并更新节点上的iptables规则或IPVS规则,使得访问ClusterIP的流量能够被转发到某个后端Pod。
值得注意的是,ClusterIP是一个虚拟IP,它并不绑定在任何网络接口上。当Pod内的应用向ClusterIP发送请求时,数据包首先到达Pod所在节点的内核,内核根据kube-proxy写入的转发规则,将目标地址改写为某个后端Pod的真实IP,这个过程叫做DNAT。因此,Service的稳定性和后端Pod的动态性被彻底解耦了。这也是为什么即使所有后端Pod都重启,只要Service的ClusterIP不变,客户端就无需修改配置。
然而,ClusterIP本身也是需要被发现的。在早期Kubernetes版本中,可以通过环境变量注入的方式将Service的IP和端口写入Pod,但这种方式要求Service必须先于Pod创建,且修改Service后Pod无法自动感知。DNS的出现解决了这个问题。通过DNS,Pod只需要记住服务名,剩下的解析工作交给DNS服务器即可。Service与DNS之间的桥梁就是CoreDNS对API Server的watch机制:每当Service创建、更新或删除,CoreDNS都会收到事件,并更新自己内部的DNS记录。
CoreDNS的部署方式与配置解析
Kubernetes从1.11版本开始将CoreDNS作为默认的集群DNS服务器,替代了早期的kube-dns。CoreDNS是一个用Go语言编写的DNS服务器,它最大的特点是插件化架构。在Kubernetes集群中,CoreDNS通常以Deployment的形式运行在kube-system命名空间下,并通过一个名为kube-dns的Service暴露给所有Pod。Pod内的resolv.conf文件中的nameserver通常指向这个Service的ClusterIP,例如10.96.0.10。
CoreDNS的核心配置文件叫做Corefile,它定义了各个DNS区域的处理插件。一个典型的Kubernetes Corefile如下所示:
.: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 {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
在这个配置中,kubernetes插件是服务发现的关键。它配置了cluster.local作为集群域名,以及in-addr.arpa和ip6.arpa用于反向解析。该插件会通过Kubernetes API的watch接口,实时同步集群中所有Service和Pod的信息,并生成对应的DNS记录。例如,对于名为web的Service在default命名空间中,CoreDNS会生成一条A记录:web.default.svc.cluster.local 指向该Service的ClusterIP。同时,对于每个Pod,如果设置了pods insecure,还会生成基于Pod IP的解析记录,格式为pod-ip.namespace.pod.cluster.local。
除了kubernetes插件,forward插件也很重要。它规定了对于不在cluster.local域内的查询,CoreDNS会转发到上游DNS服务器,上游地址通常来自CoreDNS Pod自身的/etc/resolv.conf,这样可以保证Pod既能解析集群内部服务名,也能解析外部域名。如果forward配置错误,可能导致所有外部域名解析超时。
Pod内DNS解析流程与典型问题排查
每个Pod在启动时,kubelet会根据Pod的dnsPolicy字段来生成/etc/resolv.conf文件。默认的dnsPolicy是ClusterFirst,这意味着Pod内的所有DNS查询都会先发送给集群DNS服务(即kube-dns Service的ClusterIP),只有集群DNS无法解析时,才会根据resolv.conf中的其他配置转发到上游。一个典型的Pod内resolv.conf内容如下:
nameserver 10.96.0.10 search default.svc.cluster.local svc.cluster.local cluster.local options ndots:5
这里search域定义了DNS查询的搜索顺序。当应用尝试解析一个不包含点号或者点号数量小于ndots设定值(默认为5)的名称时,系统会依次将搜索域附加到名称后面进行尝试。例如,在default命名空间的Pod中解析web,实际会依次查询web.default.svc.cluster.local、web.svc.cluster.local、web.cluster.local,最后才查询原始名称web。这种机制方便了同命名空间内的服务互访,但也会带来一些性能问题:对于外部域名如www.ipipp.com,由于包含的点号少于5个,系统会先尝试四次集群内部查询,全部失败后才转发到上游,增加了DNS解析延迟。可以通过调整ndots值或使用完整域名来优化。
跨命名空间的访问格式也容易混淆。在default命名空间的Pod要访问production命名空间的api服务,应该使用api.production.svc.cluster.local或者简写为api.production。但简写依赖search域的顺序,如果当前Pod不在production命名空间,search域中不会自动包含production.svc.cluster.local,所以简写api.production会失败,除非使用完整域名。此外,Headless Service(即clusterIP: None的Service)不会获得ClusterIP,其DNS记录会直接返回所有后端Pod的IP地址列表,这对于需要自定义负载均衡或服务发现的场景非常有用,例如StatefulSet中的Pod需要稳定的网络标识。
排查DNS问题时,通常先进入Pod执行nslookup或dig命令,观察解析到的IP是否正确。如果返回NXDOMAIN,需要检查Service是否存在于正确命名空间,以及CoreDNS的API访问权限是否正常。如果解析超时,则要检查CoreDNS Pod是否健康、kube-dns Service的Endpoint是否就绪,以及Pod到CoreDNS Service的网络是否畅通。一个常见的错误是修改了CoreDNS的ConfigMap后没有执行reload,导致新配置不生效。CoreDNS本身支持热加载,但需要确保ConfigMap挂载正确并且reload插件被启用。
Kubernetes服务发现DNS解析CoreDNS修改时间:2026-09-17 04:47:24