技术实战

让局域网主机直连 K8s Pod IP:Flannel VXLAN → host-gw 实战

默认 K8s 集群的 Pod 网络是 overlay,只有节点之间能互通,局域网内的开发机完全摸不到 Pod IP。本文记录了如何将 Flannel 从 VXLAN 切换到 host-gw 模式,改动仅一行配置 + 一次滚动重启,之后开发机只需加一条静态路由就能直接 ping 10.244.0.231curl http://10.244.0.231:8080

Flannel host-gw VXLAN 切换 零依赖

导读

开发团队在局域网调试时,经常需要直接访问 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
CNIFlannel(VXLAN 模式)
Pod CIDR10.244.0.0/16
节点 IP192.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 子网下一跳是谁,然后物理网卡直接转发。

对比:

维度VXLANhost-gw
节点间通信UDP 封装(8472端口)直接 IP 路由
额外延迟~0.1ms
LAN 主机访问❌ 不可达✅ 一条路由可达
依赖UDP 8472 端口放通无额外依赖
内核要求3.7+任意(纯 ip route)

实施步骤

  1. 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. 2 滚动重启 Flannel DaemonSet

    滚动重启 = 一个节点一个节点来,Pod 网络中断在秒级。监控进度:

    kubectl --kubeconfig /home/k8s/config-24.yaml \
      rollout restart daemonset -n kube-system kube-flannel-ds
    

    kubectl –kubeconfig /home/k8s/config-24.yaml
    rollout status daemonset -n kube-system kube-flannel-ds

  3. 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. 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。