K8s 测试集群部署与镜像拉取排障手册
K8s 测试集群部署与镜像拉取排障手册
AI生成内容
环境:Ubuntu 24.04 单节点 K8s v1.34.8 + containerd 2.2.1
主机:192.168.1.88,SSH 端口 2222(不是默认 22)
最后更新:2026-05-29
1. 快速连接
# 从本机连接虚拟机
ssh -p 2222 walker@192.168.1.88
# 或使用项目脚本(Windows 本机)
python scripts/ssh_helper.py "kubectl get nodes"
| 项目 | 值 |
|---|---|
| IP | 192.168.1.88 |
| SSH 端口 | 2222 |
| 用户 | walker |
| 系统 | Ubuntu 24.04.3 LTS |
| K8s 版本 | v1.34.8(kubeadm 单节点) |
| 容器运行时 | containerd 2.2.1 |
2. 一键诊断清单
遇到 Pod ImagePullBackOff 时,按顺序执行:
# ① 网络:外网路由 vs 镜像仓库
ping -c 2 8.8.8.8
curl -sfI --connect-timeout 8 https://registry-1.docker.io/v2/ # 预期:超时或 refused
curl -sfI --connect-timeout 8 https://docker.m.daocloud.io/v2/ # 预期:HTTP 401(说明可达)
# ② CRI 插件是否正常(最关键)
sudo ctr plugins ls | grep cri
# 期望:io.containerd.cri.v1 images = ok
# io.containerd.grpc.v1 cri = ok
# ③ containerd 是否有配置冲突
sudo journalctl -u containerd -n 20 | grep -iE 'error|fail|mirrors'
sudo containerd config dump | grep -A 10 'registry'
# ④ 直接测试拉镜像
sudo ctr -n k8s.io images pull docker.m.daocloud.io/library/busybox:latest # 应成功
sudo crictl pull busybox:latest # 短名可能失败
# ⑤ 查看 Pod 事件
kubectl describe pod <pod-name> | tail -15
3. 本次踩坑:两个叠加的根因
3.1 containerd CRI 插件崩溃(主因)
现象:
rpc error: code = Unimplemented desc = unknown service runtime.v1.ImageService
rpc error: code = Unimplemented desc = unknown service runtime.v1.RuntimeService
crictl pull 完全不可用
根因: /etc/containerd/config.toml 中同时配置了 mirrors 和 config_path。
containerd 2.x 不允许两者共存,导致 CRI 镜像服务插件加载失败。
日志关键字:
invalid cri image config: `mirrors` cannot be set when `config_path` is provided
错误配置示例(不要这样写):
[plugins.'io.containerd.cri.v1.images'.registry]
config_path = '/etc/containerd/certs.d'
[plugins.'io.containerd.cri.v1.images'.registry.mirrors]
[plugins.'io.containerd.cri.v1.images'.registry.mirrors.'docker.io']
endpoint = ["https://registry.cn-hangzhou.aliyuncs.com"] # ❌ 与 config_path 冲突
正确做法: 二选一
- 用
config_path+/etc/containerd/certs.d/下的hosts.toml(推荐) - 或在旧版 containerd 中只用
mirrors(containerd 2.x 不推荐)
修复后的 config.toml 片段:
[plugins.'io.containerd.cri.v1.images'.registry]
config_path = '/etc/containerd/certs.d'
# 不要写 mirrors 块!
3.2 Docker Hub 直连不通(次因)
现象:
dial tcp xxx:443: connect: connection refused # registry-1.docker.io
结论:
- 能 ping 通 8.8.8.8,但 Docker Hub 443 端口被阻断
- 阿里云 registry 域名可解析,但不是 Docker Hub 的 proxy,不能把
docker.io/library/busybox转发到registry.cn-hangzhou.aliyuncs.com/library/busybox - daocloud 镜像可用:
docker.m.daocloud.io
4. 标准修复步骤
4.1 修复 containerd 配置
# 备份
sudo cp /etc/containerd/config.toml /etc/containerd/config.toml.bak
# 删除 mirrors 块(保留 config_path)
sudo sed -i \
'/\[plugins\.'\''io\.containerd\.cri\.v1\.images'\''\.registry\.mirrors\]/,+2d' \
/etc/containerd/config.toml
# 确保启用 config_path
sudo sed -i \
"s|# config_path = \"/etc/containerd/certs.d:/etc/docker/certs.d\"|config_path = '/etc/containerd/certs.d'|" \
/etc/containerd/config.toml
4.2 配置镜像加速(hosts.toml)
# docker.io
sudo mkdir -p /etc/containerd/certs.d/docker.io
sudo mkdir -p /etc/containerd/certs.d/registry-1.docker.io
sudo tee /etc/containerd/certs.d/docker.io/hosts.toml <<'EOF'
server = "https://registry-1.docker.io"
[host."https://docker.m.daocloud.io"]
capabilities = ["pull", "resolve"]
override_path = true
EOF
# 两个目录都写一份(containerd 解析路径可能不同)
sudo cp /etc/containerd/certs.d/docker.io/hosts.toml \
/etc/containerd/certs.d/registry-1.docker.io/hosts.toml
# registry.k8s.io(可选,用于 k8s 官方镜像)
sudo mkdir -p /etc/containerd/certs.d/registry.k8s.io
sudo tee /etc/containerd/certs.d/registry.k8s.io/hosts.toml <<'EOF'
server = "https://registry.k8s.io"
[host."https://registry.aliyuncs.com/google_containers"]
capabilities = ["pull", "resolve"]
override_path = true
EOF
4.3 重启并验证
sudo systemctl restart containerd
sleep 3
sudo systemctl restart kubelet
sleep 5
# 验证 CRI 插件
sudo ctr plugins ls | grep cri
# 期望全部 ok
# 安装 crictl(如未安装)
VERSION="v1.34.0"
curl -sfL -o /tmp/crictl.tgz \
https://github.com/kubernetes-sigs/cri-tools/releases/download/${VERSION}/crictl-${VERSION}-linux-amd64.tar.gz
sudo tar zxf /tmp/crictl.tgz -C /usr/local/bin
sudo tee /etc/crictl.yaml <<'EOF'
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
EOF
5. 镜像拉取:三种可用方案
方案 A:Pod 中直接使用 daocloud 完整地址(✅ 推荐)
apiVersion: v1
kind: Pod
metadata:
name: nginx-test
spec:
containers:
- name: nginx
image: docker.m.daocloud.io/library/nginx:alpine
ports:
- containerPort: 80
kubectl run test-nginx --image=docker.m.daocloud.io/library/nginx:alpine --restart=Never
已验证: kubelet 可自动拉取并成功启动 Pod。
方案 B:手动导入 + 短名 + IfNotPresent
适用于 busybox:latest 等短名镜像:
# 1. 从 daocloud 拉取
sudo ctr -n k8s.io images pull docker.m.daocloud.io/library/busybox:latest
# 2. 打 tag 为 docker.io 短名
sudo ctr -n k8s.io images tag \
docker.m.daocloud.io/library/busybox:latest \
docker.io/library/busybox:latest
# 3. 部署时使用 IfNotPresent
kubectl apply -f scripts/test-busybox.yaml
scripts/test-busybox.yaml 关键字段:
image: busybox:latest
imagePullPolicy: IfNotPresent
方案 C:离线导入(网络完全不通时)
在能联网的机器上:
docker pull busybox:latest
docker save busybox:latest -o busybox.tar
scp -P 2222 busybox.tar walker@192.168.1.88:/tmp/
在虚拟机上:
sudo ctr -n k8s.io images import /tmp/busybox.tar
sudo ctr -n k8s.io images tag docker.io/library/busybox:latest busybox:latest
kubectl run test --image=busybox:latest --image-pull-policy=IfNotPresent -- sleep infinity
6. 常见错误对照表
| 报错 | 含义 | 处理 |
|---|---|---|
Connection timed out(SSH) |
端口错误 | 用 -p 2222,不是 22 |
Permission denied(SSH) |
密码/账号错误 | 检查 walker 密码 |
unknown service runtime.v1.ImageService |
CRI 插件未加载 | 检查 mirrors/config_path 冲突 |
mirrors cannot be set when config_path is provided |
containerd 配置错误 | 删除 mirrors 块 |
connection refused(docker.io) |
Docker Hub 被阻断 | 改用 daocloud 完整地址 |
pull access denied(阿里云私有路径) |
镜像地址不存在 | 不要用 registry.cn-hangzhou.aliyuncs.com/liqi/... 这类无效路径 |
ImagePullBackOff + 短名镜像 |
本地无缓存且 mirror 未生效 | 用方案 A 或 B |
7. 项目脚本索引
| 脚本 | 用途 |
|---|---|
scripts/ssh_helper.py |
从 Windows 本机远程执行命令 |
scripts/run_remote.py |
上传并执行 shell 脚本 |
scripts/diagnose-pull.sh |
一键诊断网络 + CRI + 拉镜像 |
scripts/fix-containerd-mirror-v2.sh |
修复 containerd 配置冲突 |
scripts/apply_mirror_fix.py |
上传 hosts.toml 并测试 |
scripts/test-busybox.yaml |
测试 Pod 模板(IfNotPresent) |
scripts/deploy_test_pod.py |
部署测试 Pod |
Windows 本机常用命令:
# 查看集群状态
python scripts/ssh_helper.py "kubectl get nodes; kubectl get pods -A"
# 上传并执行修复脚本
python scripts/run_remote.py scripts/fix-containerd-mirror-v2.sh
# 部署测试 Pod
python scripts/deploy_test_pod.py
8. 新集群初始化备忘(从零开始)
如果是全新 Ubuntu 虚拟机,推荐顺序:
# 1. 基础依赖
sudo apt update
sudo apt install -y curl wget apt-transport-https ca-certificates
# 2. 安装 containerd(或 kubeadm 会自动装)
# 3. kubeadm init 前,先写好 containerd config(避免 mirrors 冲突)
# 4. 安装 K8s 组件
# 5. kubeadm init
# 6. 安装网络插件(Calico)
# 7. 配置镜像加速 hosts.toml
# 8. 用 daocloud 地址部署第一个 Pod 验证
containerd 初始化建议:
sudo containerd config default | sudo tee /etc/containerd/config.toml
# 然后只添加 config_path,不要添加 mirrors 块
# 再写 /etc/containerd/certs.d/docker.io/hosts.toml
sudo systemctl restart containerd
9. 验证集群健康的最终命令
# 节点
kubectl get nodes
# 期望:Ready
# 核心组件
kubectl get pods -n kube-system
# 期望:coredns、etcd、calico 等全部 Running
# 业务 Pod 测试
kubectl run ping-test \
--image=docker.m.daocloud.io/library/busybox:latest \
--restart=Never --command -- sleep 3600
kubectl get pod ping-test
# 期望:Running
# CRI 工具
sudo crictl images | head
sudo ctr plugins ls | grep cri
# 期望:cri 相关插件全部 ok
10. 关键经验总结
- SSH 端口是 2222,密码错误和端口不通的报错不同(timeout vs Permission denied)。
- containerd 2.x 最坑的配置:
mirrors与config_path不能共存,会导致整个 CRI 挂掉,比网络问题更严重。 - 阿里云 registry ≠ Docker Hub mirror,写
endpoint = ["https://registry.cn-hangzhou.aliyuncs.com"]无法代理docker.io/library/*。 - daocloud 实测可用:
docker.m.daocloud.io/library/<image>:<tag>是最省心的方式。 - 短名 mirror(hosts.toml)对 CRI 不一定生效,生产环境建议 Pod 里写完整 mirror 地址,或手动 ctr pull + tag。
- 诊断优先看 containerd 日志,不要只看 kubectl describe:
journalctl -u containerd | grep -i error。

浙公网安备 33010602011771号