诊断流程的基本概念
集群内部域名:CoreDNS会将集群中的服务暴露为集群内部域名,默认以.cluster.local结尾,这类域名的解析通过CoreDNS内部缓存完成,不会从上游DNS服务器查询。
VPC、地域内部域名:在VPC、地域内DNS服务器中注册的天翼云各产品内部服务域名,默认以.cnsp-internal.ctyun.cn结尾,这类域名通常与地域、VPC相关,由CoreDNS的上游DNS服务器负责解析,CoreDNS仅做解析请求转发。
公网域名:在第三方DNS服务商、天翼云DNS云解析等产品注册的权威解析,这类域名由CoreDNS的上游DNS服务器负责解析,CoreDNS仅做解析请求转发。
业务Pod:部署在Kubernetes集群中的容器Pod,特指非Kubernetes自身系统组件的容器。
使用CoreDNS的业务Pod:容器内DNS服务器地址为CoreDNS的Service IP(查看容器/etc/resolv.conf文件得到的DNS服务器地址与CoreDNS的Service IP相同)。
使用NodeLocal DNSCache的业务Pod:集群中安装了NodeLocal DNSCache插件后,通过自动或手动方式注入DNSConfig的业务Pod。这类Pod在解析域名时,会优先访问本地缓存组件。在访问本地缓存组件不通或无DNS缓存记录时,会再次访问CoreDNS提供的kube-dns服务。
异常诊断流程
检查业务Pod的DNS配置
#以default命名空间的nginx容器为例
#查看nginx容器的YAML配置,并确认DNSPolicy字段是否符合预期。
kubectl get pod nginx-xxxxxxxxx-xxxxx -n default -o yaml
#当DNSPolicy符合预期时,可以进一步进入Pod容器中,查看实际生效的DNS配置。
#通过bash命令进入nginx容器,若bash不存在可使用sh代替。
kubectl exec -it nginx-xxxxxxxxx-xxxxx -n default bash
#进入容器后,可以查看DNS配置,nameserver后面为DNS服务器地址。
cat /etc/resolv.conf
检查业务Pod的DNS配置
DNS Policy示例如下所示
apiVersion: v1
kind: Pod
metadata:
name: nginx-xxxxxxxxx-xxxxx
namespace: default
spec:
containers:
- image: <container-image>
name: <container-name>
#默认场景下的DNS Policy。
dnsPolicy: ClusterFirst
#使用了NodeLocal DNSCache时的DNS Policy。
dnsPolicy: None
dnsConfig:
nameservers:
- 192.168.0.10
- 10.96.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服务器 |
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。 |
Default | 只适用于不需要访问集群内部服务的场景。Pod创建时会从主机节点/etc/resolv.conf文件继承DNS服务器列表。 |
检查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 2h 172.16.6.53 192.168.0.1
执行以下命令,查看Pod的实时资源使用情况(依赖于metrics-server采集相关数据,需要事先安装监控组件cube-metrics-server)。
# kubectl -n kube-system top pod -l k8s-app=kube-dns
NAME CPU(cores) MEMORY(bytes)
coredns-xxxxxxxxx-xxxxx 4m 20Mi
如果Pod不处于Running状态,可以通过kubectl -n kube-system describe podcoredns-xxxxxxxxx-xxxxx命令,查询问题原因。
检查CoreDNS运行日志
执行以下命令,输出CoreDNS Pod最新的500行日志和对应的日志时间。
# kubectl -n kube-system logs --tail=500 --timestamps coredns-xxxxxxxxx-xxxxx
检查CoreDNS DNS查询请求日志
DNS查询请求日志仅会在开启CoreDNS的log插件后,才会打印到容器日志中。
开启log插件的具体操作如下:
通过控制台或命令行工具编辑kube-system命名空间下的coredns configmap,增加log模块
# kubectl edit configmap -n kube-system coredns
Corefile: |
.:53 {
errors
log
health {
lameduck 15s
}
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 {
prefer_udp
}
cache 30
loop
reload
loadbalance
}
检查CoreDNS Pod的网络连通性
登录CoreDNS Pod所在集群节点。
执行ps aux | grep coredns,查询CoreDNS的进程ID。
执行nsenter -t <pid> -n bash,进入CoreDNS所在容器网络命名空间,其中pid为上一步得到的进程ID。
测试网络连通性
运行telnet <apiserver_clusterip> 6443,测试Kubernetes API Server的连通性。其中apiserver_clusterip为default命名空间下Kubernetes服务的IP地址,默认为10.96.0.1。
运行dig <domain> @<upstream_dns_server_ip>,测试CoreDNS Pod到上游DNS服务器的连通性。其中domain为测试域名,upstream_dns_server_ip为上游DNS服务器地址。
常见问题
现象 | 原因 | 处理方案 |
CoreDNS无法连通Kubernetes API Server | APIServer异常、机器负载高、kube-proxy 没有正常运行等。 | 请提交工单排查。 |
CoreDNS无法连通上游DNS服务器 | 机器负载高、CoreDNS配置错误、专线路由问题等。 | 请提交工单排查。 |
检查业务Pod到CoreDNS的网络连通性
选择以下任意一种方式,进入客户端Pod容器网络。
方法一(适合镜像本身含有sh、ping等命令的Pod):
使用kubectl exec -it -n <命名空间> <PodName> /bin/sh命令进入到Pod中。
方法二(适合镜像本身不含常用命令、且稳定运行的Pod):
登录业务Pod所在集群节点。
执行ps aux | grep <业务进程名>命令,查询业务容器的进程ID。
执行nsenter -t <pid> -n bash命令,进入业务Pod所在容器网络命名空间。其中pid为上一步得到的进程ID。
方法三:(适合频繁重启的Pod)
如果运行时是docker:登录业务Pod所在集群节点。
执行docker ps -a | grep <业务容器名>命令,查询k8s_POD_ 开头、使用镜像为pause的沙箱容器,记录容器ID。
执行docker inspect <沙箱容器 ID> | grep netns命令,查询/var/run/docker/netns/xxxx的容器网络命名空间路径。
执行nsenter -n<netns 路径> bash命令,进入容器网络命名空间。(其中netns 路径为上一步得到的路径,且-n和<netns 路径>之间不加空格)。
如果运行时是containerd:登录业务Pod所在集群节点,执行crictl ps -a | grep <PodName>根据Pod名称查看容器id。
根据查到的容器id,执行ctr -n k8s.io container ls | grep <容器id>查找完整的容器id(ctr使用的时候,必须写全,而docker对于id的长度没要求)
执行ctr -n k8s.io container info <完整的容器id> | grep -i pid -C 3获取业务Pod中pause容器的pid
path里面的数字即为pause容器的pid。
执行nsenter -t <pid> -n 进入容器网络命名空间。
进入容器网络命名空间后,测试网络连通性。
执行dig <domain> @<kube_dns_svc_ip>命令,测试业务Pod到CoreDNS服务kube-dns解析查询的连通性。其中<domain>为测试域名,<kube_dns_svc_ip>为kube-system命名空间中kube-dns的服务IP。默认为10.96.0.10。
执行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容器副本 | 容器网络异常或安全组没有放开ICMP。 | 检查安全组是否放开ICMP,若已放开请提交工单排查。 |
业务Pod无法通过CoreDNS服务IP解析 | 机器负载高、kube-proxy没有正常运行、安全组没有放开UDP协议53端口等。 | 检查安全组是否放开UDP 53端口,若已放开请提交工单排查。 |
业务Pod无法通过CoreDNS容器IP解析 | 机器负载高、安全组没有放开UDP协议53端口等。 | 检查安全组是否放开UDP 53端口,若已放开请提交工单排查。 |
当无法定位问题时,需要抓包进行辅助诊断。在正常情况下,抓包对业务无影响,仅会增加小部分的CPU负载和磁盘写入。
登录出现异常的业务Pod、CoreDNS Pod所在节点。
在ECS(非容器内)执行以下命令 tcpdump -i any port 53 -C 20 -W 200 -w /tmp/client_dns.pcap ,可以将最近所有的53端口信息抓取到文件中。以上命令会对抓取到的包进行rotate,最多可以写200个20 MB的.pcap文件。
结合业务日志的报错定位到精准的报错时间的报文信息。
集群外部域名解析异常
问题现象
业务Pod可以正常解析集群内部域名,但无法解析某些集群外部域名。
问题原因
上游服务器域名解析返回异常。
解决方案
修改CoreDNS配置文件corefile,开启log模块以打印查询请求日志,检查CoreDNS DNS查询请求日志。
常见请求日志
CoreDNS接收到请求并回复客户端后会打印一行日志,示例如下:
# 其中包含状态码RCODE NOERROR,代表解析结果正常返回。
[INFO] 172.20.2.25:44525 - 36259 "A IN nginx.default.svc.cluster.local. udp 56 false 512" NOERROR qr,aa,rd 110 0.000116946s
常见返回码RCODE
返回码RCODE | 含义 | 原因 |
SERVFAIL | 上游服务器异常 | 常见于无法连接上游DNS服务器等情况。 |
REFUSED | 拒绝应答 | 常见于CoreDNS配置或集群节点/etc/resolv.conf文件指向的上游DNS服务器无法处理该域名的情况,请排查CoreDNS配置文件。 |
NXDOMAIN | 域名不存在 | 容器内请求域名时,会被拼接上search后缀,若拼接的结果域名不存在,则会出现该请求码。如果确认日志中请求的域名内容存在,则说明存在异常。 |
当CoreDNS DNS查询请求日志中显示集群外部域名返回为NXDOMAIN、SERVFAIL、REFUSED时,说明CoreDNS的上游DNS服务器返回异常。请提交工单排查。
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名称,确保和其暴露SVC的名字一致。
CoreDNS Pod负载高
问题现象
部分节点或全部节点接入CoreDNS的业务,Pod解析域名的延迟增加、概率性或持续性失败。
检查CoreDNS Pod运行状态发现各副本CPU、Memory使用量接近其资源限制。
问题原因
由于CoreDNS副本数不足、业务请求量高等情况导致的CoreDNS负载高。
解决方案
考虑采用NodeLocal DNSCache缓存方案,提升DNS解析性能,降低CoreDNS负载。
适当扩充CoreDNS副本数。
常见请求日志
CoreDNS Pod负载不均
问题现象
部分接入CoreDNS的业务Pod解析域名的延迟增加、概率性或持续性失败。
检查CoreDNS Pod运行状态发现各副本CPU使用量负载不均衡。CoreDNS副本数少于两个,或多个CoreDNS副本位于同节点上。
问题原因
由于CoreDNS副本调度不均、Service亲和性设置导致CoreDNS Pod负载不均衡。
解决方案
扩容并打散CoreDNS副本到不同的节点上。参考:合理分配CoreDNS副本运行的位置。
负载不均衡时,可禁用kube-dns服务的亲和性属性。参考:关闭kube-dns服务的session亲和性配置。
合理分配CoreDNS副本运行的位置
建议您在部署CoreDNS副本时,应将CoreDNS副本打散在不同可用区、不同集群节点上,避免单节点、单可用区故障。CoreDNS默认配置了按节点的弱反亲和性,可能会因为节点资源不足导致部分或全部副本部署在同一节点上,如果遇到这种情况,请删除Pod重新触发其调度来调整。
CoreDNS所运行的集群节点应避免CPU、内存用满的情况,否则会影响域名解析的QPS和响应延迟。当集群节点条件允许时,可以考虑使将CoreDNS调度至独立的集群节点上,以提供稳定的域名解析服务。
手动扩容副本数
当集群节点数长时间较为固定时,您可以通过以下命令扩容CoreDNS副本数,如:扩容副本数为3。
kubectl scale --replicas=3 deployment/coredns -n kube-system
关闭kube-dns服务的session亲和性配置
session亲和性配置可能导致CoreDNS不同副本间存在较大负载差异,建议按以下步骤关闭。
控制台操作方式
登录云容器引擎管理控制台。
在控制台左侧导航栏中,点击集群。
在集群列表页面中,点击目标集群名称。
在集群管理页左侧导航栏中,选择网络 > 服务。
在kube-system命名空间下,点击服务kube-dns右侧的查看YAML。
如果发现sessionAffinity字段为None,则无需进行以下步骤。如果发现sessionAffinity为ClientIP,则进行以下步骤。
点击编辑按钮,删除sessionAffinity、sessionAffinityConfig及所有子键,然后点击保存。
# 删除以下所有内容。 sessionAffinity: ClientIP sessionAffinityConfig: clientIP: timeoutSeconds: 10800
再次点击服务kube-dns右侧的查看YAML,校验sessionAffinity字段是否为None,为None则Kube-DNS服务变更成功。
命令行方式
执行以下命令查看kube-dns服务配置信息。
kubectl -n kube-system get svc kube-dns -o yaml
如果发现sessionAffinity字段为None,则无需执行以下步骤。如果发现sessionAffinity为ClientIP,则执行以下步骤:打开并编辑名为kube-dns的服务。
kubectl -n kube-system edit service kube-dns
删除sessionAffinity相关设置(sessionAffinity、sessionAffinityConfig及所有子键),并保存退出。
# 删除以下所有内容。
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800
修改完成后,再次运行以下命令查看sessionAffinity字段是否为None,为None则Kube-DNS服务变更成功。
kubectl -n kube-system get svc kube-dns -o yaml
A记录和AAAA记录并发解析异常
问题现象
接入CoreDNS的业务Pod解析域名概率性失败。
从抓包或检查CoreDNS DNS查询请求日志可以发现,A和AAAA通常在同一时间的出现,并且请求的源端口一致。
问题原因
并发A和AAAA的DNS请求触发Linux内核Conntrack模块缺陷,导致UDP报文丢失。
解决方案
考虑采用NodeLocal DNSCache缓存方案,提升DNS解析性能,降低CoreDNS负载。
CentOS、Ubuntu等基础镜像,可以通过options timeout:2 attempts:3 rotate single-request-reopen等参数优化。
如果容器镜像是以Alpine制作的,建议更换基础镜像。
PHP类应用短连接解析问题较多,如果使用的是PHP Curl的调用,可以使用CURL_IPRESOLVE_V4参数仅发送IPv4解析。
NodeLocal DNSCache未生效
问题现象
NodeLocal DNSCache没有流量进入,所有请求仍在CoreDNS上。
问题原因
未配置DNSConfig注入,业务Pod实际仍配置了CoreDNS kube-dns服务IP作为DNS服务器地址。
业务Pod采用Alpine作为基础镜像,Alpine基础镜像会并发请求所有nameserver,包括本地缓存和CoreDNS。
解决方案
配置DNSConfig自动注入。
如果容器镜像是以Alpine制作的,建议更换基础镜像。