K8s 排障
问题概述
在部署 jjt-test 应用时出现多种镜像拉取异常,症状包括:
ImagePullBackOffErrImagePull- 使用
ctr与使用crictl的结果不一致 - Docker Hub 无法访问导致镜像无法拉取
- pause 容器镜像被墙
- Deployment 滚动更新失败导致多个 ReplicaSet 残留
- Pod Running 但无 Service,无法访问
排障过程
Pod 镜像拉取失败
遇到的问题:
kubectl describe pod 显示无法连接 registry-1.docker.io。
问题原因: 国内无法访问 Docker Hub。
如何解决:
- 使用国内镜像源(如 Daocloud、阿里云、华为云)
- 配置 containerd hosts.toml 以加速拉取
containerd 配置加速源不生效
遇到的问题:
crictl pull成功ctr images pull失败- Pod 依然拉取失败
问题原因:
containerd 新版本不再读取 config.toml 里的 mirrors,而是使用 /etc/containerd/certs.d/<registry>/hosts.toml。最开始的加速源配置没有生效。
如何解决:
- 在
/etc/containerd/certs.d/docker.io/hosts.toml和/etc/containerd/certs.d/registry.k8s.io/hosts.toml中正确配置国内镜像源 - 重启 containerd 服务
pause 镜像无法拉取(重大阻塞点)
遇到的问题: 报错:
Failed to pull image "k8s.gcr.io/pause:3.10"
问题原因:
- pause 镜像是 Kubernetes Pod 的基础镜像
- Docker Hub 或 Google Container Registry 在国内不可访问
如何解决:
ctr -n k8s.io images pull registry.aliyuncs.com/google_containers/pause:3.10
ctr -n k8s.io images tag registry.aliyuncs.com/google_containers/pause:3.10 k8s.gcr.io/pause:3.10
systemctl restart kubelet
说明:
- pause 是 Pod 的根容器,负责提供网络、IPC、PID namespace
- 所有业务容器(如 nginx)都在 pause 的 namespace 里运行
- pause 镜像节点级缓存,一次拉取即可,不会每个 Pod 都拉一次
阿里云 nginx 仓库路径无权限
遇到的问题: 尝试拉取:
registry.cn-hangzhou.aliyuncs.com/library/nginx
报错:
insufficient_scope
问题原因:
- 仓库不存在或非公有,当前节点无法访问
如何解决:
- 使用 Daocloud 镜像源拉取 nginx:
docker.m.daocloud.io/library/nginx:1.25.3
- 更新 Deployment:
kubectl set image deploy/jjt-test jjt=docker.m.daocloud.io/library/nginx:1.25.3
- Pod 成功 Running
ReplicaSet 残留导致副本不一致
遇到的问题:
kubectl get rs | grep jjt-test
发现多个 ReplicaSet,Deployment 显示 UP-TO-DATE 不一致
问题原因:
- Deployment 滚动更新失败或中断
- 遗留旧 ReplicaSet 导致副本不对
如何解决:
kubectl scale deploy jjt-test --replicas=0
kubectl scale deploy jjt-test --replicas=2
Pod IP 正常但无法访问
遇到的问题: Pod IP 如:
10.244.166.140
10.244.219.102
无法访问 nginx 页面
问题原因: Pod IP 属于 CNI 内部网段,外部无法直接访问
如何解决:
- 创建 Service,将 Pod 暴露成 NodePort 或 ClusterIP + Ingress
三、关键配置
containerd 加速源(正确写法)
## /etc/containerd/certs.d/docker.io/hosts.toml
server = "https://registry-1.docker.io"
[host."https://docker.m.daocloud.io"]
capabilities = ["pull", "resolve"]
四、crictl 与 ctr 区别总结
| 对比项 | crictl | ctr |
|---|---|---|
| 调用接口 | 通过 CRI(Container Runtime Interface)调用 containerd | 直接调用 containerd API |
| kubelet 使用关系 | kubelet 实际调用的就是 CRI,因此 和 Pod 启动行为完全一致 | kubelet 不会使用 ctr,ctr 结果不代表 kubelet 行为 |
| 典型用途 | 调试 K8s 容器、拉取 Pod 镜像、查看 Pod 容器状态 | containerd 底层调试、直接管理镜像和快照、非 K8s 环境操作 |
| 拉取镜像行为 | 会遵循 /etc/containerd/certs.d 配置(和 kubelet 一致) |
不读取 certs.d,必须额外指定 hosts.toml 或不用加速源 |
| 是否支持 k8s sandbox(pause) | ✓ 支持 | ✓ 支持,但使用 namespace:ctr -n k8s.io |
| 命令风格 | 类似 docker,对 K8s 运维更友好 |
更底层,参数多,不适合日常 K8s 运维 |
| 典型命令 | crictl pull <img> crictl ps |
ctr -n k8s.io images pull <img> |
| 使用场景 | 排查 Pod 镜像问题时应该优先使用它 | 只有在确认 containerd 本身问题时才使用 |
一句话总结:
crictl 决定 Pod 能否启动,而 ctr 不决定。Pod 拉取失败时看 crictl 的结果最准确。