背景
今天遇到一个奇怪的问题,集群中有个节点在重启后,调度到该节点上的CoreDNS就一直处于CrashLoopBackOff状态
查看CoreDNS pod的日志有报错
CoreDNS-1.9.3
linux/amd64, go1.18.2, 45b0a11
[FATAL] plugin/loop: Loop (127.0.0.1:38718 -> :53) detected for zone ".", see https://coredns.io/plugins/loop#troubleshooting. Query: "HINFO 1602357022762538710.2777015015780658687."
排查流程
1、根据错误日志提供的文档描述,这个报错是在loop插件发现了循环解析的问题,意味着CoreDNS forward给上游的请求还是回到了自身。
2、查看CoreDNS的配置,找到forward的上游是来自/etc/resolv.conf
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
}
prometheus :9153
forward . /etc/resolv.conf {
prefer_udp
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
3、现在我们需要知道容器里面的文件内容/etc/resolv.conf
,最简单的方式就是exec到容器里面,但这个pod启动就crash了,没法exec进入容器,那应该如何debug。
常见思路
a、调整容器的启动命令改成sleep 3600,让容器处于运行状态,但coredns是核心组件,这么改服务就烂了,不可取。
b、复制pod的内容,改启动命令,并指定启动节点,这个思路在当前场景下是可行的。
这里我提供另一个思路,因为resolv.conf
配置内容是容器创建时生成好再挂载到容器里面,所以在异常节点上是能直接查找容器resolv.conf
对应在宿主机上的路径。
查找流程
# 先找到异常节点上coredns对应pod的id
$ sudo crictl pods |grep coredns
a70dbd1526493 37 minutes ago Ready coredns-676c5c79f-k74zq kube-system 0 (default)
# 解析出resolv.conf在主机上的路径
$ sudo crictl inspectp a70dbd1526493 | jq '.info.runtimeSpec.mounts[] | select(.destination == "/etc/resolv.conf") | .source'
"/var/lib/containerd/io.containerd.grpc.v1.cri/sandboxes/a70dbd15264930d9969a684dbcd0429a4846d747410726859052e710a2930611/resolv.conf"
# 查看内容
$ sudo cat /var/lib/containerd/io.containerd.grpc.v1.cri/sandboxes/a70dbd15264930d9969a684dbcd0429a4846d747410726859052e710a2930611/resolv.conf
search xxxx.com
nameserver 127.0.0.53 ## 这里证实coredns forward的地址确实是本地的
options edns0 trust-ad
对比正常运行的coredns容器, 发现正常节点会forward到其他地址
cat /var/lib/containerd/io.containerd.grpc.v1.cri/sandboxes/aef05855960d32e3bc2559cb93fe626a67a18e45b90a12bc19a15a70e6e810a6/resolv.conf
search xxxx.com
nameserver 169.254.25.10
nameserver 8.8.8.8
排查到这里,原因基本清晰了,因为而这个127.0.0.53
是本地回环IP,所以coredns forward给这个地址的请求又会回到自身,从而导致循环。
答疑解惑
为什么会出现resolv.conf不一致的内容
对比排查后发现,异常CoreDNS的resolv.conf
内容来源和宿主机/etc/resolv.conf
内容是相同,而正常的CoreDNS的resolv.conf
内容来源和宿主机/run/systemd/resolve/resolv.conf
内容是相同。
而控制这个配置来源在kubelet
的/etc/kubernetes/kubelet-config.yaml
配置文件里面
# 异常节点上
clusterDNS:
- 169.254.25.10
resolvConf: "/etc/resolv.conf"
# 正常节点
clusterDNS:
- 169.254.25.10
resolvConf: "/run/systemd/resolve/resolv.conf"
为什么异常节点上的业务pod的dns解析是正常的
根据上面的结论,如果pod没有使用host网络,那dns请求到127.0.0.53
会导致pod服务解析域名,但排查过程中发现异常节点上的业务pod是能正常进行域名解析的。
通过查看业务pod容器里面的resolv.conf
, 配置内容如下
search monitoring.svc.cluster.local svc.cluster.local cluster.local xxxx.com
nameserver 169.254.25.10
options ndots:5
熟悉pod配置的小伙伴应该反应过来了,这是因为pod的dnsPolicy
不一样.
CoreDNS配置的是dnsPolicy: Default
, 使用的dns配置内容来自kubelet
的resolvConf
而业务pod的配置是dnsPolicy: ClusterFirst
, 对应的配置dns配置内容来自kubelet
的clusterDNS
。
修复方案
如果宿主机使用systemd-resolved.service
来管理节点的dns解析配置,那kubelet
里面配置的resolvConf
需要还原为/run/systemd/resolve/resolv.conf
,然后delete掉异常的coredns pod后就恢复正常了。
为什么节点kubelet配置会被改错
因为k8s集群的管理是通过kubespray维护,所有节点的配置按说应该都一样,为什么有些节点的配置不对呢?