在一次 Kubernetes 单节点集群部署中,我遇到一个令人困惑的问题:Pod 启动失败、Kubernetes 控制面组件无法解析 DNS、etcd 报错……最终发现元凶竟然是 /etc/resolv.conf 的“断链”!
本文将记录问题发生的背景、分析过程和最终解决方案,并解释为什么这种事在“什么都没动”的情况下仍然会发生。
问题现场
在我的hyperv环境更换vswitch后,做kubectl get操作
kubectl get nodes
报出如下错误:
E0521 15:16:21.293830 2123 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"https://k8sm01.lab.com:6443/api?timeout=32s\": dial tcp 172.20.32.247:6443: connect: connection refused"
解读如下:
-
- E0521
E 代表 Error,表示这是一个错误级别的日志。
0521 表示发生的日期是 5 月 21 日(即 05 月 21 日)。
- E0521
-
- 15:16:21.293830
表示错误发生的 时间,精确到微秒:
15 点 16 分 21 秒 293830 微秒。
- 15:16:21.293830
-
- 2123
这是日志产生的 进程 ID (PID),即哪个程序进程产生了这条日志。
- 2123
-
- memcache.go:265
表示出错的文件和行数:
在源码文件 memcache.go 的 第 265 行。
- memcache.go:265
-
- “Unhandled Error”
这是一个通用错误标签,表示这个错误没有被特别处理(即程序没有处理它,只是记录下来)。
- “Unhandled Error”
-
- err=“couldn’t get current server API group list: …”
这是错误的具体内容:
- err=“couldn’t get current server API group list: …”
Get \"https://k8sm01.lab.com:6443/api?timeout=32s\":
dial tcp 172.20.32.247:6443: connect: connection refused"
说明客户端(kubectl 或 kubelet)尝试访问 API Server 时,目标 IP 172.20.32.247 上的端口 6443 无响应,连接被拒绝。
查因过程
查看kubelet状态
systemctl status kubelet.service
输出:
systemctl status kubelet.service
● kubelet.service - kubelet: The Kubernetes Node Agent
Loaded: loaded (/usr/lib/systemd/system/kubelet.service; enabled; preset: enabled)
Drop-In: /usr/lib/systemd/system/kubelet.service.d
└─10-kubeadm.conf
Active: active (running) since Wed 2025-05-21 15:07:30 UTC; 39min ago
Docs: https://kubernetes.io/docs/
Main PID: 2046 (kubelet)
Tasks: 10 (limit: 2194)
Memory: 26.0M (peak: 27.9M)
CPU: 1min 16.014s
CGroup: /system.slice/kubelet.service
└─2046 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/>
May 21 15:47:02 k8sm01.lab.com kubelet[2046]: E0521 15:47:02.112556 2046 pod_workers.go:1301] "Error syncing pod, skipping" err="failed to \"CreatePodSandbox\" for >
May 21 15:47:02 k8sm01.lab.com kubelet[2046]: E0521 15:47:02.113034 2046 dns.go:284] "Could not open resolv conf file." err="open /run/systemd/resolve/resolv.conf: >
May 21 15:47:02 k8sm01.lab.com kubelet[2046]: E0521 15:47:02.113069 2046 kuberuntime_sandbox.go:45] "Failed to generate sandbox config for pod" err="open /run/syste>
May 21 15:47:02 k8sm01.lab.com kubelet[2046]: E0521 15:47:02.113090 2046 kuberuntime_manager.go:1170] "CreatePodSandbox for pod failed" err="open /run/systemd/resol>
May 21 15:47:02 k8sm01.lab.com kubelet[2046]: E0521 15:47:02.113137 2046 pod_workers.go:1301] "Error syncing pod, skipping" err="failed to \"CreatePodSandbox\" for
由于systemctl输出的日志被截断,使用journalctl获取完整日志:
journalctl -b 0 --since=today -u kubelet --no-pager
输出:
......
May 21 15:51:50 k8sm01.lab.com kubelet[2046]: E0521 15:51:50.122721 2046 dns.go:284] "Could not open resolv conf file." err="open /run/systemd/resolve/resolv.conf: no such file or directory"
May 21 15:51:50 k8sm01.lab.com kubelet[2046]: E0521 15:51:50.122778 2046 kuberuntime_sandbox.go:45] "Failed to generate sandbox config for pod" err="open /run/systemd/resolve/resolv.conf: no such file or directory" pod="kube-system/kube-scheduler-k8sm01.lab.com"
May 21 15:51:50 k8sm01.lab.com kubelet[2046]: E0521 15:51:50.122814 2046 kuberuntime_manager.go:1170] "CreatePodSandbox for pod failed" err="open /run/systemd/resolve/resolv.conf: no such file or directory" pod="kube-system/kube-scheduler-k8sm01.lab.com"
May 21 15:51:50 k8sm01.lab.com kubelet[2046]: E0521 15:51:50.122879 2046 pod_workers.go:1301] "Error syncing pod, skipping" err="failed to \"CreatePodSandbox\" for \"kube-scheduler-k8sm01.lab.com_kube-system(0b6d714931c0469558b131ba4fad4975)\" with CreatePodSandboxError: \"Failed to generate sandbox config for pod \\\"kube-scheduler-k8sm01.lab.com_kube-system(0b6d714931c0469558b131ba4fad4975)\\\": open /run/systemd/resolve/resolv.conf: no such file or directory\"" pod="kube-system/kube-scheduler-k8sm01.lab.com" podUID="0b6d714931c0469558b131ba4fad4975"
May 21 15:51:50 k8sm01.lab.com kubelet[2046]: E0521 15:51:50.369772 2046 controller.go:145] "Failed to ensure lease exists, will retry" err="Get \"https://k8sm01.lab.com:6443/apis/coordination.k8s.io/v1/namespaces/kube-node-lease/leases/k8sm01.lab.com?timeout=10s\": dial tcp 172.20.32.247:6443: connect: connection refused" interval="7s"
.......
kubelet 日志已经揭示了关键问题:Kubelet 在尝试创建 Pod 的 Sandbox 时失败了,而根本原因是它无法读取 DNS 配置文件 /run/systemd/resolve/resolv.conf
根因
在使用 systemd-resolved 的 Linux 系统中(如 Ubuntu 18+),/etc/resolv.conf 默认是个符号链接,指向:
ls -alh /etc/resolv.conf
lrwxrwxrwx 1 root root 39 Apr 23 2024 /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf
由于某些原因, /etc/resolv.conf 仍指向那个已不存在的文件,就会造成容器(例如 etcd、kubelet、CoreDNS)无法解析 DNS。
解决方案
方法一:恢复 /etc/resolv.conf 到标准格式
检查当前链接:
ls -l /etc/resolv.conf
如果输出类似:
/etc/resolv.conf -> /run/systemd/resolve/resolv.conf
但 /run/systemd/resolve/resolv.conf 不存在,那么可以临时替换为传统 resolv.conf:
sudo rm /etc/resolv.conf
echo "nameserver 8.8.8.8" | sudo tee /etc/resolv.conf
重新启动 kubelet:
sudo systemctl restart kubelet
方法二:重新启用 systemd-resolved(如需要)
如果你之前禁用了 systemd-resolved,可以启用它:
sudo systemctl enable systemd-resolved --now
然后检查:
ls -l /etc/resolv.conf
# 如果还没有指向 /run/systemd/resolve/resolv.conf
sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf
验证
确保 /etc/resolv.conf 文件有效:
cat /etc/resolv.conf
应该能看到:
nameserver 8.8.8.8
然后再检查做kubectl get操作恢复正常。
kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8sm01.lab.com Ready control-plane 17d v1.31.8
k8sw01.lab.com NotReady <none> 17d v1.31.8
总结
这个错误的根本原因是:/etc/resolv.conf 指向了一个不存在的文件,导致 Kubernetes 组件无法进行 DNS 解析。
最直接修复方法 是用 echo “nameserver 8.8.8.8” > /etc/resolv.conf 方式写入一个有效 DNS 配置,然后重启 kubelet。