本文介绍关于DNS解析异常的诊断流程、排查思路、常见解决方案和排查方法。
索引
类别 | 内容 |
诊断流程 | |
排查思路 | |
常见排查方法 | |
常见问题及解决方案 |
诊断流程
基本概念
集群内部域名:CoreDNS会将集群中的服务暴露为集群内部域名,默认以
.cluster.local
结尾,这类域名的解析通过CoreDNS内部缓存完成,不会从上游DNS服务器查询。集群外部域名:在第三方DNS服务商、阿里云DNS云解析、PrivateZone等产品注册的权威解析,这类域名由CoreDNS的上游DNS服务器负责解析,CoreDNS仅做解析请求转发。
业务Pod:您部署在Kubernetes集群中的容器Pod,不包含Kubernetes自身系统组件的容器。
接入CoreDNS的业务Pod:容器内DNS服务器指向了CoreDNS的业务Pod。
接入NodeLocal DNSCache的业务Pod:集群中安装了NodeLocal DNSCache插件后,通过自动或手动方式注入DNSConfig的业务Pod。这类Pod在解析域名时,会优先访问本地缓存组件。如果访问本地缓存组件不通时,会访问CoreDNS提供的kube-dns服务。
异常诊断流程
判断当前的异常原因。具体信息,请参见常见客户端报错。
如果以上排查无果,请按以下步骤排查。
检查业务Pod的DNS配置,是否已经接入CoreDNS。具体操作,请参见检查业务Pod的DNS配置。
如果没有接入CoreDNS,则考虑是客户端负载原因或Conntrack表满导致解析失败。具体操作,请参见客户端负载原因导致解析失败和Conntrack表满。
如果接入了CoreDNS,则按以下步骤排查。
通过检查CoreDNS Pod运行状态进行诊断。具体操作,请参见检查CoreDNS Pod运行状态和CoreDNS Pod运行状态异常。
通过检查CoreDNS运行日志进行诊断。具体操作,请参见检查CoreDNS运行日志和集群外部域名解析异常。
确认异常是否能够稳定复现。
如果异常稳定复现,请参见检查CoreDNS DNS查询请求日志和检查业务Pod到CoreDNS的网络连通性。
如果异常不能稳定复现,请参见抓包。
如果使用了NodeLocal DNSCache,请参见NodeLocal DNSCache未生效和PrivateZone域名解析异常。
如果以上排查无果,请提交工单排查。
常见客户端报错
客户端 | 报错日志 | 可能异常 |
ping |
| 域名不存在或无法连接域名服务器。如果解析延迟大于5秒,一般是无法连接域名服务器。 |
curl |
| |
PHP HTTP客户端 |
| |
Golang HTTP客户端 |
| 域名不存在。 |
dig |
| |
Golang HTTP客户端 |
| 无法连接域名服务器。 |
dig |
|
排查思路
排查思路 | 排查依据 | 问题及解决方案 |
按解析异常的域名类型排查 | 集群内外域名都异常 | |
仅集群外部域名异常 | ||
仅PrivateZone 、vpc-proxy域名解析异常 | ||
仅Headless类型服务域名异常 | ||
按解析异常出现频次排查 | 完全无法解析 | |
异常仅出现在业务高峰时期 | ||
异常出现频次非常高 | ||
异常出现频次非常低 | ||
异常仅出现在节点扩缩容或CoreDNS缩容时 |
常见检查方法
检查业务Pod的DNS配置
命令
#查看foo容器的YAML配置,并确认DNSPolicy字段是否符合预期。 kubectl get pod foo -o yaml #当DNSPolicy符合预期时,可以进一步进入Pod容器中,查看实际生效的DNS配置。 #通过bash命令进入foo容器,若bash不存在可使用sh代替。 kubectl exec -it foo bash #进入容器后,可以查看DNS配置,nameserver后面为DNS服务器地址。 cat /etc/resolv.conf
DNS Policy配置说明
DNS Policy示例如下所示。
apiVersion: v1 kind: Pod metadata: name: <pod-name> namespace: <pod-namespace> spec: containers: - image: <container-image> name: <container-name> #默认场景下的DNS Policy。 dnsPolicy: ClusterFirst #使用了NodeLocal DNSCache时的DNS Policy。 dnsPolicy: None dnsConfig: nameservers: - 169.254.20.10 - 172.21.0.10 options: - name: ndots value: "3" - name: timeout value: "1" - name: attempts value: "2" searches: - default.svc.cluster.local - svc.cluster.local - cluster.local securityContext: {} serviceAccount: default serviceAccountName: default terminationGracePeriodSeconds: 30
DNSPolicy字段值
使用的DNS服务器
Default
只适用于不需要访问集群内部服务的场景。Pod创建时会从ECS节点/etc/resolv.conf文件继承DNS服务器列表。
ClusterFirst
此为DNSPolicy默认值,Pod会将CoreDNS提供的kube-dns服务IP作为DNS服务器。开启HostNetwork的Pod,如果选择ClusterFirst模式,效果等同于Default模式。
ClusterFirstWithHostNet
开启HostNetwork的Pod,如果选择ClusterFirstWithHostNet模式,效果等同于ClusterFirst。
None
配合DNSConfig字段,可用于自定义DNS服务器和参数。在NodeLocal DNSCache开启注入时,DNSConfig会将DNS服务器指向本地缓存IP及CoreDNS提供的kube-dns服务IP。
检查CoreDNS Pod运行状态
命令
执行以下命令,查看容器组信息。
kubectl -n kube-system get pod -o wide -l k8s-app=kube-dns
预期输出:
NAME READY STATUS RESTARTS AGE IP NODE coredns-xxxxxxxxx-xxxxx 1/1 Running 0 25h 172.20.6.53 cn-hangzhou.192.168.0.198
执行以下命令,查看Pod的实时资源使用情况。
kubectl -n kube-system top pod -l k8s-app=kube-dns
预期输出:
NAME CPU(cores) MEMORY(bytes) coredns-xxxxxxxxx-xxxxx 3m 18Mi
如果Pod不处于Running状态,可以通过
kubectl -n kube-system describe pod <CoreDNS Pod名称>
命令,查询问题原因。
检查CoreDNS运行日志
命令
执行以下命令,检查CoreDNS运行日志。
kubectl -n kube-system logs -f --tail=500 --timestamps coredns-xxxxxxxxx-xxxxx
参数 | 描述 |
| 持续输出。 |
| 输出最后500行日志。 |
| 同时显示日志打印的时间。 |
| CoreDNS Pod副本的名称。 |
检查CoreDNS DNS查询请求日志
命令
DNS查询请求日志仅会在开启CoreDNS的Log插件后,才会打印到容器日志中。关于开启Log插件的具体操作,请参见CoreDNS配置说明。
命令与检查CoreDNS运行日志相同,请参见检查CoreDNS运行日志。
检查CoreDNS Pod的网络连通性
您可以使用控制台或命令行方式检查CoreDNS Pod的网络连通性。
控制台
您可以通过集群下提供的网络诊断能力进行诊断。
登录容器服务管理控制台,在左侧导航栏选择集群。
在集群列表页面,单击目标集群名称,然后在左侧导航栏,选择巡检和诊断>故障诊断。
在故障诊断页面,单击网络诊断。
在网络诊断页面单击诊断,然后在访问信息面板,根据以下内容填写诊断参数:
源地址:输入CoreDNS Pod的IP。
目标地址:输入上游DNS服务器地址,默认可选100.100.2.136或100.100.2.138。
端口:
53
协议:
udp
填写完成后仔细阅读注意事项,选中我已知晓并同意,然后单击发起诊断。
在诊断结果页面,能够查看网络诊断结果,并且在访问全图区域,会呈现出本次诊断访问链路的全景图。
命令行
操作步骤
登录CoreDNS Pod所在集群节点。
执行
ps aux | grep coredns
,查询CoreDNS的进程ID。执行
nsenter -t <pid> -n -- <相关命令>
,进入CoreDNS所在容器网络命名空间,其中pod
为上一步得到的coredns
进程ID。测试网络连通性。
运行
telnet <apiserver_clusterip> 6443
,测试Kubernetes API Server的连通性。其中
apiserver_clusterip为default命名空间下Kubernetes服务的IP地址。
运行
dig <domain> @<upstream_dns_server_ip>
,测试CoreDNS Pod到上游DNS服务器的连通性。其中
domain
为测试域名,upstream_dns_server_ip
为上游DNS服务器地址,默认为100.100.2.136和100.100.2.138。
常见问题
现象 | 原因 | 处理方案 |
CoreDNS无法连通Kubernetes API Server | APIServer异常、机器负载高、kube-proxy 没有正常运行等。 | 可提交工单排查。 |
CoreDNS无法连通上游DNS服务器 | 机器负载高、CoreDNS配置错误、专线路由问题等。 | 可提交工单排查。 |
检查业务Pod到CoreDNS的网络连通性
您可以使用控制台或命令行方式检查业务Pod到CoreDNS的网络连通性。
控制台
登录容器服务管理控制台,在左侧导航栏选择集群。
在集群列表页面,单击目标集群名称,然后在左侧导航栏,选择巡检和诊断>故障诊断。
在故障诊断页面,单击网络诊断。
在网络诊断页面单击诊断,然后在访问信息面板,根据以下内容填写诊断参数:
源地址:输入业务Pod的IP。
目标地址:输入CoreDNS实例的PodIP或者ClusterIP。
端口:
53
协议:
udp
填写完成后仔细阅读注意事项,选中我已知晓并同意,然后单击发起诊断。
在诊断结果页面,能够查看网络诊断结果,并且在访问全图区域,会呈现出本次诊断访问链路的全景图。
命令行
操作步骤
选择以下任意一种方式,进入客户端Pod容器网络。
方法一:使用
kubectl exec
命令。方法二:
登录业务Pod所在集群节点。
执行
ps aux | grep <业务进程名>
命令,查询业务容器的进程ID。执行
nsenter -t <pid> -n bash
命令,进入业务Pod所在容器网络命名空间。其中
pid
为上一步得到的进程ID。
方法三:如果频繁重启,请按以下步骤操作。
登录业务Pod所在集群节点。
执行
docker ps -a | grep <业务容器名>
命令,查询k8s_POD_
开头的沙箱容器,记录容器ID。执行
docker inspect <沙箱容器 ID> | grep netns
命令,查询/var/run/docker/netns/xxxx的容器网络命名空间路径。执行
nsenter -n<netns 路径> bash
命令,进入容器网络命名空间。其中
netns 路径
为上一步得到的路径。说明-n
和<netns 路径>
之间不加空格。
测试网络连通性。
执行
dig <domain> @<kube_dns_svc_ip>
命令,测试业务Pod到CoreDNS服务kube-dns解析查询的连通性。其中
<domain>
为测试域名,<kube_dns_svc_ip>
为kube-system命名空间中kube-dns的服务IP。执行
ping <coredns_pod_ip>
命令,测试业务Pod到CoreDNS容器副本的连通性。其中
<coredns_pod_ip>
为kube-system命名空间中CoreDNS Pod的IP。执行
dig <domain> @<coredns_pod_ip>
命令,测试业务Pod到CoreDNS容器副本解析查询的连通性。其中
<domain>
为测试域名,<coredns_pod_ip>
为kube-system命名空间中CoreDNS Pod的IP。
常见问题
现象 | 原因 | 处理方案 |
业务Pod无法通过CoreDNS服务kube-dns解析 | 机器负载高、kube-proxy没有正常运行、安全组没有放开UDP协议53端口等。 | 检查安全组是否放开UDP 53端口,若已放开请提交工单排查。 |
业务Pod无法连通CoreDNS容器副本 | 容器网络异常或安全组没有放开ICMP。 | 检查安全组是否放开ICMP,若已放开请提交工单排查。 |
业务Pod无法通过CoreDNS容器副本解析 | 机器负载高、安全组没有放开UDP协议53端口等。 | 检查安全组是否放开UDP 53端口,若已放开请提交工单排查。 |
抓包
当无法定位问题时,需要抓包进行辅助诊断。
登录出现异常的业务Pod、CoreDNS Pod所在节点。
在ECS(非容器内)执行以下命令,可以将最近所有的53端口信息抓取到文件中。
tcpdump -i any port 53 -C 20 -W 200 -w /tmp/client_dns.pcap
结合业务日志的报错定位到精准的报错时间的报文信息。
说明在正常情况下,抓包对业务无影响,仅会增加小部分的CPU负载和磁盘写入。
以上命令会对抓取到的包进行rotate,最多可以写200个20MB的.pcap文件。
集群外部域名解析异常
问题现象
业务Pod可以正常解析集群内部域名,但无法解析某些集群外部域名。
问题原因
上游服务器域名解析返回异常。
解决方案
检查CoreDNS DNS查询请求日志。
常见请求日志
CoreDNS接收到请求并回复客户端后会打印一行日志,示例如下:
# 其中包含状态码RCODE NOERROR,代表解析结果正常返回。
[INFO] 172.20.2.25:44525 - 36259 "A IN redis-master.default.svc.cluster.local. udp 56 false 512" NOERROR qr,aa,rd 110 0.000116946s
常见返回码RCODE
关于返回码RCODE定义的具体信息,请参见规范。
返回码RCODE | 含义 | 原因 |
NXDOMAIN | 域名不存在 | 容器内请求域名时,会被拼接上search后缀,若拼接的结果域名不存在,则会出现该请求码。如果确认日志中请求的域名内容存在,则说明存在异常。 |
SERVFAIL | 上游服务器异常 | 常见于无法连接上游DNS服务器等情况。 |
REFUSED | 拒绝应答 | 常见于CoreDNS配置或集群节点/etc/resolv.conf文件指向的上游DNS服务器无法处理该域名的情况,请排查CoreDNS配置文件。 |
当CoreDNS DNS查询请求日志中显示集群外部域名返回为NXDOMAIN
、SERVFAIL
、REFUSED
时,说明CoreDNS的上游DNS服务器返回异常。
默认情况下,集群中CoreDNS的上游DNS服务器是VPC提供的DNS服务器(100.100.2.136 和 100.100.2.138)。您可以提交工单至云服务器ECS产品。提交工单时请注明以下信息。
字段 | 含义 | 示例 |
受损域名 | CoreDNS日志中返回码RCODE异常的集群外部域名 | www.aliyun.com |
解析返回码RCODE | 具体解析报错(NXDOMAIN、SERVFAIL、REFUSED) | NXDOMAIN |
受损时间 | 日志出现的时间(精确到秒) | 2022-12-22 20:00:03 |
受损ECS | CoreDNS各副本Pod所处的ECS实例ID | i-xxxxx i-yyyyy |
新增Headless类型域名无法解析
问题现象
接入CoreDNS的业务Pod无法解析新增的Headless类型域名。
问题原因
1.7.0以前版本CoreDNS会在API Server抖动时异常退出,导致Headless域名停止更新。
解决方案
升级CoreDNS至1.7.0以上。具体操作,请参见【组件升级】CoreDNS升级公告。
Headless类型域名解析失败
问题现象
接入CoreDNS的业务Pod无法解析Headless类型的域名。使用dig
解析时,返回中显示tc
标志,表示响应消息过大。
问题原因
当Headless类型的域名对应的IP条目数量过多时,客户端通过UDP方式发送DNS请求可能会超出UDP DNS报文的大小限制,导致解析失败。
解决方案
为了避免解析失败,您可以将客户端业务调整为使用TCP方式进行DNS查询。CoreDNS同时支持TCP和 UDP查询,以下是根据不同业务场景的修改方式:
使用glibc相关的解析器。
如果您的客户端业务使用的是glibc相关的Resolve解析器,可以在
dnsConfig
中增加use-vc
配置使用TCP进行DNS查询。这些设置将在/etc/resolv.conf
中的映射到相应的options
配置。关于options
配置详细说明请参见Linux man pages。dnsConfig: options: - name: use-vc
Golang实现的业务代码逻辑
如果您使用Golang进行开发,可以参考以下代码,使用TCP进行DNS查询。
package main import ( "fmt" "net" "context" ) func main() { resolver := &net.Resolver{ PreferGo: true, Dial: func(ctx context.Context, network, address string) (net.Conn, error) { return net.Dial("tcp", address) }, } addrs, err := resolver.LookupHost(context.TODO(), "example.com") if err != nil { fmt.Println("Error:", err) return } fmt.Println("Addresses:", addrs) }
升级CoreDNS后Headless类型域名无法解析
问题现象
部分较低版本开源组件(低版本etcd、nacos、kafka等)在K8s 1.20及以上版本和 CoreDNS 1.8.4及以上版本的环境中无法正常工作。
问题原因
1.8.4及以上版本的CoreDNS优先使用EndpointSlice API同步K8s内服务IP信息。一些开源组件在初始化阶段会使用Endpoint API 提供的注解service.alpha.kubernetes.io/tolerate-unready-endpoints
来发布尚未就绪的服务。该注解在EndpointSlice API中已经废弃,并被publishNotReadyAddresses
所替代。因此CoreDNS升级后,无法发布未就绪的服务,导致这些组件无法进行服务发现。
解决方案
检查开源组件的YAML或Helm Chart中是否包含service.alpha.kubernetes.io/tolerate-unready-endpoints
注解,如果包含则可能无法正常工作,您需要升级开源组件或咨询开源组件社区。
StatefulSets Pod域名无法解析
问题现象
Headless服务无法通过Pod域名解析。
问题原因
StatefulSets Pod YAML中ServiceName必须和其暴露的SVC名字一致,否则无法访问Pod域名(例如pod.headless-svc.ns.svc.cluster.local),只能访问到服务域名(例如headless-svc.ns.svc.cluster.local)。
解决方案
修改StatefulSets Pod YAML中ServiceName名称。
安全组、交换机ACL配置错误
问题现象
部分节点或全部节点上接入CoreDNS的业务,Pod解析域名持续性失败。
问题原因
修改了ECS或容器使用的安全组(或交换机ACL),拦截了UDP协议下53端口的通信。
解决方案
恢复安全组、交换机ACL的配置,放开其以UDP协议对53端口的通信。
容器网络连通性异常
问题现象
部分节点或全部节点上接入CoreDNS的业务,Pod解析域名持续性失败。
问题原因
由于容器网络或其它原因导致的UDP协议53端口持续性不通。
解决方案
CoreDNS Pod负载高
问题现象
部分节点或全部节点接入CoreDNS的业务,Pod解析域名的延迟增加、概率性或持续性失败。
检查CoreDNS Pod运行状态发现各副本CPU、Memory使用量接近其资源限制。
问题原因
由于CoreDNS副本数不足、业务请求量高等情况导致的CoreDNS负载高。
解决方案
考虑采用NodeLocal DNSCache缓存方案,提升DNS解析性能,降低CoreDNS负载。具体操作,请参见使用NodeLocal DNSCache。
适当扩充CoreDNS副本数,使每个Pod的峰值CPU始终低于节点空闲CPU数。
CoreDNS Pod负载不均
问题现象
部分接入CoreDNS的业务Pod解析域名的延迟增加、概率性或持续性失败。
检查CoreDNS Pod运行状态发现各副本CPU使用量负载不均衡。
CoreDNS副本数少于两个,或多个CoreDNS副本位于同节点上。
问题原因
由于CoreDNS副本调度不均、Service亲和性设置导致CoreDNS Pod负载不均衡。
解决方案
扩容并打散CoreDNS副本到不同的节点上。
负载不均衡时,可禁用kube-dns服务的亲和性属性。具体操作,请参见配置Kube-DNS服务。
CoreDNS Pod运行状态异常
问题现象
部分接入CoreDNS的业务Pod解析域名的延迟增加、概率性或持续性失败。
CoreDNS副本状态Status不处于Running状态,或重启次数RESTARTS持续增加。
CoreDNS运行日志中出现异常。
问题原因
由于CoreDNS YAML模板、配置文件等导致CoreDNS运行异常。
解决方案
检查CoreDNS Pod运行状态和运行日志。
常见异常日志及处理方案
日志中字样 | 原因 | 处理方案 |
| 配置文件和CoreDNS不兼容, | 从kube-system命名空间中CoreDNS配置项中删除ready插件,其它报错同理。 |
| 日志出现时间段内,API Server中断。 | 如果是日志出现时间和异常不吻合,可以排除该原因,否则请检查CoreDNS Pod网络连通性。具体操作,请参见检查CoreDNS Pod的网络连通性。 |
| 日志出现时间段内,CoreDNS无法连接到上游DNS服务器。 |
客户端负载原因导致解析失败
问题现象
业务高峰期间或突然偶发的解析失败,ECS监控显示机器网卡重传率、CPU负载异常。
问题原因
接入CoreDNS的业务Pod所在ECS负载达到100%等情况导致UDP报文丢失。
解决方案
建议提交工单排查原因。
考虑采用NodeLocal DNSCache缓存方案,提升DNS解析性能,降低CoreDNS负载。具体操作,请参见使用NodeLocal DNSCache。
Conntrack表满
问题现象
部分节点或全部节点上接入CoreDNS的业务,Pod解析域名在业务高峰时间段内出现大批量域名解析失败,高峰结束后失败消失。
运行
dmesg -H
,滚动到问题对应时段的日志,发现出现conntrack full
字样的报错信息。
问题原因
Linux内Conntrack表条目有限,无法进行新的UDP或TCP请求。
解决方案
增加Conntrack表限制。具体操作,请参见如何提升Linux连接跟踪Conntrack数量限制?。
AutoPath插件异常
问题现象
解析集群外部域名时,概率性解析失败或解析到错误的IP地址,解析集群内部域名无异常。
高频创建容器时,集群内部服务域名解析到错误的IP地址。
问题原因
CoreDNS处理缺陷导致AutoPath无法正常工作。
解决方案
按照以下步骤,关闭AutoPath插件。
执行
kubectl -n kube-system edit configmap coredns
命令,打开CoreDNS配置文件。删除
autopath @kubernetes
一行后保存退出。检查CoreDNS Pod运行状态和运行日志,运行日志中出现
reload
字样后说明修改成功。
A记录和AAAA记录并发解析异常
问题现象
接入CoreDNS的业务Pod解析域名概率性失败。
从抓包或检查CoreDNS DNS查询请求日志可以发现,A和AAAA通常在同一时间的出现,并且请求的源端口一致。
问题原因
并发A和AAAA的DNS请求触发Linux内核Conntrack模块缺陷,导致UDP报文丢失。
低版本的libc(<2.33)在ARM机型上在同时发起A和AAAA请求时的并发问题,导致请求超时重传,请参见GLIBC#26600。
解决方案
考虑采用NodeLocal DNSCache缓存方案,提升DNS解析性能,降低CoreDNS负载。具体操作,请参见使用NodeLocal DNSCache。
CentOS、Ubuntu等使用libc的基础镜像,升级libc版本到2.33或以上版本避免A和AAAA并发解析问题。
CentOS、Ubuntu等基础镜像,可以通过
options timeout:2 attempts:3 rotate single-request-reopen
等参数优化。如果容器镜像是以Alpine制作的,建议更换基础镜像。更多信息,请参见Alpine。
PHP类应用短连接解析问题较多,如果使用的是PHP Curl的调用,可以使用
CURL_IPRESOLVE_V4
参数仅发送IPv4解析。更多信息,请参见函数说明。
IPVS缺陷导致解析异常
问题现象
当集群节点扩缩容、节点关机、CoreDNS缩容时,出现概率性解析失败,通常时长在五分钟左右。
问题原因
若您集群的kube-proxy负载均衡模式为IPVS,在CentOS、Alibaba Cloud Linux 2内核版本小于4.19.91-25.1.al7.x86_64的节点上,摘除IPVS UDP类型后端后,一段时间内若新发起的UDP报文源端口冲突,该报文会被丢弃。
解决方案
考虑采用NodeLocal DNSCache缓存方案,可以容忍IPVS丢包。具体操作,请参见使用NodeLocal DNSCache。
优化IPVS UDP超时时间。具体操作,请参见配置IPVS类型集群的UDP超时时间。
NodeLocal DNSCache未生效
问题现象
NodeLocal DNSCache没有流量进入,所有请求仍在CoreDNS上。
问题原因
未配置DNSConfig注入,业务Pod实际仍配置了CoreDNS kube-dns服务IP作为DNS服务器地址。
业务Pod采用Alpine作为基础镜像,Alpine基础镜像会并发请求所有nameserver,包括本地缓存和CoreDNS。
解决方案
配置DNSConfig自动注入。具体操作,请参见使用NodeLocal DNSCache。
如果容器镜像是以Alpine制作的,建议更换基础镜像。更多信息,请参见Alpine。
PrivateZone域名解析异常
问题现象
对于接入NodeLocal DNSCache的业务,Pod无法解析PrivateZone上注册的域名,或无法解析包含vpc-proxy字样的阿里云云产品API域名,或解析结果不正确。
问题原因
PrivateZone不支持TCP协议,需要使用UDP协议访问。
解决方案
在CoreDNS中配置prefer_udp
。具体操作,请参见CoreDNS配置说明。