简介这份资源面向正在搭建 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 一直 ImagePullBackOffflannel 的镜像标签里架构后缀很容易被忽略。比如你在 amd64 机器上拉quay.io/coreos/flannel:v0.24.0docker 会自动选择 amd64 的 manifest但导出的 tar 包在 arm64 节点上导入后kubelet 仍然会去拉取对应架构的镜像发现本地没有就报ImagePullBackOff。解决方法是要么在每个架构的机器上分别拉取并导出要么直接使用不带架构后缀的多架构标签让容器运行时自己选。另一个坑是镜像名不一致。flannel 官方 YAML 里写的可能是quay.io/coreos/flannel:v0.24.0但你导入后本地镜像名可能变成quay.io/coreos/flannel:v0.24.0-amd64kubelet 按 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_TARflannel-images.tar NODE_LISTnodes.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-amd64YAML 里写的是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_forward1并写入/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-idcrictl默认连接 containerd 的k8s.io命名空间所以它看到的镜像就是 kubelet 能用的镜像。如果crictl images里没有 flannel那 Pod 一定起不来。这一步能快速定位是镜像问题还是配置问题。5.2 跨节点 Pod 通信测试flannel 正常后在不同节点上各起一个测试 Pod互相 ping。可以用kubectl run快速创建# 在节点 A 上创建测试 Pod kubectl run test-a --imagebusybox --restartNever -- sleep 3600 # 在节点 B 上创建测试 Pod kubectl run test-b --imagebusybox --restartNever -- 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 集群的稳定性会好很多。希望帮到你。本文还有配套的精品资源点击获取