一次 Cilium L2 Announcement 导致 LoadBalancer VIP 无法访问的排查记录
文章目录环境现象代表没有任何节点回应 ARP过程第一步确认 VIP 是否真的分配成功第二步确认 L2 Announcement Policy第三步查看 Lease第四步确认 Cilium Agent 是否成功取得 Lease第五步检查 L2 Announcement 使用的网络接口第六步建立专属的 L2 Announcement Policy 进行验证第七步重新验证 ARP 回应结论最近在 Kubernetes 集群中部署 Istio Gateway并使用 Cilium 1.17 的 L2 Announcement 为 LoadBalancer Service 提供 VIP 时遇到了一个非常有意思的问题。Service 已经成功获得了 External IP但是同网段主机始终无法访问整个排查过程涉及了 Kubernetes、Cilium、ARP、Linux Neighbor Cache 等多个层面记录下来希望能帮助遇到类似问题的人。环境Kubernetes v1.32.1Cilium v1.17.0使用 Cilium LB IPAM使用 Cilium L2 AnnouncementVMware 虚拟化环境Istio Gateway ServiceServicetype: LoadBalancer EXTERNAL-IP:172.20.107.230现象开发人员反馈部署在 Kubernetes 集群上的系统无法正常访问。为了确认网络连通性我首先在客户端执行了连线测试powershell tnc whs.lab.com-port443输出结果如下RemoteAddress:172.20.107.230 RemotePort:443InterfaceAlias:WLAN SourceAddress:172.20.106.143 TcpTestSucceeded:False从测试结果可以确认DNS 解析正常whs.lab.com 已正确解析到 172.20.107.230。TCP 443 连接失败说明客户端无法与 Kubernetes Service 建立连接。确认 Service 是否已经成功取得 LoadBalancer IPkubectl-ncn-lab-whs get svc cn-lab-whs-gateway-istio输出如下NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE cn-lab-gateway-istio LoadBalancer 10.248.156.4 172.20.107.230 15021:31177/TCP,80:31897/TCP,443:30139/TCP 42m可以确认Service 类型为LoadBalancerCilium 已经成功分配了 VIP172.20.107.230因此可以先排除LB IPAM 未分配 IP的问题。查看 ARPipneigh show|grep230得到172.20.107.230 FAILED代表没有任何节点回应 ARP过程第一步确认 VIP 是否真的分配成功首先确认 Kubernetes Service 是否已经成功取得 LoadBalancer IP。执行以下命令kubectl-ncn-lab-whs get svc cn-lab-whs-gateway-istio输出如下NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)AGE cn-lab-whs-gateway-istio LoadBalancer10.248.156.4172.20.107.23015从输出可以确认Service 类型为 LoadBalancer。Cilium 已成功为该 Service 分配 External IP 172.20.107.230。Service 已建立 ClusterIP且对外提供 80、443 及 15021 等埠。这表示 LoadBalancer IPAM 已正常运作VIP 已成功分配因此可以先排除 IP Pool 配置错误或 IP 分配失败的可能性。第二步确认 L2 Announcement Policy查看 Policykubectl get l2announcement l2announce-argo-oyaml输出节选spec: externalIPs:trueloadBalancerIPs:trueinterfaces: - ens160 nodeSelector: matchExpressions: - key: node-role.kubernetes.io/control-plane operator: DoesNotExist表示Worker 才会宣告 VIP使用 ens160Policy 看起来没有问题。第三步查看 LeaseCilium L2 Announcement 使用 Kubernetes Lease 机制进行 Leader Election同一个 LoadBalancer IP 在任一时间只会由一个节点负责宣告ARP/NDP。因此真正决定哪个 Node 宣告 VIP 的不是 Service而是 Lease。查看kubectl-nkube-system get lease|grepcn-lab-whs输出入下NAME HOLDER AGE cilium-l2announce-cn-lab-whs-cn-lab-whs-gateway-istio cnk8sw01-d 53m从输出可以确认已成功建立 cilium-l2announce-cn-lab-whs-cn-lab-whs-gateway-istio Lease。Lease Holder 为 cnk8sw01-d。这表示 Cilium 已完成 Leader Election后续应由 cnk8sw01-d 负责回应 172.20.107.230 的 ARP 请求第四步确认 Cilium Agent 是否成功取得 Lease虽然 Kubernetes 已完成 Leader Election但仍需确认负责该节点的 Cilium Agent 是否真正取得 Lease并开始负责 L2 Announcement。首先查看运行在cnk8sw01-d节点上的 Cilium Podkubectl-nkube-system get pods-lk8s-appcilium-owide输出如下节录NAME READY STATUS RESTARTS AGE IP NODE cilium-7zs7g 1/1 Running 0 93m 172.20.107.56 cnk8sw01-d接着查看该 Pod 的日志kubectl-nkube-system logs cilium-7zs7g|grepacquired lease输出如下time2026-07-03T08:53:44.406990705Z levelinfo msgsuccessfully acquired lease kube-system/cilium-l2announce-cn-lab-whs-cn-lab-whs-gateway-istio subsysklog从日志可以确认Cilium Agent 已成功取得cn-lab-whs-gateway-istio的 Lease并开始负责该 Service 对应 VIP 的 L2 Announcement。因此可以进一步确认Kubernetes Leader Election 正常。Cilium Agent 已成功接管该 VIP 的宣告工作。问题并非出在 Lease 竞争或 Cilium Agent 未取得 Leader 身份。第五步检查 L2 Announcement 使用的网络接口再次查看CiliumL2AnnouncementPolicy后发现L2 Announcement 被限制只能在ens160接口上进行宣告。相关配置如下kubectl get ciliuml2announcementpolicy policy-oyaml输出节录spec:externalIPs:trueloadBalancerIPs:trueinterfaces:-ens160nodeSelector:matchExpressions:-key:node-role.kubernetes.io/control-planeoperator:DoesNotExist由于第三步已经确认cnk8sw01-d为 Lease Holder因此接着检查该节点实际的网络接口ipa|grepens输出如下2: ens18: BROADCAST,MULTICAST,UP,LOWER_UP mtu 1500 qdisc mq state UP group default qlen 1000 inet 172.20.107.56/24 brd 172.20.107.255 scope global ens18另一方面控制节点 (cnk8sm01-d) 的网络接口则为ipa|grepens输出如下2: ens160: BROADCAST,MULTICAST,UP,LOWER_UP mtu 1500 qdisc mq state UP group default qlen 1000 inet 172.20.107.53/24 brd 172.20.107.255 scope global ens160可以发现不同节点使用了不同的网络接口名称节点网络接口cnk8sm01-dens160cnk8sw01-dens18由于CiliumL2AnnouncementPolicy指定只能使用ens160而实际负责宣告 VIP 的 Worker 节点只有ens18因此一度怀疑 Cilium 无法在正确的网络接口上发送 Gratuitous ARP导致 VIP 无法被同网段设备学习。这是排查过程中一个非常值得注意的线索也是后续重点验证的方向。第六步建立专属的 L2 Announcement Policy 进行验证前面发现 CiliumL2AnnouncementPolicy 中指定的网络接口与 Lease Holder 的实际接口名称不一致因此开始怀疑是否为 L2 Announcement Policy 所造成的问题。不过由于现有的 CiliumL2AnnouncementPolicy 已被丛集中多个 LoadBalancer Service 使用若直接修改既有 Policy可能影响其他正式运行中的服务。为了降低变更风险我决定建立一个新的 CiliumL2AnnouncementPolicy仅让 cn-lab-whs 的 LoadBalancer Service 套用以验证是否真的是 Policy 配置所导致的问题。建立新的 PolicyapiVersion: cilium.io/v2alpha1 kind: CiliumL2AnnouncementPolicy metadata: name: cn-lab-whs-l2announcement spec: serviceSelector: matchExpressions: - key: io.kubernetes.service.namespace operator: In values: - cn-lab-whs nodeSelector: matchExpressions: - key: node-role.kubernetes.io/control-plane operator: DoesNotExist interfaces: - ens18 - ens160 externalIPs:trueloadBalancerIPs:true套用设定kubectl apply-fcn-lab-whs-l2announcement.yaml输出如下ciliuml2announcementpolicy.cilium.io/cn-lab-whs-l2announcement created建立完成后由于新的 Policy 仅匹配 cn-lab-whs Namespace 中的 Service因此不会影响其他 LoadBalancer Service可安全地验证此次的假设。接下来重新测试 VIP 的 ARP 响应情况确认新的 Policy 是否已生效。第七步重新验证 ARP 回应套用新的 CiliumL2AnnouncementPolicy 后再次查看 Neighbor Cacheipneigh show|grep172.20.107.230输出如下172.20.107.230 dev ens160 lladdr 00:0c:29:f1:89:68 STALE虽然 Neighbor Cache 已经存在 172.20.107.230 对应的 MAC 地址但 STALE 仅表示 Linux 保留了一笔邻居缓存记录无法确认这笔资料是否为目前重新学习而来或只是先前留下的缓存记录。因此为了重新验证 ARP 流程先删除该笔 Neighbor Cacheipneigh del172.20.107.230 dev ens160确认已成功删除ipneigh show|grep172.20.107.230若没有任何输出即表示 Neighbor Cache 已清除。接着主动发送 ARP Requestarping172.20.107.230-c4此时Lease Holder (cnk8sw01-d) 应重新回复 ARPLinux 也会重新建立 Neighbor Cache。再次确认ipneigh show|grep172.20.107.230以及arp-an|grep172.20.107.230输出如下(172.20.107.230)at 00:0c:29:f1:89:68[ether]on ens160最后再到 Lease Holder 查询网络接口的 MACiplinkshow ens18可以确认 00:0c:29:f1:89:68 正是 cnk8sw01-d 的 MAC 地址。至此可以证明172.20.107.230 的 ARP 响应确实来自 Lease Holder而非旧的 Neighbor Cache表示 Cilium L2 Announcement 已正常运作。结论这次故障排查一开始以为是 Cilium L2 Announcement 没有正常运作但随着一步步验证逐渐排除了多个可能性。整个排查过程依序确认了• LoadBalancer Service 已成功取得 VIP表示 LB IPAM 正常运作。• Kubernetes Lease 已成功建立Leader Election 正常完成。• Cilium Agent 成功取得 Lease代表 VIP 已指派由特定节点负责宣告。• 透过 tcpdump、arping、ip neigh 及 arp -an 验证后确认 Lease Holder 已能正常回应 ARPVIP 的 L2 Announcement 功能运作正常。其中最容易让人误判的是 ip neigh 显示的 STALE 状态。STALE 并不代表故障而是表示 Linux Neighbor Cache 中已存在对应的 MAC 地址。若要确认是否真的由目前的 Lease Holder 响应 ARP建议先删除 Neighbor Cache再利用 arping 主动触发 ARP Resolution重新建立 Neighbor Cache才能确认目前的 ARP 回应来源。此外在排查过程中并未直接修改既有的 CiliumL2AnnouncementPolicy而是建立一组仅作用于 cn-lab-whs Service 的新 Policy 进行验证。这种方式能有效降低对既有 LoadBalancer Service 的影响也是一种值得采用的变更策略。回头来看这次最大的收获并不是单纯解决了一个 VIP 无法存取的问题而是厘清了 Cilium L2 Announcement 的完整运作流程LoadBalancer Service │ ▼ LB IPAM 分配 VIP │ ▼ Leader ElectionLease │ ▼ Lease Holder 回应 ARP │ ▼ Client 建立 Neighbor Cache │ ▼ 流量进入 Kubernetes Node │ ▼ Cilium BPF 转发 │ ▼ Backend Pod当未来再次遇到 LoadBalancer VIP 无法联机的问题时只要依照 VIP 分配 → Lease → Cilium Agent → ARP → Neighbor Cache → Service → Endpoint 的顺序逐层验证就能快速缩小问题范围而不是一开始就怀疑整个 Cilium 或 Kubernetes 网络。