Kubernetes 故障排查:API Server 无法连接与 CRI-Dockerd 开机自动启动失败

0 阅读2分钟

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. 原因分析

  1. API Server 无法连接的原因:Kubernetes v1.24+ 移除了 Dockershim,使用 Docker 必须通过 cri-dockerd 中间件。如果 cri-dockerd 未运行,Kubelet 就无法驱动 Docker,导致 API Server 容器无法启动。
  2. cri-dockerd 服务启动失败的原因cri-dockerd 通常配置为 Socket Activation 模式。这意味着 cri-dockerd.socket 负责创建 /var/run/cri-dockerd.sock 文件并监听请求,收到请求后才拉起 cri-dockerd.service。如果 Socket 没启动,Kubelet 就找不到 sock 文件。
  3. Socket未启动的原因
    • 命名不一致:Socket 文件配置了 PartOf=cri-dockerd.service,但文件系统中只有 cri-docker.service(少了个 d),导致 Systemd 找不到依赖服务。
    • 语法错误:Systemd Unit 文件中不支持行内注释(如 Requires=xxx # 注释),这会导致整行解析失败,依赖关系失效。

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