这破事儿到底啥意思?
ImagePullBackOff —— 你要是跑过 Kubernetes 生产环境,大概率见过这玩意儿。Pod 状态卡在 ImagePullBackOff,然后 kubelet 会不断重试拉取镜像,每次重试间隔会指数级增长(backoff)。10秒、20秒、40秒……直到你疯掉或者找到问题。
我见过不少团队在这上面翻车。有个哥们儿在 Reddit 上吐槽,说他们的 CI 突然全挂了,所有新部署的 Pod 全部 ImagePullBackOff,排查了半天发现是镜像仓库的 token 过期了。这种事儿太常见了。
症状描述
Pod 状态长这样:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
my-app-7d4f8b6c9-x2k9j 0/1 ImagePullBackOff 0 5m
用 kubectl describe pod 能看到更详细的信息:
$ kubectl describe pod my-app-7d4f8b6c9-x2k9j
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 6m10s default-scheduler Successfully assigned...
Normal Pulling 5m50s (x4 over 6m10s) kubelet Pulling image "myregistry.io/my-app:v1.0.0"
Warning Failed 5m50s (x4 over 6m10s) kubelet Failed to pull image "myregistry.io/my-app:v1.0.0": rpc error: code = Unknown desc = Error response from daemon: manifest for myregistry.io/my-app:v1.0.0 not found: manifest unknown: manifest unknown
Warning BackOff 5m35s (x7 over 6m5s) kubelet Back-off pulling image "myregistry.io/my-app:v1.0.0"
注意那个 Back-off pulling image —— 这就是 kubelet 在告诉你:“我试了,没成功,我先歇会儿再试。”
根因分析:到底是谁的锅?
我根据过去几年在生产环境踩过的坑,把 ImagePullBackOff 的根因分了四大类:
| 根因分类 | 典型错误信息 | 发生频率 |
|---|---|---|
| 镜像名称/标签错误 | manifest unknown 或 not found | 极高 |
| 私有仓库认证失败 | authentication required 或 denied | 高 |
| 网络/仓库不可达 | dial tcp: lookup 或 connection refused | 中 |
| 镜像拉取策略/配额 | ImagePullBackOff 无具体错误 | 低 |
1. 镜像名称或标签写错了
这是最常见的坑。我见过有人把 nginx:latest 写成 nginix:lates,然后盯着屏幕看了十分钟。
# 错误示例
containers:
- name: my-app
image: myregistry.io/my-app:v1.0.0 # 这个 tag 不存在!
排查命令:
# 先手动拉一下试试
$ docker pull myregistry.io/my-app:v1.0.0
Error response from daemon: manifest for myregistry.io/my-app:v1.0.0 not found: manifest unknown
# 看看仓库里到底有啥 tag
$ skopeo list-tags docker://myregistry.io/my-app
{
"Repository": "myregistry.io/my-app",
"Tags": [
"v1.0.1",
"v1.1.0",
"latest"
]
}
看到了吧?v1.0.0 根本不存在。这种问题在 CI/CD 流程里特别容易出 —— 镜像还没 push 完,kubelet 就开始拉了。
2. 私有仓库认证炸了
这玩意儿坑过我们团队两次。一次是 Harbor 的 robot account 过期了,另一次是 ECR 的 token 刷新逻辑写错了。
$ kubectl describe pod my-app
...
Failed to pull image "my-private-registry.io/my-app:latest":
rpc error: code = Unknown desc = Error response from daemon: pull access denied for my-private-registry.io/my-app, repository does not exist or may require 'docker login': denied
排查步骤:
# 检查当前使用的 imagePullSecrets
$ kubectl get pod my-app -o yaml | grep -A 5 imagePullSecrets
imagePullSecrets:
- name: my-registry-cred
# 看看这个 secret 是不是还活着
$ kubectl get secret my-registry-cred -o yaml
# 检查 dockerconfigjson 里的 creds 是否已过期
# 重新创建 secret(以 Docker Hub 为例)
$ kubectl create secret docker-registry my-registry-cred \
--docker-server=https://index.docker.io/v1/ \
--docker-username=myuser \
--docker-password=mypassword \
--docker-email=myemail@example.com
一个更隐蔽的坑: 如果你用 ECR,token 每 12 小时过期。很多团队忘了写 token 刷新逻辑,结果半夜 Pod 全挂了。我们后来用 k8s-at-home/ecr-token-refresh 这个 sidecar 解决了这个问题。
3. 网络问题
这个比较少见,但一旦出现就是大问题。比如集群节点没法访问镜像仓库。
$ kubectl describe pod my-app
...
Failed to pull image "myregistry.io/my-app:latest":
rpc error: code = Unknown desc = Error response from daemon: Get "https://myregistry.io/v2/": dial tcp: lookup myregistry.io on 10.0.0.10:53: no such host
排查命令:
# 进到节点上测试 DNS
$ kubectl debug node/worker-node-1 -it --image=busybox
# 在 debug pod 里
/ # nslookup myregistry.io
Server: 10.0.0.10
Address: 10.0.0.10:53
** server can't find myregistry.io: NXDOMAIN
# 测试网络连通性
/ # wget -O- https://myregistry.io/v2/
wget: bad address 'myregistry.io'
网络问题常见于:
- 自建仓库没有配置正确的 DNS
- 私有网络没有打通(VPC peering / NAT 网关配置错误)
- 镜像仓库做了 IP 白名单,但集群节点 IP 不在白名单里
4. 镜像拉取策略
imagePullPolicy 这个字段很多人不注意。默认情况下:
- 如果 tag 是
latest,策略是Always - 如果 tag 是具体的版本号,策略是
IfNotPresent
但如果你改了策略,可能会导致奇怪的问题。比如你设了 imagePullPolicy: Always,但仓库里其实没有这个 tag,kubelet 就会一直尝试拉取。
containers:
- name: my-app
image: my-app:latest
imagePullPolicy: Always # 每次都会尝试拉取
修复步骤:一套标准的排查流程
这是我个人的排查流程,踩过无数次坑后总结出来的:
Step 1: 查看 Pod 状态和事件
$ kubectl get pods --all-namespaces | grep -E "ImagePullBackOff|ErrImagePull"
$ kubectl describe pod <pod-name> -n <namespace>
重点关注 Events 部分的 Failed 消息。这里会直接告诉你拉取失败的原因。
Step 2: 手动拉取镜像验证
# 在任意一个集群节点上
$ docker pull <your-image>:<tag>
如果 docker 能拉下来,但 kubelet 拉不下来,那问题大概率在认证或网络层面。
Step 3: 检查 imagePullSecrets
$ kubectl get pod <pod-name> -o yaml | grep -A 10 imagePullSecrets
$ kubectl get secret <secret-name> -o yaml | head -20
确认 secret 存在且内容正确。可以用 base64 解码看看:
$ kubectl get secret <secret-name> -o jsonpath="{.data.\.dockerconfigjson}" | base64 -d
Step 4: 检查镜像仓库是否可达
# 创建一个 debug pod
$ kubectl run debug --image=busybox -it --rm --restart=Never -- wget -O- https://your-registry.com/v2/
Step 5: 检查镜像是否存在
# 用 skopeo 或 crane 检查
$ crane manifest <your-image>:<tag>
# 或者
$ skopeo inspect docker://<your-image>:<tag>
社区里大家都在吐槽什么
最近在 Reddit 和 Hacker News 上看到不少关于 ImagePullBackOff 的讨论。
有个帖子讲的是他们团队迁移到新的镜像仓库后,CI 一直报 ImagePullBackOff。排查了两天,发现是 kubelet 的 --pod-infra-container-image 参数指向了旧仓库。这个问题在升级 Kubernetes 版本时特别常见。
另一个哥们儿吐槽说他们的 EKS 集群突然大面积出现 ImagePullBackOff,原因是 AWS ECR 的认证 token 过期了,但他们用的 kubelet 版本不支持自动刷新。最后他们写了个 cronjob 每 6 小时刷新一次 token。
还有人提到 Docker Hub 的拉取限制 —— 匿名用户每 6 小时只能拉 100 次。如果你用 imagePullPolicy: Always,Pod 多了很容易触发这个限制。解决方案是换成 IfNotPresent 或者配一个 Docker Hub 的 pull secret。
进阶:自动化排查脚本
我写了个小脚本,一键排查集群里所有 ImagePullBackOff 的 Pod:
#!/bin/bash
# k8s-imagepullbackoff-checker.sh
echo "=== 检查 ImagePullBackOff Pod ==="
kubectl get pods --all-namespaces | grep -E "ImagePullBackOff|ErrImagePull" | while read ns name rest; do
echo "Pod: $ns/$name"
echo "--- 事件 ---"
kubectl describe pod $name -n $ns | grep -A 10 "Events:" | tail -20
echo ""
done
FAQ
Q: ImagePullBackOff 和 ErrImagePull 有什么区别?
A: ErrImagePull 是第一次拉取失败时的状态,ImagePullBackOff 是失败后 kubelet 进入退避重试状态。前者是尝试拉取但失败了,后者是"我先歇会儿再试"。
Q: kubelet 重试 ImagePullBackOff 的间隔是多少?
A: 默认从 10 秒开始,每次翻倍,上限 5 分钟。计算公式是 min(5min, 10s * 2^(retry_count-1))。如果你删掉 Pod 重新创建,计数器会重置。
Q: 为什么我的 imagePullSecrets 配置了但还是认证失败?
A: 常见原因:1) Secret 所在的 namespace 不对(secret 必须和 Pod 在同一个 namespace);2) Secret 内容已过期(尤其是 ECR token);3) Pod 的 spec 里没有引用这个 secret。
Q: 镜像拉取失败会影响集群的其他 Pod 吗?
A: 不会。ImagePullBackOff 只影响出问题的那个 Pod。但如果你用 DaemonSet 或 StatefulSet,可能会影响整个应用的可用性。
Q: 如何避免 ImagePullBackOff?
A: 1) CI/CD 流程里加镜像验证步骤;2) 配置 imagePullSecrets 自动刷新;3) 用 imagePullPolicy: IfNotPresent 减少拉取次数;4) 监控镜像仓库的可用性和认证状态。
参考与社区洞察
本文的技术观点综合自以下平台的真实工程讨论:Hacker News、Reddit 的 r/kubernetes 和 r/devops 社区。特别感谢那些在生产环境踩过坑还愿意分享的工程师们。