DNS 深度剖析:从第一性原理到 Kubernetes
从 dig 追踪到 Kubernetes 中的 CoreDNS,彻底搞懂 DNS。一份面向实战的容器 DNS 调试指南。
一切的起点:那个问题
我们当时要构建的一个功能需要从 Kubernetes Pod 内部调用一个内部 API。这个 API 可以通过一个私有域名访问,但 Pod 内部的 DNS 解析却悄无声息地失败了——请求只是一直超时。追查问题之后,修复方案原来只是在 CoreDNS ConfigMap 里加上了一小段配置:
internal.example.com:53 {
errors
cache 30
forward . 10.0.0.2
}
这段配置告诉 CoreDNS:将针对 internal.example.com 的查询转发到我们位于 10.0.0.2 的内部 DNS 服务器,而不是使用默认的上游解析器。修复很简单,但要理解它为什么有效,需要你扎实掌握 DNS 从第一性原理到 Kubernetes 实现的整条工作链路。
本文将完整走一遍这条链路。
第一部分:DNS 基础
什么是 DNS?
DNS(域名系统,Domain Name System)只做一件事:将域名映射为 IP 地址。它维护着一个分布式的名称到地址映射数据库,因此当你输入 example.com 时,浏览器就知道该去连接 93.184.216.34。
关键词是分布式。没有任何一台服务器持有全世界所有的 DNS 记录。相反,DNS 被组织成一个分层的、逐级委派的系统——理解这个层级结构,是在任何环境中调试 DNS 问题的基础。
DNS 层级结构
DNS 是一个树状结构。每个域名都从右往左读,从最具体的标签一直读到根:
从上到下的各个层级:
| 层级 | 说明 | 示例 |
|---|---|---|
| 根区(Root Zone) | 所有 DNS 查询的起点。写作一个点(.),通常被省略。技术上每个域名都以它结尾:example.com. | . |
| 顶级域名(TLD) | 由注册管理机构管理。分为通用顶级域名(.com、.org、.net)和国家代码顶级域名(.uk、.cn、.de)。 | .com |
| 二级域名 | 你向注册商注册的那个域名。 | example.com |
| 子域名 | 由域名所有者自行创建,无需注册。 | api.example.com |
这个层级结构之所以重要,是因为只有上一级才知道下一级的域名服务器。根服务器知道 TLD 服务器。.com 的 TLD 服务器知道 example.com 的域名服务器。而 example.com 的域名服务器知道 api.example.com 的地址。解析过程就是沿着这棵树逐级向下走。
用 dig 剖析一次 DNS 查询
dig(Domain Information Groper)命令是 DNS 调试中最重要的工具。让我们用一次真实查询,逐段拆解它的输出:
$ dig example.com
头部(Header)部分
; <<>> DiG 9.18.24 <<>> example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 42781
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
这些标志告诉你发生了什么:
- qr —— 这是一个查询响应(而非查询本身)
- rd —— 期望递归(客户端请求解析器完成整个查询过程)
- ra —— 递归可用(服务器支持递归查询)
- aa —— (此处不存在)如果出现,表示这是权威应答,即响应直接来自该域名的域名服务器,而非缓存
各类计数(QUERY: 1, ANSWER: 1)告诉你每个部分中有多少条记录。
问题(Question)部分
;; QUESTION SECTION:
;example.com. IN A
这里确认了所查询的内容:example.com 的一条 A 记录(IPv4 地址)。IN 代表 Internet 类别——几乎所有 DNS 查询都使用这个类别。
应答(Answer)部分
;; ANSWER SECTION:
example.com. 86400 IN A 93.184.216.34
应答结果:example.com 解析为 93.184.216.34,TTL(Time to Live,生存时间)为 86400 秒(24 小时)。任何缓存解析器都可以将这个应答存储 24 小时,之后才需要重新查询。
对于位于 CDN 或负载均衡器之后的域名,你常常会看到 CNAME 链:
;; ANSWER SECTION:
app.example.com. 300 IN CNAME d1234abcdef.cloudfront.net.
d1234abcdef.cloudfront.net. 60 IN A 13.224.67.101
d1234abcdef.cloudfront.net. 60 IN A 13.224.67.42
CNAME(Canonical Name,规范名称)记录是一个别名——它表示“要找到 app.example.com 的地址,请改为查询 d1234abcdef.cloudfront.net”。解析器随后沿着这条链继续,直到得到最终的 A 记录。
权威(Authority)与附加(Additional)部分
;; AUTHORITY SECTION:
example.com. 86400 IN NS a.iana-servers.net.
;; ADDITIONAL SECTION:
a.iana-servers.net. 86400 IN A 199.43.135.53
权威部分列出该域名的权威域名服务器。附加部分提供这些域名服务器的 IP 地址(这是一种性能优化,称为“粘合记录 / glue records”,使解析器无需再单独查询一次)。
统计(Statistics)
;; Query time: 12 msec
;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)
;; WHEN: Wed Jan 10 09:15:22 UTC 2024
;; MSG SIZE rcvd: 56
这里告诉你哪台 DNS 服务器处理了本次查询(此处是位于 127.0.0.53 的本地 systemd-resolved 桩解析器)、耗时多久,以及响应的大小。
用 dig +trace 沿层级逐级追踪
+trace 标志会让 dig 从根开始执行迭代解析,展示每一个步骤。这对于诊断委派(delegation)问题极其有用:
$ dig +trace example.com
第 1 步 —— 根服务器:
. 518400 IN NS a.root-servers.net.
. 518400 IN NS b.root-servers.net.
. 518400 IN NS c.root-servers.net.
;; ... (13 root server clusters total)
;; Received 239 bytes from 127.0.0.53#53(127.0.0.53) in 4 ms
你的本地解析器提供了全部 13 个根服务器集群的列表。它们是硬编码的(即“根提示 / root hints”文件),几乎从不改变。
第 2 步 —— 查询根,获取 .com TLD 服务器:
com. 172800 IN NS a.gtld-servers.net.
com. 172800 IN NS b.gtld-servers.net.
;; ... (13 .com TLD servers)
;; Received 1175 bytes from 170.247.170.2#53(b.root-servers.net) in 24 ms
根服务器并不知道 example.com 的答案,但它知道谁管理 .com——也就是 gTLD 服务器。于是它返回一个引荐(referral)。
第 3 步 —— 查询 .com TLD,获取 example.com 的域名服务器:
example.com. 172800 IN NS a.iana-servers.net.
example.com. 172800 IN NS b.iana-servers.net.
;; Received 170 bytes from 192.12.94.30#53(e.gtld-servers.net) in 18 ms
.com 的 TLD 服务器将我们引荐至 example.com 的权威域名服务器。
第 4 步 —— 查询权威服务器,获取最终答案:
example.com. 86400 IN A 93.184.216.34
;; Received 56 bytes from 199.43.135.53#53(a.iana-servers.net) in 32 ms
权威服务器返回真正的 IP 地址。整条链到此结束。
递归查询 vs 迭代查询
你可能已经注意到,dig +trace 比普通的 dig 慢得多。这恰好体现了两种查询模式的区别:
递归查询(图中第 1 步和第 8 步):你的应用向本地解析器询问“example.com 的 IP 是什么?”,并期望得到一个最终答案。解析器完成所有工作后返回结果。这就是正常运行时发生的情况——你的应用发起一次调用,得到一个答案。
迭代查询(第 2 至 7 步):解析器依次联系层级结构中的每一级,不断收到引荐(“我不知道,但你可以去问这台服务器”),直到抵达权威应答。使用 dig +trace 时,是你的机器直接执行了这些迭代查询。
在实际运行中,递归解析更快,因为解析器很可能已经缓存了根服务器和 TLD 服务器的响应,甚至可能缓存了域名本身。而 +trace 方式会绕过所有缓存,这正是它更慢的原因——但它能精确揭示问题可能出现在链路中的哪个环节。
第二部分:Docker 容器中的 DNS
在进入 Kubernetes 之前,先了解一下 Docker 是如何处理 DNS 的会很有帮助。
容器如何获得它们的 DNS 配置
默认情况下,Docker 在容器启动时会将宿主机的 /etc/resolv.conf 复制进每个容器。这意味着容器会继承宿主机所使用的 DNS 服务器。有两种方式可以覆盖这一行为:
-
修改宿主机的
/etc/resolv.conf—— 影响所有新建的容器(对已运行的容器无效——它们需要重启才能获取变更)。 -
在容器启动时使用
--dns标志:
docker run --dns 8.8.8.8 --dns 8.8.4.4 nginx
这会将指定的域名服务器写入容器的 /etc/resolv.conf,覆盖宿主机的默认设置。
对于 Docker Compose,你可以按服务逐个设置:
services:
app:
image: myapp:latest
dns:
- 8.8.8.8
- 1.1.1.1
重要提醒: 如果宿主机的 /etc/resolv.conf 指向 127.0.0.53(在使用 systemd-resolved 的 Ubuntu 上很常见),Docker 会检测到这一点并回退到 8.8.8.8,因为在拥有独立网络命名空间的容器内,环回地址是无法工作的。
第三部分:Kubernetes DNS —— CoreDNS
Kubernetes 将 DNS 配置提升到了另一个层次。每个 Pod 都会自动注入 DNS 设置,集群还运行着自己的 DNS 服务,同时处理内部服务发现和外部解析。
架构
各个部件是这样组合在一起的:
- kubelet 配置每个 Pod 的
/etc/resolv.conf,使其指向集群 DNS 服务(通常是10.96.0.10)。 - kube-dns Service(
kube-system中的一个ClusterIP服务)在多个 CoreDNS Pod 之间做负载均衡。 - CoreDNS Pod(通常由一个 Deployment 管理的 2 个副本)处理所有 DNS 查询。它们知道如何通过与 Kubernetes API 通信来解析
cluster.local名称,并将其他所有查询转发到上游。 - 上游 DNS 就是节点的
/etc/resolv.conf所指定的那个。在 EKS 上,这是位于169.254.169.253的 Amazon VPC DNS 解析器。
从 Kubernetes 1.29+ 开始,CoreDNS 是唯一的 DNS 提供者。旧版的 kube-dns(基于 dnsmasq)在更早的版本中已被移除。为了向后兼容,该服务仍然名为 kube-dns,但它路由到的是 CoreDNS Pod。
Pod DNS 策略
每个 Pod 都有一个 dnsPolicy 字段,用于控制其 DNS 的配置方式。共有四个选项:
ClusterFirst(默认)
DNS 查询首先发送到 CoreDNS。如果查询匹配 cluster.local(或所配置的集群域),CoreDNS 会使用 Kubernetes API 进行解析。其他所有查询都被转发到上游。这是大多数工作负载所需要的策略。
Pod 的 /etc/resolv.conf 看起来像这样:
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
Default
Pod 继承其所运行节点的 DNS 配置。CoreDNS 被完全绕过。这意味着该 Pod 无法解析像 my-service.my-namespace.svc.cluster.local 这样的 Kubernetes 服务名。
仅在 Pod 不需要与其他 Kubernetes 服务通信时才使用它。
ClusterFirstWithHostNet
当运行设置了 hostNetwork: true 的 Pod 时必须使用。如果没有这个策略,一个使用 ClusterFirst 的 hostNetwork Pod 会悄悄地回退到 Default 行为,因为该 Pod 共享了节点的网络命名空间。显式设置 ClusterFirstWithHostNet 会强制让 DNS 走 CoreDNS,即便 Pod 使用的是宿主机网络。
apiVersion: v1
kind: Pod
metadata:
name: host-network-pod
spec:
hostNetwork: true
dnsPolicy: "ClusterFirstWithHostNet"
containers:
- name: app
image: myapp:latest
None
Kubernetes 不注入任何 DNS 设置。你必须通过 dnsConfig 自行提供。这让你拥有完全的控制权:
apiVersion: v1
kind: Pod
metadata:
name: custom-dns-pod
spec:
dnsPolicy: "None"
dnsConfig:
nameservers:
- 10.96.0.10
- 8.8.8.8
searches:
- my-namespace.svc.cluster.local
- svc.cluster.local
options:
- name: ndots
value: "2"
- name: edns0
containers:
- name: app
image: myapp:latest
在 Pod 内部生成的 /etc/resolv.conf 如下:
nameserver 10.96.0.10
nameserver 8.8.8.8
search my-namespace.svc.cluster.local svc.cluster.local
options ndots:2 edns0
ndots:5 性能问题
默认的搜索配置值得特别关注。在 ndots:5 的设置下,任何点数少于 5 个的域名都会被当作“相对”名称处理,解析器会先逐个追加每个搜索域,之后才尝试按原样查询该名称。
当一个 Pod 查询 api.example.com(含 2 个点,少于 5 个)时:
api.example.com.default.svc.cluster.local—— 未命中api.example.com.svc.cluster.local—— 未命中api.example.com.cluster.local—— 未命中api.example.com—— 命中
一次外部域名查询就发起了 4 次 DNS 查询。对于需要大量外部调用的高流量服务而言,这会显著放大 DNS 负载。两种常见的修复方式:
方式一:为外部 FQDN 追加一个末尾的点,在你的应用配置中:
api.example.com. # <-- trailing dot means "this is absolute, don't search"
方式二:在 Pod spec 中降低 ndots:
dnsConfig:
options:
- name: ndots
value: "2"
设置 ndots:2 意味着只有点数少于 2 个的名称才会走搜索列表。api.example.com(2 个点)会立即被当作绝对名称处理。代价是:像 my-service(0 个点)这样的短 Kubernetes 服务名仍会走搜索列表,并被正确解析。
CoreDNS 配置深度剖析
CoreDNS 通过 kube-system 命名空间中的一个 ConfigMap 进行配置。下面是带注释的默认配置:
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors # Log errors to stdout
health { # Health check endpoint on :8080/health
lameduck 5s # Wait 5s before shutting down (graceful)
}
ready # Readiness probe on :8181/ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure # Resolve pod A records (IP-based names)
fallthrough in-addr.arpa ip6.arpa # Pass reverse lookups to next plugin
ttl 30 # Cache Kubernetes records for 30s
}
prometheus :9153 # Expose Prometheus metrics
forward . /etc/resolv.conf # Forward non-cluster queries upstream
cache 30 # Cache all responses for 30s
loop # Detect and break forwarding loops
reload # Auto-reload Corefile on ConfigMap changes
loadbalance # Round-robin A/AAAA records
}
我们来详细看看几个关键插件。
kubernetes 插件
它是 Kubernetes DNS 的核心。它监视 Kubernetes API,并应答以下查询:
- Service:
my-service.my-namespace.svc.cluster.local解析为 Service 的 ClusterIP - Headless service: 返回各个 Pod IP,而非一个 ClusterIP
- Pod:
10-244-1-5.my-namespace.pod.cluster.local(当设置了pods insecure时) - SRV 记录:
_http._tcp.my-service.my-namespace.svc.cluster.local返回端口信息
forward 插件
将查询转发到上游 DNS 服务器。.(点)表示“匹配所有未被前面插件处理的查询”。你可以指定多个上游:
forward . 8.8.8.8 8.8.4.4 {
max_concurrent 1000
policy round_robin
}
在 EKS 上,节点的 /etc/resolv.conf 通常指向位于 169.254.169.253 的 VPC DNS 解析器,它负责处理 Route 53 私有托管区域以及标准的公共 DNS 解析。
hosts 插件
允许内联的主机到 IP 映射,在无需外部 DNS 服务器的情况下覆盖特定名称时非常有用:
hosts {
10.0.0.50 legacy-db.internal
10.0.0.51 legacy-api.internal
fallthrough
}
fallthrough 指令至关重要——如果没有它,任何未匹配到 hosts 条目的查询都会返回 NXDOMAIN,而不会被传递给后续插件。
rewrite 插件
在处理之前重写查询。适用于域名别名或迁移场景:
rewrite name old-service.default.svc.cluster.local new-service.default.svc.cluster.local
file 插件
从区域文件(zone file)提供 DNS 区域服务。当你需要直接在 CoreDNS 中托管内部 DNS 区域时很有用:
file /etc/coredns/db.internal.example.com internal.example.com
添加自定义 DNS 转发
最常见的 CoreDNS 定制就是将特定域名转发到特定的 DNS 服务器。这正是本文开头那个修复方案。下面是一个完整的、可用于生产环境的示例:
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
internal.example.com:53 {
errors
cache 30
forward . 10.0.0.2
}
corp.mycompany.net:53 {
errors
cache 60
forward . 10.1.0.53 10.1.0.54 {
policy sequential
}
}
.: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
}
编辑完 ConfigMap 后,CoreDNS 会自动获取变更(得益于 reload 插件)。无需重启——但请给它最多 30 秒的时间。
你可以用以下命令验证:
kubectl rollout status deployment/coredns -n kube-system
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=20
第四部分:EKS 特有的 DNS 注意事项
Amazon EKS 有一些值得了解的 DNS 特性。
VPC DNS 解析器
每个 VPC 都在“VPC 基址 + 2”的地址上有一个 DNS 解析器(例如,如果你的 VPC CIDR 是 10.0.0.0/16,那么解析器就在 10.0.0.2)。EKS 节点还能在 169.254.169.253 这个链路本地地址上访问到它,该地址无论 VPC CIDR 是什么都始终可用。
这个解析器负责处理:
- 挂载到该 VPC 的 Route 53 私有托管区域
- VPC 接口端点(例如
com.amazonaws.us-east-1.s3) - 标准公共 DNS 解析
- 用于转发到本地(on-premises)DNS 的 Route 53 Resolver 规则
当 CoreDNS 将查询转发到上游时,它们会发往这个 VPC 解析器,这意味着你的 Pod 无需任何 CoreDNS 配置就能自动获得 Route 53 私有区域解析。
Amazon VPC CNI 与 DNS
Amazon VPC CNI 插件直接为 Pod 分配 VPC IP 地址。这意味着 Pod 的 DNS 流量像其他任何流量一样走 VPC 网络——不存在覆盖网络(overlay)的转换。从 Pod 发往 169.254.169.253 VPC 解析器的 DNS 数据包,与从 EC2 实例发出的 DNS 数据包走的是同一条路径。
DNS 解析限制
VPC DNS 解析器强制施加了每个网络接口每秒 1024 个数据包的限制。在拥有大量 Pod 且 DNS 查询繁重的集群中,这可能成为瓶颈。解决方案包括:
- NodeLocal DNSCache —— 在每个节点上运行一个 DNS 缓存,减少上游查询
- CoreDNS 自动扩缩 —— 按集群规模成比例增加副本数
- 在你的应用中启用 DNS 缓存(许多 HTTP 客户端和连接池已经这么做了)
第五部分:调试 Kubernetes 中的 DNS
当集群中的 DNS 出问题时,你需要一套系统化的方法。下面就是你的工具箱。
从 Pod 快速检查 DNS
启动一个带有 DNS 工具的调试 Pod:
kubectl run dns-debug --image=busybox:1.36 --rm -it --restart=Never -- sh
在 Pod 内部:
# Check what DNS server the pod is using
cat /etc/resolv.conf
# Test Kubernetes service resolution
nslookup kubernetes.default.svc.cluster.local
# Test external resolution
nslookup example.com
# Check a specific DNS server
nslookup example.com 10.96.0.10
若要进行更详细的查询,使用一个带 dig 的 Pod:
kubectl run dns-debug --image=alpine/bind-tools --rm -it --restart=Never -- sh
# Inside the pod:
dig kubernetes.default.svc.cluster.local
dig +trace example.com
dig @10.96.0.10 my-service.my-namespace.svc.cluster.local
对已有 Pod 使用 kubectl exec
如果某个特定 Pod 出现了 DNS 问题,就从它内部进行测试:
# Check the pod's DNS configuration
kubectl exec -it <pod-name> -- cat /etc/resolv.conf
# Test resolution (if the pod has nslookup/dig)
kubectl exec -it <pod-name> -- nslookup my-service.my-namespace.svc.cluster.local
# If no DNS tools available, use wget as a proxy test
kubectl exec -it <pod-name> -- wget -q -O- http://my-service.my-namespace:8080/health
检查 CoreDNS 健康状况
# Are CoreDNS pods running?
kubectl get pods -n kube-system -l k8s-app=kube-dns
# Check CoreDNS logs for errors
kubectl logs -n kube-system -l k8s-app=kube-dns --tail=50
# Is the kube-dns service healthy?
kubectl get svc kube-dns -n kube-system
# Check endpoints (should list CoreDNS pod IPs)
kubectl get endpoints kube-dns -n kube-system
# View the current CoreDNS configuration
kubectl get configmap coredns -n kube-system -o yaml
CoreDNS 指标
CoreDNS 在端口 9153 上暴露 Prometheus 指标。需要关注的关键指标:
coredns_dns_requests_total—— 按类型和区域统计的查询总数coredns_dns_responses_total—— 按响应码(NOERROR、NXDOMAIN、SERVFAIL)统计的响应数coredns_forward_requests_total—— 转发到上游的查询数coredns_cache_hits_total/coredns_cache_misses_total—— 缓存有效性coredns_panics_total—— 应始终为 0
你可以直接查询这些指标:
kubectl exec -n kube-system <coredns-pod> -- wget -q -O- http://localhost:9153/metrics | grep coredns_dns_requests_total
启用 CoreDNS 调试日志
若要进行深度调试,可在 CoreDNS ConfigMap 中临时启用 log 插件:
.:53 {
log # <-- add this line; logs every query
errors
# ... rest of config
}
警告: 在生产环境中这会产生海量日志数据。请短暂启用,捕获你需要的内容后立即移除。
DNS 缓存问题
如果你更新了一条 DNS 记录,但 Pod 仍然看到旧值:
- CoreDNS 缓存 —— 默认 30 秒。等待,或在 ConfigMap 中临时设置
cache 0。 - 应用层缓存 —— Java 的
InetAddress默认会缓存 DNS(在较新的 JVM 中,成功查询缓存 30 秒,失败缓存 10 秒)。Go 的标准库遵循 TTL。Node.js 自 v20 起也默认缓存。 - VPC DNS 缓存 —— VPC 解析器根据 TTL 进行缓存。除了等待之外,你无能为力。
- conntrack 条目 —— 过期的 UDP conntrack 条目会导致 DNS 超时。用
conntrack -L -p udp --dport 53检查。
排查清单
当你在 Kubernetes 中遇到 DNS 问题时,按这份清单逐项排查:
- 确认症状。 是超时、NXDOMAIN、SERVFAIL,还是 IP 错误?
- 检查 Pod 的
/etc/resolv.conf。 nameserver 是否正确(10.96.0.10或你的集群 DNS IP)?搜索列表是否存在? - 检查 Pod 的
dnsPolicy。 如果是Default,Pod 无法解析 Kubernetes 服务。如果是hostNetwork: true,你需要ClusterFirstWithHostNet。 - 确认 CoreDNS 正在运行。
kubectl get pods -n kube-system -l k8s-app=kube-dns—— 所有 Pod 是否都处于Running状态? - 检查 CoreDNS 日志。 留意
SERVFAIL、i/o timeout或connection refused之类的消息。 - 从调试 Pod 进行测试。 这能隔离出问题究竟出在应用上,还是出在 DNS 本身。
- 测试不同的查询类型。 Pod 能解析 Kubernetes 服务名吗?外部名称呢?两者都能吗?
- 检查 CoreDNS ConfigMap。
forward指令是否指向了一个可达的上游?如果你有自定义域名区块,语法是否正确? - 检查网络策略。 如果你使用 Calico、Cilium 或其他带有网络策略的 CNI,确保 Pod 能通过 UDP/TCP 53 端口访问 CoreDNS。
- 检查是否是 ndots 问题。 如果外部域名解析失败,但追加一个末尾的点(
.)就能修复,那说明搜索列表在干扰解析。 - 检查 DNS 速率限制。 在 EKS 上,VPC 解析器将查询限制为每个 ENI 每秒 1024 个。在高负载下检查是否有
SERVFAIL响应。 - 验证上游 DNS 的健康状况。 CoreDNS Pod 自身能否解析外部名称?
kubectl exec -n kube-system <coredns-pod> -- nslookup example.com
小结
Kubernetes 中的 DNS 是一个多层系统。最底层是 DNS 协议本身——一个分层的、逐级委派的数据库,通过从根到叶遍历一棵树来解析名称。Docker 增加了一层,将宿主机的 DNS 配置复制进容器。Kubernetes 又增加了一层,运行 CoreDNS 作为集群的 DNS 提供者,同时处理内部服务发现和外部解析。
Kubernetes 中的大多数 DNS 问题都可归入以下几类:
dnsPolicy错误 —— Pod 根本无法访问到 CoreDNS- 缺少自定义转发规则 —— 从 CoreDNS 无法访问内部/私有域名
ndots:5开销 —— 外部查询产生了不必要的查询- 网络策略阻断了 DNS —— Pod 无法通过 53 端口访问 CoreDNS
- 上游 DNS 问题 —— CoreDNS 本身没问题,但它的上游(VPC 解析器、企业 DNS)坏了
有了本文介绍的 dig 和 nslookup 技巧,再加上这份系统化的排查清单,你应该能够将任何 DNS 问题从 Pod 一路追踪到根服务器,再追踪回来。
参考资料
- Kubernetes DNS for Services and Pods —— DNS 策略与 CoreDNS 的官方文档
- CoreDNS Manual —— 完整的插件参考
- Amazon EKS Best Practices: DNS —— EKS 特有的指南
- DNS Query Walkthrough (RFC 1035) —— 最初的 DNS 规范
- EDNS(0) Extension Mechanisms —— OPT 伪区段详解
参考资料
- DNS for Services and Pods — Kubernetes Documentation
- CoreDNS manual — CoreDNS
- RFC 1035: Domain Names — Implementation and Specification — IETF