K8s 系列 | 第 28 天:集群升级最佳实践:Control Plane 与 Node 升级步骤


title: K8s 系列 | 第 28 天:集群升级最佳实践:Control Plane 与 Node 升级步骤
tags:
– Kubernetes
– K8s系列
– DevOps
– 集群运维
– 升级迁移


K8s 系列 | 第 28 天:集群升级最佳实践:Control Plane 与 Node 升级步骤

第 28/30 天 | 本文将从零梳理 K8s 集群升级的全流程,涵盖 Control Plane 升级、Node 升级、kubelet/kubectl 版本管理、升级前检查与回滚方案,帮助你安全高效地完成生产集群的大版本更新。


一、引言

Kubernetes 版本迭代非常迅速——每个小版本约 3 个月发布一次,社区通常同时维护最新的三个 minor 版本(例如 v1.28、v1.29、v1.30)。运行老旧集群意味着错过安全补丁、新特性和性能优化,甚至面临上游停止支持的风险。

然而,集群升级也是生产事故的高发操作。错误的升级顺序、遗漏的 API 变更、未备份的 etcd,都可能导致集群不可用。本文将基于 kubeadm 工具,详细讲解从 v1.28.x → v1.29.x 的完整升级步骤,并涵盖 Drain/Uncordon 策略、kubelet 滚动更新、节点池分批升级等核心实践。


二、升级前的准备工作

2.1 版本兼容性规则

K8s 的版本偏移规则(skew policy)要求:

组件 允许的版本差
kube-apiserver N 或 N-1
kube-controller-manager / kube-scheduler N 或 N-1
kubelet N-2 到 N(推荐 N-1)
kube-proxy 与 kubelet 一致
kubectl N-1 到 N+1

实战建议一次只升一个 minor 版本(如 v1.28 → v1.29),不建议跨多个 minor 版本升级,否则风险倍增。

2.2 升级前 Checklist

# 1. 检查当前版本
kubectl version --short
kubeadm version

# 2. 检查集群健康状态
kubectl get nodes -o wide
kubectl get pods -A | grep -v Running

# 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 
  snapshot save /backup/etcd-snapshot-$(date +%Y%m%d).db

# 4. 查看 API 弃用资源(重点!)
kubectl get apiservice

2.3 检查 CRD 和 API 兼容性

每个 K8s 大版本都会移除一批旧版 API(例如 v1.22 移除了 extensions/v1beta1 所有资源、v1.26 移除了 flowcontrol.apiserver.k8s.io/v1beta1)。你需要提前检查集群中是否使用了即将被移除的 API 版本:

# 安装 pluto 工具检测 API 兼容性
wget https://github.com/FairwindsOps/pluto/releases/latest/download/pluto_5.19.0_linux_amd64.tar.gz
tar -xzf pluto_5.19.0_linux_amd64.tar.gz
./pluto detect-all --target-version=v1.29

三、Control Plane 升级步骤

Control Plane 是集群的大脑,升级时必须遵循 先 Control Plane、后 Worker Node 的顺序。如果有多主节点,需逐个升级。

3.1 在第一个 Control Plane 节点执行

# 第一步:确定可用的目标版本
apt update
apt-cache madison kubeadm | grep 1.29

# 第二步:锁住 kubeadm 版本并安装
apt-mark unhold kubeadm
apt-get install -y kubeadm=1.29.10-1.1
apt-mark hold kubeadm

# 第三步:检查升级计划(关键步骤)
kubeadm upgrade plan

# 第四步:应用升级(将 1.29.10 替换为你的目标版本)
kubeadm upgrade apply v1.29.10

kubeadm upgrade apply 会自动执行以下操作:
– 检查集群是否可升级
– 更新 kube-apiserver、kube-controller-manager、kube-scheduler 的静态 Pod 镜像版本
– 更新 kube-dns / CoreDNS 配置
– 更新 kube-proxy 配置
– 更新 etcd(如果也在 Control Plane 上)

3.2 升级 kubelet 和 kubectl

# 更新 kubelet 和 kubectl
apt-mark unhold kubelet kubectl
apt-get install -y kubelet=1.29.10-1.1 kubectl=1.29.10-1.1
apt-mark hold kubelet kubectl

# 重启 kubelet 服务
systemctl daemon-reload
systemctl restart kubelet

# 验证节点状态
kubectl get nodes

3.3 处理 Control Plane 节点上的 Taint

升级完成后,Control Plane 节点默认带有 node-role.kubernetes.io/control-plane 污点。此时你的工作负载不会被调度到该节点。你可以通过以下命令确认:

kubectl describe node <control-plane-node> | grep Taints

在生产环境中,建议保留这个污点——不要让业务 Pod 跑到 Control Plane 节点上。

3.4 升级其他 Control Plane 节点(可选)

如果集群有多个 Control Plane 节点(高可用架构),在第一个节点成功升级后,在其他 Control Plane 节点上依次执行:

# 在第二、第三个控制平面节点上(不用运行 kubeadm upgrade apply)
kubeadm upgrade node

四、Worker Node 升级步骤

Worker Node 是整个集群的”体力担当”,承载着业务 Pod。升级时要注意逐节点进行、确保 Pod 被安全迁移

4.1 节点逐批升级流程

# ---- 在目标 Worker Node 上执行 ----

# 步骤 1:标记节点不可调度并驱逐 Pod
NODE_NAME=$(hostname)
kubectl cordon $NODE_NAME
kubectl drain $NODE_NAME --ignore-daemonsets --delete-emptydir-data

# 步骤 2:升级 kubeadm
apt-mark unhold kubeadm
apt-get install -y kubeadm=1.29.10-1.1
apt-mark hold kubeadm
kubeadm upgrade node

# 步骤 3:升级 kubelet 和 kubectl
apt-mark unhold kubelet kubectl
apt-get install -y kubelet=1.29.10-1.1 kubectl=1.29.10-1.1
apt-mark hold kubelet kubectl
systemctl daemon-reload
systemctl restart kubelet

# 步骤 4:恢复节点调度
kubectl uncordon $NODE_NAME

4.2 批量化升级脚本示例

#!/bin/bash
# batch-upgrade-worker.sh - 逐节点批量升级 Worker Node
NODES=("node-1" "node-2" "node-3" "node-4")
TARGET_VERSION="1.29.10"

for NODE in "${NODES[@]}"; do
  echo ">>> 开始升级节点: $NODE"

  # Drain 节点
  kubectl cordon "$NODE"
  kubectl drain "$NODE" --ignore-daemonsets --delete-emptydir-data --timeout=300s

  # SSH 到节点执行升级(实际中你可能使用 Ansible 或直接 SSH)
  ssh "$NODE" bash -s << EOF
    apt-mark unhold kubeadm
    apt-get install -y kubeadm=${TARGET_VERSION}-1.1
    apt-mark hold kubeadm
    kubeadm upgrade node

    apt-mark unhold kubelet kubectl
    apt-get install -y kubelet=${TARGET_VERSION}-1.1 kubectl=${TARGET_VERSION}-1.1
    apt-mark hold kubelet kubectl
    systemctl daemon-reload
    systemctl restart kubelet
EOF

  # 恢复调度
  kubectl uncordon "$NODE"
  echo "<<< 节点 $NODE 升级完成"

  # 批次间等待,观察集群稳定性
  sleep 60
done

echo "所有 Worker Node 升级完成!"

4.3 Drain 超时处理

kubectl drain 默认超时,如果你的 Pod 带 PDB(PodDisruptionBudget),drain 会遵守 PDB 规则。假如某个节点始终无法完成 drain:

# 强制忽略所有 Pod(谨慎使用!)
kubectl drain $NODE_NAME --ignore-daemonsets --delete-emptydir-data --force

# 如果还不行,取消 cordon 并排查:
kubectl uncordon $NODE_NAME
kubectl get pods -A --field-selector spec.nodeName=$NODE_NAME

五、升级后验证

5.1 集群健康检查

# 1. 检查所有节点状态
kubectl get nodes -o wide
# 输出示例:
# NAME     STATUS   ROLES           VERSION
# master1  Ready    control-plane   v1.29.10
# node-1   Ready    <none>          v1.29.10
# node-2   Ready    <none>          v1.29.10

# 2. 检查所有核心组件
kubectl get pods -n kube-system

# 3. 检查 API 版本
kubectl api-resources | grep -E "v1beta1|v1alpha1"

5.2 功能验证

# 部署一个测试 Pod 验证调度正常
kubectl run test-pod --image=nginx --restart=Never
kubectl get pod test-pod -o wide
kubectl delete pod test-pod

# 验证 Service 联通性
kubectl run curl-test --image=curlimages/curl --restart=Never --rm -it -- curl -s http://kubernetes.default.svc

5.3 回滚方案

如果升级后发现问题,使用 etcd 快照恢复是最可靠的回滚方式:

# 停止 kube-apiserver
mv /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/

# 恢复 etcd 快照
ETCDCTL_API=3 etcdctl snapshot restore /backup/etcd-snapshot-20250714.db 
  --data-dir=/var/lib/etcd-restored

# 修改 etcd 配置指向恢复后的数据目录
# 然后重启所有 Control Plane 组件

# 重新安装旧版本的 kubelet
apt-get install -y kubeadm=1.28.15-1.1 kubelet=1.28.15-1.1 kubectl=1.28.15-1.1
systemctl restart kubelet

六、常见问题与避坑指南

❌ 坑 1:跳过小版本直接升级

kubeadm upgrade apply 会检查是否支持跳过版本。K8s 官方只保证从 N-1 到 N 的升级路径。从 v1.26 → v1.29 必须分三步:v1.26 → v1.27 → v1.28 → v1.29。

❌ 坑 2:未检查 CRD 和 API 废弃

升级后才发现某个 CRD 调用旧版 API 报错,导致 Operator 不可用。升级前务必运行 pluto detect-all

❌ 坑 3:忘记解除 PodDisruptionBudget

某些 PDB 设置 maxUnavailable: 0minAvailable: 100%,会导致 drain 一直等待。升级前检查:

kubectl get pdb -A

❌ 坑 4:节点升级顺序错误

先升 Worker Node 再升 Control Plane 会导致 kubelet 告知 apiserver 版本不匹配(kubelet 最多只能比 apiserver 低 2 个小版本)。一定要先升 Control Plane。


七、总结

K8s 集群升级并不是一件可怕的事,只要你按部就班:

阶段 关键动作
准备 备份 etcd、检查 API 弃用、确认版本兼容性
Control Plane 先第一个节点(kubeadm upgrade apply),再其他节点(kubeadm upgrade node
Worker Node cordon → drain → 升级 → uncordon,逐节点进行
验证 节点状态、Pod 调度、Service 联通性
回滚 etcd 快照恢复 + 降级组件版本

记住一条黄金法则:升级前必须有可验证的 etcd 快照,一次只升一个 minor 版本,分阶段逐步推进。


下期预告

第 29 天:多集群管理:Federation / Cluster API / 多集群服务网格——当你的团队拥有 5 个、10 个甚至 50 个 K8s 集群时,单集群的运维经验已远远不够。我们将深入探讨多集群管理的三种主流方案:KubeFed(Federation v2)、Cluster API(声明式集群生命周期管理)以及服务网格的多集群互通模式。

📖 系列目录K8s 30 天系列 – 全部文章

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复

    暂无评论内容