1.5. 集群升级、证书与节点维护
升级不是「换一个 kubelet 二进制」,而是一次涉及控制平面、etcd、证书、CNI 与入口控制器的协同变更。这一章把 kubeadm 的升级路径、version skew 约束、drain/uncordon 与回滚方案串成一条可演练的流水线,并在三节点集群上完成 v1.30 → v1.31 的实操。
- 层级:
L2 · 核心
- 难度:
进阶
- 预计:
70 分钟
- 前置:
集群部署、etcd 与 PKI 基础
你将学到
kubeadm 的逐 minor 升级路径与控制平面 → worker 的顺序约束。
version skew 的硬性边界,以及为什么不能先升 kubelet。
kubeadm upgrade plan/apply、kubeadm upgrade node各自改了什么。drain/cordon/uncordon 与 PDB 的配合,证书轮换与 etcd 快照恢复。
升级前检查表、验证清单、失败回滚与排错路径。
1.5.1. 升级到底改了什么
kubeadm 管理的集群里,版本不是一个数字,而是一组组件的组合:静态 Pod 形式的 kube-apiserver、kube-controller-manager、kube-scheduler、etcd,DaemonSet 形式的 kube-proxy,节点上的 kubelet 与 kubectl,以及 addon 层的 CoreDNS。kubeadm upgrade apply 负责前四类与 addon 的镜像和配置,kubelet 必须由你在每台节点上单独升级,CNI、Ingress、CSI 则各有自己的发布节奏。
升级的动机通常有三类:跟进安全补丁、使用新特性、以及保持版本仍在社区支持窗口内。Kubernetes 每个 minor 大约支持 14 个月,跨过三个 minor 就会同时失去补丁与文档支持,所以生产集群应把「每年至少两次 minor 升级」写进维护计划,而不是等到 EOL 再一次性跳版。
1.5.1.1. version skew 的三条硬约束
Kubernetes 只承诺有限范围内的版本偏差,超出范围的行为没有保证。规划升级时先对照这张表:
组件 |
与 kube-apiserver 的允许偏差 |
说明 |
|---|---|---|
kube-apiserver |
基准 |
集群里版本最高的组件 |
controller-manager / scheduler |
不得更新,最多旧 1 个 minor |
新 API 由 apiserver 提供,控制器可以稍旧 |
kubelet |
最多旧 3 个 minor,不得更新 |
先升控制平面再升 kubelet 的根因 |
kube-proxy |
与 kubelet 同 minor,最多旧 3 个 minor |
通常随 kubelet 一起换镜像 |
kubectl |
新旧各 1 个 minor 以内 |
客户端兼容窗口,超出会报字段错误 |
由此得到两条操作纪律:第一,控制平面必须先升,让 apiserver 成为最新版本,旧的 kubelet 仍在支持窗口内;第二,kubeadm 不能跨 minor,v1.29 → v1.31 必须经过 v1.30,但同一个 minor 内的 patch 可以自由选择。
1.5.1.2. 顺序不能变
图:一次 minor 升级的固定顺序。每个 worker 内部又是 drain → 升包 →
kubeadm upgrade node→ uncordon 的循环。
多控制平面集群先升第一个节点,其余控制平面用 kubeadm upgrade node 升级;worker 一次只动一台,确保业务副本始终有地方调度。
1.5.2. 升级前的准备
1.5.2.1. 备份 etcd
etcd 是集群唯一的事实来源,升级前必须有一份可验证的快照。把备份做成脚本,避免每次手敲参数出错:
#!/usr/bin/env bash
set -euo pipefail
SNAP="/var/backups/etcd-$(date +%F-%H%M).db"
sudo mkdir -p /var/backups
sudo 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 "$SNAP"
sudo ETCDCTL_API=3 etcdctl snapshot status "$SNAP" -w table
snapshot status 会输出 hash、revision 与总 key 数;只看到文件生成不算成功,revision 非零才说明快照完整。快照应复制到集群外的对象存储,和集群放在同一块磁盘上的备份在磁盘故障时一起消失。
1.5.2.2. 检查证书与升级计划
sudo kubeadm certs check-expiration
sudo kubeadm upgrade plan
第一条列出每张证书的残存时间,重点看 ca(默认 10 年)与 apiserver、etcd 证书(默认 1 年)。第二条会打印当前版本、可升级到的 patch/minor、以及「必须手动升级的组件」清单。kubeadm upgrade plan 不会检查 CNI 与 Ingress,那部分要靠发布说明。
警告
控制平面升级会短暂中断 API
kubeadm upgrade apply 会重建 apiserver、controller-manager、scheduler 的静态 Pod,API 通常抖动 30–60 秒。请从集群外的跳板机执行,并确认控制平面 VIP 后面不止一个健康端点;单控制平面集群只能接受这段停机窗口。
1.5.2.3. API 与插件兼容性
升级前扫一遍集群里实际使用的 API 组,确认没有落在下一个版本移除清单里的资源:
kubectl api-resources --verbs=list --namespaced -o name | sort -u > /tmp/resources.txt
kubectl get ingress,networkpolicy,poddisruptionbudget -A
kubectl get --raw '/metrics' | grep apiserver_requested_deprecated_apis
第三行依赖 apiserver 的 deprecated API 指标,能直接看出哪些客户端还在调用已废弃的接口。控制器、Operator、CRD 的 conversion webhook 也要一并确认,它们往往比工作负载更晚适配新版本。
升级前检查表
检查项 |
命令或动作 |
通过标准 |
|---|---|---|
etcd 快照 |
|
revision 非零,且已复制到集群外 |
证书有效期 |
|
剩余时间覆盖升级窗口 |
节点健康 |
|
全部 |
工作负载 |
|
无 CrashLoop / Pending |
PDB |
|
允许至少一个副本被驱逐 |
版本偏差 |
|
当前版本与目标只差一个 minor |
插件矩阵 |
CNI / Ingress / CSI 发布说明 |
明确声明支持目标 minor |
回滚预案 |
快照 + 旧版本 deb 包 + 演练记录 |
能在测试环境 30 分钟内恢复 |
1.5.3. 演练:三节点集群 v1.30 → v1.31
实验环境:master01(控制平面 + etcd)、node01、node02,OS 为 Ubuntu 22.04,包管理使用 Kubernetes 官方 apt 源。所有节点先确认 kubeadm version -o short 为 v1.30.x。
1.5.3.1. 步骤 1 · 升级第一个控制平面
sudo apt-mark unhold kubeadm
sudo apt-get update && sudo apt-get install -y kubeadm='1.31.4-1.1'
sudo apt-mark hold kubeadm
sudo kubeadm upgrade plan
sudo kubeadm upgrade apply v1.31.4 --yes
upgrade apply 会依次完成:预检(API 可达、版本偏差、证书、etcd 健康)、拉取新镜像、重建静态 Pod、升级 CoreDNS 与 kube-proxy 的 addon 清单、续签受管证书。输出末尾出现 SUCCESS! Your cluster was upgraded to "v1.31.4" 才算控制平面完成。
接着升级本节点的 kubelet 与 kubectl:
sudo apt-mark unhold kubelet kubectl
sudo apt-get install -y kubelet='1.31.4-1.1' kubectl='1.31.4-1.1'
sudo apt-mark hold kubelet kubectl
sudo systemctl daemon-reload && sudo systemctl restart kubelet
kubectl get nodes -o wide
此时 master01 已是 v1.31.4,两个 worker 仍是 v1.30.x,这是 skew 允许的中间状态,但不应超过一个维护窗口。
1.5.3.2. 步骤 2 · 逐台升级 worker
在 node01 上先由控制面驱逐业务 Pod,再升级:
kubectl drain node01 --ignore-daemonsets --delete-emptydir-data --timeout=180s
ssh node01
sudo apt-mark unhold kubeadm && sudo apt-get install -y kubeadm='1.31.4-1.1'
sudo kubeadm upgrade node
sudo apt-mark unhold kubelet kubectl
sudo apt-get install -y kubelet='1.31.4-1.1' kubectl='1.31.4-1.1'
sudo systemctl daemon-reload && sudo systemctl restart kubelet
exit
kubectl uncordon node01
worker 上的 kubeadm upgrade node 不带版本号:它从集群读取 kubelet 配置并写入 /var/lib/kubelet/config.yaml,不会碰控制平面。node02 重复同一组命令,不要并行,否则 PDB 可能同时卡住两台的驱逐。
1.5.3.3. 步骤 3 · 收尾验证
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
master01 Ready control-plane 412d v1.31.4
node01 Ready <none> 412d v1.31.4
node02 Ready <none> 412d v1.31.4
kubectl -n kube-system get pods
kubectl -n kube-system get deploy coredns -o jsonpath='{.spec.template.spec.containers[0].image}'
kubectl -n kube-system get ds kube-proxy -o jsonpath='{.spec.template.spec.containers[0].image}'
kubectl run dnstest --image=busybox:1.36 --rm -it --restart=Never -- nslookup kubernetes.default
CoreDNS 与 kube-proxy 的镜像 tag 应随之变为 v1.31 对应版本;如果它们仍停在旧 tag,说明 addon 被 Helm 或手工清单接管,需要按各自的方式单独升级。
1.5.3.4. 插件兼容性核对
控制平面升级不会替你升级 CNI、Ingress 与 CSI。按这张表逐项确认:
组件 |
升级方式 |
检查点 |
|---|---|---|
CoreDNS |
|
是否自建 ConfigMap 覆盖, |
kube-proxy |
随 kubeadm 更新 DaemonSet |
是否被 Helm 接管、是否启用了 nftables 模式 |
CNI |
按 Calico/Cilium 发行版单独升级 |
发布说明中的 Kubernetes 支持矩阵 |
Ingress |
Helm 或清单单独升级 |
ingress-nginx 1.12+ 才声明支持 1.31 |
CSI |
驱动与 sidecar 分开升级 |
external-provisioner 与快照 CRD 的兼容表 |
运行时 |
containerd / CRI-O 与 k8s minor 对齐 |
|
CNI 与 Ingress 的选型细节见 CNI 网络 与 负载均衡与 Ingress。
1.5.4. 证书轮换与 etcd 恢复
1.5.4.1. 证书轮换
kubeadm 默认签发 1 年期的叶子证书,upgrade apply 会顺带续签。如果集群长期不升级,需要单独轮换:
sudo kubeadm certs check-expiration
sudo kubeadm certs renew all
sudo kubeadm init phase kubeconfig admin --kubeconfig-dir /etc/kubernetes
sudo systemctl restart kubelet
renew all 只重写 /etc/kubernetes/pki 下的文件,而 admin.conf、controller-manager.conf、scheduler.conf 内嵌了旧的客户端证书,必须用 kubeadm init phase kubeconfig 重新生成,然后重启静态 Pod。kubelet 的客户端证书默认开启轮换(rotateCertificates: true),服务端证书需要显式打开 serverTLSBootstrap 并手动批准 CSR。
1.5.4.2. etcd 快照恢复
单节点 stacked etcd 的恢复流程是「停 etcd → 换数据目录 → restore → 放回静态 Pod 清单」:
#!/usr/bin/env bash
set -euo pipefail
SNAP="${1:?usage: restore-etcd.sh <snapshot>}"
sudo mv /etc/kubernetes/manifests/etcd.yaml /root/etcd.yaml
sudo mv /var/lib/etcd /var/lib/etcd.broken
sudo ETCDCTL_API=3 etcdctl snapshot restore "$SNAP" --data-dir /var/lib/etcd
sudo mv /root/etcd.yaml /etc/kubernetes/manifests/etcd.yaml
恢复后 etcd 会以快照时刻的状态启动,快照之后写入的对象全部丢失。三节点 etcd 不能只恢复一个成员就宣布完成:要么恢复全部成员并重建 quorum,要么恢复一个成员后用 etcdctl member add 重新加入其余成员。恢复前先确认 apiserver 已停止写入,否则会制造新的不一致。
警告
回滚不是 downgrade
kubeadm 不支持把控制平面降回旧 minor。真正的回滚只有两条路:用升级前的 etcd 快照恢复并重装旧版本二进制,或者在测试环境验证后重建集群。worker 可以重装旧版 kubelet 包回退,但一旦控制平面已写入新版本数据,降级 etcd 才是唯一一致的选择。
1.5.5. 升级失败与回滚
现象 |
可能原因 |
处理方式 |
|---|---|---|
|
etcd 成员异常或磁盘满 |
先修 etcd; |
升级后 apiserver 起不来 |
证书过期、 |
|
kubelet |
containerd 与 kubelet 配置不一致 |
统一为 |
|
副本数不足或 PDB |
先扩容副本或临时放宽 PDB,记录变更 |
worker 升级后 |
CNI 版本不兼容新 kubelet |
升级 CNI; |
Ingress 返回 404 / 503 |
控制器版本过旧,或 Endpoints 尚未收敛 |
升级 ingress-nginx;观察 |
|
客户端与 apiserver 版本偏差超 1 个 minor |
升级 kubectl 到 |
升级后 CoreDNS |
自定义 Corefile 与新版 CoreDNS 插件不兼容 |
回退 ConfigMap,按新版语法修正后滚动重启 |
回滚的决策点要提前定:如果 30 分钟内无法让控制平面恢复 Ready,直接走快照恢复,而不是继续在故障集群上试命令。恢复完成后先验证 kubectl get nodes、etcd endpoint health 与关键 Namespace 的对象数量,再恢复业务流量。
验证清单
三个节点
VERSION均为目标版本,STATUS全Ready。kubectl -n kube-system get pods无非 Running 的静态 Pod 与 addon。CoreDNS 能解析
kubernetes.default,跨节点 Pod 互通。Ingress 入口返回预期状态码,证书链有效。
kubeadm certs check-expiration中所有证书剩余时间大于 30 天。etcd
endpoint health为 healthy,快照可 restore 到测试目录。关键 Deployment 副本数恢复,PDB 不再阻止驱逐。
监控里 apiserver 5xx、etcd leader change、节点 NotReady 无异常抬升。
1.5.6. 常见问题与排错
问题 |
排查入口 |
|---|---|
升级顺序能不能反过来? |
不能。先升 kubelet 会违反「kubelet 不得新于 apiserver」,API 会拒绝注册或行为未定义 |
能不能跳过 v1.30 直接到 v1.31? |
不能。kubeadm 只支持逐 minor;跨版需要多次 apply |
控制平面节点要不要 drain? |
第一个控制平面通常直接 apply,接受 API 抖动;其余控制平面先 drain 再 |
升级后旧镜像能删吗? |
确认回滚窗口结束后,用 |
托管集群也要做这些吗? |
控制平面由云厂商升级,节点池升级仍需 drain、PDB 与插件兼容检查,命令换成各云的 CLI |
1.5.7. 进阶挑战
在测试集群故意把 worker 的 kubelet 升到比 apiserver 新一个 minor,记录
kubectl get nodes与 kubelet 日志的表现,再按正确顺序修复。用
kubeadm upgrade apply --dry-run观察预检项,给每一项写一条对应的自动化检查命令。把 etcd 快照恢复演练做成 CronJob 之外的 runbook:在独立目录 restore 一份快照,用
etcdutl snapshot status校验后删除。关闭一个节点的 kubelet 证书自动轮换,手动走完 CSR 申请与批准流程,比较两种方式的运维成本。
1.5.8. 小结
升级的主线是「控制平面先行、worker 逐台、插件单独跟进」,version skew 决定了这个顺序不能随意调整。
upgrade plan负责告诉你目标版本与手动项,upgrade apply负责控制平面,upgrade node负责其余节点,三者不要混用。回滚的底气来自可验证的 etcd 快照与旧版本安装包,而不是
kubeadm的降级参数——它并不存在。
延伸阅读