让局域网主机直连 K8s Pod IP:Flannel VXLAN → host-gw 实战
默认 K8s 集群的 Pod 网络是 overlay,只有节点之间能互通,局域网内的开发机完全摸不到 Pod IP。本文记录了如何将 Flannel 从 VXLAN 切换到 host-gw 模式,改动仅一行配置 + 一次滚动重启,之后开发机只需加一条静态路由就能直接 ping 10.244.0.231、curl http://10.244.0.231:8080。
导读
开发团队在局域网调试时,经常需要直接访问 Pod 的 IP 和端口来排查问题或验证服务。默认情况下这做不到——Pod 网络走的 VXLAN overlay,只有集群节点看得见。本文的核心思路:因为所有 K8s 节点恰好在同一 L2 子网(192.168.2.0/24),overlay 封装完全多余。切到 host-gw 模式后,Pod 网络退化为普通 IP 路由。
结论先看
- 改一行 Flannel ConfigMap:
VXLAN → host-gw - 滚动重启 Flannel DaemonSet,中断秒级
- 开发机一条静态路由指向 K8s 任意节点即可访问所有 Pod IP
- 可随时回滚(改回 VXLAN + 重启)
环境与问题
| 项目 | 详情 |
|---|---|
| 集群 | dev1(3 master + 2 worker) |
| K8s 版本 | v1.20.2 |
| OS / 内核 | CentOS 7 / 3.10.0 |
| 容器运行时 | Docker 20.10 |
| CNI | Flannel(VXLAN 模式) |
| Pod CIDR | 10.244.0.0/16 |
| 节点 IP | 192.168.2.12/17/21/25/27 |
| 目标 | LAN 主机(192.168.2.x)能直连 Pod IP |
核心约束:内核 3.10 排除了 Cilium(需要 eBPF);K8s 1.20 排除了新版 CNI 特性;Docker 运行时使 Calico 迁移成本偏高。在 Flannel 体系内做模式切换是投入产出比最高的选择。
为什么是 host-gw?
Flannel 支持三种后端:VXLAN(全封装)、host-gw(直连路由)、UDP(已淘汰)。
所有节点在同一个 L2 子网时,VXLAN 的封装/解封操作完全多余——每个节点本来就互相可达。host-gw 模式的本质就是让每个节点通过 ip route 知道其他节点的 Pod 子网下一跳是谁,然后物理网卡直接转发。
对比:
| 维度 | VXLAN | host-gw |
|---|---|---|
| 节点间通信 | UDP 封装(8472端口) | 直接 IP 路由 |
| 额外延迟 | ~0.1ms | 无 |
| LAN 主机访问 | ❌ 不可达 | ✅ 一条路由可达 |
| 依赖 | UDP 8472 端口放通 | 无额外依赖 |
| 内核要求 | 3.7+ | 任意(纯 ip route) |
实施步骤
-
1
切换 Flannel ConfigMap
一条命令完成。ConfigMap 即时更新,但需重启 Flannel Pods 才生效:
kubectl --kubeconfig /home/k8s/config-24.yaml \ patch cm -n kube-system kube-flannel-cfg \ --type='merge' \ -p '{"data":{"net-conf.json":"{\"Network\":\"10.244.0.0/16\",\"Backend\":{\"Type\":\"host-gw\"}}"}}' -
2
滚动重启 Flannel DaemonSet
滚动重启 = 一个节点一个节点来,Pod 网络中断在秒级。监控进度:
kubectl --kubeconfig /home/k8s/config-24.yaml \ rollout restart daemonset -n kube-system kube-flannel-dskubectl –kubeconfig /home/k8s/config-24.yaml
rollout status daemonset -n kube-system kube-flannel-ds -
3
验证路由表已切换
在任意节点执行
ip route | grep 10.244,确认路由下一跳是真实节点 IP 而非dev flannel.1:# 切换前(VXLAN) 10.244.4.0/24 via 10.244.4.0 dev flannel.1 onlink切换后(host-gw)
10.244.4.0/24 via 192.168.2.12 dev enp4s0 10.244.5.0/24 via 192.168.2.17 dev enp4s0
-
4
验证跨节点 Pod 通信
kubectl –kubeconfig /home/k8s/config-24.yaml
run test-net –image=busybox –rm -it –restart=Never – sh -c
“ping -c2 10.244.4.89 && echo ‘OK’"
开发机配置(发给团队)
# Linux / macOS
sudo ip route add 10.244.0.0/16 via 192.168.2.21
# Windows(管理员 CMD,-p 持久化)
route add 10.244.0.0 mask 255.255.0.0 192.168.2.21 -p
验证:ping 10.244.0.231 或浏览器访问 http://10.244.0.231:8080/。
流量路径解析
开发机 (192.168.2.100)
│
│ ip route: 10.244.0.0/16 → 192.168.2.21
▼
dev-21 (192.168.2.21)
│ 查本地路由表:
│ 10.244.4.0/24 → 192.168.2.12 (dev-12)
│ 10.244.5.0/24 → 192.168.2.17 (dev-17)
│ 10.244.0.0/24 → 本地 (dev-21)
▼
目标 Pod (10.244.4.89)
开发机只负责把包交给 dev-21,之后由 dev-21 的路由表接力转发到 Pod 所在节点。全程纯 IP 路由,零封装。
踩坑与判断点
- 路由表验证是最可靠的判断依据。看到
dev flannel.1 onlink就是 VXLAN,看到真实节点 IP 就是 host-gw。 - ip route add 默认不持久化。Linux 重启后路由消失。CentOS 写入
/etc/sysconfig/network-scripts/route-<接口名>,Ubuntu 用 netplan。Windows 带-p参数则持久。 - Service ClusterIP 仍然不可达。10.96.x.x 是 kube-proxy 在 iptables 中虚拟的,只有集群内部可见。开发机请直接使用 Pod IP,或者已有的 NodePort。
- 如果多集群 Pod CIDR 冲突(如两个集群都用 10.244.0.0/16),只能在路由层面做区分(不同开发机指向不同网关),或用策略路由。
- host-gw 要求所有节点在同一 L2 子网。如果节点跨越三层,需要用 VXLAN 或 IPIP。
- 回滚只需把 ConfigMap 改回
"Type":"vxlan",再次重启 Flannel。
高可用优化(可选)
当前方案中 via 192.168.2.21 是单点——dev-21 宕机则路由黑洞。可以用 keepalived 做 VIP:
# 在 dev-21 和 dev-25 上部署 keepalived,VIP = 192.168.2.200
# 开发机路由改为指向 VIP:
ip route add 10.244.0.0/16 via 192.168.2.200
总结
- 最终结果:开发机一条路由命令即可访问所有 Pod IP,无需 NodePort、port-forward、VPN。
- 关键经验:所有节点同 L2 子网时 overlay 是多余开销,切直连路由是性能最优、配置最简的方案。
- 可扩展方向:相同的思路适用于 Calico(IPIP → CrossSubnet/Never)、Cilium(L2 Announcement)等其他 CNI。