一、问题现象

Kubernetes Pod 出现:

ImagePullBackOff
ErrImagePull
ContainerCreating

查看事件:

kubectl describe pod POD名称 -n 命名空间

典型报错:

Head "https://registry-1.docker.io/v2/...":
dial tcp xxx.xxx.xxx.xxx:443: i/o timeout

服务器能正常访问国内网络,但 Docker Hub 超时,说明需要为 containerd 配置镜像加速。


二、确认运行时类型

查看节点运行时:

kubectl get nodes -o wide

查看 kubelet:

ps -ef | grep kubelet | grep -v grep

如果看到:

--container-runtime-endpoint=unix:///run/containerd/containerd.sock

并且服务为:

systemctl status containerd

说明当前是:

kubeadm + kubelet + containerd

配置文件为:

/etc/containerd/config.toml

不要配置:

/etc/rancher/rke2/registries.yaml

后者只适用于 RKE2。


三、备份 containerd 配置

在每个可能调度业务 Pod 的节点执行:

sudo cp -a /etc/containerd/config.toml \
  /etc/containerd/config.toml.bak.$(date +%Y%m%d_%H%M%S)

四、配置 containerd 镜像目录

编辑:

sudo vim /etc/containerd/config.toml

删除旧的:

[plugins."io.containerd.grpc.v1.cri".registry.mirrors]

增加:

[plugins."io.containerd.grpc.v1.cri".registry]
  config_path = "/etc/containerd/certs.d"

核心配置示例:

version = 2

root = "/var/lib/containerd"
state = "/run/containerd"

[grpc]
  address = "/run/containerd/containerd.sock"

[plugins]
  [plugins."io.containerd.grpc.v1.cri"]
    sandbox_image = "kubesphere/pause:3.9"

    [plugins."io.containerd.grpc.v1.cri".cni]
      bin_dir = "/opt/cni/bin"
      conf_dir = "/etc/cni/net.d"
      max_conf_num = 1

    [plugins."io.containerd.grpc.v1.cri".registry]
      config_path = "/etc/containerd/certs.d"

  [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
    runtime_type = "io.containerd.runc.v2"

    [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
      SystemdCgroup = true

五、配置 Docker Hub 加速

创建目录:

sudo mkdir -p /etc/containerd/certs.d/docker.io

创建文件:

sudo tee /etc/containerd/certs.d/docker.io/hosts.toml >/dev/null <<'EOF'
server = "https://dockerproxy.net"

[host."https://dockerproxy.net"]
  capabilities = ["pull", "resolve"]
  dial_timeout = "15s"
EOF

这里直接把 dockerproxy.net 设置为服务端,避免失败后再次回退到 Docker Hub。


六、配置私有镜像仓库

私有仓库地址:

172.17.0.169:30050

创建目录:

sudo mkdir -p '/etc/containerd/certs.d/172.17.0.169:30050'

创建配置:

sudo tee '/etc/containerd/certs.d/172.17.0.169:30050/hosts.toml' >/dev/null <<'EOF'
server = "http://172.17.0.169:30050"

[host."http://172.17.0.169:30050"]
  capabilities = ["pull", "resolve", "push"]
EOF

私有仓库使用 HTTP 时,必须明确写 http://


七、重启 containerd

sudo systemctl restart containerd
sudo systemctl status containerd --no-pager -l

检查日志:

sudo journalctl -u containerd -n 100 --no-pager

生产环境可先禁止调度:

kubectl cordon node1

重启完成后恢复:

kubectl uncordon node1

八、验证镜像加速

先测试基础镜像:

sudo crictl pull docker.io/library/busybox:1.36
sudo crictl pull docker.io/library/redis:7.2

成功示例:

Image is up to date for sha256:...

查看镜像:

sudo crictl images | grep -E 'busybox|redis'

只要 docker.io/library/redis:7.2 可以拉取,说明 containerd 镜像加速已经生效。


九、常见错误判断

网络问题

i/o timeout
DeadlineExceeded

说明仓库访问超时。

镜像不存在

NotFound
manifest unknown

说明镜像路径或标签错误。

权限问题

403 Forbidden
pull access denied
insufficient_scope

说明仓库需要认证,或代理不支持该镜像。

DNS 问题

Could not resolve host
no such host

说明节点 DNS 异常。


十、几个常见坑

1. 阿里云 ACR 地址不是 Docker Hub 加速器

下面配置通常无效:

endpoint = ["https://registry.cn-hangzhou.aliyuncs.com"]

它只是阿里云镜像仓库地址,不会自动代理所有 Docker Hub 镜像。

2. 必须在 Pod 所在节点测试

查看 Pod 调度节点:

kubectl get pod -A -o wide

Pod 在 node1,就必须在 node1 执行:

sudo crictl pull 镜像地址

3. Helm 的 APP VERSION 不等于镜像标签

例如:

APP VERSION: 8.8.1

不代表一定存在:

bitnami/redis:8.8.1

返回 NotFound 时,应检查实际镜像标签,而不是继续排查网络。

4. Bitnami Chart 不能直接换官方 Redis 镜像

Bitnami Chart 依赖:

/opt/bitnami/scripts/

不能直接把:

bitnami/redis

替换成:

library/redis

否则镜像能拉取,容器也可能无法启动。


十一、生产环境建议

公共镜像代理只适合临时拉取。

生产环境应将 Redis、RabbitMQ、Kafka 等镜像同步到内部仓库:

172.17.0.169:30050

业务 YAML 统一使用:

image: 172.17.0.169:30050/middleware/redis:固定版本

最终推荐架构:

公共镜像仓库
      ↓
私有 Registry / Harbor
      ↓
Kubernetes 节点

这样可以避免 Docker Hub 超时、代理限流、镜像下架和标签失效。

更多推荐