1. 问题现象
在部署 Kubernetes v1.24+ 版本(如 v1.35)并使用 Docker 作为运行时环境时,重启 Master 节点后,执行 kubectl get nodes 出现连接被拒绝的报错:
E0123 06:25:39.253691 5936 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"https://master:6443/api?timeout=32s\": dial tcp 192.168.109.100:6443: connect: connection refused"
进一步排查 kubelet 服务日志 (journalctl -xefu kubelet),发现关键报错:
Error while dialing: dial unix /var/run/cri-dockerd.sock: connect: no such file or directory
检查 cri-dockerd 服务状态时,发现服务未启动。但其实我是配置了cri-dockerd服务开机自动启动的,所以这里的主要问题是为什么自动启动失败。
进一步查看报错,提示 Socket 无法加载主服务:
systemd[1]: cri-docker.socket: Socket service cri-docker.service not loaded, refusing.
systemd[1]: Failed to listen on cri-docker.socket.
2. 原因分析
- API Server 无法连接的原因:Kubernetes v1.24+ 移除了 Dockershim,使用 Docker 必须通过
cri-dockerd中间件。如果cri-dockerd未运行,Kubelet 就无法驱动 Docker,导致 API Server 容器无法启动。 cri-dockerd服务启动失败的原因:cri-dockerd通常配置为 Socket Activation 模式。这意味着cri-dockerd.socket负责创建/var/run/cri-dockerd.sock文件并监听请求,收到请求后才拉起cri-dockerd.service。如果 Socket 没启动,Kubelet 就找不到 sock 文件。- Socket未启动的原因:
- 命名不一致:Socket 文件配置了
PartOf=cri-dockerd.service,但文件系统中只有cri-docker.service(少了个 d),导致 Systemd 找不到依赖服务。 - 语法错误:Systemd Unit 文件中不支持行内注释(如
Requires=xxx # 注释),这会导致整行解析失败,依赖关系失效。
- 命名不一致:Socket 文件配置了
3. 解决方案
1. 先停止并禁用可能存在的旧服务,避免冲突
systemctl stop cri-docker.socket cri-docker.service
systemctl disable cri-docker.socket cri-docker.service
# 如果文件存在,删除它们
rm -f /etc/systemd/system/cri-docker.socket
rm -f /etc/systemd/system/cri-docker.service
2.规范化服务名称与配置
为了避免混淆,建议统一将 Service 和 Socket 文件命名为 cri-dockerd(带 d)。
修复 Service 文件 (/etc/systemd/system/cri-dockerd.service):
注意:去掉所有行内注释,确保无多余换行。
[Unit]
Description=CRI Interface for Docker Application Container Engine
Documentation=https://docs.mirantis.com
After=network-online.target firewalld.service docker.service
Wants=network-online.target
Requires=cri-dockerd.socket
[Service]
Type=notify
ExecStart=/usr/local/bin/cri-dockerd --pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.10 --network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/cni/bin --container-runtime-endpoint=unix:///var/run/cri-dockerd.sock --docker-endpoint=unix:///var/run/docker.sock --cri-dockerd-root-directory=/var/lib/docker
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutSec=0
RestartSec=2
Restart=always
StartLimitBurst=3
StartLimitInterval=60s
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
TasksMax=infinity
Delegate=yes
KillMode=process
[Install]
WantedBy=multi-user.target
修复 Socket 文件 (/etc/systemd/system/cri-dockerd.socket):
注意:PartOf 必须严格匹配 Service 的文件名。
[Unit]
Description=CRI Docker Socket for the API
PartOf=cri-dockerd.service
[Socket]
ListenStream=/var/run/cri-dockerd.sock
SocketMode=0660
SocketUser=root
SocketGroup=docker
[Install]
WantedBy=sockets.target
3. 重新加载并启动
如果之前存在不一致的文件(如 cri-docker.service),需要清理掉,避免 Systemd 冲突。
# 重新加载配置
systemctl daemon-reload
# 启动顺序:先 Socket 后 Service
systemctl enable --now cri-dockerd.socket
systemctl enable --now cri-dockerd.service
4. 验证状态
systemctl status cri-dockerd.socket
如果这个是 Active (running),那么 /var/run/cri-dockerd.sock 就会存在,kubelet也就能正常连接了。
5. 最后重启Kubelet
systemctl restart kubelet