MetalLB L2 模式 ARP 不通问题排查与解决(IPVS + Calico 实战)

关键词:MetalLB、L2、ARP、IPVS、Calico、kube-proxy、rp_filter、arp_ignore

适用环境:裸金属 / 虚拟机 Kubernetes 集群,无云厂商 LB


一、问题现象

在 Kubernetes 集群中部署 MetalLB(L2 模式),为 nginx-ingress 创建 Service type=LoadBalancer

  • Service 已正常分配 External IP(如 10.0.3.20
  • Pod、Service、Endpoints 全部正常
  • MetalLB speaker 无报错
  • 但局域网其他机器访问 External IP 失败
  • arping 10.0.3.20 无任何回复

⚠️ 特殊现象

ip addr add 10.0.3.20/32 dev ens33

手动把 IP 绑到网卡后,访问立刻恢复正常。


二、环境信息

项目
Kuberneteskubeadm 集群 v1.34.3
CNICalico(vxlan)v3.31.3
kube-proxyIPVS 模式
MetalLBv0.15.2+(CRD 模式)
Node 网卡ens33
Servicenginx-ingress (LoadBalancer)

metallb部署:

kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.15.2/config/manifests/metallb-native.yaml

[root@k8smaster01 ~]# cat metallb-config.yaml 
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: metallb-ippool
  namespace: metallb-system
spec:
  addresses:
  - 10.0.3.20-10.0.3.50
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: metallb-pcauto-adver
  namespace: metallb-system
spec:
  ipAddressPools:
  - metallb-ippool
  interfaces:
  - ens33

三、关键排查过程

1️⃣ MetalLB 状态确认

kubectl get pods -n metallb-system
  • controller / speaker 均 Running
  • Service 被正常 reconcile
kubectl logs -n metallb-system -l component=speaker | grep announce

speaker 日志显示:

created ARP responder for interface ens33

👉 MetalLB 确实在监听 ARP


2️⃣ External IP 实际落点

ip addr show kube-ipvs0 | grep 10.0.3.20
inet 10.0.3.20/32 scope global kube-ipvs0

📌 关键点:

  • External IP 并不在 ens33 上
  • 而是在 kube-ipvs0 虚拟接口

3️⃣ ARP 抓包分析(核心)

在节点执行:

tcpdump -i ens33 arp

现象:

  • 可以看到局域网发来的 ARP Request
  • 完全没有 ARP Reply(10.0.3.20)
arping 10.0.3.20
# 0 response

👉 说明 内核没有对 ARP 请求做出响应


四、真正根因分析(重点)

❌ 问题不是 MetalLB

MetalLB L2 模式的工作方式是:

通过 speaker 使用内核 ARP 栈应答 ARP

不自己构造 ARP 包,而是依赖 Linux 内核。


❌ Linux 默认 ARP / rp_filter 策略导致丢包

在 IPVS + kube-proxy 场景下:

  • External IP 位于 kube-ipvs0
  • ARP 请求从 ens33 进入

但 Linux 默认策略是:

  • rp_filter = 1(严格反向路径校验)
  • arp_ignore = 1
  • arp_announce = 2

📛 结果

内核认为:

“这个 IP 不在 ens33 上,我不能从 ens33 回 ARP”

➡️ ARP 被直接丢弃


五、解决方案(最终生效)

✅ 1️⃣ 关闭 rp_filter(关键)

sysctl -w net.ipv4.conf.all.rp_filter=0
sysctl -w net.ipv4.conf.default.rp_filter=0
sysctl -w net.ipv4.conf.ens33.rp_filter=0

✅ 2️⃣ 调整 ARP 行为

sysctl -w net.ipv4.conf.all.arp_ignore=0
sysctl -w net.ipv4.conf.all.arp_announce=0
sysctl -w net.ipv4.conf.default.arp_ignore=0
sysctl -w net.ipv4.conf.default.arp_announce=0
sysctl -w net.ipv4.conf.kube-ipvs0.arp_ignore=0
sysctl -w net.ipv4.conf.kube-ipvs0.arp_announce=0

✅ 3️⃣ 刷新 ARP 并重建 Service

ip neigh flush all
kubectl delete -f loadbalancer.yaml
kubectl apply -f loadbalancer.yaml

六、验证结果

ARP 正常响应

arping 10.0.3.20
Unicast reply from 10.0.3.20

tcpdump 可见 ARP Reply

ARP, Reply 10.0.3.20 is-at xx:xx:xx:xx:xx

外部访问成功

curl http://10.0.3.20

🎉 问题彻底解决


七、为什么“手动绑 IP 就能好”?

ip addr add 10.0.3.20/32 dev ens33

此操作等价于:

  • 告诉内核:

“这个 IP 属于 ens33,可以直接 ARP 回复”

⚠️ 但这是 错误用法,会破坏 MetalLB 调度


八、生产环境建议

✅ sysctl 持久化(推荐)

# /etc/sysctl.d/99-metallb.conf
net.ipv4.conf.all.rp_filter=0
net.ipv4.conf.default.rp_filter=0
net.ipv4.conf.all.arp_ignore=0
net.ipv4.conf.all.arp_announce=0
sysctl --system

🚀 更优方案

场景建议
二层网络可控MetalLB L2
三层 / 大规模MetalLB BGP
IngressexternalTrafficPolicy: Local

九、核心经验总结

✅ MetalLB L2 100% 依赖 Linux ARP 行为

✅ IPVS 会把 External IP 放到 kube-ipvs0

✅ 默认内核参数 不适合 LB 场景

📌 一句话结论

MetalLB L2 不通,80% 是 Linux ARP / rp_filter 的锅,不是 MetalLB 配置问题


如果你在:

  • 裸金属 K8s
  • 家庭实验室
  • ESXi / Proxmox 虚拟化

这篇文章基本可以帮你 一次解决同类问题

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

给TA打赏
共{{data.count}}人
人已打赏
Kubernetes云原生

CentOS 7下使用RKE2部署Rancher完整指南

2025-8-4 17:43:03

Kubernetes云原生

Kubernetes 1.34 + Cilium + kube-vip 高可用集群部署实战

2026-1-5 22:49:57

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索
本站支持IPv6访问