简介:这份资源面向在 ARM64 架构服务器、开发板或边缘设备上部署 Kubernetes 集群的运维人员和云原生学习者,围绕 flannel v0.11.0 版本提供可落地的网络组件材料,主要解决节点间容器网络互通、覆盖网络初始化以及容器运行时参数对接等基础问题。压缩包共 3 个文件,整体约 8.03MB,内含 flanneld 主程序、说明文档和用于生成 Docker 网络参数的脚本,分别承担组件运行、配置查阅与运行时参数生成三类用途,体量轻便,便于在资源受限环境中快速传输和部署。目前已有 455 人学习或下载。读者可以结合说明文档理解 flannel 的启动参数、网络配置思路与容器运行时对接方式,也能在实验集群中复现跨节点通信、排查网络不通等问题,适合用作旧版本 flannel 部署、维护和排错时的参考材料。
1. flannel v0.11.0 的 arm64 二进制包:为什么值得单独留一份
在 arm64 集群里装网络插件,最烦的不是配置有多复杂,而是包不对。源码编译要拖一整条工具链,容器镜像在离线环境里又拉不下来,最后卡在一个“架构不匹配”的报错上,前面调好的 kubelet 参数全白费。手上这个flannel-v0.11.0-linux-arm64.tar.gz就是针对这种场景的现成产物:Linux、arm64、v0.11.0,三个属性定死了它的适用范围,也决定了它不能随便往 x86 节点上扔。它解决的是 Kubernetes 集群 Pod 跨节点通信的底层组网问题,适合正在搭 arm64 测试集群、边缘节点或国产化硬件环境的运维和平台开发。下面按“它是什么、怎么跑起来、哪里翻车”的顺序拆开讲。
2. 拆包看结构:三个二进制和一份 CNI 配置的分工
这个压缩包不大,但里面的东西各司其职。先弄清楚里面有什么、谁依赖谁,再谈部署,否则很容易出现“二进制跑起来了但 Pod 还是不通”的玄学问题。v0.11.0 这个版本的功能边界也要先划清:它属于 flannel 较成熟的迭代,后端支持 host-gw、vxlan、udp、ali-vpc 等,配置仍然走 ConfigMap 下发,没有后来那些实验性开关。
2.1 包内文件与运行时角色
解压之后典型会看到三个可执行文件和一份 CNI 配置。不同构建批次文件名可能略有差异,但角色固定:
| 文件 | 作用 | 部署位置 | 是否常驻 |
|---|---|---|---|
flanneld | 主守护进程,负责分配子网、维护路由和 FDB 表 | /opt/bin/或/usr/local/bin/ | 是,systemd 托管 |
mk-docker-opts.sh | 生成 docker 启动参数(仅 docker 场景需要) | 任意 PATH 目录 | 否,一次性脚本 |
flannel | CNI 插件可执行文件,被 kubelet 调用 | /opt/cni/bin/ | 否,按需调用 |
10-flannel.conflist | CNI 配置,声明插件链和 delegate | /etc/cni/net.d/ | 否,静态配置 |
容易搞混的是flanneld和flannel:前者是“大脑”,负责跟 apiserver 打交道、拿集群网段、算本机子网、写路由;后者是“手脚”,kubelet 创建 Pod 沙箱时调用它,它再去问 flanneld 要结果。两者缺一,或者版本对不上,就会出现 Pod 一直 ContainerCreating、describe里报failed to find plugin "flannel"之类的现象。
2.2 为什么选二进制部署而不是直接用 DaemonSet
绝大多数人第一反应是kubectl apply一个官方 yaml 了事。但在 arm64 环境里,这套流程经常断在镜像上:节点没有外网、私有仓库没同步多架构镜像、镜像 tag 和二进制版本错位。二进制部署的好处是把“网络插件”从容器编排里摘出来,变成一个普通的 systemd 服务,出问题时systemctl status flanneld和journalctl -u flanneld就能看到全貌,不用先exec进容器再折腾。
代价是你要自己管四件事:CNI 目录、网段参数、后端类型、与 kubelet 的启动顺序。这四件事下面逐个落到命令上。选 v0.11.0 的另一个现实理由是配置项稳定,网上流传的net-conf.json写法基本通用,不会因为版本差异导致字段被静默忽略。
2.3 部署前的环境核对
动手前先确认三件事,能省掉一半排错时间。第一,节点架构确实是 arm64,uname -m应输出aarch64;第二,kubelet 的--network-plugin在较新版本里已经移除,CNI 目录要由--cni-conf-dir和--cni-bin-dir指定,默认分别是/etc/cni/net.d和/opt/cni/bin;第三,集群的pod-network-cidr必须和 flannel 配置里的Network一致,这个值在kubeadm init时就定死了,事后改要动 apiserver 和 controller-manager。
# 核对架构与内核模块,vxlan 后端依赖内核支持 uname -m lsmod | grep vxlan || modprobe vxlan # 确认 CNI 目录存在且 kubelet 指向它们 ls -ld /etc/cni/net.d /opt/cni/bin ps -ef | grep kubelet | grep -o -- '--cni-[a-z-]*=[^ ]*' # 查看集群初始化时定的 Pod 网段 kubectl cluster-info dump | grep -m1 pod-network-cidr第一段确认硬件和内核能力,modprobe vxlan失败说明内核裁剪过,得换后端或换内核。第二段把 kubelet 实际使用的目录打出来,很多人栽在“二进制拷到了/usr/libexec/cni,kubelet 却看/opt/cni/bin”。第三段拿到集群网段,后面写配置时直接对齐,避免出现节点间路由表里有条目但流量黑洞的情况。
3. 从解压到 Pod 互通:一份可复现的落地流程
这一章是完整的操作链:放文件、写配置、起服务、验证连通。每一步都给出可抄的命令,并说明失败时先看哪里。流程以 vxlan 后端为主线,因为它在跨网段、跨云环境的适应性最好;同网段场景可以换 host-gw,性能更高但要求二层可达。
3.1 放置二进制与 CNI 配置
先把包解开,按角色分发文件。注意flannel这个 CNI 插件必须放进 kubelet 扫描的目录,flanneld放系统 PATH 即可,但建议固定到/opt/bin方便 systemd 引用绝对路径。
tar -xzf flannel-v0.11.0-linux-arm64.tar.gz -C /tmp/flannel-pkg # 主进程与 CNI 插件分开放,职责不混 install -m 0755 /tmp/flannel-pkg/flanneld /opt/bin/flanneld install -m 0755 /tmp/flannel-pkg/flannel /opt/cni/bin/flannel install -m 0755 /tmp/flannel-pkg/mk-docker-opts.sh /opt/bin/mk-docker-opts.sh # CNI 配置落位,文件名决定加载顺序,10 开头保证靠前 install -m 0644 /tmp/flannel-pkg/10-flannel.conflist /etc/cni/net.d/10-flannel.conflistinstall -m比cp多一层权限控制,避免拷过去发现没有执行位。CNI 配置文件名前缀是数字,kubelet 按字典序选第一个 conflist,所以10-是有意为之;如果机器上还残留 calico 的10-calico.conflist,两个文件会打架,必须先清掉旧插件。
10-flannel.conflist的内容一般长这样,重点是type要和二进制名一致,delegate段决定 IPAM 行为:
{ "name": "cbr0", "cniVersion": "0.3.1", "plugins": [ { "type": "flannel", "delegate": { "hairpinMode": true, "isDefaultGateway": true } }, { "type": "portmap", "capabilities": { "portMappings": true } } ] }cniVersion要低于或等于 kubelet 支持的版本,0.3.1 在 v0.11.0 时期是主流。hairpinMode打开后同一个 Pod 内通过 Service 访问自己能通,做 sidecar 场景必须开。isDefaultGateway让 flannel 给 Pod 配默认路由,不走宿主机转发,省一层 NAT。portmap是独立插件,负责 hostPort 映射,镜像里通常自带,没有的话 hostPort 会静默失效。
3.2 写 net-conf.json 并启动 flanneld
flanneld 的配置有两种来源:读 apiserver 里的 ConfigMap,或读本地文件。二进制部署用后者更直接,配合--kube-subnet-mgr=false走本地子网管理。配置文件放在/etc/kube-flannel/net-conf.json:
{ "Network": "10.244.0.0/16", "Backend": { "Type": "vxlan", "VNI": 1, "Port": 8472 } }Network必须和kubeadm init --pod-network-cidr的值完全一致,写错会出现“flanneld 正常但 Pod 拿不到 IP”。VNI是 vxlan 的网络标识,多集群共用物理网络时要错开,否则会串包。Port默认 8472,安全组和防火墙要放行 UDP 8472,这是最容易漏的一条。
接着写 systemd unit,关键在启动参数和环境变量:
[Unit] Description=Flanneld overlay address etcd agent After=network-online.target Before=docker.service kubelet.service [Service] Type=notify EnvironmentFile=-/run/flannel/subnet.env ExecStart=/opt/bin/flanneld \ --ip-masq \ --kube-subnet-mgr=false \ --subnet-file=/run/flannel/subnet.env \ --public-ip=$(hostname -I | awk '{print $1}') Restart=always RestartSec=5 [Install] WantedBy=multi-user.targetBefore=保证 flanneld 先于 kubelet 就绪,否则 kubelet 启动时 CNI 还没准备好,节点会报NetworkPluginNotReady。--ip-masq给出集群的流量做 SNAT,不开的话 Pod 访问外网会断。--public-ip在多网卡机器上必须显式指定,否则 flanneld 可能选到管理网卡,导致 vxlan 隧道建在错误的链路上——这是 arm64 边缘设备上最常见的翻车点之一,因为这类设备经常有 WiFi、4G、有线三张网卡。
启动并观察:
systemctl daemon-reload systemctl enable --now flanneld systemctl status flanneld --no-pager journalctl -u flanneld -n 50 --no-pager # 确认子网环境文件已生成 cat /run/flannel/subnet.env正常的subnet.env会长这样,FLANNEL_SUBNET是节点专属子网,每个节点不同:
FLANNEL_NETWORK=10.244.0.0/16 FLANNEL_SUBNET=10.244.1.0/24 FLANNEL_MTU=1450 FLANNEL_IPMASQ=true看到FLANNEL_SUBNET就说明 flanneld 已经从大网段里切出了本节点的份额。如果文件不存在,先看日志里有没有failed to acquire lease,那基本是网段冲突或 apiserver 连接问题。
3.3 路由、FDB 与连通性验证
flanneld 跑起来只是第一步,真正决定通不通的是内核里的路由和邻居表。vxlan 后端的原理是每个节点分配一个/24子网,flanneld 把“其他节点的子网 → 对应节点 IP”写成路由,再通过 vxlan 设备封装二层帧发出去。
# 看 vxlan 设备和路由表,应能看到其他节点的 /24 网段 ip -d link show flannel.1 ip route | grep 10.244 # 看 FDB,vxlan 靠它把目的 MAC 映射到远端 VTEP bridge fdb show dev flannel.1 | grep -v permanentip -d link show flannel.1输出里的vxlan id 1要和配置的VNI对上。路由表里每个远端节点一条/24,via后面是 flannel.1 的地址,onlink标志表示下一跳不要求直连。FDB 表里应该有远端节点的 MAC 与dst <节点IP>的对应关系,这条是动态学习的,刚启动时可能为空,触发一次跨节点通信后才会出现。
验证顺序建议从内到外:先在同节点起两个 Pod 互 ping,验证 CNI 和网桥;再跨节点 ping,验证 vxlan 隧道;最后测 Service 和出网。
# 起两个测试 Pod,一个留在本节点,一个调度到其他节点 kubectl run nettest-a --image=busybox --restart=Never -- sleep 3600 kubectl run nettest-b --image=busybox --restart=Never -- sleep 3600 kubectl get pod -o wide | grep nettest # 跨节点互 ping 与出网测试 kubectl exec nettest-a -- ping -c 3 <nettest-b的PodIP> kubectl exec nettest-a -- ping -c 3 8.8.8.8 kubectl exec nettest-a -- nslookup kubernetes.default跨节点 ping 不通时,先回到ip route看目的网段有没有对应条目,再看 FDB 有没有学到 MAC,最后确认 UDP 8472 是否被安全组拦了。出网不通但内网通,基本是--ip-masq没开或 iptables 规则被其他组件刷掉了。DNS 解析失败则要检查 kube-dns 的 Service IP 是否落在Network网段内。
4. 避坑与排查:arm64 环境里最容易翻车的五处
这一章的每一条都来自实际踩过的坑,按“现象 → 原因 → 解决”组织。arm64 的问题往往不在 flannel 本身,而在周边环境的不一致,所以排查思路是先证伪外围,再怀疑插件。
4.1 Pod 一直 ContainerCreating,事件里说找不到 flannel 插件
现象:kubectl describe pod的事件里出现failed to find plugin "flannel" in path [/opt/cni/bin],节点上flanneld却是 active。
原因:CNI 插件二进制没放对目录,或者放了但没有执行权限;另一种情况是 kubelet 的--cni-bin-dir被改过,和你放的位置不一致。
解决:先用ps -ef | grep kubelet确认实际的--cni-bin-dir,再把flannel拷过去并chmod 0755。改完不需要重启 kubelet,下一次 Pod 创建会重新扫描目录,但已经卡住的 Pod 要删掉重建。
4.2 跨节点 ping 不通,同节点正常
现象:同节点两个 Pod 互通,跨节点 100% 丢包,ip route里能看到远端网段条目。
原因:UDP 8472 被防火墙或安全组拦了;或者节点有多张网卡,flanneld 选的public-ip不是实际承载 vxlan 的那张,隧道包发到了错误链路。
解决:先tcpdump -i any -n udp port 8472在两端抓包,看包有没有出去、有没有回来。只有单向包就是链路或安全组问题。多网卡场景在 unit 里显式写--public-ip=<承载网IP>,重启 flanneld 后 FDB 会重新学习。
4.3 flanneld 反复重启,日志报 lease 获取失败
现象:journalctl -u flanneld里循环出现failed to acquire lease或network is not ready。
原因:net-conf.json里的Network和集群pod-network-cidr不一致;或者集群里已有其他网络插件占用同一网段;或者 apiserver 地址配错,flanneld 连不上。
解决:三处对齐——kubeadm初始化参数、net-conf.json的Network、已存在的 ConfigMap(如果走过 DaemonSet 部署,残留的kube-flannel-cfg会干扰)。确认后清掉/var/run/flannel/subnet.env再重启,让 flanneld 重新申请。
4.4 Pod 能通但 Service 访问超时
现象:Pod IP 直连正常,访问 ClusterIP 超时,nslookup也失败。
原因:kube-proxy 的 iptables 规则没生效,或者hairpinMode没开导致回环流量被丢;也可能是Network网段和 Service 网段重叠。
解决:先iptables -t nat -L KUBE-SERVICES -n看规则是否存在,不存在就查 kube-proxy 日志。网段重叠要改service-cidr,这属于初始化阶段的问题,事后调整成本很高,建议部署前就用表格把三个网段列出来核对。
4.5 arm64 上二进制报 “Exec format error”
现象:systemctl start flanneld直接失败,日志里是Exec format error。
原因:包不是 arm64 的,或者解压时文件损坏;也有一种情况是把 amd64 的包混进了 arm64 节点。
解决:file /opt/bin/flanneld应显示ELF 64-bit LSB executable, ARM aarch64。对不上就重新获取对应架构的包。顺手sha256sum比对一下,传输中断导致的半截文件也会报各种奇怪错误。
5. 版本边界与验证习惯:把 v0.11.0 用稳的几个技巧
v0.11.0 不是新版本,用它就得接受它的边界。最实际的一条:这个版本的 flanneld 对 Kubernetes 新特性适配有限,如果你集群的 kubelet 版本很新,CNI 配置里的cniVersion别写太高,0.3.1 足够,写 0.4.0 反而可能被旧插件拒识。另一个边界是它不带 NetworkPolicy 能力,需要策略隔离时得叠 calico 或 cilium,别指望 flannel 单独扛。
验证习惯上,我一般按“三层确认法”走一遍,比看日志猜快得多:
| 层级 | 检查命令 | 正常特征 |
|---|---|---|
| 进程层 | systemctl is-active flanneld | active,且 5 分钟内无重启 |
| 数据面 | ip route | grep 10.244、bridge fdb show dev flannel.1 | 每个远端节点一条路由,FDB 有动态条目 |
| 业务面 | 跨节点 Pod 互 ping、ClusterIP 访问、出网 | 三项全通才算部署完成 |
三层里任何一层不过,就不要往下走,否则问题会叠加,排查成本翻倍。还有一个容易被忽略的点:节点扩容后,老节点上的路由和 FDB 不会自动补齐,新节点加入后要在老节点上确认ip route是否多了新网段,没有的话重启老节点的 flanneld 触发一次全量同步。
# 节点扩容后的快速核对脚本,在所有老节点上跑一遍 NEW_NODE_SUBNET="10.244.5.0/24" if ! ip route | grep -q "$NEW_NODE_SUBNET"; then echo "missing route to $NEW_NODE_SUBNET, restarting flanneld" systemctl restart flanneld sleep 3 ip route | grep "$NEW_NODE_SUBNET" fi这段脚本把“新节点子网”作为参数,检查本机路由表里有没有对应条目,没有就重启 flanneld 让它重新 watch 集群状态。sleep 3是给 flanneld 留出同步时间,实际环境里如果节点多、apiserver 压力大,可以适当放宽到 5 到 10 秒。脚本只做检测和兜底重启,不做复杂判断,避免引入新的不确定性。
最后说一个血泪教训:有次在 arm64 边缘设备上部署,一切指标正常,唯独跨节点偶发丢包,查了两天才发现是 MTU。vxlan 封装会多出 50 字节开销,FLANNEL_MTU默认 1450,但那条链路上有一台设备的 MTU 只有 1400,导致大包被静默丢弃,小包 ping 完全正常。从那以后我每次部署完都会用大包验证一遍:ping -s 1400 -M do,通不过就把FLANNEL_MTU调小到链路能承载的值,再重启 flanneld 让 CNI 配置重新下发。这个习惯帮我省掉了后面无数次“玄学丢包”的排查。希望帮到你。
本文还有配套的精品资源,点击获取