theme: default themeName: "默认主题" title: "K8s集群升级翻车实录:从v1.28到v1.31的血泪教训"
K8s集群升级翻车实录:从v1.28到v1.31的现血泪教训
今年初,我们做了一次K8s集群大版本升级,从v1.28直接跳到v1.31。升级方案评审时大家信心满满,觉得自己经验丰富不会有问题。结果升级过程翻了三次车,生产环境中断了4个小时。
事后复盘,三次翻车全是"以为不会出问题"的地方。今天把整个过程写出来,希望你能从我们的教训中学到东西,而不是自己踩一遍。
背景:为什么要跨3个大版本升级
我们的集群从2023年搭建后一直运行在v1.28上。2025年底,v1.28进入EOL(End of Life),不再接收安全补丁。安全审计要求必须升级。
本来应该按顺序升级:v1.28 → v1.29 → v1.30 → v1.31。但项目组觉得跨版本升级"效率更高",加上官方文档说支持n-2版本跳转,就决定一步到位。
这是第一个错误。 官方说的"支持"不等于"推荐",更不等于"没有风险"。翻车一:API版本废弃导致的灾难
升级开始很顺利,kubeadm upgrade一路绿灯。但在切换流量到新版本master后,一堆Pod开始报CrashLoopBackOff。
查看Pod事件:
kubectl describe pod my-app-7d8f9c6b5-x2k4j
错误信息:
Warning Failed 3s kubelet Error: failed to create containerd task: failed to create shim task:
APIVersion "k8s.io/api/core/v1" is not supported by this version
不对,v1是稳定版API,不可能不支持。仔细看日志,发现真正的错误是:
error: unable to recognize "deployment.yaml": no matches for kind "Deployment" in version "apps/v1beta1"
`apps/v1beta1`在v1.16就已经废弃了,但我们有几个老旧的Helm Chart还在用这个版本。之前一直没出问题是因为K8s做了兼容保留,v1.31彻底移除了。
排查过程:# 找出所有使用废弃API的资源
kubectl get --raw /metrics grep apiserver_requested_deprecated_apis
更直接的方法:审计集群中的废弃API
kubectl api-resources grep -i deprecated
扫描所有Helm Release
helm list -A awk '{print $1, $2}' while read ns release; do
helm get values "$release" -n "$ns" grep -i "apiVersion"
done
修复: 把所有Helm Chart更新到支持`apps/v1`的版本。问题是有些Chart已经没人维护了,只能自己fork修改。
# 修改前
apiVersion: apps/v1beta1 kind: Deployment
修改后
apiVersion: apps/v1 kind: Deployment
这个坑花了2小时排查和修复。
教训:升级前一定要用`pluto`或`kubent`扫描废弃API。# 用pluto检测废弃API
pluto detect-helm -o wide pluto detect-api-resources -o wide
用kubent检测
kubent
翻车二:CNI插件不兼容
API问题修好后,业务Pod能创建了,但互相之间无法通信。服务发现也挂了——CoreDNS无法解析任何域名。
# 测试Pod间通信
kubectl exec -it test-pod -- ping 10.244.1.5
PING 10.244.1.5: 56 data bytes
--- 10.244.1.5 ping statistics ---
5 packets transmitted, 0 received, 100% packet loss
测试DNS解析
kubectl exec -it test-pod -- nslookup kubernetes.default
;; connection timed out; no servers could be reached
问题出在CNI插件上。我们用的是Calico v3.25,而v3.25不支持K8s v1.31的新特性——结构性鉴权(Structural Authentication)变更导致CNI插件无法正确配置iptables规则。
排查过程:# 查看Calico Pod状态
kubectl get pods -n kube-system -l k8s-app=calico-node
查看Calico日志
kubectl logs -n kube-system -l k8s-app=calico-node --tail=50
关键错误
calico-node: "Failed to program iptables rules: exit status 2"
修复: 升级Calico到v3.28+。
# 升级Calico
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/calico.yaml
重启Calico Pod
kubectl rollout restart daemonset calico-node -n kube-system
这个坑花了1.5小时。
教训:升级K8s前,一定要确认所有组件(CNI、CSI、Ingress Controller、Cert Manager等)的版本兼容性矩阵。 我后来整理了一份检查清单:| 组件 | v1.28兼容版本 | v1.31兼容版本 | 需要升级 |
| Calico | v3.25+ | v3.28+ | ✅ |
| Nginx Ingress | v1.8+ | v1.10+ | ✅ |
| Cert Manager | v1.13+ | v1.14+ | ✅ |
| CSI Driver | v1.8+ | v1.9+ | ✅ |
| Metrics Server | v0.6+ | v0.7+ | ✅ |
翻车三:etcd数据迁移的坑
前两个问题解决后,业务基本恢复了。但监控发现etcd的响应延迟从5ms飙升到200ms,集群性能明显下降。
查看etcd状态:
# 查看etcd成员
kubectl get pods -n kube-system -l component=etcd
查看etcd性能
etcdctl endpoint status --cluster -w table
关键指标
DB SIZE: 8.2GB ← 这个异常大
RAFT APPLIED INDEX: 落后leader很多
8.2GB的etcd数据库——正常不应该超过2GB。原因是我们在v1.28上跑了很多Lease没有正确释放的CronJob,etcd里积攒了大量过期的key-value。
更严重的是,kubeadm upgrade过程中etcd做了数据格式转换,但没有自动执行压缩(compaction)。8GB的未压缩历史数据让etcd慢如蜗牛。
修复:# 1. 先备份(非常重要!)
ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-snapshot.db \ --endpoints=https://127.0.0.1:2379 \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt \ --key=/etc/kubernetes/pki/etcd/server.key
2. 获取当前版本号
REVISION=$(ETCDCTL_API=3 etcdctl endpoint status --write-out=json python3 -c "import sys,json; print(json.load(sys.stdin)[0]['Status']['header']['revision'])")
3. 执行压缩
ETCDCTL_API=3 etcdctl compact "$REVISION"
4. 执行碎片整理
ETCDCTL_API=3 etcdctl defrag --cluster
5. 验证
ETCDCTL_API=3 etcdctl endpoint status --cluster -w table
DB SIZE: 1.8GB ← 恢复正常
这个坑花了30分钟排查,但压缩操作本身很快。
教训:升级前先清理etcd。 清理废弃的Lease和过期的key,再执行compaction+defrag,然后才能升级。升级后的正确姿势
经过这次教训,我总结了一套"零翻车"升级流程:
升级前(至少提前2周)
1. 兼容性扫描:
# 废弃API检测
pluto detect-helm -o wide pluto detect-api-resources -o wide kubent
组件版本兼容性检查
kubeadm config images list --kubernetes-version=v1.31.0
2. etcd清理:
# 清理过期Lease
ETCDCTL_API=3 etcdctl lease list
压缩和碎片整理(见上文)
3. 在测试环境完整演练一遍升级流程。
4. 准备回滚方案。 用Velero备份整个集群。
升级中
1. 一次只跨一个大版本。 v1.28 → v1.29 → v1.30 → v1.31,不要跳版本。 2. 先升级控制面,再升级节点。 每次只升级一个master节点,确认etcd健康后再升级下一个。 3. 逐节点升级worker。 使用`kubectl cordon` + `kubectl drain`逐个升级,确保业务Pod有足够副本在其他节点运行。
# 标记节点不可调度
kubectl cordon node-1
驱逐Pod
kubectl drain node-1 --ignore-daemonsets --delete-emptydir-data
升级节点
kubeadm upgrade node
恢复调度
kubectl uncordon node-1
升级后
1. 全面验证: 所有Pod状态、Service可达性、Ingress路由、PVC挂载。 2. 监控观察48小时。 3. 写升级报告,记录遇到的问题和解决方案。
做IT这么多年,见过太多"早知道就好了"的情况。
希望这篇文章能帮你少走弯路。如果真的遇到问题,别一个人扛着——易云城IT服务随时待命。
📞 服务热线:13708730161 💬 微信:eyc1689 📧 邮箱:service@eycit.com 🌐 https://www.eycit.com
您身边的IT专家。