如何平滑升级 Kubernetes 集群从 1.26 版本到 1.27 版本避免业务中断?

文章导读
Kubernetes 从 1.26 升到 1.27,中断原因通常不是版本本身,而是升级顺序乱、节点排空时副本不够、或遗漏了被移除的 API。我习惯把升级当作流水线:先保证本机命令版本一致,再动控制平面,然后一台台处理工作节点,最后留好可回滚的 etcd 快照。具体等待时长和回滚条件要结合集群规模来定。
📋 目录
  1. 先把 kubeadm、kubelet、kubectl 的版本差拉平
  2. 控制平面先动,工作节点挨个确认
  3. 排空节点前先算好副本余量
  4. 升级前先处理 API 版本弃用
  5. 留好回滚标记,别等到故障扩大再处理
A A

Kubernetes 从 1.26 升到 1.27,中断原因通常不是版本本身,而是升级顺序乱、节点排空时副本不够、或遗漏了被移除的 API。我习惯把升级当作流水线:先保证本机命令版本一致,再动控制平面,然后一台台处理工作节点,最后留好可回滚的 etcd 快照。具体等待时长和回滚条件要结合集群规模来定。

先把 kubeadm、kubelet、kubectl 的版本差拉平

执行kubeadm upgrade plan之前,先确认当前kubeadm、kubelet、kubectl的版本均为1.26.x,且三者小版本差异不超过1。若kubeadm已提前升级到1.27,而kubelet仍停留在1.26,则plan命令会直接报错,导致无法生成升级步骤。稳妥做法是在每台节点上分别运行kubeadm version和kubelet --version,确保版本匹配后再进行后续操作,避免升级中途出现API版本不兼容的异常。

这里说的匹配不是只看主版本,而是三者小版本落在同一个安全范围。节点 A 是 1.26.4,节点 B 是 1.26.1,通常没问题;如果 kubectl 变成 1.27,客户端连接旧 API Server 时一般会提示 version skew。可以先在每台节点上跑一遍下面的命令,确认输出里的版本号一致再执行 plan。

kubeadm version
kubelet --version
kubectl version --client

控制平面先动,工作节点挨个确认

Kubernetes官方推荐的升级顺序是先升级控制平面节点,再升级工作节点。在控制平面升级完成之前,不要对任何工作节点执行kubeadm upgrade node,否则可能出现kubelet与API Server版本不匹配,导致节点反复重启。每升级完一台节点,应通过kubectl get nodes观察其STATUS变为Ready,并确认kubelet日志中无tls握手失败或Unauthorized等错误,再继续升级下一台。

如何平滑升级 Kubernetes 集群从 1.26 版本到 1.27 版本避免业务中断?

实际操作时,我习惯把控制平面节点当作串行任务。第一台执行 kubeadm upgrade apply v1.27.0 后,先看静态 Pod 是否全部 Running:

kubectl get pods -n kube-system -o wide

等 kube-apiserver、etcd、kube-controller-manager 稳定,再继续下一台。如果某个 Pod 反复重启,先处理日志,不要带着未恢复的组件去碰下一台。

排空节点前先算好副本余量

为避免驱逐Pod造成业务中断,建议在升级工作节点前,检查该节点上的Pod副本数是否满足最小可用需求。若运行的是Deployment,确保replicas至少比节点数多1,且PodDisruptionBudget的minAvailable配置合理。当节点被封锁(cordon)并排空(drain)时,Pod会迁移到其他节点,如果副本数不足,服务将进入只读或不可用状态。保守做法是先扩容,再执行drain,升级完成并恢复节点后缩容。

如何平滑升级 Kubernetes 集群从 1.26 版本到 1.27 版本避免业务中断?

判断余量不能光看总副本数,还要确认 Pod 调度分布。比如 3 个副本都落在同一台机器,drain 后所有实例同时重建,仍然会断。用 kubectl get pods -o wide -l app=服务名 看分布,再用 kubectl get pdb -n 命名空间 确认 ALLOWED DISRUPTIONS 不是 0。PDB 为 0 时,drain 会卡住等待,这是保护机制生效。另一个容易忽略的是 DaemonSet 和 emptyDir 数据。drain 时如果节点上有本地数据,要确认业务能否接受重建;不接受就先迁移任务。可以把 --ignore-daemonsets 带上,但不要随手带 --delete-emptydir-data,除非确认数据可丢。这样最多让 drain 中途失败,不会静默丢数据。

升级前先处理 API 版本弃用

Kubernetes 1.27移除了多个旧版本API,例如flowcontrol.apiserver.k8s.io/v1beta2已不再提供服务。升级前通过kubectl get --raw /apis检查集群里是否还有使用旧API的对象。若存在,先用kubectl convert命令将自定义资源或内置资源迁移到新API版本,并更新所有引用该资源的控制器或脚本。忽略这一步可能导致升级后无法list或watch原有资源,业务侧持续报404错误,并且短时间内难以回滚。

如何平滑升级 Kubernetes 集群从 1.26 版本到 1.27 版本避免业务中断?

可以执行 kubectl get --raw /apis | grep -i flowcontrol,再配合 kubectl get flowcontrol.apiserver.k8s.io --all-namespaces 找出旧对象。如果输出里有 v1beta2,先迁移到 v1beta3 或 v1。同时要检查集群外脚本和 Helm 模板,它们可能写死了旧 apiVersion,升级后仍会报错。搜索一遍 CI/CD 配置和 Git 仓库里的 YAML,比只处理集群内对象更稳妥。

留好回滚标记,别等到故障扩大再处理

在升级主节点前,建议先备份etcd快照并记录当前kubeadm配置。执行kubeadm upgrade apply v1.27.0后,观察etcd、kube-apiserver、kube-controller-manager等静态Pod是否全部恢复Running。若在5分钟内仍有组件CrashLoopBackOff,立即使用备份的etcd快照和原kubeadm.yaml执行kubeadm upgrade revert,不要等待自动重试。回滚完成后要清理可能写入的新版本证书,并重启kubelet,避免新旧证书混用。

5分钟是一个保守阈值,节点多、证书轮换慢的集群可以放宽到10分钟,但不要超过一个完整 Pod 重启周期再做决定。执行回滚前先确认 etcd 快照文件存在且可读,我见过备份路径写错、快照只有 0 字节,真到回滚时才发现。比较稳的做法是把快照放到独立目录,并记录当前 kubeadm config 的 sha256,便于回滚后核对。不要把回滚和重启混成一步。先恢复 etcd,再执行 kubeadm upgrade revert,最后重启 kubelet。重启前检查证书目录的修改时间,确认没有混入升级时生成的新证书。