集群升级与维护

概念引入

你还记得第一次用 Kind 创建集群的场景吗(#02)?创建集群只是开始——生产环境的集群需要定期升级来修漏洞、加功能。

集群不是装完就一劳永逸的。K8s 每 4 个月发一个新版本,每个版本维护约 14 个月。不升级意味着:

  • 🔒 安全漏洞没人修
  • 🐛 Bug 永远存在
  • 🚫 新特性用不上

集群运维的三大核心任务:

集群运维三大任务

滚动升级控制面

定期备份

过期前续期

🔧 升级
kubeadm upgrade

💾 备份
etcd snapshot

📜 证书
certs check/renew

Control Plane

S3/远程存储

证书文件

学完本篇你将能够:

  • 用 kubeadm 执行集群控制面的滚动升级,最小化停机时间
  • 用 etcdctl 创建和恢复 etcd 快照,应对集群灾难场景
  • 检查和续期集群证书,防止证书过期导致集群不可用

原理讲解

etcd 备份与恢复

etcd 是 K8s 的"大脑"——所有集群状态都存在里面。etcd 坏了 = 集群失忆 = 灾难。

# 安装 etcdctl
ETCD_VER=v3.5.17
curl -L https://github.com/etcd-io/etcd/releases/download/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz | tar xz
sudo mv etcd-${ETCD_VER}-linux-amd64/etcdctl /usr/local/bin/
# 创建快照(备份)
ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-$(date +%Y%m%d).db \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

# 验证快照
ETCDCTL_API=3 etcdctl snapshot status /backup/etcd-20260722.db --write-out=table

# 恢复快照
ETCDCTL_API=3 etcdctl snapshot restore /backup/etcd-20260722.db \
  --data-dir=/var/lib/etcd-restore
# 然后修改 etcd manifest 的 data-dir 指向恢复的目录

恢复流程

备份流程

正常运行

etcd

API Server

etcdctl snapshot save
→ etcd-20260722.db

etcdctl snapshot restore
→ 恢复数据目录

建议 说明
备份频率 每日 自动备份(CronJob 或 systemd timer)
备份位置 异地存储(S3/GCS/NFS),别和集群放在同一台机器
验证备份 每月 做一次恢复演练
保留策略 保留最近 30 天 的备份

kubeadm 集群升级

升级有严格的顺序和版本限制:

版本升级规则:
  - 不能跳版本!1.28 → 1.29 ✅   1.28 → 1.30 ❌
  - 控制面先升级,再升级节点
  - kubectl / kubelet 版本 ≤ kube-apiserver 版本

Step 3: 升级工作节点

kubectl drain ← 先排空

kubeadm upgrade node

升级 kubelet + 重启

kubectl uncordon ← 恢复调度

Step 2: 升级控制面

kubeadm upgrade apply v1.29.x

升级 kubelet + kubectl

重启 kubelet

Step 1: 升级 kubeadm

apt upgrade kubeadm

kubeadm upgrade plan ← 检查升级路径

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

# 2. 查看升级计划
kubeadm upgrade plan

# 3. 执行控制面升级
kubeadm upgrade apply v1.29.6

# 4. 升级 kubelet 和 kubectl
apt install -y kubelet=1.29.6-* kubectl=1.29.6-*
systemctl daemon-reload && systemctl restart kubelet

# 5. 升级工作节点(在每个节点上)
kubectl drain worker-1 --ignore-daemonsets --delete-emptydir-data
# SSH 到 worker-1
kubeadm upgrade node
apt install -y kubelet=1.29.6-*
systemctl restart kubelet
kubectl uncordon worker-1

证书管理

K8s 控制面的所有组件都用证书做 TLS 通信。证书有有效期——过期了控制面就挂了。

# 检查所有证书的过期时间
kubeadm certs check-expiration
# 输出:
# [check-expiration]
# admin.conf                 Jul 22, 2027 15:00 UTC   364d
# apiserver                  Jul 22, 2027 15:00 UTC   364d
# apiserver-kubelet-client   Jul 22, 2027 15:00 UTC   364d
# ...

# 续期所有证书
kubeadm certs renew all

# 续期后重启控制面组件才能生效
#(kubeadm 自动处理 manifest 目录变化,kubelet 会重启静态 Pod)

关键证书

ca.crt
K8s CA 根证书
(10 年有效)

apiserver.crt
API Server 服务端证书

etcd/server.crt
etcd 服务端证书

front-proxy-client.crt
聚合 API 客户端证书

⚠️ CA 证书过期 = 整个集群需要重建
定期用 kubeadm certs check-expiration 检查!

节点维护:Cordon 与 Drain

命令 作用 Pod 行为
kubectl cordon 标记节点为不可调度 已有 Pod 继续运行,新 Pod 不会调度过来
kubectl drain 排空节点 驱逐所有 Pod + 标记不可调度
kubectl uncordon 恢复调度 允许新 Pod 调度过来
# 维护前
kubectl cordon worker-1          # 1. 先阻止新 Pod
kubectl drain worker-1           # 2. 再排空已有 Pod
        --ignore-daemonsets       #    DaemonSet 不管
        --delete-emptydir-data    #    允许删除 emptydir 数据
        --timeout=300s            #    最多等 5 分钟

# 维护完成后
kubectl uncordon worker-1         # 3. 恢复调度

动手实验

配套实验位于 docs/labs/beginner/cluster-upgrade/

Kind 用 Docker 容器模拟节点,升级流程有所不同。本实验模拟核心操作。

步骤 1:部署实验环境

cd docs/labs/beginner/cluster-upgrade
bash setup.sh

步骤 2:检查当前版本

kubectl version --short 2>/dev/null || kubectl version
kubectl get nodes -o wide

步骤 3:模拟 etcd 备份

# etcdctl 在 etcd 静态 Pod 容器内,不在 Kind 外层容器中
# 通过 kubectl exec 进入 etcd Pod 执行备份
ETCD_POD=$(kubectl get pods -n kube-system -l component=etcd -o jsonpath='{.items[0].metadata.name}')

kubectl exec -n kube-system $ETCD_POD -- \
  etcdctl snapshot save /tmp/etcd-backup.db \
    --cacert=/etc/kubernetes/pki/etcd/ca.crt \
    --cert=/etc/kubernetes/pki/etcd/server.crt \
    --key=/etc/kubernetes/pki/etcd/server.key

# 验证快照
kubectl exec -n kube-system $ETCD_POD -- \
  etcdctl snapshot status /tmp/etcd-backup.db --write-out=table

步骤 4:检查证书过期时间

docker exec k8s-guide-control-plane kubeadm certs check-expiration

步骤 5:模拟节点 Cordon + Uncordon

# 获取节点名
NODE=$(kubectl get nodes -o jsonpath='{.items[0].metadata.name}')

# Cordon(不可调度)
kubectl cordon $NODE
kubectl get nodes  # 观察 STATUS 列出现 SchedulingDisabled

# 尝试创建新 Pod(会被挂起)
kubectl run test-schedule --image=busybox:1.36 --restart=Never -- sleep 10
kubectl get pods -w  # 观察 test-schedule 处于 Pending

# Uncordon(恢复调度)
kubectl uncordon $NODE
kubectl get pods  # test-schedule 应该被调度并完成

步骤 6:清理

bash teardown.sh

自检问题

  1. [基础] kubectl cordonkubectl drain 有什么区别?什么时候用哪个?

  2. [理解] K8s 为什么不允许跳版本升级(如 1.28 → 1.30)?etcd 备份为什么必须异地存储?

  3. [应用] 你管理一个 3 节点生产集群(K8s 1.28),需要升级到 1.29。写出完整的升级步骤和执行顺序。

查看答案
  1. Cordon 只是标记节点不可调度,不影响已有 PodDrain 会驱逐所有 Pod 然后标记不可调度。Cordon 用在"我想让这个节点慢慢闲置"(比如怀疑硬件有问题但不确定),Drain 用在"我现在就要把节点拿去做维护"(比如升级内核、换硬盘)。

  2. 不允许跳版本是因为 API 废弃(deprecation)策略——K8s API 的 Beta 版本在 3 个版本后移除。如果你从 1.25 跳到 1.29,中间可能有些 API 版本已被移除,你的老 YAML 直接报错找不到。逐版本升级可以让你在每个版本检测 API 废弃警告并修复。etcd 备份必须异地因为如果备份和集群在同一台机器/磁盘上,硬件故障(磁盘坏、机房断电)会同时毁掉集群和备份。

  3. 完整步骤:

# 准备工作
# 1. etcd 备份
etcdctl snapshot save /backup/pre-upgrade-1.29.db
# 2. 确认所有节点状态正常
kubectl get nodes && kubectl get pods -A | grep -v Running

# 控制面节点
# 3. 升级 kubeadm
apt install -y kubeadm=1.29.6-*
# 4. 查看升级计划
kubeadm upgrade plan
# 5. 执行升级
kubeadm upgrade apply v1.29.6
# 6. 升级 kubelet
apt install -y kubelet=1.29.6-* kubectl=1.29.6-*
systemctl restart kubelet

# 工作节点(逐个操作)
# 7. drain
kubectl drain worker-1 --ignore-daemonsets --delete-emptydir-data
# 8. 在 worker-1 上升级 kubeadm + kubelet
# 9. uncordon
kubectl uncordon worker-1
# 10. 重复 7-9 直到所有工作节点升级完毕

# 验证
kubectl version && kubectl get nodes

下一步

运维技能齐了。最后一篇——把 29 篇的知识全部串起来,部署一个完整的微服务应用:

30. 综合实战:微服务部署

📚 本文来自 K8s Guide —— 开源免费的 Kubernetes 中文学习指南

  • 🗺️ 初学者轨道 + 面试轨道,从零基础到拿 Offer 一站式覆盖
  • 🧪 每篇文章配套 Kind 实验脚本,本地一键运行
  • 🔗 本文源码:docs/beginner/20-gateway-api.md
  • ⭐ 如果对你有帮助,欢迎 Star! github.com/callmebg/k8s-guide
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐