TT Lab
博客

한국어 English 日本語 中文

开始
博客

博客

公网连接中断的那一天:MetalLB L2 广播四处飘移,最初的对策是让所有人都不要广播

状态:已解决 (2026-10-14),记录基准。 当天的数字(广播节点变动次数、speaker 重启次数)来自工作备忘录,当前的配置与节点标签通过 2026-10-16 的集群 读取进行确认。由于未能查看路由器日志,关于路由器内部发生的事情记录为 推测。 编写规则:仅依据记录中的事实,猜测标记为「推测」,未测量的内容标记为「未实测」。

目录

一句话总结

影响 外部无法访问网站(80 和 443 端口在 No route to host)。最初的对策实施后,短暂出现了 连内网也 无法访问的情况
原因 通过 ARP 响应网关 IP 的节点(广播节点)不断变化,其中甚至包含路由器无法触及的节点(推测)
最初对策的失误 将广播节点固定为控制平面三节点组 → 三个节点都带有 exclude-from-external-load-balancers 标签,导致 无人进行广播
修复方案 固定到两个有线连接的工作节点。在外部三个地点确认返回 200
防止复发 选择广播节点时,优先检查排除标签。制定排查顺序。向 MetalLB 文档提交了指出此陷阱的 PR

1. 架构:路由器 → 网关 IP → 「拥有」该 IP 的单个节点

发往网站的请求会经过家用路由器的端口转发。路由器将 80 和 443 端口转发到集群的网关服务 IP,该 IP 是 MetalLB 通过 L2 模式分配的 LoadBalancer 地址。

在 L2 模式下,该 IP 并不绑定到任何一台机器的网卡上。当路由器通过 ARP 询问「这个 IP 的 MAC 地址是什么?」时,运行 MetalLB speaker 的 其中一个节点会回答「是我」。MetalLB 文档对确定该节点的方法进行了如下说明(基于文档):

공유기는 게이트웨이 IP 의 주인을 ARP 로 묻는다 바깥 사용자 공유기 포트 포워딩 80·443 → 게이트웨이 IP(MetalLB L2) 그 IP 에 답하는 노드(광고 노드)는 한 번에 하나뿐이다. ① 그날: 광고가 노드 사이를 떠돌았다 CP 1 CP 2 CP 3 워커 A 워커 B 그 밖 크래시 루프 광고 노드가 1,275번 바뀌었고, 노트북 노드에도 앉았다 ② 첫 처방: 컨트롤 플레인 셋에 고정 — 틀렸다 CP 1 제외 라벨 CP 2 제외 라벨 CP 3 제외 라벨 워커 A 워커 B 그 밖 고른 노드가 모두 광고를 거절 → 아무도 답하지 않음 ③ 지금: 유선으로 붙은 워커 둘에 고정 CP 1 CP 2 CP 3 워커 A 광고 중 워커 B 대기 그 밖 하나가 죽으면 남은 하나가 넘겨받는다(memberlist)
图示:广播节点四处飘移时、最初对策、当前状态

2. 症状:仅外部无法访问

「内部可以访问」这一信号最让人迷惑。网关以及它后面的 Web Pod 都很健康。中断的地方在前面,即 路由器与广播节点之间。

3. 排查:广播节点变动了上千次

在网关服务的事件中,每当 speaker 开始广播时就会留下 announcing from node "…" (这也是 MetalLB 故障排查文档推荐的确认方法)。当天清点这些事件时,发现 广播节点变动了 1,275 次(工作备忘录)。

No route to host 出现这一现象的路径 推测如下。路由器通过 ARP 询问要转发的目标 IP 的 MAC 地址,如果那一瞬间的广播节点是路由器无法触及的地方(如无线网络或虚拟网络背后的笔记本节点等)或者正在完成接管,就不会收到回复。如果没有回复,路由器会返回「无法到达主机」的 ICMP,客户端将其显示为 No route to host 。未能查看路由器的 ARP 表和日志(未证实)。

4. 最初的对策是错误的

如果广播节点四处飘移是问题所在,那么将其绑定到最稳固的节点即可。最初想到的对象是 控制平面三节点组。不仅重启概率低,工作负载也很少。我们将 L2Advertisement 的 nodeSelectors 缩小到了这三个节点。

结果反而更糟了。 连内网中也 无法访问网站了。这是因为这次没有任何节点在进行广播。

原因是单个节点标签。

即使标签值为空,只要 存在键 就会被排除。2026-10-16 读取时发现,所有控制平面节点都无值地挂载了此标签。

# 광고 노드를 고르기 전에 이것부터
kubectl get nodes -L node.kubernetes.io/exclude-from-external-load-balancers

nodeSelectors 如果选择的节点都带有排除标签,服务就会在保留外部 IP 的同时在任何地方都不予响应。不会显眼地留下错误或事件。为了将这一点记录在文档中,我向 MetalLB 提交了 PR — metallb/metallb#3156(截至 2026-10-16 处于开启状态)。

5. 修复方案:绑定两个有线工作节点

排除控制平面后,剩下的标准是「与路由器处于同一有线局域网、不经常重启的工作节点」。我们将其固定到两台这样的迷你 PC 上。

apiVersion: metallb.io/v1beta1
kind: L2Advertisement
spec:
  ipAddressPools: [<게이트웨이 풀>]
  nodeSelectors:
    - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values: [<워커 A>, <워커 B>]

设置两台的原因是为了确保当其中一台宕机时另一台可以接管。如果绑定到一台,每次重启该节点时网站都会中断。重启一个工作节点后,在外部三个地点确认了 HTTP 200。

6. 然而内网中依然不可见:发卡(Hairpin)

修复后,在家里通过公网地址(域名)依然无法打开。这是另一个问题。本路由器不支持从内网流向公网 IP、再折返进入内网的路径(发卡 NAT,NAT 回环)。因此,无法在家里告知外部「无法访问」的状态,反过来,家里的「可以访问」也无法保证外部的情况。

确认分为两个方向:

# 안: 공인 DNS 를 건너뛰고 게이트웨이 IP 로 바로 — 이름(SNI·Host)은 그대로 둔다
curl -sS -o /dev/null -w '%{http_code}\n' --resolve <도메인>:443:<게이트웨이 IP> https://<도메인>/

# 밖: 여러 나라에서 HTTP 요청을 보내 주는 외부 점검 서비스(check-host.net 등)로

7. 以后听到「无法连接」时

  1. 直接通过网关 IP(--resolve)访问。如果这里失败,则是集群内部的问题。
  2. 外部检查服务。如果 1 成功但 2 失败,则问题出在路由器和广播节点之间。
  3. 查看网关服务事件中的当前广播节点和变动次数(kubectl get events --field-selector involvedObject.name=<서비스>).
  4. 查看 speaker 日志中的 memberlist 加入失败次数和重启次数。
  5. 如果需要更改广播节点,请 优先 检查排除标签。

8. 遗留问题与局限性

Titi 的碎碎念。 「内部可以访问啊?」是最让人害怕的话。内部和外部的路径不同,一边的绿灯并不能说明另一边的情况。而且选择节点时,比起名称更要看标签!一个静静的标签可能正在说「我就先告退了」。

参考

所有资料均于 2026-10-16 查阅。

登录后即可点赞

评论

还没有评论。

登录后即可发表评论