一次 Calico 升级埋下的”隐形雷”:GitLab 521 与 IPv6 回源故障排查全记录

 

故障时间:2026-08-10 ~ 08-13
影响范围:gitlab.maoyulong.clubblog.maoyulong.club 返回 521,持续约 3 天
根因:Calico v3.19.1 → v3.25.2 升级后,CNI 默认不再为 Pod 分配 IPv6,导致 DCDN 的 IPv6 回源链路断裂
修复方式:修正 CNI conflist + configMap 模板 + 清理 IPAM 脏数据

ps: 本次事故复盘由ai辅助编写


一、问题现象:GitLab 突然开始返回 5xx

8 月 10 号左右,访问 gitlab.maoyulong.clubblog.maoyulong.club 时,浏览器开始报错——GitLab 返回 521,博客也打不开。

521 是 Cloudflare 系(这里实际是腾讯 EdgeOne/DCDN)的特定错误码,含义是:源站主动拒绝了 CDN 节点的连接。也就是说,问题不在应用层(GitLab 本身没崩),而在 CDN 节点到源站之间的回源链路上。

我家的服务是通过 DCDN 回源到 homelab 内网的,回源地址配的是 mastera 节点的 IPv6 地址240e:...::270)加 NodePort。所以第一反应就是:IPv6 回源链路断了


二、排查过程:层层剥洋葱

2.1 第一层:确认 521 是 IPv6 回源问题

用 curl 直接测两个域名:

curl -6 https://gitlab.maoyulong.club/   # IPv6 访问 → 失败
curl -4 https://gitlab.maoyulong.club/   # IPv4 访问 → 正常

结果很明确:IPv4 全链路正常,IPv6 彻底不通。问题锁定在集群的 IPv6 数据面。

2.2 第二层:Pod 根本没有 IPv6 地址

进一步查 GitLab Pod 的网络信息,发现一个奇怪的现象:

GitLab Pod podIPs: 100.91.240.49/32   ← 只有 IPv4,没有 IPv6

而它的 Service 却是 RequireDualStack(要求同时有 IPv4 + IPv6),并且分配了 IPv6 ClusterIP fd00::424d。也就是说:

Service 要求双栈,但后端 Pod 只有 IPv4,IPv6 ClusterIP 的 DNAT 根本找不到目标。

2.3 第三层:IPv6 数据面规则完全缺失

继续往下查,发现更底层的证据:

检查项 结果
ip6tables-nft 中的 cali-* 0 个(IPv4 有 220 个)
IPv6 ClusterIP fd00::424d 可达性 code=000,不通
IPv6 NodePort 24725 code=000,不通
Calico IPv6 IPAM block 有 3 个 fc00::/122,但 affinity 为 <none>

Calico 的 IPv6 转发链(cali-*)完全没有生成,这说明 Calico 数据面根本没有为 IPv6 工作。

2.4 第四层:CNI conflist 缺了关键字段

顺着 Calico 的 CNI 日志往下挖,找到了决定性证据:

CNI 日志:IPAM request count IPv4=1 IPv6=0

Pod 创建时,CNI 只向 IPAM 申请了 IPv4,根本没申请 IPv6。而最后一条 IPv6=1 的记录停在 2026-06-16

这个时间点太关键了——正是我把 Calico 从 v3.19.1 升级到 v3.25.2 的那天

对比升级前后的 CNI 配置,根因浮出水面:

// v3.19.1 时期(正常):ipam 块默认就会分配 IPv6
{ "ipam": { "type": "calico-ipam", "ipv4_pools": [...], "ipv6_pools": [...] } }

// v3.25.2 之后(异常):assign_ipv6 默认变 false,Pod 只拿 IPv4
{ "ipam": { "type": "calico-ipam" } }

Calico v3.25 中,assign_ipv6 的默认值是 false,而 v3.19 的行为不同。升级时只替换了 manifest,没有显式加上 assign_ipv6: "true",于是从升级那一刻起,所有新建 Pod 都不再获得 IPv6。

2.5 第五层:真正的持久根因在 configMap

一开始我以为改一下各节点上的 /etc/cni/net.d/10-calico.conflist 就够了。但深挖 install-cni 的机制后发现:

Calico 的 install-cni initContainer 每次启动时,都会用 configMap calico-config 里的 cni_network_config 模板重新生成 conflist,覆盖节点上的文件。

所以即使手动改了节点文件,只要 calico-node 重启,改动就会被模板覆盖回退。真正的持久根因在 configMap 的模板里——它的 ipam 块同样只有 "type": "calico-ipam",缺 assign_ipv6

此外还发现一个连带问题:masterb、masterc 两个节点上的 conflist,nodename 字段都错写成了 mastera(历史手动覆盖污染)。这导致这两个节点上的 Pod 会跑去 mastera 的 IPAMBlock 里抢 IP,埋下了后面 IPAM 脏数据的隐患。


三、根因时间线还原

把整件事串起来,其实是一条清晰的因果链:

2026-06-16  Calico v3.19.1 → v3.25.2 升级
              ↓  升级后 assign_ipv6 默认 false,configMap 模板也没写死
           从此所有新建 Pod 都不再获得 IPv6
              ↓  但老 Pod 还带着 IPv6,短期内服务"看起来正常"
2026-08-10  之前某次 GitLab/WordPress Pod 重建,新 Pod 变成纯 IPv4
              ↓  DCDN 源站 IPv6 回源彻底断链
           gitlab.maoyulong.club / blog.maoyulong.club 返回 521

一句话总结:两个月前的一次 CNI 升级,静默地关掉了 IPv6 分配,直到业务 Pod 重建才把雷引爆。


四、修复过程

4.1 修复 CNI conflist(三个节点)

在 conflist 的 ipam 块补上两个字段,并顺带修正 masterb/masterc 的 nodename

{
  "ipam": {
    "type": "calico-ipam",
    "assign_ipv4": "true",
    "assign_ipv6": "true",
    "ipv4_pools": ["default-ipv4-ippool"],
    "ipv6_pools": ["default-ipv6-ippool"]
  }
}

4.2 持久化修复 configMap 模板

这一步才是治本。修改 kube-system/calico-configcni_network_config,让 install-cni 未来生成 conflist 时天然带上 assign_ipv6,防止下次 calico-node 重启时回退。

4.3 重建关键 Pod

修改完成后,滚动重建 GitLab 和 WordPress,让它们重新获取双栈 IP:

gitlab-ee:  100.91.240.15 + fc00::8f6a:a5eb:fa40:23c5:467a   ✅ 双栈
wordpress:  100.91.240.38 + fc00::...:4650                     ✅ 双栈

4.4 验证结果

验证项 结果
blog.maoyulong.club(IPv6) HTTP 200
gitlab.maoyulong.club(IPv6) HTTP 302 ✅(正常跳转登录页)
masterb/masterc 测试 Pod 均拿到双栈 IP ✅

两个域名的 IPv6 回源链路彻底恢复。


五、清理 IPAM 脏数据(连带问题)

修复回源后,发现 nodename 错误的遗留影响还没清干净:7 个跑在 masterb 上的 Pod,历史性地从 mastera 的 IPAMBlock(100.91.240.0/26)里分配了 IP,形成跨节点占用。

其中 .1 / .8 / .50 / .62 在 IPAM 里已经显示为 free(会再次被分配出去),这正是之前 WordPress 反复报 route already exists tunl0 冲突的元凶。

处理方式:逐个重建这 7 个 Pod,让它们回到各自节点的 IPAMBlock:

Pod 原错误 IP(mastera block) 新正确 IP
minio 100.91.240.50 100.91.240.49 + IPv6
coredns 100.91.240.1 100.91.240.11 + IPv6
mindustry 100.91.240.66 100.127.90.251 + IPv6
jellyfin 100.91.240.67 100.127.90.245 + IPv6
share-manager(harbor scandata) 100.91.240.8 100.127.90.224 + IPv6
share-manager(minio 4Ti) 100.91.240.65 100.127.90.250 + IPv6
instance-manager(masterb) 100.91.240.62 100.127.90.246 + IPv6

清理后重新跑了一遍 IPAM 一致性分析:

  • IPAM 泄漏(已分配但无 Pod):0
  • 反向泄漏(Pod 有 IP 但 IPAM 无记录):0(仅剩 hostNetwork Pod,属正常)

一个小插曲

重建 instance-manager(masterb 的卷管理组件)时,masterb 上所有 Longhorn 卷短暂中断,连锁导致 harbor-database-0 重启 → harbor-core 短暂连不上库 → harbor-jobservice 崩溃了 3 次。这是删除卷管理组件的预期副作用,几分钟内全部自愈,无需干预。


六、经验与反思

6.1 升级 CNI 一定要核对”默认值变化”

这次最大的坑是 Calico v3.25 把 assign_ipv6 的默认值改成了 false。升级 manifest 时只关注了显式配置项(CIDR、IPIP、镜像源等),漏掉了这种”默认行为变化”。教训是:大版本升级时,除了 diff 自己的配置,还要去翻 changelog 看默认值有没有变。

6.2 别被”表面正常”骗了

故障其实是两个月前就埋下的,但因为老 Pod 还带着 IPv6,服务一直”看起来正常”。直到某次 Pod 自然重建,新 Pod 变成纯 IPv4,才把雷引爆。“配置变更后短期正常”不等于”没问题”,尤其是 CNI 这种影响所有新建资源的底层组件。

6.3 改节点文件是治标,改 configMap 才是治本

一开始改 conflist 只是让当前 Pod 恢复了。如果不追到 install-cni 从 configMap 生成配置这一层,下次 calico-node 一重启,改动就会被静默覆盖回退,故障必然复发。排查时多问一句”这个文件是谁生成的、什么时候会重新生成”,能少走很多弯路。

6.4 两个看似独立的问题,可能是同一个根因

这次事件里,IPv6 回源失败、WordPress 的 route already exists tunl0 冲突,表面看是两个问题,实际都指向同一个根因链:Calico 升级 + nodename 污染。排查时把零散症状串起来看,往往能找到更深层的共同源头。

 

 

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇