把 Kubernetes 升级从"吞时间怪兽"变成流水线

2026-05-11 22 预计阅读时间:1 分钟
来源:cncf.io AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:10 分钟

Kubernetes 每年发布约三个小版本,每个版本的支持周期大约一年。这意味着你要么跟上节奏定期升级,要么冒险运行一个不再接收安全补丁的集群。现实是,很多团队每次升级都要花数周甚至数月——读 changelog、排查 API 废弃、逐个节点滚动、验证工作负载……升级本身成了工程时间的黑洞。

问题不在于 Kubernetes 太复杂,而在于我们把升级当成了"一次性大事件",而不是一条可重复的流水线。下面是几条把升级时间抢回来的具体策略。

先搞清楚你落后了多少

升级的第一步不是下载新版本,而是量化现状。你需要知道:当前版本距离最新稳定版差几步?中间有哪些 API 已经被移除?

# 查看集群当前版本
kubectl version --short

# 列出所有已废弃但仍在使用的 API 版本(1.22+ 可用)
kubectl get --raw /metrics | grep deprecated_api_version_request_count

# 更直接的方式:用 kubectl explain 检查资源是否仍支持旧 API
kubectl explain deployment.apiVersion

如果你的集群还在跑 1.26,而上游已经到 1.30,中间隔着四个版本——每个版本都有自己的废弃列表。不要跳版本升级,逐个走完,但每一步要尽量自动化。

用 Sonobuoy 把"手动验证"变成一条命令

升级后最耗时间的环节不是升级本身,而是验证"我的工作负载还正常吗"。手动检查几十个服务、看日志、跑集成测试,一轮下来一两天就没了。

Sonobuoy 是 CNCF 的一致性测试工具,但它同样适合做升级后的快速健康检查:

# 安装 sonobuoy
curl -L https://github.com/vmware-tanzu/sonobuoy/releases/download/v0.57.2/sonobuoy_0.57.2_linux_amd64.tar.gz | tar xz
sudo mv sonobuoy /usr/local/bin/

# 对集群跑快速模式(只跑 E2E conformance 的快速子集,约 10-20 分钟)
sonobuoy run --mode quick --wait

# 查看结果
sonobuoy results $(sonobuoy retrieve)

# 清理测试资源
sonobuoy delete

把这条命令嵌入升级流程,你至少省掉了"人肉翻日志确认集群没坏"的半天时间。如果快速模式全过,再跑一轮你的应用级集成测试,信心就很高了。

把升级过程写成可复用的脚本

手动升级最大的问题是不可重复——这次升级的步骤和上次不一样,下次又要重新想。把步骤固化成脚本,升级就从"研究+操作"变成了"执行+观察"。

以下是一个针对 kubeadm 管理的集群升级控制平面的脚本骨架:

#!/usr/bin/env bash
# upgrade-control-plane.sh — 升级 kubeadm 集群控制平面
# 用法: ./upgrade-control-plane.sh <目标版本>,例如 1.30.1-1

set -euo pipefail

TARGET_VERSION="${1:?请指定目标 K8s 版本,如 1.30.1-1}"
KUBEADM_PKG="kubeadm-${TARGET_VERSION}"
KUBELET_PKG="kubelet-${TARGET_VERSION}"
KCTL_PKG="kubectl-${TARGET_VERSION}"

echo "=== 升级目标: v${TARGET_VERSION} ==="

# 1. 升级 kubeadm 本身
echo "[1/4] 安装新版本 kubeadm..."
apt-get update && apt-get install -y "${KUBEADM_PKG}"

# 2. 升级计划预检
echo "[2/4] 预检升级计划..."
kubeadm upgrade plan v${TARGET_VERSION%-*}

# 3. 执行控制平面升级
echo "[3/4] 应用升级..."
kubeadm upgrade apply v${TARGET_VERSION%-*} -y

# 4. 升级 kubelet 和 kubectl
echo "[4/4] 升级 kubelet & kubectl..."
apt-get install -y "${KUBELET_PKG}" "${KCTL_PKG}"
systemctl restart kubelet

echo "=== 控制平面升级完成,等待节点 Ready ==="
kubectl wait --for=condition=Ready node/"$(hostname)" --timeout=120s
echo "=== Done ==="

工作节点升级更简单,可以并行推到多个节点:

#!/usr/bin/env bash
# upgrade-worker.sh — 升级单个工作节点
set -euo pipefail

TARGET_VERSION="${1:?请指定目标版本}"

# 在控制平面执行:标记节点不可调度,驱逐工作负载
NODE_NAME="$(hostname)"
kubectl cordon "${NODE_NAME}"
kubectl drain "${NODE_NAME}" --ignore-daemonsets --delete-emptydir-data --timeout=120s

# 升级软件包
apt-get update && apt-get install -y \
  "kubeadm-${TARGET_VERSION}" \
  "kubelet-${TARGET_VERSION}" \
  "kubectl-${TARGET_VERSION}"

kubeadm upgrade node
systemctl restart kubelet

# 恢复调度
kubectl uncordon "${NODE_NAME}"

把这些脚本放在 Git 仓库里,每次升级只需改版本号参数。升级从"翻文档+手动敲命令"变成 ./upgrade-control-plane.sh 1.30.1-1

用 Cluster API 让升级变成声明式操作

如果你的集群是用 Cluster API 管理的,升级甚至不需要脚本——改一行 YAML 就行:

# 原来的 MachineDeployment
apiVersion: cluster.x-k8s.io/v1beta1
kind: MachineDeployment
metadata:
  name: my-cluster-md-0
spec:
  template:
    spec:
      bootstrap:
        configRef:
          apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
          kind: KubeadmConfigTemplate
          name: my-cluster-md-0
      infrastructureRef:
        apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
        kind: AWSMachineTemplate
        name: my-cluster-md-0
      version: "v1.29.3"   # ← 改这里

versionv1.29.3 改成 v1.30.1,Cluster API 会自动滚动替换节点。旧节点逐个驱逐、销毁,新节点用新版本创建,整个过程和你更新 Deployment 的 rollingUpdate 一样可控。

如果你还在手动管理集群,Cluster API 的迁移成本不小。但如果你已经管理了五个以上的集群,这笔投入会在后续每次升级中收回。

用 managed Kubernetes 把升级推给平台方

EKS、GKE、AKS 这些托管服务把控制平面升级从你的责任清单里移走了。你只需要处理工作负载兼容性。

GKE 的自动升级策略可以直接在集群配置里声明:

# GKE cluster autoscaling + upgrade policy(Terraform 示例)
resource "google_container_cluster" "primary" {
  name     = "my-cluster"
  location = "us-central1"

  # 让 GKE 自动升级控制平面,窗口设在周末凌晨
  maintenance_policy {
    recurring_window {
      start = "2024-01-01T02:00:00Z"
      end   = "2024-01-01T06:00:00Z"
      recurrence = "FREQ=WEEKLY;BYDAY=SA"
    }
  }

  # 节点自动升级
  node_pool_auto_config {
    network_tags {
      tags = ["auto-upgrade"]
    }
  }
}

托管方案省掉了控制平面升级的全部时间,代价是灵活度降低(你不能自定义控制平面组件的参数)和版本可用性滞后(EKS 通常比上游晚 2-3 个月)。对于大多数业务团队,这个交换是划算的。

升级前的 API 废弃扫描

每次升级最隐蔽的坑是 API 废弃。你的 Helm chart 或 YAML 里还在用 v1beta1,新版本已经不认了——升级完直接报错。

在升级前跑一轮扫描:

# 用 pluto 扫描集群和 Git 仓库中的废弃 API
# 安装
curl -L https://github.com/FairwindsOps/pluto/releases/download/v5.18.4/pluto_5.18.4_linux_amd64.tar.gz | tar xz
sudo mv pluto /usr/local/bin/

# 扫描集群中正在运行的废弃 API 资源
pluto detect-helm -o wide

# 扫描本地 Git 仓库中的 YAML 文件
pluto detect-files -d ./k8s-manifests/ -o wide

pluto 会告诉你哪些资源用了即将被移除的 API,以及这些 API 在哪个版本被删除。提前改完,升级当天就不会踩坑。

把升级时间抢回来的清单

做法 省掉的时间 适用场景
Sonobuoy 快速验证 半天~一天的手动检查 所有集群
升级脚本固化 每次省 1-2 天的"重新研究步骤" kubeadm 集群
Cluster API 声明式升级 几乎零操作时间 多集群环境
托管服务自动升级 控制平面升级时间归零 业务集群(不需要深度定制)
pluto 废弃 API 扫描 升级后排查 API 报错的数小时 所有集群,每次升级前必跑

核心思路只有一个:把升级从事件变成流程。能自动化的不要手动,能声明的不要脚本,能交给平台的不要自己扛。每次升级省下来的工程时间,才是真正回到产品开发里的时间。


相关推荐