用 KylinOS V10 的 UBI 重新编译 calico-node 即可修复,不用降版本、不用换 CNI
太长不看
现象:Kubespray 部署完 K8s 1.32,calico-node pod 全部 CrashLoopBackOff,dmesg 报 start_runit segfault in libc-2.28.so。
根因:Calico 官方镜像使用 Red Hat UBI 基础镜像,其 glibc 的 CPU 特性探测(hwcaps)与海光 C86(Zen1 架构)微码不兼容,启动时选择错误代码路径导致 SIGSEGV。
修法(三选一):
| 方案 | 操作 |
|---|---|
| 最快 | 换 Flannel,镜像已有今天可通 |
| 保留 Calico | 用 bclinux-for-hygon/kylinv10sp3-ubi 替换 Red Hat UBI,重新编译 calico-node |
| 不推荐 | 降 K8s 到 1.25 + Calico v3.23.5,需重建集群 |
环境
| 项 | 值 |
|---|---|
| CPU | 海光 C86(Hygon C86-4G / 7285 / 7265,Zen1 架构) |
| OS | 银河麒麟 V10(内核 4.19.90,glibc 2.28) |
| 部署工具 | Kubespray v2.28.1 |
| K8s | v1.32.8(9 节点:3 master + 6 worker) |
| CNI | Calico(默认 v3.29.5 → 降级 v3.28.2 → 全部崩溃) |
排障经过
整个过程走了 4 次弯路,每条弯路都有它的"为什么",记录在这里供大家参考。
❌ 弯路 1:降 Calico 版本
Kubespray 默认装 v3.29.5,第一个念头——"内核 4.19 太老了,降版本吧"。
查官方文档:Calico v3.29+ 最低内核从 3.10 提到了 5.10。那降到 v3.28.2(要求 ≥3.10)不就行了?
结果:同样 segfault。dmesg 时间戳显示 11:30,证明是新 pod 触发的,不是旧残留。
教训:当降级后同样位置崩溃,说明问题不在版本号这个维度。
❌ 弯路 2:GOAMD64=v2 理论
社区有说法:Calico v3.26+ 用 GOAMD64=v2 编译,老 CPU 不支持就崩。
花了半小时拉三个版本的 Calico 源码全量搜索:
grep -r "GOAMD64" calico/Makefile calico/node/Makefile calico/felix/Makefile
# 结果:啥都没有。所有版本都是 Go 默认 v1
又去查海光 CPU flags:
aes, avx, avx2, bmi2, sse4.1, sse4.2, popcnt, fma, rdrand
x86-64-v2 要求的每一条指令都能在 /proc/cpuinfo 里找到。
教训:一个看起来完美的理论,如果和源代码对不上,那就是错的。下沉到源码之前不要下结论。
⚠️ 弯路 3:RDRAND 微码 bug
海光 C86 基于 AMD Zen1 授权演进,与 Ryzen 3000 系列同源。AMD Ryzen 3000 有个著名的 RDRAND 微码 bug(ArsTechnica 有篇经典文章分析过),导致 Go 调用 crypto/rand 时 CRNGT failed。CSDN 上也有文章说"升级 BIOS 就好了"。
查了一下——这个 bug 确实存在,但本环境的表现不同:RDRAND 指令可正常调用,crash 发生在 glibc 启动初始化阶段,不是 Go 运行时。
教训:"症状相似"不等于"根因相同"。关联性排查不能替代因果性验证。
✅ 最终定位:UBI glibc hwcaps
回到 Dockerfile 层面。Calico 在 v3.23.0 版本做了一个关键变更:
# v3.22.0 及更早:最终镜像从 CentOS 8 构建
FROM scratch
COPY --from=centos / /
# v3.23.0 开始:从 Red Hat UBI 构建
FROM ${UBI_IMAGE} as ubi
FROM scratch
COPY --from=ubi / /
顺手查了下容器里的 glibc:
docker run --rm --entrypoint /bin/sh calico/node:v3.28.2 -c \
"cat /etc/redhat-release; strings /lib64/libc.so.6 | grep 'GNU C Library'"
# Red Hat Enterprise Linux release 8.10 (Ootpa)
# glibc 2.28-98
宿主机也是 glibc 2.28。版本号一样,但 Red Hat 在 UBI 8 的生命周期内向 glibc 2.28 持续向后移植了 hwcaps(硬件能力探测) 优化——启动时探测 CPU 特性,自动选择 AVX2 优化的 memcpy/strcmp 等路径。海光 C86 在探测中返回的信息让 glibc 选了一条实际走不通的路。
用 KylinOS V10 自己的 glibc(针对海光调优)重新编译 calico-node,正常运行。
这根链条在 GitHub Issue #11937 和 #10479 里有完整记录,Calico 官方回复是"不支持的芯片组,我们没有环境验证"。
解决方案
场景判断
| 你的情况 | 建议 |
|---|---|
| 今天要让集群通 | Flannel,10 分钟 |
| K8s 1.32 + Calico 必须 | 自编译 calico-node,用 KylinV10 UBI |
| 有老集群在用低版本 Calico | 别动,继续用 |
方案一:Flannel(最快)
# Kubespray 配置
kube_network_plugin: flannel
flannel_backend_type: "vxlan"
# 部署
ansible-playbook -i inventory/hosts.yml cluster.yml --tags=flannel
Flannel 在信创环境里被大量验证过,稳定。
方案二:自编译 Calico(保留全部能力)
用海光专用 UBI 镜像替换 Red Hat UBI:
docker pull bclinux-for-hygon/kylinv10sp3-ubi:latest
make build-node-image ARCH=amd64 \
UBI_IMAGE=bclinux-for-hygon/kylinv10sp3-ubi:latest
已有多位社区用户(wz0311、redklouds、flyingtang)验证可行。
复盘:排错的教训
- 先查基础镜像的 glibc 版本和编译配置,比从通用运维路径开始排查快得多
- 不要相信单一的版本门槛解释——"v3.29+ 需要内核 5.10" 这个说法没错,但它不是根因,只是混淆因素
- 源码比社区说法可靠——"GOAMD64=v2" 传得很广,但 Calico 源码里就是没有
- 国内信创环境 + 国际基础软件 = 兼容性问题的大头。Calico 用 Red Hat UBI,CentOS 停更后漂到 CentOS Stream/AlmaLinux,每次基础镜像的变更都可能引入 CPU 级别的行为差异
参考
- GitHub Issue #11937 — 最直接的 C86 修复讨论
- GitHub Issue #10479 — Hygon 7285 segfault
- GitHub Issue #9562 — 同 dmesg 报错
- GitHub Issue #11352 — CPU does not support x86-64-v2
- CSDN 文章 — 海光C86 + Calico BIOS 升级修复
- ArsTechnica — AMD Zen1 RDRAND bug 深度分析
- docker-library#16488 — 海光专用 KylinV10 UBI 镜像 PR