在Kubernetes集群的日常运维中,节点升级是一项高风险操作。一旦升级过程中出现容器运行时与kubelet版本不兼容、Pod驱逐失败或者镜像回滚没做好,轻则业务中断几分钟,重则整个集群瘫痪。回滚机制的设计,往往比升级本身更考验运维人员的功底。本文将从操作流程、版本兼容性、回滚预案三个维度,详细拆解容器化节点升级与回滚的完整方案。

一、节点升级的核心操作:cordon、drain与uncordon
升级一个工作节点之前,必须先把它从集群的调度池中摘除,否则新创建的Pod还会被调度到这台即将重启的机器上。Kubernetes提供了三个命令来完成这个动作,它们各有分工,缺一不可。
kubectl cordon的作用是将节点标记为不可调度状态。执行之后,调度器不会再把新的Pod分配到该节点,但节点上已经运行的Pod不受任何影响。这是一个非常温和的操作,适合在升级前先执行,给集群一个缓冲期。可以这样理解:cordon是关门,但屋里的人还在。
# 将节点标记为不可调度 kubectl cordon worker-node-01 # 查看节点状态,此时应出现 SchedulingDisabled kubectl get nodes
kubectl drain则是真正开始驱逐Pod。它会优雅地终止节点上的所有Pod(DaemonSet管理的Pod默认除外),并等待这些Pod在其他节点上重新调度和启动。drain有几个常用参数需要注意:--ignore-daemonsets用于忽略DaemonSet控制器管理的Pod,因为这类Pod在每个节点都必须存在,驱逐了还会被拉起来;--delete-emptydir-data用于处理使用了emptyDir存储的Pod,这类Pod被驱逐后emptyDir中的数据会丢失,生产环境要谨慎评估。
# 安全驱逐节点上的所有Pod kubectl drain worker-node-01 \ --ignore-daemonsets \ --delete-emptydir-data \ --grace-period=120 \ --timeout=10m
升级完成并且节点重新加入集群后,执行kubectl uncordon恢复调度。这个顺序绝对不能乱:先cordon再drain,升级后最后uncordon。如果跳过cordon直接drain,在驱逐过程中新Pod仍可能被调度到该节点,造成业务异常。
二、版本兼容性:最容易踩坑的升级盲区
很多升级事故的根源不在操作流程,而在版本兼容性上。容器化节点的软件栈由多个组件构成,包括kubelet、容器运行时(containerd或Docker)、CNI插件、操作系统内核等,它们之间有严格的版本约束关系。
首先是kubelet与API Server的版本关系。官方的规则是:kubelet的版本不能高于API Server,最多可以比API Server低三个次要版本。举例来说,API Server是v1.28,那么kubelet可以是v1.28、v1.27、v1.26或v1.25。如果先升级了工作节点的kubelet而控制面还没升级,就可能出现API不兼容的问题。
其次是容器运行时的版本。以containerd为例,不同版本的Kubernetes对containerd有最低版本要求。如果使用kubeadm升级,工具会自动检查,但手动升级操作系统包时很容易忽略这一点。建议在升级前用下面的命令确认当前各组件的实际版本:
# 查看节点上的kubelet版本 kubectl get nodes -o wide # 查看containerd版本 ctr version # 查看节点详细信息,包括容器运行时 kubectl describe node worker-node-01 | grep -A 3 "Container Runtime"
另一个常见坑是CNI插件。Calico、Flannel等网络插件通常以DaemonSet形式部署在节点上,它们的内核模块依赖(比如ipset、vxlan)在不同操作系统版本之间可能存在差异。升级操作系统内核后CNI Pod频繁重启,是节点升级后非常典型的故障现象。因此升级前务必查看CNI插件的官方兼容性矩阵,确认它支持新的内核版本。
最后强调一点:升级应该逐节点进行,不要批量操作。每升级完一个节点,观察至少十分钟,确认Pod调度正常、网络连通性正常,再处理下一个节点。虽然慢,但这是把爆炸半径控制在单节点范围内的最有效手段。
三、回滚预案设计:升级失败时的救命稻草
回滚不是升级失败后临时想出来的操作,而应该在升级前就完整设计好。一个可执行的回滚预案至少包含四个部分:版本固定、配置备份、etcd快照和验证标准。
第一是版本固定。升级前记录所有组件的确切版本号,包括操作系统补丁级别、containerd版本、kubelet版本、CNI插件版本。回滚时要能精确回到这些版本,而不是简单地安装一个大概的旧版本。使用apt或yum安装软件包时,建议用apt-mark hold或yum versionlock锁住关键包,防止意外的自动升级。
# Debian/Ubuntu 系统锁定kubelet版本 apt-mark hold kubelet kubeadm kubectl # 升级前备份节点配置 cp -r /etc/kubernetes /etc/kubernetes.bak-$(date +%Y%m%d) cp /etc/containerd/config.toml /etc/containerd/config.toml.bak # 执行etcd快照(在控制面节点上) 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
第二是etcd快照。etcd保存了集群的全部状态数据,是控制面回滚的最后防线。快照应该在升级控制面节点之前执行,并拷贝到集群外部存储。回滚控制面时,可以通过etcdctl snapshot restore恢复数据,但要注意这种方式会丢失快照之后的所有变更,所以快照与升级操作之间的时间间隔要尽量短。
第三是节点级回滚的具体步骤。如果升级后节点kubelet无法启动,应该停止kubelet服务,用包管理器降级到之前锁定的版本,然后恢复备份的配置文件。如果容器运行时升级导致Pod无法创建,同样降级containerd并恢复config.toml。整个降级过程的耗时应该提前演练,做到心中有数。
# 降级kubelet(Ubuntu示例) apt-mark unhold kubelet kubeadm kubectl apt install kubelet=1.27.4-00 kubeadm=1.27.4-00 kubectl=1.27.4-00 systemctl daemon-reload systemctl restart kubelet # 恢复配置文件 cp /etc/kubernetes.bak-20240101/admin.conf /etc/kubernetes/ # 确认节点恢复 kubectl get nodes
第四是验证标准。回滚完成后不能只看节点状态是Ready就结束,还需要验证:核心业务Pod是否全部恢复、Service的Endpoints是否正常、跨节点网络是否连通、DNS解析是否正常。建议提前准备一份验证脚本或检查清单,把每项验证命令固化下来,避免故障时刻手忙脚乱。
四、生产环境的进阶建议
如果集群规模较大,手动逐节点操作既低效又容易出错,可以考虑引入自动化工具。Ansible配合kubeadm是最常见的组合,把cordon、drain、升级、验证、uncordon这套流程写成playbook,每一步都带失败中断逻辑,任何一个环节出错就自动停止并保留现场。
对于使用托管Kubernetes服务(如各云厂商的ACK、TKE、EKS)的用户,节点升级策略则简单得多,控制面和节点池可以分开升级,节点池支持滚动升级并自动处理drain逻辑。但托管服务不等于零风险,托管节点升级时本地磁盘数据依然会丢失,有状态应用还是需要提前做好数据迁移。
还有一个容易被忽视的点:升级窗口的选择。即使方案再完善,也应该把升级安排在业务低峰期,并提前通知相关团队。同时确保升级期间有至少一个人能实时观察监控面板和告警,一旦出现异常指标立即触发回滚流程。宁可多回滚一次,也不要带着问题状态硬扛,这是容器化运维中用无数次教训换来的经验。
总结一下,容器化节点的升级与回滚是一个系统工程:流程上遵守cordon、drain、升级、验证、uncordon的固定顺序;版本上严格控制kubelet、容器运行时、CNI之间的兼容关系;预案上做好版本固定、配置备份和etcd快照。三者缺一不可,只有把回滚当成升级的一部分来设计,节点变更才能真正可控。
容器化Kubernetes节点升级滚动更新与回滚修改时间:2026-09-10 05:38:37