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. 关键经验总结

  1. SSH 端口是 2222,密码错误和端口不通的报错不同(timeout vs Permission denied)。
  2. containerd 2.x 最坑的配置:mirrors 与 config_path 不能共存,会导致整个 CRI 挂掉,比网络问题更严重。
  3. 阿里云 registry ≠ Docker Hub mirror,写 endpoint = ["https://registry.cn-hangzhou.aliyuncs.com"] 无法代理 docker.io/library/*。
  4. daocloud 实测可用:docker.m.daocloud.io/library/<image>:<tag> 是最省心的方式。
  5. 短名 mirror(hosts.toml)对 CRI 不一定生效,生产环境建议 Pod 里写完整 mirror 地址,或手动 ctr pull + tag。
  6. 诊断优先看 containerd 日志,不要只看 kubectl describe:journalctl -u containerd | grep -i error。

posted @ 2026-05-29 15:21  超难微猫  阅读(108)  评论(0)    收藏  举报