三台物理机上把 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 |
| KubeVirt | 13 台运行中的虚机:10 台业务、3 台保留的实验虚机 |
| Helm release | 69 个 |
| Ceph | 3 个 MON、18 个 OSD、745 个 PG |
节点数量减少,并没有让维护更简单。排空一台机器,会同时触及数据库主备、虚机磁盘、Ceph OSD、网络组件,以及依赖它们的镜像仓库。
因此,本次只保留了上次“逐项核对、保留旧文件、逐节点恢复”的思路,没有直接照搬旧 inventory 或整段升级命令。
本次实际升级的完整清单
最初确定的是 15 项组件族。执行中发现 Multus 的缓存兼容问题,另行纳入 4.3.1 和存量缓存修复,最终如下。
| 组件 | 升级前 | 本次结果 |
|---|---|---|
| kubeasz 管理代码、控制容器 | 3.6.7 | 3.6.8,保留现有定制 |
| 控制容器 Ansible / Python | ansible-core 2.14.4 / Python 3.11.3 | 2.20.9 / 3.12.14,ansible.posix 2.2.2 |
| Kubernetes 六个程序 | 1.33.1 | 1.34.12 |
| etcd / etcdctl / etcdutl | etcd、etcdctl 3.5.21;节点未配齐 etcdutl | 3.5.34 → 3.6.15,工具同步补齐 |
| containerd / ctr / shim 二进制 | 2.1.1 | 2.2.9 → 2.3.6;存活的旧 shim 另述 |
| runc | 1.2.6 | 1.4.3 |
| Calico node / CNI / kube-controllers | 3.28.4 | 3.31.7 |
| calicoctl | 3.28.3 | 3.31.7 |
| 通用 CNI plugins | 多项为 1.6.2 | 1.8.0,实际更新 14 个通用插件 |
| CoreDNS | 1.12.1 | 1.12.4 |
| NodeLocal DNSCache | 1.25.0 | 1.26.4 |
| metrics-server | 0.7.2 | 0.8.1 |
| crictl | 1.32.0 | 1.34.0 |
| kubeasz 内置 Helm | 3.17.2 | 3.21.4;日常使用的另一份本来就是此版本 |
| kubeasz 内置 Docker Compose | 2.32.4 | 2.39.3,只更新 CLI |
| Multus thick | 4.3.0 | 4.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 |
| Ceph | 18 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。二进制文件替换后,原进程仍可以继续持有旧可执行文件。
检查这一层,可以按进程而不是磁盘路径读取版本:
# 在节点上只读核对;按实际权限执行。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" -vdone这些旧 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:06 | Multus 修复、缓存验证、PVC 虚机探针通过 |
| 03:42 | containerd 两轮完成 |
| 03:52 | etcd 两轮完成 |
| 04:03 | 三台 Kubernetes 控制面到 1.34.12 |
| 04:17 | node-b 恢复验收完成 |
| 04:22–04:51 | node-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 生命周期