自建 Kubernetes 安全升级 Hands-on
验证:未记录
- 适用版本
- 未记录
- 验证范围
- 尚未登记验证记录。
本文缩写(英文全称与中文含义)
| 缩写 | 英文全称/名称 | 中文含义 |
|---|---|---|
| API | Application Programming Interface | 应用程序编程接口 |
| AZ | Availability Zone | 可用区 |
| CNI | Container Network Interface | 容器网络接口 |
| CPU | Central Processing Unit | 中央处理器 |
| CRD | Custom Resource Definition | Kubernetes 自定义资源定义 |
| CRI | Container Runtime Interface | 容器运行时接口 |
| CSI | Container Storage Interface | 容器存储接口 |
| DNS | Domain Name System | 域名系统 |
| GitOps | GitOps | 以 Git 保存期望状态、由自动化持续协调实际状态的运维方式 |
| GPU | Graphics Processing Unit | 图形处理器 |
| HA | High Availability | 高可用 |
| IaC | Infrastructure as Code | 基础设施即代码 |
| IAM | Identity and Access Management | 身份与访问管理 |
| IP | Internet Protocol | 网际协议;IP 地址是网络寻址标识 |
| LB | Load Balancer | 负载均衡器 |
| OS | Operating System | 操作系统 |
| PDB | Pod Disruption Budget | Pod 中断预算,约束自愿驱逐 |
| PKI | Public Key Infrastructure | 公钥基础设施 |
| PV | Persistent Volume | 持久卷 |
| PVC | Persistent Volume Claim | 持久卷申领 |
| RKE2 | Rancher Kubernetes Engine 2 | Rancher Kubernetes 发行版 |
| SLO | Service Level Objective | 服务等级目标 |
| SSH | Secure Shell | 安全远程登录协议 |
适用:kubeadm、Ubuntu/Debian、systemd、containerd、3 个控制面节点。单控制面需接受 API 中断;RKE2、k3s、Kubespray 等应使用各自升级工具,不能直接套用 kubeadm。命令使用 Bash,逐段执行,不能整篇一键运行。
1. 先确定升级路径#
| 类型 | 做法 |
|---|---|
| 补丁:1.N.a → 1.N.b | 同一 minor,可直接到已验证的目标 patch;仍检查变更记录、备份并滚动升级 |
| 次版本:1.N → 1.(N+1) | 先补齐当前 minor 的稳定补丁,再完成一次完整升级与验收 |
| 跨多个次版本 | N → N+1 → N+2,每一跳都完成控制面、插件、节点、备份与观察;禁止控制面跳级 |
| 真正主版本:1.x → 2.x | 另查届时迁移政策,本手册不假设兼容 |
| 严重落后、依赖断档 | 优先新建受支持集群,迁移应用与数据,再切流 |
通常口语里的“跨大版本”指 minor 升级。HA API Server 最多相差一个 minor;kubelet 不能比它可能访问的 API Server 更新;kubectl 保持在服务端前后一个 minor 内。本文采用更保守顺序:全部控制面完成 → 控制面 kubelet → worker。版本偏差规则
2. 开始前必须通过的检查#
| 检查 | 放行条件 |
|---|---|
| 环境 | 确认 context、节点名/IP、LB、部署工具版本;有主机 SSH、sudo 和备用管理入口 |
| API | 查目标版本移除项;扫描 Git、Helm 渲染结果、审计日志、Operator/CRD 和 webhook;只看 api-resources 不够 |
| 兼容 | 列出 CNI、CSI、CoreDNS、kube-proxy、Ingress、监控、安全组件、Service Mesh 的当前/目标版本和支持矩阵 |
| 主机 | 核对 OS、内核、CRI/containerd、cgroup、证书到期、时间同步、磁盘空间、镜像仓库连通性 |
| 可用性 | 核心服务多副本且跨节点/可用区;PDB 允许一次驱逐;readiness、优雅退出和 LB 摘流已验证 |
| 容量 | 少一台节点仍可调度;检查 requests、亲和性、污点、GPU、卷所在 AZ、卷挂载数量及 IP 余量 |
| 数据 | etcd 快照和异机副本校验通过;PKI、配置、加密密钥可恢复;PV/数据库另备份,隔离恢复演练成功 |
| 变更 | 测试环境完成同路径升级;冻结应用/Schema 变更,防止 GitOps、自动缩容与升级冲突;指定负责人和恢复时限 |
按API 迁移指南逐跳检查。插件尽量先升级到同时支持新旧 Kubernetes 的版本;没有兼容交集时,按供应商迁移步骤拆分操作。不要同时换 OS、CNI 和 CPU 架构。
以下从管理机采集基线;保存输出到受限目录,不能把 Secret/etcd 快照提交 Git:
kubectl config current-context
kubectl version
kubectl get nodes -o wide
kubectl get pods -A -o wide
kubectl get pdb -A
kubectl get pvc -A
kubectl get events -A --sort-by=.metadata.creationTimestamp
kubectl get --raw='/readyz?verbose'在控制面检查 sudo kubeadm certs check-expiration,并按 etcd 运维与灾备执行全成员健康检查、快照和恢复演练。etcd 备份不包含 PV 业务数据;外部 etcd 必须另有符合其版本政策的滚动升级方案。
升级前写下停止阈值:例如关键交易连续失败、5xx 超 SLO、p95 持续高于基线、DNS/PVC 异常、节点超过约定时间不 Ready。阈值和观察时间按业务调整,不能只验收 Pod Running。
每一跳升级先填写 变更执行记录,明确节点/业务超时阈值、恢复负责人及最晚决策时间;回退资格以本篇恢复章节和当次平台查询结果为准。
3. 每台主机准备精确版本#
先从目标 minor 的官方 kubeadm 文档确认路径。下面的 1.N 等是占位符,必须替换;包版本从仓库查询,不能猜测后缀。每换一台主机重新设置变量。
export TARGET_MINOR='1.N'
export TARGET_VERSION='1.N.P'
# 已采用 pkgs.k8s.io 的主机;先备份并检查现有源,避免重复配置。
cat /etc/apt/sources.list.d/kubernetes.list
sudo cp -a /etc/apt/sources.list.d/kubernetes.list /etc/apt/sources.list.d/kubernetes.list.before-upgrade
sudo install -d -m 0755 /etc/apt/keyrings
curl -fsSL "https://pkgs.k8s.io/core:/stable:/v${TARGET_MINOR}/deb/Release.key" -o /tmp/kubernetes-release.key
sudo gpg --dearmor --yes -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg /tmp/kubernetes-release.key
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v${TARGET_MINOR}/deb/ /" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
apt-cache madison kubeadm kubelet kubectl确认签名与仓库更新成功,再设置查询到的精确包版本并安装 kubeadm;此时不升级 kubelet:
export PKG_VERSION='1.N.P-1.1' # 替换为查询结果,三包均须存在
sudo apt-mark unhold kubeadm
sudo apt-get install -y "kubeadm=$PKG_VERSION" && sudo apt-mark hold kubeadm
kubeadm version
sudo kubeadm config images pull --kubernetes-version "v$TARGET_VERSION"任一命令失败即停下修复;不要在下载或安装失败后继续。离线集群先同步目标镜像和软件包。
4. 顺序升级控制面#
一次只操作一台,始终保持 etcd 多数派。先在第一台控制面执行:
sudo kubeadm upgrade plan
# 人工核对目标、etcd/CoreDNS/kube-proxy 变化后执行,保留交互确认。
sudo kubeadm upgrade apply "v$TARGET_VERSION"检查本机静态 Pod、API/LB 与 etcd 健康;必要时先从 LB 摘除故障 API 后端。确认恢复正常,再在其余控制面逐台完成第 3 节并执行:
sudo kubeadm upgrade node每台都验收后再继续下一台。不要在其他控制面重复 upgrade apply,也不要并行重启控制面。kubeadm 的较新版本会等控制面全部升级后处理 CoreDNS/kube-proxy;旧集群要查对应版本行为。CNI 按兼容计划单独升级。kubeadm 升级流程
确认每个 API Server 静态 Pod 镜像都已到目标版本,并检查各后端健康;不能只经 LB 查询一次 kubectl version 就认定全部完成。
5. 逐台升级 kubelet 与 worker#
先控制面节点,再一台普通 worker 试点,最后其余 worker。每台重复以下过程;控制面已执行 upgrade node/apply,无需再执行。
在管理机:
export NODE='替换为一个节点名'
kubectl get pods -A --field-selector "spec.nodeName=$NODE" -o wide
kubectl drain "$NODE" --ignore-daemonsets --timeout=15mdrain 失败就停止:先处理 PDB、容量、裸 Pod、local PV/hostPath/emptyDir。不要默认加 --force、--disable-eviction 或 --delete-emptydir-data;后者会丢弃临时卷数据,必须先确认可丢弃。DaemonSet、静态 Pod 不会像普通 Pod 一样迁走。安全排空节点
在当前节点:worker 先执行第 3 节安装 kubeadm,再执行 sudo kubeadm upgrade node。随后所有节点执行:
sudo apt-mark unhold kubelet kubectl
sudo apt-get install -y "kubelet=$PKG_VERSION" "kubectl=$PKG_VERSION" && sudo apt-mark hold kubelet kubectl
sudo systemctl daemon-reload
sudo systemctl restart kubelet
sudo systemctl is-active kubelet
sudo journalctl -u kubelet -n 60 --no-pager没有安装 kubectl 的 worker 只升级 kubelet。若主机确需重启,在排空状态逐台进行,不能顺带批量升级 OS。
回管理机,确认新版本、Ready、CNI/CSI DaemonSet 健康后恢复调度:
kubectl get node "$NODE" -o wide
kubectl wait --for=condition=Ready "node/$NODE" --timeout=5m
kubectl uncordon "$NODE"
kubectl get pods -A -o wide在试点节点实际调度测试 Pod,验证 DNS、Service、外网/私网、NetworkPolicy、PVC 读写、业务身份及外部 LB 请求。通过后逐台推进,避免只是看到旧节点上的业务还正常。
每批可执行检查#
在管理机选定本批新节点上的实际业务 Pod,不要拿旧节点健康结果代替。先核对 Pod 的 NODE 列,再填写变量;命令只读,需 kubectl 权限。
export CHECK_NS='orders'
export CHECK_DEPLOYMENT='orders-api'
kubectl -n "$CHECK_NS" get pods -o wide
export CHECK_POD='REPLACE_POD_ON_UPGRADED_NODE'
kubectl -n "$CHECK_NS" wait --for=condition=Ready "pod/$CHECK_POD" --timeout=5m
kubectl -n "$CHECK_NS" rollout status "deployment/$CHECK_DEPLOYMENT" --timeout=5m
kubectl -n "$CHECK_NS" get pod "$CHECK_POD" -o jsonpath='{.spec.nodeName}{"\n"}{range .status.containerStatuses[*]}{.name}{" ready="}{.ready}{" restarts="}{.restartCount}{" last="}{.lastState}{"\n"}{end}'
kubectl -n "$CHECK_NS" get events --field-selector "involvedObject.kind=Pod,involvedObject.name=$CHECK_POD" --sort-by=.metadata.creationTimestamp预期:Pod 位于本批节点、Ready、发布完成;观察窗口内无新增异常重启或挂载/网络事件。然后执行该应用既有业务验收,并通过日志/追踪确认请求确实命中新节点副本;涉及数据库、消息和卷时必须覆盖实际读写。任一失败不推进下一批;仅上述命令成功仍不构成业务验收。
6. 验收与故障处置#
| 现象 | 行动 |
|---|---|
| 单个 worker 异常 | 保持 cordon,暂停下一台;查 kubelet/runtime/CNI/CSI 日志;修复或按当前控制面兼容版本重建节点 |
| 一个控制面异常 | 停止继续升级,保护剩余 etcd 多数派;修复本机配置/证书/磁盘,可按原计划重试失败的 kubeadm 操作 |
| API/etcd 大面积失效 | 启动预演过的灾备流程,隔离旧控制面并评估数据回退损失;不能往运行中集群随意塞入旧快照 |
| 应用不兼容 | 优先恢复兼容应用/插件;控制面降级不是 kubeadm 支持的通用回滚方案 |
kubeadm 生成的 manifest/etcd 临时备份只用于对应故障恢复,不能代替灾备。不要直接把 kubelet、API Server 和 etcd 二进制统一降级来“回滚”。
结束前确认:所有节点目标版本且无遗留 cordon;控制面/etcd、插件、PVC 正常;业务读写和异步任务通过;监控稳定覆盖代表性流量及定时任务。保存版本清单、结果、耗时和新备份,更新 IaC/GitOps,再恢复暂停的自动化。完成本跳观察后才开始下一 minor。
跨度过大采用新集群迁移:还原对象与数据 → 验证 IAM/网络/存储 → 小流量验证 → 停写并完成最终同步 → 切 LB/DNS → 保留旧集群观察。防止两边同时写入;DNS 缓存和切流后的新增数据必须纳入回切方案。
核对日期:2026-09-23。本文命令未在真实集群执行;落地前必须在与你生产相同的版本与插件组合上演练。