AIGC标识 节点 IPv6 前缀漂移:一次「只是告警」背后的跨节点 Pod 网络静默失效

前言

之前国庆 homelab 升级为 ipv4+ipv6 双栈,近期又对 4个节点进行了更新和重启。结果发现:一条 kubelet 告警,每 10 秒重复一次。

failed to validate secondaryNodeIP

第一反应通常是配置没对齐,重启一下 k3s 就完事。

实际取证的结果比这个严重得多:跨节点 IPv6 pod 通信已经断了,只是业务侧没报出来。

告警和真实影响面之间,差了整整一层。

现象

时间点:2026-10-29。

同一轮重启里,ISP 重新委派了 IPv6 前缀:

  • 旧:240e:xxxxxxxx:ae43::/64
  • 新:240e:xxxxxxxx:6440::/64

集群是 4 节点 k3s 双栈,Ubuntu 26.04,内核 7.0.0-42。

/etc/rancher/k3s/config.yaml 的 node-ip 里写的是旧 GUA 的字面量。

新前缀下发后,那个地址不再存在于任何接口上。

kubelet 每 10 秒做一次 secondaryNodeIP 校验,失败,相关字段就不写入 Node 状态。

Node 对象的 IPv6 InternalIP 停在旧地址上。

根因

节点 IPv6 身份绑定在了 ISP 下发的 GUA 字面量上。

GUA 由运营商给,前缀什么时候轮换由 ISP 说了算。

家庭宽带的场景下,前缀轮换是常态,不是异常。

把节点身份写死在一个运营商随时会改的地址上,这个设计本身就有问题。

取证:告警之外的真实影响

告警只是入口,真正要看的是网络 Cilium 那边。

Cilium agent 启动时读取节点地址,写入 CiliumNode 对象。

旧地址写进去之后,auto-direct-node-routes 生成不出跨节点 IPv6 直连路由。

修复前 ip -6 route 里只有本机 pod 的 /64,没有任何一条跨节点路由。

跨节点 IPv6 pod 通信实际中断。

集群上 pod(semaphore、mysql)的 IPv6 EndpointSlice 指向一个不存在的地址。

📝Notes:

这类失效不触发业务告警。pod 本身是 Running 的,断的是跨节点路径。

方案取舍

四个方向摆出来对比:

方案 做法 代价
A 节点 IPv6 改用站点 ULA,GUA 只留接口做出站 / NAT 一次性改动,之后不随 ISP 漂移
B systemd ExecStartPre 每次重算 GUA 每次漂移都要重启,Node / Endpoint 全变
C 向 ISP 申请静态前缀 最干净,家庭宽带通常拿不到
D 退回单栈 IPv4 与既有双栈规格冲突,要重建 pod

最终方案选 A。

我的判断是:节点身份只需要在站点内稳定可达,不需要绑定运营商前缀。

GUA 保留在接口上,出站和 NAT 照常用。

Pod 的 /56 和 Service 的 /112 网段不变,业务侧无感知。

选定的 ULA 是 fdb5:xxxxxxxx:1::/64,主机位 ::11 到 ::14:

  • ::11 → n100-jumper-0
  • ::12 → n100-jumper-1
  • ::13 → n100-jumper-2
  • ::14 → n100-cheshi-0

实现:几处改动

  • 修改 /etc/netplan/60-homelab-ula.yaml 写入固定的规划好的 ULA IPv6 地址并 netplan apply

YAML 内容如下:

# 由 Ansible 管理,手工改动会在下次运行时被覆盖。
#
# 稳定节点 IPv6:站点 ULA(RFC 4193),与 ISP 下发的 GUA 前缀解耦。
# 只写 ULA 这一条:netplan 按文件名顺序合并多个文件,addresses 是追加语义,
# 因此 cloud-init 的 50-cloud-init.yaml 里的 IPv4 地址与 dhcp6 不受影响。
# 依据与实测见 specs/012-stable-node-ula-ipv6/plan.md。
network:
  version: 2
  ethernets:
    enp2s0:
      addresses:
        - fdb5:xxxxxxxx:1::14/64

IPv4 地址和默认路由都保留,不需要重写 cloud-init 生成的 50-cloud-init.yaml。该文件 YAML 内容示例如下:

# This file is generated from information provided by the datasource.  Changes
# to it will not persist across an instance reboot.  To disable cloud-init's
# network configuration capabilities, write a file
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
# network: {config: disabled}
network:
    ethernets:
        enp2s0:
            addresses:
            - 192.168.3.154/24
            dhcp4: false
            dhcp6: true
            gateway4: 192.168.3.1
            nameservers:
                addresses:
                - 223.5.5.5
            wakeonlan: true
    version: 2

📝Notes:

netplan 多文件按文件名排序合并,60-homelab-ula.yaml 排在 50-cloud-init.yaml 之后。

生效路径:只重启 k3s 不够

只重启 k3s, 不重启 cilium agent: 这是整个处置里最容易漏的一步。

改完 config.yaml 重启 k3s,Node 对象的 InternalIP 会更新。但 cilium-dbg node list 里还是旧地址。跨节点路由还是缺的。Cilium agent 只在启动时读取 Node 地址,写进 CiliumNode。Node 更新了它不会自己重新读。必须逐台重启 cilium agent。

重启后 cilium-dbg node list 的 IPv6 列变成各自 ULA,ip -6 route 里出现 via <对方 ULA> 的直连路由。

📝Notes:

逐台重启,别一把全停。agent 停掉期间该节点的 pod 网络会短暂中断。

验证

线上验收覆盖 6 个维度:

  1. 4 台 Node 的 IPv6 InternalIP 均为规划 ULA,状态 Ready
  2. secondaryNodeIP 计数为 0
  3. cilium-dbg node list 的 IPv6 列是各自 ULA
  4. 出现 3 条跨节点直连路由
  5. 跨节点 pod IPv6 连通,三个业务命名空间 Running 且 HTTP 可用
  6. TestNodeAddressing PASS

路由器侧也做了只读核查:LAN 是 br-lan 桥,没有 ULA 配置,没有该 /64 路由与邻居表项。节点间 IPv6 走二层转发,路由器不需要改动。

总结

本次的经验教训:

  • 节点 IPv6 身份不该绑定 ISP 下发的 GUA,站点内可达就够了
  • 告警的严重程度和实际影响面不一定对得上,要自己取证
  • Cilium agent 只在启动时读 Node 地址,Node 更新不会自动传导过去
  • netplan 多文件对 addresses 是追加语义,独立文件可以只写 ULA

千磨万击还坚劲,任尔东西南北风。

IPv6 GUA 地址前缀会漂,架构不该跟着漂。

以上。

📚️参考文档

posted @ 2026-11-04 21:24  东风微鸣  阅读(9)  评论(0)    收藏  举报