K8s 镜像拉取失败 ImagePullBackOff 排查
2026-08-11 00:53:03 # Kubernetes

Pod 卡在 ErrImagePullImagePullBackOff,无法启动。这类问题在对接私有镜像仓库(Harbor / 自建 Registry)时尤其常见,根因几乎都在「鉴权、地址、证书、可达性」四类中。

问题现象与背景原因

1
2
3
kubectl get pod
NAME READY STATUS RESTARTS AGE
app-5d7c9b8f-x9p2 0/1 ErrImagePull 0 2m

ImagePullBackOffErrImagePull 的退避状态——拉取失败后 kubelet 按指数退避重试。常见根因:

  1. 私有仓库未配置 imagePullSecret:Pod 没有携带访问仓库的账号凭据,被仓库拒绝(pull access denied)。
  2. 镜像地址 / Tag 不存在或拼错:仓库里没有该 tag,或镜像名写错(manifest unknown / not found)。
  3. 仓库不可达:节点到镜像仓库网络不通、域名解析失败、仓库服务挂了(connection refused / i/o timeout)。
  4. 自签证书不被信任:Harbor 使用自签 / 内网 CA,节点容器运行时未配置信任该 CA(x509: certificate signed by unknown authority)。
  5. Harbor 项目私有无权限:用了错误的机器人账号,或账号对该项目无 pull 权限。

排查过程(思路与定位方法)

第一步:直接读拉取失败的原因。 describe 的 Events 会给出具体的 registry 报错,这是定位核心:

1
2
kubectl describe pod app-5d7c9b8f-x9p2
kubectl get events --sort-by=.lastTimestamp | tail -15

常见错误与含义:

报错片段 含义 方向
pull access denied 无权限 imagePullSecret / 账号
manifest unknown / not found tag 不存在 镜像地址 / tag
x509: certificate signed by unknown authority 证书不信任 CA 配置
connection refused / i/o timeout 不可达 网络 / 域名
toomanyrequests 拉取限流 配额 / 镜像缓存

第二步:在节点上用运行时手动拉取验证。 把 kubelet 的拉取行为在节点侧复现,能排除「集群配置」问题、锁定「仓库 / 网络 / 证书」问题:

1
2
3
4
5
# containerd
sudo crictl pull <registry>/<project>/<image>:<tag>

# 或 docker
sudo docker pull <registry>/<project>/<image>:<tag>

手动拉取同样的报错,说明问题在节点到仓库这一层(证书 / 网络 / 权限),与 K8s 配置无关。

第三步:确认 Secret 是否配置正确。

1
2
kubectl get secret
kubectl get pod app-5d7c9b8f-x9p2 -o jsonpath='{.spec.imagePullSecrets}'

若为空,或 Secret 的 docker-server 与镜像仓库域名不一致,就会导致鉴权失败。

提示:Harbor 等私有仓库的 imagePullSecret 必须用和镜像同域名的 registry 地址创建;docker-server 写错一个字符就会 pull access denied

最终的解决方法

  • 未配置 / 配错 Secret:创建 docker-registry 类型 Secret,并在 Pod 或 ServiceAccount 上引用:

    1
    2
    3
    4
    5
    6
    7
    8
    kubectl create secret docker-registry regcred \
    --docker-server=harbor.example.com \
    --docker-username='robot$app' \
    --docker-password='<token>' \
    --docker-email=ci@example.com -n <namespace>

    kubectl patch serviceaccount default -n <namespace> \
    -p '{"imagePullSecrets":[{"name":"regcred"}]}'
  • Tag 不存在 / 地址错误:修正镜像全名(仓库地址 / 项目名 / 镜像名 / tag),先手动 crictl pull 验证存在。

  • 自签证书不信任:将 Harbor 的 CA 证书配置到节点容器运行时的信任列表中并重启运行时:

    1
    2
    3
    4
    # containerd:在 /etc/containerd/config.toml 的
    # [plugins."io.containerd.grpc.v1.cri".registry.configs."harbor.example.com".tls]
    # 下配置 ca_file = "/etc/containerd/harbor-ca.crt"
    sudo systemctl restart containerd

    或临时测试可在运行时配置 insecure_skip_verify(仅排错,生产不推荐)。

  • 仓库不可达:检查节点 DNS 与到仓库的网络连通性(dig / curl -v);确认仓库域名在节点侧可解析。

  • Harbor 无权限:给机器人账号授予该项目的 pull 权限,或用有该权限的账号重建 Secret。

操作命令(可直接复制执行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 1. 看拉取失败原因(核心)
kubectl describe pod <pod-name> | grep -A5 -i 'Failed'
kubectl get events --sort-by=.lastTimestamp | tail -15

# 2. 确认 imagePullSecret 是否引用
kubectl get pod <pod-name> -o jsonpath='{.spec.imagePullSecrets}'
kubectl get secret

# 3. 节点侧手动复现拉取
sudo crictl pull <image-full-path>
# 或 sudo docker pull <image-full-path>

# 4. 创建私有仓库 Secret
kubectl create secret docker-registry regcred \
--docker-server=<registry-domain> \
--docker-username=<user> \
--docker-password='<password>' \
--docker-email=<email> -n <namespace>

# 5. 绑定到 ServiceAccount(一劳永逸)
kubectl patch serviceaccount default -n <namespace> \
-p '{"imagePullSecrets":[{"name":"regcred"}]}'