集群升级是 Kubernetes 平台团队风险最高、却最容易被忽视的日常操作。它不像加个节点那样可逆——一次不成功的控制面升级可能让整个集群进入不可服务状态,回滚还涉及 etcd 数据恢复。本指南从版本策略到执行步骤再到回滚与演练,带你建立一套「不慌不忙、可重复、可回滚」的生产升级体系。
目录
- 1. 版本策略:想清楚"要不要升、什么时候升"
- 2. 升级前检查:五分钟的健康快照
- 3. 升级的两种模式:kubeadm 与托管集群
- 4. 升级过程中的驱逐与滚动策略
- 5. 升级后的验证与回滚方案
- 6. GitOps 驱动的版本管理与自动化
- 7. 升级风险与回滚策略
- 8. 升级演练与团队落地
- 9. 常见坑与总结
1. 版本策略:想清楚"要不要升、什么时候升"
1.1 Kubernetes 的版本生命周期
Kubernetes 是持续演进的项目:每 3 个月发布一个小版本,同时官方只维护最近 3 个 minor 版本。这意味着如果你不主动升级,就会落入 EOL(End of Life)泥潭——老版本无安全补丁、无 bug 修复,控制面组件与 kubelet 的 API 兼容性逐渐断裂。
vX.Y 发布 ──▶ vX.Y.Z patch(月级滚动)──▶ vX.(Y+3) 发布后 vX.Y 进入 EOL(不再维护)
│
└── 官方只支持最近 3 个 minor 版本
- minor 版本:每 3 个月(1.27 → 1.28 → 1.29…)
- patch 版本:每月滚动,含安全与 bug 修复
- EOL:当第 (n+3) 个 minor 发布时,第 n 个 minor 停止维护
关键认知:若当前在用版本(如 1.27)已接近 EOL,而你同时维护 20 个集群,等于系统性风险在快速累积。升级不是为了尝鲜,而是避免被时代抛弃的安全债务。
1.2 设计升级节奏
| 节奏 | 说明 | 适用场景 |
|---|---|---|
| 紧跟 minor(每 3 月) | 版本落后不超过一个 minor | 测试集群、灰度集群 |
| 落后一个 minor(约 6 月) | 有缓冲,给第三方依赖适配时间 | 稳妥的生产默认 |
| 落后两个 minor(约 9 月) | 风险敞口变大,但 patch 仍维护 | 大型遗留平台 |
关键决策:升级不只是"升版本",还涉及 API 移除、存储后端变更、Webhook 兼容。推荐小步快跑:先升级 patch 稳定,再升 minor,且一次只跨一个 minor(官方硬限制)。
1.3 用版本矩阵做规划
建议维护一张版本策略表:
目标集群 当前版本 目标版本 负责人 计划日期 已验证恢复
生产-核心 1.28.2 1.29.x 张三 2026-10 是
生产-边缘 1.28.2 1.29.x 李四 2026-11 是
Dev/QA 1.29.5 1.30.0 王五 2026-09 否
版本矩阵的产出不只是「升级」,而是可预测、可回滚、可审计的节奏感。
1.4 升级的收益与代价
| 收益 | 代价 |
|---|---|
| 安全补丁、漏洞修复 | 服务中断窗口(可滚动规避) |
| 新特性(如稳定版网关 API) | 三方 Operator 兼容适配 |
| 性能改进、bug 修复 | 存储/网络插件需同版验证 |
| 长期支持的地基 | 需要升级演练投入 |
一句话:版本策略的本质是用"主动规划"换"被动救火"——知道何时该升、能跳多远,升级就不是赌运气。
2. 升级前检查:五分钟的健康快照
在任何升级动作之前,先跑一遍健康基线,确保升级失败后能快速确认"是不是升级引起的"。
2.1 系统健康检查
# 1. 节点 Ready 状态
kubectl get nodes -o wide
# 2. 控制面组件健康
kubectl get --raw='/healthz'
kubectl get --raw='/readyz?verbose'
# 3. etcd 健康
ETCDCTL_API=3 etcdctl --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 \
endpoint health
2.2 升级前兼容性检查
版本升级最大的坑是某个 API 被删除或改名。用工具先行校验:
kubectl convert --context=current --dry-run=client -f ./manifests/ -o yaml
同时检查三方组件兼容性矩阵:CNI(Calico/Cilium)、CSI(存储插件)、Ingress Controller、CoreDNS、外部 webhook 是否支持目标版本。
2.3 关键资源快照
升级前记录节点数、版本、最大副本数、关键 Deployment 状态,用于升级后的比对。可导出关键资源清单存档:
kubectl get deploy,sts,ds -A -o yaml > pre-upgrade-snapshot.yaml
kubectl get pvc -A -o wide
一句话:升级前检查 = 健康基线 + API 兼容性 + 资源快照——这三样让"升级是否引入回归"有据可查。
3. 升级的两种模式:kubeadm 与托管集群
3.1 kubeadm 升级(自建集群)
顺序必须是 控制面 → worker(绝不能反过来):
# ① 在第一个控制面节点
apt-get update && apt-get install -y kubeadm=1.29.0-00
kubeadm upgrade plan # 查看可升级的目标版本
kubeadm upgrade apply v1.29.0 # 升级控制面
kubectl drain <control-node> --ignore-daemonsets
apt-get install -y kubelet=1.29.0-00 kubectl=1.29.0-00
systemctl restart kubelet
kubectl uncordon <control-node>
# ② 其余控制面节点
kubeadm upgrade node
# ③ 每个工作节点
apt-get install -y kubeadm=1.29.0-00 kubelet=1.29.0-00
kubeadm upgrade node config --kubelet-version v1.29.0
systemctl restart kubelet
kubectl drain <node> --ignore-daemonsets
kubectl uncordon <node>
核心纪律:kubeadm upgrade apply 只升级控制面;kubelet 必须逐节点单独升级。把所有 worker 的 kubelet 一次性升级是错误操作,官方要求kubelet 版本不得高于控制面。
3.2 托管集群(EKS / AKS / GKE)
托管集群屏蔽了控制面升级细节,但仍需规划:
- 控制面升级由云厂商控制(通常先升控制面,再建议滚动升级节点池)
- 节点池是版本无关的编排单元,通过滚动替换节点池实现升级
- 操作:更新节点池的
kubernetesVersion→ 触发滚动替换 → 检查调度与驱逐策略
# EKS 节点组版本更新示例(Terraform)
resource "aws_eks_node_group" "main" {
cluster_name = aws_eks_cluster.main.name
node_group_name = "main-${var.k8s_version}"
version = var.k8s_version # 1.29 触发滚动替换
...
}
3.3 两种模式对比
| 维度 | kubeadm 自建 | 托管集群 |
|---|---|---|
| 控制面升级 | 手工逐组件 | 云厂商负责 |
| 节点升级 | 逐节点 drain/升级 | 节点池滚动替换 |
| 回滚能力 | 快照可完整恢复 | 回滚受限(需重建) |
| 运维成本 | 高 | 低 |
| 可控性 | 高 | 中 |
3.4 关键陷阱
| 陷阱 | 后果 |
|---|---|
| 先升 kubelet 再升控制面 | 新旧版本 API 往返,可能不兼容 |
| 一次升级跨两个 minor | 官方只支持跨一个 minor,跳版本需分步 |
| 升完 worker 忘升 kubelet | 节点 NotReady,资源占用不均 |
| 无备份直接升级 | 一旦 etcd 损坏无法快速恢复 |
| 控制面与 worker 版本差过大 | kubelet 版本须 ≤ 控制面版本 |
一句话:升级模式二选一——自建要"逐节点手工 + 快照兜底",托管要"节点池滚动 + 依赖版本矩阵";无论哪种,“kubelet 不高于控制面"是铁的纪律。
4. 升级过程中的驱逐与滚动策略
4.1 用 drain 优雅地排空节点
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data --force
--ignore-daemonsets:保留 DaemonSet(网络/存储插件),它们随后由新版 kubelet 拉起--delete-emptydir-data:排空含本地数据的 Pod--force:强制排空不健康的 Pod
4.2 用 PodDisruptionBudget 保证可用性
升级时若不设 PDB,驱逐可能把所有副本同时排空,造成服务中断。PDB 保证同一时刻最多不可用 N 个 Pod:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb
spec:
minAvailable: 2 # 至少保持 2 个副本可用
selector:
matchLabels:
app: web
4.3 拓扑分布与滚动顺序
- 先升级非关键节点池,再升级承载核心业务的池;
- 升级节点池时,先加满新版本节点池再缩容旧池,这是「扩容-迁移-缩容」的经典替换法;
- 结合拓扑分布约束(topologySpreadConstraints),避免同一时间把同一 Zone 的副本全部驱逐。
# 拓扑分布:把副本均匀打散到 3 个 zone
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: web
一句话:升级驱逐 = PDB 兜底 + 新池先扩容旧池再缩容 + 拓扑打散——把"中断"变成"可用性无损的滚动”。
5. 升级后的验证与回滚方案
5.1 升级后验证清单
kubectl get nodes # 所有节点 Ready,版本正确
kubectl get pods -A -o wide # 业务 Pod 运行
kubectl get --raw='/readyz?verbose' # apiserver 自检
kubectl get crd # 自定义资源未受损
kubectl top nodes # 资源水位正常
curl -k https://<ingress>/healthz # 业务健康
5.2 自建集群回滚方案
升级前必须做 etcd 快照:
ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-1.28.snap
若升级失败:
- 控制面:回滚 kubelet 二进制 → 恢复 etcd 快照
- 数据面:通过镜像标签回滚 Deployment 到旧版本
- 数据面回滚:
kubectl rollout undo deployment/web
5.3 托管集群回滚
托管集群控制面升级后通常不能降级(云厂商限制),回滚策略是:
- 保留旧版本节点池至稳定期后再删除
- 通过渐进发布:先在非生产验证,确认稳定后再升级生产
- 依赖配置(Infra-as-Code)恢复到旧版本重建
5.4 多集群旁路升级
大型平台常用**「金丝雀集群优先升级 + 生产集群次之」**:
灰度集群(预发)→ 完整演练通过 → 生产集群滚动 → 边缘集群最后
(保留旧节点池至稳定窗口结束)
一句话:回滚不是"可选动作"而是升级的前置条件——自建靠 etcd 快照,托管靠保留旧池 + 金丝雀前置验证。
6. GitOps 驱动的版本管理与自动化
6.1 让版本变更可 Git 化
把集群版本作为代码的一部分,升级走 PR 评审:
# cluster-manifests/cluster/version.yaml
apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
kubernetesVersion: v1.29.0
升级 = 改版本号 → 提交 PR → CI 校验 → 批准后流水线执行升级 → 自动验证 → 完成。
6.2 自动化工具链
| 工具 | 定位 |
|---|---|
| kubeadm + Ansible/Terraform | 自建集群升级编排 |
| 云厂商 API | EKS/AKS/GKE 节点池滚动 |
| Crossplane / KCP | 多集群版本管理 |
| Argo Rollouts | 应用层渐进发布配合 |
| Flux/ArgoCD | 集群内资源声明式同步 |
6.3 升级流水线示例
PR 变更版本号
→ CI 校验(kubeadm plan 或云 API 校验)
→ 灰度集群执行升级
→ 冒烟测试通过
→ 人工确认
→ 生产集群滚动升级
→ 验证 + 记录
一句话:把升级声明式 + 流水线化——版本号是 Git 变更,执行是流水线,验证是门禁;升级从"手艺活"变成"可审计的工程流程"。
7. 升级风险与回滚策略
7.1 风险全景
| 风险 | 触发场景 | 缓解 |
|---|---|---|
| etcd 数据损坏 | 升级中节点异常 | 快照 + 恢复演练 |
| API 兼容断裂 | 老资源用到被删 API | 升级前 kubectl convert |
| 三方组件不兼容 | CNI/CSI/webhook 未适配 | 版本矩阵预验证 |
| kubelet 失联 | 升级后起不来 | 逐节点验证 + 保留旧包 |
| 存储后端迁移 | 卷插件行为变化 | 渐进迁移 + 数据备份 |
7.2 升级窗口与时机
- 避开业务高峰(大促、月末结算);
- 分批:控制面窗口 → 非核心池 → 核心池;
- 每次升级设最大失败容忍:连续 N 个节点失败即暂停(halt-on-error)。
7.3 升级失败时的时间线
0-5 分钟:健康检查定位影响范围
5-15 分钟:决定回滚 or 继续
15-30 分钟:执行回滚(etcd 恢复 / 旧池保留)
30-60 分钟:验证业务恢复
一句话:风险管控 = 事前版本矩阵 + 事中分批暂停 + 事后快照回滚——让升级在"最坏情况"也有章可循。
8. 升级演练与团队落地
8.1 为什么必须演练
升级失败时最贵的不是恢复,而是团队没有演练过恢复——临时翻文档、争论回滚步骤,MTTR 无限拉长。
8.2 演练内容
| 演练项 | 方法 |
|---|---|
| 升级演练 | 在灰度集群完整跑一遍升级 |
| 回滚演练 | 故意制造失败,验证 etcd 快照恢复 |
| 中断演练 | 模拟某个控制面节点挂掉 |
| 验证演练 | 升级后业务健康检查自动化 |
8.3 组织落地
- 指定升级 owner 与审批人;
- 每次升级记录升级报告(版本、耗时、问题、回滚动作);
- 定期(每 2-3 minor)举办升级游戏日。
一句话:演练让升级从"未知风险"变成"已知操作"——把"能不能升"的问题,变成"已经演练过几次"的答案。
9. 常见坑与总结
9.1 常见坑
| 坑 | 现象 | 对策 |
|---|---|---|
| 跨太多 minor | 升级失败/不兼容 | 一次只跨一个 minor |
| 不排空直接升 | Pod 驱逐混乱 | 先 drain 再升级 |
| 忘备份 etcd | 数据不可恢复 | 升级前快照 |
| 升完不验证 | 问题潜伏 | 升级后验证清单 |
| 无灰度 | 生产一次翻车 | 金丝雀集群先行 |
| 忘升 kubelet | 节点 NotReady | 逐节点升 kubelet |
9.2 总结表
| 环节 | 要点 |
|---|---|
| 版本策略 | 3 minor 生命周期、一次跨一个 minor |
| 升级前 | 健康基线 + 兼容性 + 快照 |
| 执行 | 控制面先、worker 后,PDB + 滚动 |
| 回滚 | etcd 快照 / 保留旧节点池 |
| 自动化 | 版本号 Git 化 + 流水线门禁 |
| 演练 | 升级/回滚/中断三连练 |
一句话记住:升级的本质是"对抗技术债务"——版本策略让你知道何时该升,健康检查让你升得明白,快照让你跌得回来,演练让你敢升。升级不是一次操作,而是一套可重复、可回滚、可审计的工程流程。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。