在这个实验环境中,我们将学习kubernetes集群中的DNS的工作原理,以及为什么DNS对服务发现和网络连接非常的重要。
- 了解kubelet如何配置Pod的DNS设置
- CoreDNS如何负责集群名的解析
- Pod如何执行对服务、其他Pod以及外部域名的查询
在kubernetes中,DNS是一项基础服务,它实现了可靠的服务到服务的通信,并简化了应用程序相互发现的方式
- 实现集群内部的服务发现与名称解析
- Pod使用DNS名称而非硬编码的IP,因为Pod/Service的IP会随着扩容和重启而变化
- kubelet配置每个Pod的
/et/resolv.conf, 使其使用集群DNS服务(通过kube-dns提供的CoreDNS) - 添加集群搜索域(eg.
<ns>.svc.cluster.local),使得像nginx这样的短名称能够被解析为nginx.svc.cluster.local - CoreDNS应答针对Service和Pod查询,并将为止域名转发给上游解析器
- 同时支持集群内查询服务发现,以及来自Pod的外部DNS解析
实验环境
实验环境的clab topo复用IPAM的topo
检查 ContainerLab拓扑结构
确认默认命名空间中容器和服务是否都正常
master ✗ $ kubectl get pod-owide1↵ NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES multitool-1-h5z551/1 Running02d2h192.168.112.131 calico-ipam-worker2<none><none>multitool-1-hnkl41/1 Running02d2h192.168.131.131 calico-ipam-worker<none><none>multitool-1-nh4qg1/1 Running02d2h192.168.18.73 calico-ipam-control-plane<none><none>multitool-2-6jbdt1/1 Running02d2h192.168.131.132 calico-ipam-worker<none><none>multitool-2-jql6b1/1 Running02d2h192.168.18.72 calico-ipam-control-plane<none><none>multitool-2-zlm2k1/1 Running02d2h192.168.112.130 calico-ipam-worker2<none><none>nginx-deployment-55d7bb4b86-j5q5j1/1 Running025h192.168.112.132 calico-ipam-worker2<none><none>nginx-deployment-55d7bb4b86-lqswq1/1 Running025h192.168.131.133 calico-ipam-worker<none><none>确认DNS是否设置正确
我们通过检查kub-dns Service(Pod作为其nameserver的IP)以及查看CoreDNS Corefile(它定义了集群内名称如何解析、外部查询如何转发),来验证集群DNS是否已启动并正确配置。
验证kube-dns服务
master ✗ $ kubectl get services-nkube-system NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)AGE kube-dns ClusterIP10.96.0.10<none>53/UDP,53/TCP,9153/TCP 3d1h确认集群内DNS服务(kube-dns)正以ClusterIP形式运行 10.96.0.10。Pod在53/UDP和53/TCP端口使用此IP作为DNS nameserver;9153/TCP暴漏CoreDNS的prometheus指标。
检查支撑该服务的Endpoints端点
master ✗ $ kubectl describe endpoints kube-dns-nkube-system Name: kube-dns Namespace: kube-system Labels: k8s-app=kube-dns kubernetes.io/cluster-service=true kubernetes.io/name=CoreDNS Annotations: endpoints.kubernetes.io/last-change-trigger-time:2026-09-23T04:00:29Z Subsets: Addresses:192.168.18.67,192.168.18.71 NotReadyAddresses:<none>Ports: Name Port Protocol ---- ---- -------- dns-tcp53TCP dns53UDP metrics9153TCP Events:<none>列出了Pod实际达到的、kub-dns Service背后的Endpoints(即CoreDNS Pod)。
- Addresses:
192.168.18.67,192.168.18.71是本基金群众正在运行的CoreDNS Pod的IP。 - Readiness:
NotReadyAddresses: <none>表示所有CoreDNS端点都已就绪,可以提供查询服务。 - Ports: 在53端口通过UDP/TCP提供DNS服务,并在9153/TCP提供prometheus指标
- Takeaway(要点):kube-dns的ClusterIP会负载均衡到这些CoreDNS端点,如果解析失败,首先应该检查这些地址就绪状态。
验证coredns Pod
接下来,让我们看看coredns这些容器
master ✗ $ kubectl get pods-nkube-system-lk8s-app=kube-dns-owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES coredns-5dd5756b68-m4g7n1/1 Running03d3h192.168.18.67 calico-ipam-control-plane<none><none>coredns-5dd5756b68-p9p4r1/1 Running03d3h192.168.18.71 calico-ipam-control-plane<none><none>master ✗ $ kubectl get configmap-nkube-system coredns-oyaml apiVersion: v1 data: Corefile:|.:53{errors health{lameduck 5s}ready kubernetes cluster.local in-addr.arpa ip6.arpa{pods insecure fallthrough in-addr.arpa ip6.arpa ttl30}prometheus :9153 forward./etc/resolv.conf{max_concurrent1000}cache30loop reload loadbalance}kind: ConfigMap metadata: creationTimestamp:"2026-09-23T03:28:43Z"name: coredns namespace: kube-system resourceVersion:"271"uid: a5741f28-799b-4f9a-acac-f551b44bbbd7CoreDNS Corefile中的关键字
:53: 在所有区域中通过端口53提供DNS服务kubenetes cluster.local ...: 处理集群记录,pods insecrure: 为容器IP地址启用A记录;ttl 30: 设置TTL为30s; fallthrough: 将未匹配的反向查询结果传递给下一个插件处理。forward . /etc/resolv.conf: 将非集群查询发送到节点的resolv.conf文件中所指定的上游解析器进行处理。cache 30: 缓存答案,持续30s,以减少延迟和负载。health, ready: 暴漏由k8s探针使用的 health/readiness endpointsprometheus :9153: 公开用于数据爬取的指标信息reload: 会监控Corefile中的变化,并重新加载CoreDNS的配置,而无需重启Pod。这样就能让配置更加迅速生效loadbalance: 随机排列A/AAAA记录的顺序,并轮换上游节点,以使客户端负载在各个端点上分布得更均衡。loop: 能够检测并打破DNS递归循环(例如,由于配置错误导致的CoreDNS将请求转发给另外一个resolver,而该resolver又再次将请求转发会CoreDNS的情况),这样可以避免堆栈溢出和过高的CPU使用率。
检查Host/Node解析器
master ✗ $dockerexec-itcalico-ipam-worker /bin/bash root@calico-ipam-worker:/# cat /etc/resolv.conf# Generated by Docker Engine.# This file can be edited; Docker Engine will not make further changes once it# has been modified.nameserver192.168.48.1 search.options edns0 trust-ad ndots:0# Based on host file: '/etc/resolv.conf' (internal resolver)# ExtServers: [host(127.0.0.53)]# Overrides: []# Option ndots from: internalnameserver 192.168.48.1: Node/container DNS 指向docker网桥网关(宿主机解析器路径)。search .: 别给我加任何后缀,我给什么域名你就查什么,一个字都别多拼。如果是云厂商提供的云主机,一般这里会跟着云厂商的上级域名options edns0 trust-ad ndots:0:启用了EDNS;信任AD位;ndots:0将名称视为绝对名称(不进行搜索域展开)
检查Pod DNS配置
使用exec进入到一个正在运行的Pod中查看其解析设置。
master ✗ $ kubectlexec-itmultitool-1-hnkl4 --sh/# cat /etc/resolv.confsearch default.svc.cluster.local svc.cluster.local cluster.local nameserver10.96.0.10 options ndots:5search domains: 搜索域用于名称解析的自动域名补全,这些域会自动附加到短名称域名后面,例如从default命名空间中,nginx会展开为nginx.default.svc.cluster.local,然后是nginx.svc.cluster.local,接着是nginx.cluster.local- namserver: kube-dns 服务的ClusterIP(CoreDNS)。所有的Pod DNS查询都会发送到这里
options ndots:5: 点数少于5的域名将会被视为相对域名,解析系统会先尝试使用后缀进行搜索,然后尝试使用绝对FQDN进行解析。这样对于那些较短的外部域名来说,可以生成更多的查询语句。
Kubernetes 会引入集群搜索域名机制,并将容器指向 CoreDNS 进行解析,因此集群内部使用的简短名称可以自动完成解析;而ndots:5这样的简短外部名称则可能优先使用这些后缀进行解析。
andrew•~»dockernetwork inspect kind[15:24:08][{"Name":"kind",........"IPAM":{"Driver":"default","Options":{},"Config":[{"Subnet":"fc00:f853:ccd:e793::/64"},{"Subnet":"192.168.48.0/20","Gateway":"192.168.48.1"}]},........}]服务名称解析示例
/# dig nginx-service;<<>>DiG9.16.20<<>>nginx-service;;global options: +cmd;;Got answer:;;->>HEADER<<-opcode: QUERY, status: SERVFAIL, id:15427;;flags: qr rd;QUERY:1, ANSWER:0, AUTHORITY:0, ADDITIONAL:1;;WARNING: recursion requested but not available;;OPT PSEUDOSECTION:;EDNS: version:0, flags:;udp:4096;COOKIE: 684ad39f0022a274(echoed);;QUESTION SECTION:;nginx-service. IN A;;Query time:123msec;;SERVER:10.96.0.10#53(10.96.0.10);;WHEN: Sat Sep2607:57:52 UTC2026;;MSG SIZE rcvd:54- 查询裸名称
nginx-service,未应用Pod的搜索域 - dig显示最终的问题为绝对名称nginx-service(未附加任何后缀)
- 公共/根DNS中不存在该记录,因此 10.96.0.10 上的CoreDNS返回SERVFAIL。
- 在 Kubernetes 中,短 Service 名称需要结合集群搜索后缀(例如
nginx-service.<namespace>.svc.cluster.local)才能解析,或者使用dig +search nginx-service来应用/etc/resolv.conf中的搜索域。
使用dig解析时,如果使用+search会使用/etc/resolv.conf中指定的域名后缀
/# dig +search nginx-service;<<>>DiG9.16.20<<>>+search nginx-service;;global options: +cmd;;Got answer:;;WARNING: .local is reservedforMulticast DNS;;You are currently testing what happens when an mDNS query is leaked to DNS;;->>HEADER<<-opcode: QUERY, status: NOERROR, id:61546;;flags: qr aa rd;QUERY:1, ANSWER:1, AUTHORITY:0, ADDITIONAL:1;;WARNING: recursion requested but not available;;OPT PSEUDOSECTION:;EDNS: version:0, flags:;udp:4096;COOKIE: 870917ebc77d0341(echoed);;QUESTION SECTION:;nginx-service.default.svc.cluster.local. IN A;;ANSWER SECTION: nginx-service.default.svc.cluster.local.30IN A10.96.102.172;;Query time:0msec;;SERVER:10.96.0.10#53(10.96.0.10);;WHEN: Sat Sep2608:08:17 UTC2026;;MSG SIZE rcvd:135+search: 指示 dig使用/etc/resolv.conf的Pod搜索域- 短名称
nginx-service被展开为nginx-service.default.svc.cluster.local(位于default命名空间) - CoreDNS返回该Service的ClusterIP,展示了通过该命名空间搜索后缀实现的集群服务发现。
跨命名空间域名解析
接下来我们进入foo命名空间的 mutitool-3 中的Pod检查它的DNS配置
master ✗ $ kubectlexec-itmultitool-3-92ztr-nfoo --sh1↵ /# cat /etc/resolv.confsearch foo.svc.cluster.local svc.cluster.local cluster.local nameserver10.96.0.10 options ndots:5- 基于命名空间搜索:在命名空间foo中运行时,会将第一个搜索后缀设置为
foo.svc.cluster.local(而不是default.svc.cluster.local) - 短名称解析:向nginx这样的名称首先会被解析为
nginx.foo.svc.cluster.local, 之后才会使用更通用的集群后缀表示 - resolve target:
nameserver 10.96.0.10是所有Pod所使用的CoreDNS服务的IP地址(kube-dns)。 - ndots行为:对所有
ndots:5的域名,会先尝试使用搜索后缀来匹配少于包含五个点的名称,只有在无法匹配结果的情况下,才会将其视为绝对的FQDN。
/# dig +search nginx-service;<<>>DiG9.16.20<<>>+search nginx-service;;global options: +cmd;;Got answer:;;->>HEADER<<-opcode: QUERY, status: SERVFAIL, id:50872;;flags: qr rd;QUERY:1, ANSWER:0, AUTHORITY:0, ADDITIONAL:1;;WARNING: recursion requested but not available;;OPT PSEUDOSECTION:;EDNS: version:0, flags:;udp:4096;COOKIE: 6a6460267effaa75(echoed);;QUESTION SECTION:;nginx-service. IN A;;Query time:1msec;;SERVER:10.96.0.10#53(10.96.0.10);;WHEN: Sat Sep2608:26:06 UTC2026;;MSG SIZE rcvd:54- 在命名空间foo中,通过短名称
nginx-service查询 default 中的服务 - 搜索行为:Resolver首先尝试查找
nginx-service.foo.svc.cluster.local,但发现它不存在;随后尝试查找绝对名称nginx-service. - 结果:
nginx-service.不是一个有效的公共/根DNS记录,因此CoreDNS返回了SERVFAIL - 修复:需要在命名空间中包含该名称(
nginx-service.default),或者使用完整的FQDNnginx-service.default.svc.cluster.local
/# dig +search nginx-service.default;<<>>DiG9.16.20<<>>+search nginx-service.default;;global options: +cmd;;Got answer:;;WARNING: .local is reservedforMulticast DNS;;You are currently testing what happens when an mDNS query is leaked to DNS;;->>HEADER<<-opcode: QUERY, status: NOERROR, id:54538;;flags: qr aa rd;QUERY:1, ANSWER:1, AUTHORITY:0, ADDITIONAL:1;;WARNING: recursion requested but not available;;OPT PSEUDOSECTION:;EDNS: version:0, flags:;udp:4096;COOKIE: 00d55dc943077c3c(echoed);;QUESTION SECTION:;nginx-service.default.svc.cluster.local. IN A;;ANSWER SECTION: nginx-service.default.svc.cluster.local.30IN A10.96.102.172;;Query time:0msec;;SERVER:10.96.0.10#53(10.96.0.10);;WHEN: Sat Sep2608:31:50 UTC2026;;MSG SIZE rcvd:135- 添加
.default命名空间为解析器提供了足够的上下文,将名称展开为nginx-service.default.svc.cluster.local - CoreDNS将该FQDN解析为Service的ClusterIP
- 要点:短名称通过搜索域依赖于当前活动命名空间;包含命名空间或使用完整的FQDN可得到确定性的结果。
总结
- Kubelet 配置每个 Pod 的
/etc/resolv.conf,使其使用kube-dnsClusterIP(CoreDNS),并注入命名空间作用域的搜索域。 - CoreDNS 提供集群 DNS 记录(Service,以及经由无头 Service 的 Pod A 记录),并将外部查询转发给上游解析器。
- 使用
ndots:5这样的解析器选项驱动搜索展开;短名称通过搜索后缀在命名空间内解析 - 跨命名空间查询需要带上命名空间或完整的FQDN(例如:
service.ns.svc.cluster.local)