刚接触Kubernetes 1.28时,我在部署Flannel网络插件时遇到了一个经典问题——Pod状态卡在Init:ImagePullBackOff。这个错误就像高速公路上的收费站突然关闭,所有车辆(数据流量)都被堵在原地。具体表现是执行kubectl get pods -n kube-system时,kube-flannel-ds开头的Pod始终无法就绪,状态栏显示着刺眼的Init:ImagePullBackOff。
通过kubectl describe pod kube-flannel-ds-xxxx -n kube-system查看详情,发现Events里明确提示无法从docker.io拉取flannel/flannel:v0.22.3镜像。这就像去超市购物却发现货架空空如也,容器运行时(containerd/docker)尝试从默认仓库获取镜像时遭遇了网络超时。有趣的是,同一集群的其他系统组件(如coredns、kube-proxy)却能正常启动,说明问题具有针对性。
提示:ImagePullBackOff是Kubernetes的自我保护机制,当容器镜像拉取失败达到重试上限后,系统会进入指数退避状态,避免无限制重试消耗资源。
Flannel官方YAML中镜像地址直接指向docker.io,这个位于海外的仓库对国内用户就像隔着一堵防火墙。我通过ping docker.io测试发现平均延迟高达300ms,且存在20%丢包率。更麻烦的是,containerd运行时默认不会像Docker那样自动配置镜像加速器,相当于快递员(containerd)没有配备交通工具,自然无法及时取件。
检查containerd配置(/etc/containerd/config.toml)时,发现没有配置mirrors或registry.mirrors。这就像快递公司没有设置区域中转站,所有包裹必须从总仓直发。Kubernetes 1.28默认使用containerd作为运行时,其镜像拉取策略与Docker有微妙差异,特别是当imagePullPolicy为Always时(Flannel的YAML默认设置),每次都会尝试从远程仓库拉取。
某些企业私有仓库需要认证,但Flannel的DaemonSet并没有配置imagePullSecrets。就像需要会员卡才能进入的超市,我们的Pod因为没有携带凭证被拒之门外。虽然本例中使用的是公开镜像,但这个知识点值得注意,特别是后续部署其他需要私有镜像的应用时。
Flannel v0.22.3的镜像架构列表(manifest)显示同时支持amd64和arm64,但某些边缘场景下可能出现架构不匹配。通过ctr image list查看已拉取的镜像时,要确认架构标签与实际节点匹配,就像买鞋必须选对尺码。
首先用组合命令快速定位问题:
bash复制kubectl get events --sort-by='.lastTimestamp' -A | grep -i "failed"
kubectl describe pod -n kube-system kube-flannel-ds-xxxx
journalctl -u containerd -n 50 --no-pager
关键要看Events部分的错误详情,常见的有:
当网络问题难以立即解决时,手动拉取是最快方案。使用containerd原生客户端ctr操作:
bash复制sudo ctr -n k8s.io images pull \
--hosts-dir "/etc/containerd/certs.d" \
docker.io/flannel/flannel:v0.22.3
这里有几个实用技巧:
-n k8s.io指定Kubernetes使用的命名空间registry.aliyuncs.com/google_containers/flannel:v0.22.3--debug参数可查看详细拉取过程直接编辑kube-flannel.yml,将镜像地址替换为国内源:
yaml复制containers:
- name: kube-flannel
image: registry.cn-hangzhou.aliyuncs.com/google_containers/flannel:v0.22.3
或者更优雅地使用kustomize的images字段替换:
bash复制kubectl kustomize . | kubectl apply -f -
编辑/etc/containerd/config.toml,在[plugins."io.containerd.grpc.v1.cri".registry.mirrors]下添加:
toml复制[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
endpoint = ["https://registry-1.docker.io", "https://mirror.baidubce.com"]
然后重启服务:
bash复制sudo systemctl restart containerd
搭建Harbor或使用nexus3作为代理仓库,然后在节点上配置:
bash复制sudo mkdir -p /etc/containerd/certs.d/myharbor.com
cat <<EOF | sudo tee /etc/containerd/certs.d/myharbor.com/hosts.toml
server = "https://myharbor.com"
[host."https://myharbor.com"]
capabilities = ["pull", "resolve"]
EOF
在CI/CD流水线中加入镜像预加载步骤:
bash复制# 生成镜像列表
kubectl get pods --all-namespaces -o jsonpath="{..image}" |\
tr -s '[[:space:]]' '\n' | sort | uniq > images.lst
# 批量拉取
while read img; do
ctr -n k8s.io images pull $img
done < images.lst
当使用非root用户执行ctr命令时,可能遇到"permission denied"错误。正确的做法是:
bash复制sudo ctr -n k8s.io images pull docker.io/flannel/flannel:v0.22.3
或者将用户加入docker组(不推荐生产环境)。
有时拉取的镜像SHA256校验不匹配,可以强制删除后重拉:
bash复制ctr -n k8s.io images rm docker.io/flannel/flannel:v0.22.3
ctr -n k8s.io images pull --skip-verify docker.io/flannel/flannel:v0.22.3
在混合架构集群中,可能需要明确指定架构:
bash复制ctr -n k8s.io images pull --platform linux/amd64 docker.io/flannel/flannel:v0.22.3
修复后执行以下检查:
bash复制kubectl get pods -n kube-system -l app=flannel
kubectl logs -n kube-system kube-flannel-ds-xxxx -c kube-flannel
ip a show flannel.1
正常运行的Flannel会显示:
我在实际生产环境中发现,这个问题虽然表象简单,但涉及Kubernetes的镜像拉取机制、容器运行时配置、网络策略等多个层面。建议将镜像仓库配置纳入集群初始化检查清单,就像出门前检查钥匙钱包一样成为标准流程。