6844 字
34 分钟

三台物理机上把 Kubernetes 升到 1.34.12

2026-09-29 凌晨,把三台物理节点上的 Kubernetes 从 1.33.1 升到了 1.34.12。 同一个窗口里,etcd 经 3.5 补丁过渡到 3.6,containerd 经 2.2 过渡到 2.3,Calico、CNI、DNS 和管理工具也按清单更新。

05:44,升级动作、基础验收、临时资源清理和升级后双份 etcd 快照完成,早于预定的 07:30 截止时间。上午再做只读复查,控制面、数据库、虚机和存储继续正常,但也确认了 27 个旧 containerd shim 仍在运行、两路 cAdvisor 采集失败,以及一些未闭环的应用恢复和备份问题。

文中时间均为北京时间,三台主机用 node-a、node-b、node-c 代称,省略实际地址、备份目录、镜像仓库地址和凭据。过程依据当晚执行记录,后续状态截至 09:26–09:49 的只读复查;这是一份本环境的升级复盘,不是可以原样执行的通用脚本。

从上一次升级继承了什么#

上一篇七台虚机上把 Kubernetes 升到 1.33.1,发生在 08-22 夜里到 08-23 凌晨。当时集群还在七台 PVE 虚机里,从 1.32.3 升到 1.33.1;一周后的裸机替换是另一件事。

上次沿着 kubeasz 3.6.7 的基线,更新了 Kubernetes、Rook Operator、Calico、CoreDNS、NodeLocal DNS、etcd 和 containerd。Ceph 数据面、runc、metrics-server,以及证书、Ingress、数据库和监控等平台组件,当晚刻意没有一起升级。

这次继续沿用 kubeasz 管理、systemd 运行二进制的部署方式。但环境已经变成三台物理机,每台同时承担 etcd、控制面、业务和 Ceph。维护前的实际规模是:

对象本次窗口基线
物理节点3 台,均承载控制面、etcd、worker 和 Ceph
Kubernetes 工作负载333 个 Pod,190 个 Deployment / StatefulSet / DaemonSet
持久化卷95 组业务 PV/PVC
KubeVirt13 台运行中的虚机:10 台业务、3 台保留的实验虚机
Helm release69 个
Ceph3 个 MON、18 个 OSD、745 个 PG

节点数量减少,并没有让维护更简单。排空一台机器,会同时触及数据库主备、虚机磁盘、Ceph OSD、网络组件,以及依赖它们的镜像仓库。

因此,本次只保留了上次“逐项核对、保留旧文件、逐节点恢复”的思路,没有直接照搬旧 inventory 或整段升级命令。

本次实际升级的完整清单#

最初确定的是 15 项组件族。执行中发现 Multus 的缓存兼容问题,另行纳入 4.3.1 和存量缓存修复,最终如下。

组件升级前本次结果
kubeasz 管理代码、控制容器3.6.73.6.8,保留现有定制
控制容器 Ansible / Pythonansible-core 2.14.4 / Python 3.11.32.20.9 / 3.12.14,ansible.posix 2.2.2
Kubernetes 六个程序1.33.11.34.12
etcd / etcdctl / etcdutletcd、etcdctl 3.5.21;节点未配齐 etcdutl3.5.34 → 3.6.15,工具同步补齐
containerd / ctr / shim 二进制2.1.12.2.9 → 2.3.6;存活的旧 shim 另述
runc1.2.61.4.3
Calico node / CNI / kube-controllers3.28.43.31.7
calicoctl3.28.33.31.7
通用 CNI plugins多项为 1.6.21.8.0,实际更新 14 个通用插件
CoreDNS1.12.11.12.4
NodeLocal DNSCache1.25.01.26.4
metrics-server0.7.20.8.1
crictl1.32.01.34.0
kubeasz 内置 Helm3.17.23.21.4;日常使用的另一份本来就是此版本
kubeasz 内置 Docker Compose2.32.42.39.3,只更新 CLI
Multus thick4.3.04.3.1,另修复经过审阅的缓存

这里的 Kubernetes 六个程序,指 apiserver、controller-manager、scheduler、kubelet、kube-proxy 和 kubectl。控制端分发目录也同步了目标文件,避免下一次维护又发回旧版本。

这些项目并不全是 Kubernetes 1.34 的硬性前提。运行时维护、安全修补和默认工具对齐,是这次明确纳入的范围。尤其是 containerd 2.3:选择它,是因为要离开已经结束维护的 2.1 分支,转到受维护的 LTS;不能把它写成“1.34 强制要求 2.3”。containerd 支持周期与版本矩阵

kubeasz 3.6.8 也只是起点。它的发布基线包含 Kubernetes 1.34.1、etcd 3.6.4、containerd 2.1.4、runc 1.3.1 和 metrics-server 0.8.0,本次对这些目标做了明确覆盖,而不是把发布页的数字整套照搬。kubeasz 3.6.8 发布说明

内核、Rook 和业务版本留在哪一层#

宿主机继续运行 Ubuntu 24.04.4、6.8.0-138-generic,整个窗口没有重启物理机,也没有做 kubeadm 接管。此前讨论过的 HWE 和操作系统升级,留到独立窗口。

本次沿用原内核完成了新 Pod、KubeVirt、RBD 和 CephFS 的基础验证,内核升级没有成为这次 1.34 的前置条件。这不代表宿主补丁和重启可以长期忽略;上午复查时,三台主机都还有 reboot-required,需要单独安排。

存储和虚机平台保持 Rook 1.19.10、Ceph 19.2.6、CSI 3.16.2、KubeVirt 1.9.0、CDI 1.66.0。上次要先升 Rook,是因为旧 1.16 分支的兼容范围不覆盖目标;这次 Rook 1.19 的支持范围已经覆盖 Kubernetes 1.34,没有同样的升级理由。Rook 1.19 前置条件

Docker daemon、Registry 服务、pause 3.10、kube-lb,以及 cert-manager、数据库 operator、Ingress、监控和业务镜像版本也保持原状。最终 69 个 Helm release 的 chart、revision、status 都与窗口开始时一致。这不等于 Pod 没有重建:节点排空本来就会触发重调度,部分服务还做了同版本恢复。

原有 XCC 崩溃和 KF2 failed release 单独作为基线例外记录,没有借这次升级处理它们,也没有为清空异常列表去改发布状态。

先准备恢复材料,再进入逐节点流程#

这次没有完成整个组件组合的隔离演练。最终选择是跳过这一项,但保留 新备份、目标包校验、单节点回退文件,以及每个阶段的现场验证。

00:14 开始准备,到 00:40 已有新的 etcd 快照和业务备份。43 份业务备份合计约 18.56 GB,覆盖 PostgreSQL、MariaDB、Redis、两份 VictoriaMetrics 原生快照和 GitLab repositories,在两台主机保存并核对摘要。

检查并不只看文件是否存在:PostgreSQL 读取 dump 目录,MariaDB 检查压缩完整性和结束标记,Redis 检查 RDB,GitLab 的 50 个 bundle 做了 clone/fsck。但这些仍然是备份文件检查,没有完成全量恢复演练,也没有覆盖全部 VM 磁盘、对象数据和所有业务恢复流程。双份文件也仍在同一个集群故障域里。

三台节点分别保存旧二进制、systemd unit、运行配置和 CNI 文件;目标包则逐项对上游摘要、版本输出和现有参数。后续每个关键阶段再取新的 etcd 快照,最后还有一份升级完成后的快照。

管理通道也重新确认过。规划早期的假设是 SSH 和带外管理都依赖同一条 VPN;实际执行时,操作者已经在同一内网。这个差别决定了能否维护 AnyLink 所在节点。管理员这次不依赖 VPN,不代表 VPN 客户端不会断线:AnyLink 单实例停下时,它的客户端仍会中断。

实际顺序是:

恢复材料、精确版本与镜像
→ kubeasz 控制端和管理工具
→ Calico、通用 CNI、Multus 修复
→ runc / crictl
→ containerd 2.2.9 全部完成,再到 2.3.6
→ etcd 3.5.34 全部完成,再到 3.6.15
→ 三台 apiserver,再到 controller-manager / scheduler
→ 逐节点停虚机、排空、更新 kubelet / proxy、恢复并验收
→ DNS、metrics-server、源文件对齐与最终验证

需要逐节点处理的部分,主要按 node-b → node-c → node-a 推进。每次只维护一台,恢复它的 Kubernetes、etcd、Ceph 和业务后,再动下一台。

控制端更新也没有跑 ezdown -D,生产升级没有调用 ezctl setup/upgrade。管理代码和必要模板经过审阅后更新,原来的 inventory、证书引用和数据配置继续保留。Ansible/Python 的变化只发生在控制容器里,没有替换宿主 Python。

第一个停止点:Pod 能创建,网络却删不干净#

Calico 更新时,先把 DaemonSet 临时设为 OnDelete,逐节点重建、验证,完成后恢复原滚动策略。etcd datastore、IPIP Always、原 IPPool 身份、CIDR、MTU 和证书引用都保持原值。

CNI 目录不能整包覆盖。这里同时有 Calico 安装器管理的文件、通用插件和 Multus 文件。新 Calico 镜像已经不再携带 bandwidth,所以它转入通用 CNI 1.8.0 的更新范围,最终更新了 14 个通用插件。一个目录里的文件来自不同组件,“全部解压覆盖”会把归属关系打乱。

最初的新 Pod、跨节点 HTTP、DNS、附加网卡和 RBD 重挂都通过了。真正的问题出现在删除测试 Pod时:Calico 尝试访问 Kubernetes 的 ClusterInformation,而这个集群实际使用的是 etcd datastore。

把 Multus 4.3.0 的源码和现场缓存对起来后,原因才清楚:

  • ADD 使用完整 conflist,网络能正常建起来。
  • 保存委派配置时,重新序列化的不完整类型丢掉了 etcd 地址、TLS 等字段。
  • 后续 DEL 从这份缓存取配置,Calico 缺少 etcd 参数,转而走了错误的后端。

同一份缓存的 CNINetworkConfigList.Bytes 里仍保留完整配置,这让有依据的恢复成为可能。上游 4.3.1 已包含保留 conflist 委派配置及修复缓存 DEL 的改动。Multus 4.3.1 发布说明

于是暂停运行时升级,先处理 Multus。最初审阅了 268 份候选缓存;执行时只修复仍存在、内容与预期匹配的文件,部分记录已经随正常 Pod 生命周期消失,不能把候选数当成实际写入数。其中四份活动缓存还留着旧 etcd 成员地址,一并对齐;另外 14 份旧迁移遗留缓存没有混进这次修复。

修复前保留原文件和摘要,升级 daemon 与安装器到 4.3.1,继续保留 connectionLimit: 16 和 1 GiB 内存上限。没有顺手清空 IPAM、删除网络接口或清理整个缓存目录。

02:56–03:06,三台节点逐一完成修复,再连续做两轮创建和删除测试,检查新缓存、旧缓存 DEL、跨节点 HTTP、DNS、Multus/br0 和 RBD 数据。网络验收才算闭合。

这一步改变了后面所有阶段的检查方式:新建成功和删除成功必须分别验证。 只测 ADD,会漏掉下一次排空时才触发的问题。

运行时分两轮,虚机探针用现有磁盘方式#

运行时验证最初用了临时 containerDisk 虚机,却卡在缺少 container-disk helper。现场的 KubeVirt 已是 1.9.0,而旧 Kubernetes 上未开启所需的 ImageVolume,生成的 Pod 没有保留对应卷和挂载。

没有为了一个测试临时扩大功能开关范围,而是改成 1 GiB 临时 PVC,写入公开测试镜像,按现有业务使用的 PVC 磁盘方式启动虚机。从另一个节点的测试 Pod 发起 SSH 握手,确认虚机能启动、挂盘和通信。主机源探测曾超时,最终验收使用的是 NetworkPolicy 允许的 Pod 路径,不能把它写成所有访问路径都通过。

先更新 runc 和 crictl,再对 containerd 做两轮完整的逐节点升级:

2.1.1 → 2.2.9:三台全部验证通过
2.2.9 → 2.3.6:三台再次全部验证通过

每台每轮都检查新 Pod 的创建与删除、HTTP、DNS、附加网卡、RBD 重挂,以及 PVC 测试虚机。daemon 停止时,额外保存本机运行时元数据数据库的一致副本。

本环境的 systemd unit 使用 KillMode=process。这两个运行时阶段里,原有虚机 compute 容器的 PID 都保持了下来;真正的虚机停机发生在后面的 kubelet minor 升级排空阶段。这是本次观察结果,不能推成任意 containerd 重启都不会影响虚机。

磁盘上的 config.toml 没有改写,unit 摘要也没变。2.3.6 读取旧配置后会在内存中迁移解析格式,所以“解析结果变成版本 4”与“配置文件已经迁移完成”是两回事。旧 registry/CNI 字段的弃用提示仍然保留,后续要单独迁移。

03:42,三台 containerd 主进程和新创建容器的运行时验证完成。至于仍存活的旧 shim,上午复查还有另一笔账。

etcd 先补 3.5,再升 3.6#

etcd 没有从 3.5.21 直接换到 3.6.15。先把三个成员全部升到 3.5.34,满足上游当前要求的 3.5.32 或更新版本,再进入 3.6。新版检查工具还会检查 WAL 中的旧 v2 内容,不能只看 v2 snapshot。etcd 3.5 → 3.6 升级说明

每次只停止一个成员;若它是 leader,先转移 leadership。成员停止后,离线检查 v2store 和 WAL,三台都没有自定义 v2 内容,因此没有执行任何 v2 数据清理。

进入 3.6 前,还为已停止的成员保存完整数据目录副本。原目录由 etcd 自身完成升级,不拿一个旧目录直接覆盖正在运行的成员。

03:52,两轮完成。三成员健康、成员 ID 保持,在相同 revision 上的 hashkv 一致。这里还要回归 Calico 的 ADD/DEL,因为它直接依赖 etcd,Kubernetes /readyz 正常并不能代替这条检查。

工具也必须跟上。控制端和节点补齐对应版本的 etcdctl、etcdutl;收尾时发现 kubeasz 的旧恢复 role 仍调用已移除的 etcdctl snapshot restore,改为 etcdutl 并检查参数和 Ansible 语法。本次没有运行恢复,也没有借此宣称整个旧恢复 playbook 已经演练可用。

同样,留了旧二进制和快照,不代表任何时候都能直接降级。数据库格式、集群版本和恢复流程仍然需要按对应版本处理。

Kubernetes 本体:先控制面,再排空节点#

03:53 开始 Kubernetes 部分。先升级全部 apiserver,再更新 controller-manager 和 scheduler,最后才处理逐节点的 kubelet、kube-proxy。minor 升级前要排空节点,这是官方版本偏差策略明确要求的步骤。Kubernetes 升级顺序与版本偏差

第一台 API 的“失败”是等待时间设短了#

第一次重启 API Server,执行脚本只给了 systemctl restart 60 秒,随后触发回退,恢复旧 1.33.1,readyz 通过。

回看日志,旧 API 关闭 HTTP 服务恰好等了 60 秒,新版本根本还没启动。所以这不是 1.34 启动失败,而是外层脚本提前判了超时。

把外层等待改成 210 秒,覆盖现有 systemd 的 stop/start 各 90 秒限制后重新执行,没有修改服务 unit 或降低健康检查标准。04:03,三台控制面的 apiserver、controller-manager、scheduler 全部到 1.34.12。

13 台虚机不能靠 drain 自动热迁移#

当时所有 VMI 都不可热迁移,节点维护按短时停机安排。node-b 上先正常停止九台虚机,node-c 三台,node-a 一台;各节点恢复后再继续下一台。

其中五台虚机原模板没有显式固定 MAC 和节点。维护前,临时把当前实际 MAC、当前节点写入模板,保证恢复时沿用原网络身份;恢复检查通过后再还原原模板和电源策略。没有强制关机,也没有为了排空绕过 PDB。

第一次恢复验收还踩了一个检查脚本的问题:guest-agent 报告的网卡里,包含客体内部 Docker、Kubernetes 的虚拟桥。把这些内部接口也纳入 MAC 比对,会把正常变化判成虚机身份变化。修正后,只严格比对 VM 声明的实际网卡,再核对已记录 IP 和原来可用的 SSH 端口,没有为重跑检查再停一次虚机。

停止再启动会生成新的 VMI,所以最终核对的是原逻辑 VM、磁盘绑定、网络身份和运行状态,不能要求所有 VMI UID 从头到尾都不变。

Ceph 和 operator 也在排空路径上#

每台维护前先确认该节点的六个 OSD 可以 ok-to-stop。排空期间,由 Rook 根据状态管理 OSD PDB 和 noout,等待恢复后再推进,没有强制突破预算。

首台排空时,Rook operator 自己也迁移了,新位置启动过程中曾出现 API Service 连接超时。后续维护若会影响 operator 所在节点,先正常驱逐 operator,等它在其他节点稳定、能读取 Ceph 状态,再排空数据面。

Rook 还会自动删除并重建某些 crashcollector/exporter Deployment。它们的 controller UID 变化,需要结合 CephCluster owner、节点、镜像和副本状态解释,不能一边允许 operator 正常工作,一边又声称所有 190 个 controller UID 始终未变。

CoreDNS 在排空期间临时扩到两个跨节点副本,完成后还原。AnyLink 则要单独处理:它通过 nodeName 固定在 node-a,cordon 不能阻止这种直接绑定,所以维护期间从一副本临时缩到零,恢复后还原。最终 HTTPS 入口返回 200,但没有建立真实 VPN 客户端会话。

最费时间的是镜像和应用重新启动#

04:17,node-b 完成恢复。node-c 的 kubelet/proxy 在 04:22 就已到目标版本,接下来的半小时主要花在恢复业务依赖上。

Harbor 的 NotFound 背后还有一层代理#

部分重新调度的 Pod 出现 ImagePullBackOff。起初 Harbor 在数据库和 Pod 迁移期间返回 502/503,等它健康之后,部分回源镜像仍然报 NotFound。

继续追到 ShellCrash:s6 容器处于 Running,Pod 也显示 Ready,但 CrashCore 没有启动,代理端口没有监听。它没有 readiness/liveness 探针,因此 Kubernetes 的 Ready 没有覆盖真正需要的功能。

当时使用原来的启动脚本拉起核心进程,确认监听恢复,再通过代理访问 Docker Registry。匿名请求得到预期 401,说明这条连接链路恢复。没有更换镜像、订阅配置或业务 release。

镜像缓存也暴露了另一个缺口:有的镜像在 containerd 列表里能看到元数据,导出时却缺少 blob。失败的 tar 没有拿去导入;缓存里有名字,不代表已有完整离线材料。

最后从官方来源恢复了七个原版本公共镜像,每一个 image config ID 都与原缓存记录一致,再校验归档摘要并导入目标节点。这里比较的是 image config ID,不能混写成 manifest digest。工作负载中的镜像引用没有改成其他版本,更没有临时换成 latest。

维护最后一台之前,补做了现有业务镜像的缓存准备。最初只预拉本次“要升级的镜像”还不够:排空会把没有升级的应用调度到新节点,它们同样需要完整镜像和可用的拉取链路。

Portal 暴露了节点间的 IPv6 差异#

Harbor Portal 重建到 node-c 后,Nginx 启动失败:

socket() [::]:8080 failed (97)

Portal 配置使用 IPv6 listener,但 node-c 不支持该 socket family;另外两台支持。于是正常驱逐 Portal,临时限制它回到该节点,等它在 node-a Ready 后立即恢复节点调度。没有改宿主 IPv6 配置,也没有把临时调度恢复当成根因修复。

曾审阅 ipFamily.ipv6.enabled=false 的 Helm dry-run,但渲染差异还会改变 core、jobservice、registry 的 Secret 相关 checksum,滚动范围超过一个 Portal,因此当晚没有执行这个应用发布。checksum 变化也不能直接推成密钥值已经变化。

数据库恢复,不代表启动失败的应用会自己恢复#

另一个歌词任务 worker 在数据库主备切换时,首次连接被拒绝,任务队列初始化失败。数据库恢复后,它仍然持续返回 readiness 503。

等数据库正常后,重启同版本 Deployment 才恢复。这里修复的是当晚服务状态,启动依赖重试机制仍要回到应用代码里处理。没有升级数据库、改凭据或额外执行业务迁移。

这三件事都说明了同一个现场限制:平时一直运行的 Pod,不会自动证明它在依赖短暂不可用时还能重新启动。

收尾验收没有停在 kubectl get nodes#

最后更新 CoreDNS、NodeLocal DNS 和 metrics-server,并把控制端的活动 YAML、版本字段和二进制分发副本对齐。metrics-server 的一次校验误抓到了正在删除的旧 Pod,过滤删除时间戳后,确认新版本已正常返回三节点指标,没有把旧对象退出过程判成新版本失败。

05:40 的文件与进程核验中,每节点 29 个目标二进制摘要匹配锁定材料,七个 systemd unit 和 containerd 配置摘要保持原值。常驻 daemon 通过 /proc/<MainPID>/exe 检查实际运行版本,CRI 的 RuntimeReady、NetworkReady 均为 True。

最终基础验收覆盖了这些层次:

层次实际检查
Kubernetes / etcd三节点 Ready 且可调度,API readyz,etcd 三成员健康及同 revision 的 hashkv
网络新 Pod ADD/DEL、跨节点 HTTP、Service、内外部 DNS、Multus 附加网卡
数据库7 套 PostgreSQL 实际 SQL 和 streaming replica;5 套 MariaDB 主备读写角色、复制 IO/SQL 和 primary Service;9 套 Redis 主从角色和复制链路
虚机13 台 VM/VMI Ready,恢复声明的网卡和原可探测 SSH 端口,原电源策略还原
存储95 组原 PV/PVC 的 UID 与绑定保持;四种 RBD/CephFS、SSD/HDD 存储类分别新建 1 GiB 卷,跨节点重挂读回 marker
Ceph18 OSD up/in、3 MON quorum、745 PG active+clean,含正常 scrub
入口26 个域名按实际 HTTP/HTTPS 协议检查,无连接错误或 5xx;Harbor 八组件 healthy
临时资源八个测试 PV/PVC 全部清理,独占测试 namespace 删除,节点解除 cordon,没有遗留 noout

入口验证也有边界。七个 HTTP-only 域名没有强制改用 HTTPS;API 根路径 404、GitLab KAS 的 426 保留原始结果,不能只靠根路径状态码判断完整业务功能。AnyLink 入口响应、虚机 SSH 端口可达,也都不等于已经测试真实 VPN 会话或客机内部的全部应用。

Ceph 当时显示 HEALTH_OK,但三项原有客户端 AUTH 告警仍被静默,不能据此宣称认证整改全部完成。一个旧 OSD 慢操作告警在 daemon 重启后不再显示,也只记录观察结果,没有认定根因已经修复。之前的存储调整另见 Rook/Ceph 的 msgr2、host network 与 CephX 记录。

05:42 清理测试资源,05:44 完成升级后双份 etcd 快照。06:05 的补充检查确认 KubeVirt Manager 能读到 1.34.12,网关 DNS 查询通过;这一段只读,没有继续追加生产变更。

上午复查:还有哪些账没有结清#

09:26–09:49 的只读复查,没有发现需要回退 Kubernetes 的证据。数据库复制、13 台虚机、95 组业务卷和入口基础检查继续通过。但版本表之外,还有以下事项。

主进程到 2.3.6,不等于旧 shim 已全部退出#

三台主机的磁盘二进制和 containerd 主进程都是 2.3.6,现场仍有 27 个 2.1.1 shim,每台九个,另有 295 个 2.3.6 shim。

旧进程对应的是没有随普通工作负载排空而重建的 DaemonSet,包括 Calico、Multus、virt-handler、CSI nodeplugin、MetalLB speaker 和节点 exporter。二进制文件替换后,原进程仍可以继续持有旧可执行文件。

检查这一层,可以按进程而不是磁盘路径读取版本:

Terminal window
# 在节点上只读核对;按实际权限执行。
runtime_pid=$(systemctl show containerd -p MainPID --value)
test "$runtime_pid" -gt 0 && sudo "/proc/$runtime_pid/exe" --version
pgrep -f '^/.*containerd-shim-runc-v2' | while read -r shim_pid; do
sudo "/proc/$shim_pid/exe" -v
done

这些旧 shim 要通过逐节点、逐组件受控重建对应 Pod 来收尾,不能直接 kill。本次完成的是目标二进制、主进程和新建容器验证,所有旧 shim 的退出尚未完成。 这也与前一次 containerd 存活但 CRI 失败的事故相呼应:一个版本命令或一个 systemd 状态,只能证明它实际检查的那一层。

虚机已恢复,五台模板仍提示需要重启#

撤销临时 MAC/节点绑定后,五台 VM 为 RestartRequired=True。原模板已逐项还原,当前 VMI 仍然 Ready,因此没有为了清掉提示再重启一遍。

但原模板仍未固定这些字段。未来重启前,需要明确长期的稳定网络身份策略,不能把这次成功保住 MAC 当成永久保证。

监控 Pod 正常,采集和规则仍可能失败#

复查发现已确认的范围
两个 cAdvisor target down响应约 22.0 / 19.9 MiB,超过 16 MiB 抓取上限;85 个 target 中 83 up、2 down
一条 Ceph 容量增长预测规则失败320 条规则中一条返回 422;两天窗口内新旧 mgr Pod 标签形成重复时序,连接匹配失败
MariaDB 物理备份仍失败CR 为 Complete=True,但 reason 是 JobFailed,实际 Job 没有成功;五套库均未开启 PITR

cAdvisor 这项还有历史证据:升级前的半小时采样里,两节点各 31 个 up 样本已经全为 0。它是本次复查重新确认的旧问题,不能根据监控 Pod 重启后的告警时间,就归因于 Kubernetes 1.34。

Ceph 预测规则失败,也不等于当时容量已经不足;它表示这项预警能力失效。物理备份问题则延续了此前 MariaDB Operator 维护中记录的限制,数据库主备正常不能替代备份成功。

在已检查的集群备份资源、CronJob、主机 cron 和 timer 中,也还没有找到可验收的持续备份覆盖。不能因此断言不存在任何外部调度,但当晚手工做的双份备份,显然不能自动变成日常保护能力。

ShellCrash 自启和探针、Portal 的 IPv6 节点差异、worker 的启动重试,同样只恢复了服务,没有完成长期修复。它们应该先于下一次大范围节点重建得到处理。

这一晚花了多久#

时间实际进展
00:14开始本窗口备份、材料与回退文件准备
00:50控制端和管理工具完成
01:07开始 Calico / 通用 CNI 逐节点升级
01:48因临时虚机探针与 Multus DEL 问题暂停后续阶段
02:56–03:06Multus 修复、缓存验证、PVC 虚机探针通过
03:42containerd 两轮完成
03:52etcd 两轮完成
04:03三台 Kubernetes 控制面到 1.34.12
04:17node-b 恢复验收完成
04:22–04:51node-c 二进制升级后,完成镜像依赖恢复与验收
04:59 起node-a 二进制完成,继续应用恢复与 DNS / metrics 收尾
05:44基础验收、清理与升级后双份快照完成
09:26–09:49只读复查,形成后续问题清单

从 00:14 算起约 五个半小时。这包含准备、诊断、等待恢复和验收,不是集群整体停机时长;虚机与单副本服务是在各自节点维护时分批中断。也不能把它当成下一次升级的固定耗时,镜像冷缓存和应用重新启动能力会明显影响窗口长度。

这次最值得保留的改进,是把验收放到了真实生命周期上:网络要能创建也能删除,卷要能重挂,虚机要能重新启动,数据库要查实际复制角色,代理要发真实请求,运行时要看存活进程。

至于 Kubernetes 版本本身,1.34 的官方结束支持日期是 2026-10-27。本轮目标限定为 1.33 → 1.34,完成这一步后,下一版本的研究和演练仍要尽快安排;升级到一个新版本,并不会自动获得很长的维护余量。Kubernetes 1.34 生命周期