简介:这份资源面向正在搭建 Kubernetes 集群、需要为节点配置网络插件的运维与开发人员,解决 flannel 镜像拉取困难、离线环境无法部署 CNI 网络的问题。压缩包共 3 个文件,包含 2 个 tar 镜像归档与 1 个 yaml 清单,整体约 27.34MB,其中 tar 文件用于导入 flannel 及 flannel-cni-plugin 镜像,yaml 文件则提供配套的部署清单,便于直接应用到集群。资源覆盖 flannel-cni-plugin:v1.1.2 与 flannel:v0.21.5 两个必要组件,适合内网、离线或镜像仓库受限的场景使用,能省去逐一下载与打标签的繁琐过程。目前已有 1613 人学习下载,说明该组合在 k8s 网络部署中具有较高的实用参考价值。读者可借此快速完成镜像导入与插件部署,减少因网络问题导致的 Pod 通信异常,为后续集群网络调试与排错打下基础。
1. 安装 k8s 所需 flannel 必要镜像包:为什么离线环境总卡在第一步
很多人在内网或离线机房装 k8s,控制节点 master 初始化时卡在the api server is not healthy after 4m0.00747357s,翻来覆去查 kubelet 日志,最后发现根因不是配置写错,而是 flannel 的镜像根本没拉下来。k8s 集群要跑起来,CNI 插件是绕不过去的一环,而 flannel 因为部署简单、依赖少,成了大量生产环境和学习笔记里的首选。它的镜像包不像 kube-apiserver 那样由 kubeadm 自动处理,需要你提前准备、手动导入到每个节点。这篇内容就是围绕「flannel 必要镜像包」这件事,把需要哪些镜像、怎么导出导入、参数怎么配、离线环境怎么验证讲清楚。适合正在用 kubeadm 部署集群、或者维护内网 k8s 环境的运维和开发,尤其是那些被镜像拉取失败折腾过的人。
2. flannel 镜像包到底包含什么:从组件构成到版本选择
2.1 flannel 在 k8s 里的角色与镜像依赖关系
flannel 的核心任务是给每个节点分配一个 Pod 网段,让不同节点上的 Pod 能互相通信。它在 k8s 里以 DaemonSet 形式运行,每个节点跑一个 Pod,这个 Pod 里至少包含两个容器:一个是 flannel 本体,负责监听 etcd 或 k8s API 中的网络配置并写入本机路由;另一个是 CNI 插件容器,负责在 Pod 创建时调用 flannel 的配置来设置网卡。这两个容器对应的镜像就是你要准备的核心。
常见做法是直接用quay.io/coreos/flannel这个镜像,它内部已经打包了 flanneld 和 CNI 插件,部署时只需要一个镜像就能跑起来。但有些老版本或定制版本会把 CNI 插件单独拆出来,这时候你就需要额外准备quay.io/coreos/flannel-cni之类的镜像。判断方法很简单:看你要用的 flannel 官方 YAML 里image字段写了几个不同的镜像地址。如果只有一个,那必要镜像包就是这一个;如果有两个,就都要准备。
版本选择上,flannel 的版本要和你的 k8s 版本大致匹配。k8s 1.36 这类较新版本,建议用 flannel v0.24 以上,它对较新的 CNI 规范和内核特性支持更好。不要盲目追最新版,因为 flannel 的镜像标签里带架构后缀,比如v0.24.0-amd64,选错了在 arm 节点上直接起不来。我一般会先确认集群所有节点的 CPU 架构,再统一选一个多架构 manifest 的标签,或者分别准备 amd64 和 arm64 两套。
2.2 拉取与导出:把 flannel 镜像做成 tar 包的标准流程
在有外网的机器上,先把镜像拉下来。以 flannel v0.24.0 为例,命令如下:
# 拉取 flannel 主镜像,注意标签要带架构后缀或使用多架构标签 docker pull quay.io/coreos/flannel:v0.24.0 # 如果 YAML 里还引用了 CNI 插件镜像,一并拉取 docker pull quay.io/coreos/flannel-cni:v0.24.0 # 查看镜像 ID 和大小,确认拉取成功 docker images | grep flannel拉取完成后,用docker save导出成 tar 包。这里有个细节:如果你要同时导出多个镜像,可以写在一个命令里,这样导入时一次完成,不用分多次。
# 将 flannel 相关镜像打包成一个 tar 文件 docker save -o flannel-images.tar \ quay.io/coreos/flannel:v0.24.0 \ quay.io/coreos/flannel-cni:v0.24.0 # 查看 tar 包大小,确认没有异常 ls -lh flannel-images.tardocker save的参数-o指定输出文件,后面跟一个或多个镜像名。导出的 tar 包包含了镜像的所有层,体积可能比较大,flannel 镜像通常在 100MB 左右,加上 CNI 插件会到 150MB 上下。如果节点是 containerd 运行时,导入方式不是docker load,而是ctr或nerdctl,这个后面会讲。
2.3 镜像标签与架构:避免导入后 Pod 一直 ImagePullBackOff
flannel 的镜像标签里,架构后缀很容易被忽略。比如你在 amd64 机器上拉quay.io/coreos/flannel:v0.24.0,docker 会自动选择 amd64 的 manifest,但导出的 tar 包在 arm64 节点上导入后,kubelet 仍然会去拉取对应架构的镜像,发现本地没有,就报ImagePullBackOff。解决方法是:要么在每个架构的机器上分别拉取并导出,要么直接使用不带架构后缀的多架构标签,让容器运行时自己选。
另一个坑是镜像名不一致。flannel 官方 YAML 里写的可能是quay.io/coreos/flannel:v0.24.0,但你导入后本地镜像名可能变成quay.io/coreos/flannel:v0.24.0-amd64,kubelet 按 YAML 里的名字找不到,就会去外网拉。所以导入后一定要用docker images或crictl images确认本地镜像名和 YAML 里完全一致。如果不一致,可以用docker tag重新打标签。
提示:在离线环境里,建议把 flannel 的 YAML 文件也一起准备好,里面的 image 字段改成你本地导入后的准确镜像名,避免运行时再去解析。
3. 离线环境导入 flannel 镜像包:docker 与 containerd 两条路径
3.1 docker 运行时的导入与验证步骤
如果节点用的是 docker 作为容器运行时,导入很简单:
# 将 tar 包拷贝到目标节点,然后导入 docker load -i flannel-images.tar # 确认镜像已经存在 docker images | grep flannel # 查看镜像的详细标签,确保和 YAML 里一致 docker inspect quay.io/coreos/flannel:v0.24.0 | grep -i repotags导入后,不要急着 apply flannel 的 YAML。先手动跑一个测试容器,确认镜像能正常启动:
# 测试 flannel 镜像是否能正常运行 docker run --rm quay.io/coreos/flannel:v0.24.0 --version如果输出了版本号,说明镜像完整。如果报错exec format error,那就是架构不对,需要重新拉取对应架构的镜像。这一步能帮你提前发现大部分镜像问题,比等到 Pod 起来再排查要省时间。
3.2 containerd 运行时的导入与 nerdctl 配合
现在越来越多的 k8s 集群用 containerd 作为运行时,docker 命令不一定能用。这时候导入镜像要用ctr或nerdctl。以ctr为例:
# 使用 ctr 导入镜像到 k8s.io 命名空间 ctr -n k8s.io images import flannel-images.tar # 查看导入结果 ctr -n k8s.io images ls | grep flannel注意-n k8s.io这个命名空间参数,k8s 的 containerd 运行时默认使用k8s.io命名空间,如果你不加这个参数,镜像会导入到 default 命名空间,kubelet 找不到。这是很多人踩过的坑,现象就是明明ctr images ls能看到镜像,但 Pod 还是ImagePullBackOff。
如果节点上装了nerdctl,操作会更接近 docker:
# 使用 nerdctl 导入,同样要指定 k8s.io 命名空间 nerdctl -n k8s.io load -i flannel-images.tar # 验证 nerdctl -n k8s.io images | grep flannel导入完成后,同样建议用ctr跑一个测试容器,确认镜像能正常执行。containerd 下测试命令是:
ctr -n k8s.io run --rm quay.io/coreos/flannel:v0.24.0 test-flannel --version3.3 多节点批量分发:用脚本减少重复劳动
生产环境节点多,一个个手动导入不现实。我一般会写一个简单的分发脚本,把 tar 包和导入命令一起推到每个节点。假设你有一个节点列表文件nodes.txt,每行一个 IP:
#!/bin/bash # 批量分发 flannel 镜像包并导入 IMAGE_TAR="flannel-images.tar" NODE_LIST="nodes.txt" for node in $(cat $NODE_LIST); do echo "处理节点: $node" # 拷贝 tar 包到目标节点 scp $IMAGE_TAR root@$node:/tmp/ # 根据运行时类型选择导入命令,这里以 containerd 为例 ssh root@$node "ctr -n k8s.io images import /tmp/$IMAGE_TAR && rm -f /tmp/$IMAGE_TAR" if [ $? -eq 0 ]; then echo "$node 导入成功" else echo "$node 导入失败,请检查" fi done这个脚本里,scp负责传输,ssh远程执行导入。注意ctr导入后删除 tar 包,避免占满/tmp。如果你的节点运行时是 docker,把ctr -n k8s.io images import换成docker load -i即可。脚本里没有做并发,节点多的时候可以加&和wait并行,但要注意控制并发数,避免把网络打满。
注意:批量操作前,先在单个节点上验证整个流程,确认镜像名、命名空间、导入命令都正确,再推到全部节点。我见过有人直接跑批量脚本,结果所有节点都导入了错误的架构版本,最后全部重来。
4. flannel 镜像包避坑:从 ImagePullBackOff 到网段冲突的排查记录
4.1 现象:Pod 一直 ImagePullBackOff,但本地明明有镜像
这是最常见的坑。现象是 flannel 的 DaemonSet Pod 状态为ImagePullBackOff,但你在节点上docker images或ctr images ls能看到镜像。原因通常有三个:一是镜像名和 YAML 里不一致,比如本地是flannel:v0.24.0-amd64,YAML 里写的是flannel:v0.24.0;二是 containerd 命名空间不对,镜像导入到了 default 而不是k8s.io;三是 kubelet 配置了imagePullPolicy: Always,即使本地有镜像也强制去外网拉。
解决方法是:先kubectl describe pod看 Events 里的具体拉取错误,确认它要找的镜像名是什么;然后对比本地镜像名,用docker tag或ctr images tag改成一致;如果是命名空间问题,重新导入到k8s.io;如果是imagePullPolicy,把 YAML 里的策略改成IfNotPresent。
4.2 现象:flannel Pod 起来了,但节点间 Pod 不通
flannel Pod 显示 Running,但不同节点上的 Pod 互相 ping 不通。原因可能是 flannel 的网段配置和节点已有网段冲突。比如你的节点内网是10.244.0.0/16,而 flannel 默认也用10.244.0.0/16,就会路由混乱。解决方法是修改 flannel 的 ConfigMap,把Network字段改成不冲突的网段,比如10.255.0.0/16,然后重启 flannel Pod。
另一个原因是内核没开 IP 转发。检查sysctl net.ipv4.ip_forward,如果是 0,执行sysctl -w net.ipv4.ip_forward=1并写入/etc/sysctl.conf。这个在离线环境里经常被忽略,因为默认镜像可能没带相关配置。
4.3 现象:导入镜像时报 no space left on device
镜像 tar 包通常上百 MB,导入时会解压到/var/lib/docker或/var/lib/containerd,如果这些分区空间不够,就会报no space left on device。解决方法是提前清理无用镜像和容器,或者把数据目录迁移到更大的分区。用df -h查看/var/lib/docker或/var/lib/containerd的剩余空间,确保至少有 2GB 以上。
如果空间实在紧张,可以在导出镜像时用docker save的--compress选项压缩 tar 包,但导入时仍然需要解压空间。更彻底的办法是只导出必要的镜像层,但 flannel 镜像本身不大,通常清理一下旧镜像就能解决。
4.4 现象:flannel 版本和 k8s 版本不匹配导致 API 报错
k8s 1.36 这类较新版本,对 CNI 插件的 API 版本有要求。如果你用了很老的 flannel 版本,比如 v0.11,它的 CNI 配置格式可能不被新 kubelet 识别,现象是 Pod 创建时 CNI 插件报错,flannel Pod 日志里出现failed to load network config之类的信息。解决方法是升级 flannel 到 v0.24 以上,并同步更新 YAML 里的镜像标签。
升级时注意:先导出旧版本的配置备份,再替换镜像和 YAML,最后重启 flannel DaemonSet。不要直接删除旧 Pod,否则可能影响现有 Pod 的网络。
4.5 现象:离线环境里 flannel 去拉 etcd 镜像失败
有些 flannel 部署方式依赖 etcd 存储网络配置,如果你的 YAML 里 flannel 的 backend 是 etcd,那它还会去拉 etcd 的镜像。离线环境里这个镜像也没准备,就会导致 flannel Pod 起不来。解决方法是改用 k8s API 作为 backend,或者提前把 etcd 镜像也加入必要镜像包。我一般推荐直接用 k8s API 模式,少一个依赖,少一个坑。
5. 验证 flannel 镜像包是否真正可用:三个必做检查
5.1 用 crictl 检查镜像和 Pod 状态
镜像导入后,用crictl检查是最贴近 kubelet 视角的方式:
# 查看 k8s.io 命名空间下的镜像 crictl images | grep flannel # 查看 flannel Pod 的详细状态 crictl pods --name flannel # 如果 Pod 没起来,看具体容器的日志 crictl logs <container-id>crictl默认连接 containerd 的k8s.io命名空间,所以它看到的镜像就是 kubelet 能用的镜像。如果crictl images里没有 flannel,那 Pod 一定起不来。这一步能快速定位是镜像问题还是配置问题。
5.2 跨节点 Pod 通信测试
flannel 正常后,在不同节点上各起一个测试 Pod,互相 ping。可以用kubectl run快速创建:
# 在节点 A 上创建测试 Pod kubectl run test-a --image=busybox --restart=Never -- sleep 3600 # 在节点 B 上创建测试 Pod kubectl run test-b --image=busybox --restart=Never -- sleep 3600 # 获取 test-b 的 IP kubectl get pod test-b -o wide # 在 test-a 里 ping test-b 的 IP kubectl exec test-a -- ping -c 3 <test-b-ip>如果能 ping 通,说明 flannel 的跨节点路由正常工作。如果 ping 不通,检查节点路由表ip route,看有没有 flannel 生成的10.244.x.x路由条目。没有的话,说明 flannel 没正确写入路由,可能是权限问题或 backend 配置问题。
5.3 检查 flannel 的 ConfigMap 和实际网段
flannel 的网段配置在 ConfigMap 里,用kubectl get configmap kube-flannel-cfg -n kube-flannel -o yaml查看。重点看Network字段和实际节点路由是否一致。如果 ConfigMap 里是10.244.0.0/16,但节点路由里出现的是别的网段,说明 flannel 没有正确读取配置,可能是 RBAC 权限不足,或者 flannel Pod 没挂载到 ConfigMap。
我一般会在部署后立刻做这三项检查,确认无误再继续部署业务。这样即使后面出问题,也能排除网络插件这个变量。flannel 镜像包这件事,看起来只是导个 tar 包,但真正落地时,架构、命名空间、版本、网段,每个点都可能让你多花几个小时。把这几步做扎实,后面 k8s 集群的稳定性会好很多。希望帮到你。
本文还有配套的精品资源,点击获取