Rook/Ceph 升级后:msgr2、host 网络和服务端密钥
2026-09-16 到 09-20,这套 Rook/Ceph 从 Ceph 19.2.3 / Rook 1.17.9 走到 19.2.6 / 1.19.10。版本升完之后,又拆了三个窗口:已有卷切 msgr2、服务端迁 host 网络、服务端 CephX 密钥轮换。
到 09-20 14:37(CST,UTC+8),18 个 OSD up/in、745 个 PG clean,68 个生产 RBD 映射和 23 个 CephFS 内核客户端都在用 v2。Ceph 服务端走节点网络,24 个旧类型服务端密钥归零。
Ceph 20 没有升,Rook 1.20 没有升,客户端三项 AUTH 告警也没有全部处理完。 最终状态截至这个时间点,阶段数据保留各自的采样时间。
还是那三台 R730xd,同时承载 etcd、控制面、普通业务和 Ceph。物理机替换见 把跑在 PVE 里的 Kubernetes 换成三台物理机。前面已经做完的 槽 10 WAL/DB 重建 和 HDD 块池、文件系统,这次没有重新做:磁盘、OSD 身份、池和 CRUSH 布局都保留。
下文 YAML 只摘出要合并的字段,不能作为完整 values 覆盖现网。操作顺序和放行条件同样是变更的一部分。
目录
先对清升级到了哪一档
09-20 接着操作前,先把之前的升级记录和现网版本重新对了一遍。operator、CSI、Ceph 服务端是不同层,不能只看一个镜像 tag。
| 时间(CST) | 实际动作 | Helm revision |
|---|---|---|
| 09-16 12:12 | Ceph 19.2.3 → 19.2.6,Rook 仍为 1.17.9 | cluster 33 |
| 09-17 11:06 起 | Rook 1.17.9 → 1.18.11,随后适配 StorageClass | operator 7;cluster 34、35 |
| 09-17 14:23 起 | Rook 1.18.11 → 1.19.10;CSI operator 0.6.0、CSI 3.16.2 | operator 8;cluster 36 |
| 09-20 06:55 收尾 | 生产卷 msgr2 迁移完成 | cluster 37 |
| 09-20 12:44 收尾 | MDS 分散、串行保护、host 网络迁移完成 | cluster 38–42 |
| 09-20 14:37 收尾 | 服务端 CephX generation 1、验收与清理完成 | cluster 43 |
最后三行都没有再换 Ceph、Rook 或 CSI 版本。最终仍是 Ceph 19.2.6 / Rook 1.19.10 / CSI 3.16.2,operator release 仍为 revision 8。
暂缓下一档 Ceph 的原因是通信异常还没收住。09-17 留下三条 OSD crash,其中一条栈经过:
ProtocolV1::handle_connect_message_2 → cephx_verify_authorizer → CryptoKey::decode → abort09-20 00:15 的日志采样里,还能看到来自同一个 v1 客户端的 bad crc in data。这说明错误仍在发生,但仅凭这段栈和 CRC 日志,不能把根因直接定成网卡、MTU 或 Ceph 某一个缺陷。
所以先停在 Squid 19.2.6,把协议、网络和密钥分别处理,再看后续版本。
三件事分开做
| 动作 | 改的是什么 | 对已有业务的主要影响 |
|---|---|---|
| msgr2 | Ceph 连接协议、CSI 默认挂载参数 | 已有内核连接要通过实际重挂切换 |
| host 网络 | Ceph daemon 的地址和网络路径 | 服务端滚动,客户端学习新的 map |
| CephX 轮换 | 身份使用的认证密钥 | 受管理 daemon 重启,管理凭据更新 |
这次选择 ms_mode=prefer-crc,没有打开 Rook 的连接加密开关,不能把切到 v2 写成已经开启数据传输加密。它也不会把旧 aes 类型的 CephX 密钥自动换成 aes256k。这几个开关不能当成同一件事。
顺序选成 msgr2 → host → 服务端 CephX。先把客户端连接核清,再换服务端地址,最后处理认证。三步的验收和恢复边界各自保存。
host 窗口原本建议等 msgr2 覆盖 24 小时日常负载;实际在约 4 小时后继续了。后续虽然分别补了完整的稳定观察,也不能把这一天写成“每一步都已经观察满 24 小时”。
恢复点和测试卷
etcd 快照只覆盖集群状态,不包含 PVC 里的数据库、仓库或虚机磁盘数据。
msgr2 窗口先建立了 etcd 快照。业务数据库备份是在最初的配置滚动恢复后、生产卷重挂前补齐;后面的 host 和 CephX 窗口把完整恢复资料准备放到了发布之前。
业务备份覆盖:
- 7 套 PostgreSQL:19 个数据库 archive、7 份 globals;globals 不导出角色密码。
- 5 套 MariaDB:原生全库逻辑备份,检查退出状态、gzip CRC/EOF 和完成标记。
- 9 套 Redis:从健康副本生成 RDB,用同版本原生工具检查。
- 两个 vmstorage:先创建原生快照,归档时跟随快照中的符号链接,把实际数据带走。
- GitLab repositories:原生备份后,对 40 个 bundle 实际 mirror clone、
git fsck --full。
最后一个窗口共 43 份业务恢复资料,约 13.07 GB,另有变更前后的 etcd 快照。资料放在两台节点的系统盘上,第二份逐文件核对大小和 SHA256,旧窗口的恢复点保留。
这些验证有边界:PostgreSQL 的 TOC、MariaDB 的完成标记、Redis checker,都不能写成整套业务已经做过隔离恢复演练。GitLab repositories 归档也不是全站备份。本次没有虚机镜像备份;msgr2 阶段的虚机是核对模板后正常关机、确认原映射释放,再启动原根盘。
每个窗口另外建三只独立的 1 GiB PVC:RBD、SSD CephFS、HDD CephFS。循环检查原文件 SHA256,再写新数据、fsync、读回校验。先记清 namespace、Pod、PVC、PV 的 UID 和 CSI handle,结束时只回收本次对象。
业务入口、数据库复制、虚机状态和这些测试卷一起看。Pod Ready 和 helm --wait 都不够单独证明存储路径已经恢复。
已有卷怎么切到 v2
01:43–02:00 的盘点:68 个 RBD 映射中,三台 Ubuntu 虚机的根盘已经是 v2,其余 65 个仍是 v1;当时的 20 个 CephFS 内核客户端也仍为 v1。
发布的关键字段很少。下面是合并进完整 Helm values 的片段,不能拿它覆盖原来的池、设备和文件系统配置:
cephClusterSpec: network: connections: requireMsgr2: true csi: cephfs: kernelMountOptions: ms_mode=prefer-crcRBD 另外核对了 client.csi-rbd-node 的 rbd_default_map_options=ms_mode=prefer-crc。CSI 的 MON 列表改用 :3300。
但配置改变主要影响新连接。已经挂着的内核客户端不会因为 Helm deployed 就全部重新协商。生产消费者仍要按业务分组正常停止、释放旧映射、启动,再检查实际挂载。
最先出错的是滚动预期
最初把这次变更理解得太窄,漏看了 Rook 的 ApplyNetworkEnv。实际 ROOK_MSGR2 从 false 变成 true,进入了多个 daemon 的 Pod 模板,触发滚动。
02:38–02:39 曾同时看到 4 个 OSD down。 当时还没有开始生产卷重挂。先暂停扩大业务动作,短暂把 operator 从 1 缩到 0;已经提交给 Deployment 控制器的更新仍然继续,不能靠停 operator 撤回。
等 18 OSD up/in、PG 恢复 clean、没有新 crash 后,再恢复 operator 和后续步骤。这是一次真实的预估偏差,后面的两个窗口才把串行保护明确写死。
最终判据不是 requireMsgr2: true,也不是服务端开着 3300。RBD 从 sysfs 的 client_id 对应到 debugfs 客户端,检查 monmap、osdmap;CephFS 同时核对实际挂载模式和客户端 map。只读取需要的 map,不打印完整认证选项或 keyring。
06:55 收尾时,68 个生产 RBD、23 个 CephFS 内核客户端全部 v2。客户端数量是最终实际清点值,不把操作前的 20 个 CephFS 客户端数当成固定常量。
生产重挂遇到的几件事
已有卷切协议,花时间的主要是消费者。不同数据库不能套同一份“删 Pod 等重建”。
镜像和模板也会跟着重建
QQ 组停旧实例后,原镜像标签出现 404,卡在拉取和初始化。后面靠原版本镜像及节点缓存恢复,应用版本和 PVC 没改,但中断比预估长。
正在运行不代表镜像还能拉到。 主容器、initContainer、候选节点都要核对;Always 还要检查原引用能否解析。
虚机也有类似问题:VM 模板已经是 host-passthrough,部分运行中的 VMI 仍是 host-model。关机再启动会把历史模板变化一起带进来。这次结合此前验证核对后执行,不能只检查根盘 UID 就按启动键。
数据库各有自己的控制循环
| 对象 | 实际遇到的问题 | 后续处理 |
|---|---|---|
| CNPG | cordon 节点触发额外主库切换 | 改用更窄的临时调度控制,逐组核对 primary、复制和 rw 入口 |
| MariaDB | 一组提升副本时遇到历史 GTID/binlog 冲突;另一组副本竟是可写状态 | 停止扩大批次,恢复原主、正确只读状态和复制,补备份 |
| Redis | 首组“暂停写入 + Sentinel FAILOVER”没有收敛,短时实际双 master | 停下其他组,恢复拓扑;后续协调 Operator/Sentinel 后再做原生交接 |
| GitLab KAS | Redis 恢复后仍缓存旧 Pod IP,readiness 失败 | 在镜像可用的前提下滚动刷新 KAS,再检查入口 |
Redis 那组尤其不能只看 Sentinel 的一句成功。旧主写暂停阻碍了降级,Operator 又根据实际角色重建主从,两个控制循环来回纠正。解除暂停后,真实 role、复制 offset、master Service 和三个 Sentinel 才恢复一致。
MariaDB 没有通过 RESET MASTER、手改 GTID 或清 binlog 把错误压下去。原主可写、副本只读、复制追平已经恢复,历史 GTID 遗留仍在。后续节点维护不能把“此刻 lag=0”当作自动切主必然可靠。
这些都是本次发生的偏差。复制恢复、入口通过,不足以反推这一段业务零中断或证明所有业务语义零损失。
host 网络分五步发布
这一步沿用 10.1.16.0/24、br0 和 MTU 1500。没有新增独立复制网,没有改 Calico、网卡、交换机或 jumbo frame。
开窗时发现 HDD 文件系统的两个 MDS 都在 .22,先把这个冗余问题处理掉,再迁网络。
| 阶段 | 动作 | 通过后再继续 |
|---|---|---|
| A1,rev 38 | HDD MDS 按文件系统设置硬反亲和 | active/standby-replay 分居不同节点 |
| A2,rev 39 | SSD MDS 同样分散 | 两套 FS 主备正常,测试读写正常 |
| B,rev 40 | OSD 并发 1、要求 PG clean,暂缓 MON 自动迁移 | 原 MON 名称、地址、仲裁不变 |
| C,rev 41 | provider=host、限定 public CIDR | 非 MON 后端全部完成,原三 MON 仍健康 |
| D,rev 42 | 恢复 MON 后台监控 | Rook 自动逐个替换 MON,最终三个节点地址 |
MDS 反亲和的 selector 同时匹配 app=rook-ceph-mds 和准确的 rook_file_system。若把四个 MDS 全要求放在不同节点,三台机器根本放不下。
关联 exporter 随 MDS socket 所属节点变化也出现过滚动,并非只会动 MDS 本身。
先把串行保护写实
Rook v1.19.10 的 OSD 更新并发默认值是 20。前一个窗口吃过亏,这次明确设为 1,并保留原生停止和继续检查。源码对应 OSD 更新逻辑。
最终关键配置如下。mon.disabled 只在 B/C 阶段临时设为 true,结束后恢复 false:
cephClusterSpec: network: provider: host addressRanges: public: [10.1.16.0/24] connections: requireMsgr2: true storage: osdMaxUpdatesInParallel: 1 upgradeOSDRequiresHealthyPGs: true skipUpgradeChecks: false continueUpgradeAfterChecksEvenIfNotHealthy: false healthCheck: daemonHealth: mon: disabled: false暂缓的是 MON 后台自动调谐,Kubernetes 探针和持续 quorum 检查仍在。timeout=0 不能替代这个暂停开关。
网络要测双向,也要看实际监听
三台主机和三台普通 Pod,共六个来源,分别访问三个节点的目标端口;另外验证 hostNetwork DNS,以及主机到旧 MON、OSD、MDS 的路径。临时端口探针删除、监听释放后才发布 host 配置。
RGW 有个很实际的变化:原来 Service 的 80 指向 Pod 的 8080,host 模式下进程监听 80,Service 的 targetPort 也变成 80。原 Service 地址保留,但两副本并不保证这次端口切换零中断,所以必须验证真实 S3 put/get/hash。
NodeLocal DNS 的 8080、MetalLB 的 7472 是已有监听,不能为了 Ceph 清掉。host 网络还把 MGR HTTP、指标和 exporter 端口带到了主机网络;本次核实公网只经既有入口,没有直接映射到三个节点。
11:39 发布 C,11:54:32 后端完成。operator 日志每次都先等 PG clean,再轮到下一只 OSD;这个窗口的采样最低为 17 OSD up,没有观察到两只同时 down。
MON 让 Rook 自己逐个换
v1.19.10 会把网络不匹配的 MON 放入 failover 队列,由健康检查逐个处理。这里没有叠加人工删除或缩容 MON。队列处理源码
实际替换顺序是:
| 原 MON | 新 MON | 最终节点地址 |
|---|---|---|
| j | k | .23:3300 |
| g | l | .22:3300 |
| i | m | .21:3300 |
monmap epoch 从 20 到 26。过程中有 3→4→3 的成员变化,见到过 2/3、3/4 的多数状态;2/4 不满足多数,不能只数“还有两个 MON 活着”。
12:04:25 全部收敛,再完整观察 30 分钟,最后跨节点重挂测试卷。12:44 收尾。host 阶段没有重启业务 Pod、数据库、虚机或宿主机,生产卷依靠新 map 学习服务端地址。
现场修正的三个预期
新 MON 仍监听 6789
原先从 Pod 端口声明推断新 MON 只提供 3300,现场不成立。新的 k/l/m 在 monmap 和主机 ss 中都保留 3300/6789。
因此本文只说 CSI 使用 3300、实际客户端是 v2。服务端 v1 兼容监听没有被强制关闭。
空闲客户端的旧 OSD map
迁移结束后,所有 RBD map、全部 MON/MDS map 都已更新,但 12 个低活动 CephFS 客户端还缓存着旧 OSD 地址。
对已有非凭据文件做 4 KiB 直接只读访问后,10 个自动刷新并读取成功,首次读取最长 9.706 秒。剩下两个是同一录播卷在两个节点上的空目录挂载:只有根和两个空子目录,OSD requests/homeless 均为 0,元数据访问正常。
这两个缓存没有靠重启业务或向业务目录写探针来刷新。已有数据访问、空卷无请求和新挂载分别验证,记录里保留了这个例外。只看 debugfs 中还有一个旧地址,判断不了是否有 I/O 故障;也不能据此宣称全部闲置 map 都已更新。
Helm 回滚不等于网络恢复
Rook 写进 Ceph monitor 配置库的 public_network,不会因为 values 删除该字段或切回 Pod 网络就自动消失。网络配置源码
回退还得区分 MON 有没有换身份。后端地址、Ceph 配置库、当前有效 monmap、CSI 端点都要处理,不能把旧 g/i/j 的快照直接覆盖回来。此次没有实际回退。
三节点 host 模式下,每节点一个 MON。以后坏一台,剩余 2/3 可以维持仲裁,但不能指望另外两台再自动容纳第三个 MON;要恢复原节点,或有额外的合格节点。
服务端密钥轮换
版本已经具备新密钥类型的支持,现有密钥却不会因此全部自动换掉。13:05 重新检查时,仍有 24 个旧类型服务端实体、9 个旧类型客户端实体。
其中 AUTH_INSECURE_SERVICE_KEY_TYPE 的底层 severity 是 HEALTH_ERR,只是此前被静默了。整体显示 HEALTH_WARN,不能据此说服务端安全问题已经解决。
这次仅轮换 Rook 的 daemon 范围,包括 MON、MGR、OSD、MDS、RGW,以及 admin、exporter、crashcollector 等受管理内部身份。CSI、外部客户端、mirror peer 和 S3 用户 access key 分开处理。Rook 密钥轮换说明
代次只推进一次
本集群相关当前 generation 均为空或 0,因此目标取 1。其他集群要先读自己的当前代次。渲染后的关键配置是:
cephClusterSpec: security: cephx: daemon: keyRotationPolicy: KeyGeneration keyGeneration: 1 csi: keyType: aesCSI 的 aes 原来就在 Chart 默认值中,实际只新增 daemon 的两项字段。完整渲染 18 个对象,只有 CephCluster 改变;四只 CSI Secret 只记录元数据,不读取值。
13:31:22 发布 cluster revision 43。仍是 Ceph 19.2.6,没有把轮换和 Ceph 20 升级合成一次变更。
代次是一次轮换的目标状态。观察程序失败、状态暂时没填全,都不是再次增加 generation 的理由。
admin 更新会影响管理工具
Rook 原生流程会创建临时 rotator 身份,验证新 admin,更新管理 Secret,清理临时身份,再重启 operator。13:31:57 开始,13:32:00 已记录成功重启。
日志里出现了:
successful admin cephx key rotation requires the current cluster reconcile to restart这是这条控制路径的预期结果,需要用后续状态和认证结果确认;不能看到 error 就把旧 Secret 覆盖回去。固定版本实现
另一个提前发现的问题是 Toolbox。现场 v1.19.10 Chart 的内联脚本只监测 MON 端点,启动时复制一次 admin 密钥,之后不重载 keyring。不能拿其他版本的 Toolbox 行为替它作保证。
先保留独立观察 Pod,用原生客户端直接消费投射的认证文件,绕开旧 keyring 缓存:
ceph --keyring /dev/null \ --keyfile /var/lib/rook-ceph-mon/secret.keyring status这里没有输出文件内容,也没有把密钥塞进命令行。它仍要等 Kubernetes 把新 Secret 投射到 Pod。
现场记录了约 80 秒管理查询认证间隙、12 次查询失败,期间三卷 I/O 继续通过。13:33:14 查询恢复;新认证验证后,正常重建唯一 Toolbox Pod,13:35:29 默认认证通过。
停止轮换也不会撤回已经生成的新密钥。旧 Helm values、旧 Secret、旧 etcd 快照,都不能单独当成密钥回滚方案。
一个可选字段把验收脚本挡住了
13:50,CephCluster 已经 Ready,旧服务端密钥归零,但 SSD 文件系统的 status.cephx.daemon.keyType 没有值,最初的验收脚本在这里报错。
原因在名字前缀:ceph-filesystem- 同时匹配 ceph-filesystem-hdd-。SSD 那组先完成时,Rook 的 best-effort 类型采样把还在滚动的 HDD 那组也算进来,看到 [aes256k aes],便没有填写可选字段。MDS 状态采样代码
核对日志和源码后,验收使用当前 generation、原生服务端安全检查、MDS 主备和数据访问结果;有类型字段的仍要求为 aes256k。没有为了补一个显示字段再次轮换。
13:53:13 核心验收通过:全部目标服务端组 generation 1,24 个旧服务端实体归零,MGR/OSD 报告 aes256k,两套 FS 主备正常,四只 CSI Secret 的 UID/resourceVersion 不变。
随后只解除已经消失的三项服务端检查的历史静默:
AUTH_INSECURE_SERVICE_KEY_TYPEAUTH_INSECURE_SERVICE_TICKETSAUTH_INSECURE_ROTATING_SERVICE_KEY_TYPE原生服务票据没有被强制擦除。后续若出现正常轮转过渡,需要跟踪自然更新,不能为了立刻消告警把旧票据清空。
最后怎样验收
三种测试卷都检查原持久文件,再写新数据、fsync、读回。下面只列各窗口的连续验收段,不把整段维护时长当成持续稳态:
| 窗口 | 三卷连续校验 | 覆盖范围 |
|---|---|---|
| msgr2 | 121 轮 / 1808 秒 | 跨越最后几个业务迁移批次,并非全部结束后另等 30 分钟 |
| host | 358 轮 / 1821 秒 | 全部生产迁移收敛后 |
| 服务端 CephX | 364 轮 / 1849.747 秒 | 核心轮换验收后 |
host 和 CephX 两个窗口都在观察后,额外把测试卷放到三个节点重新挂载,先读回上一节点落盘的 hash,再开始新写入。这样才覆盖“现有连接能继续用”和“以后新挂载也能成功”两条路径。
数据库另外查真实角色、复制和入口;GitLab 查完整 readiness/Gitaly,Harbor 查 health 和实际镜像内容,监控查查询结果,RGW 查真实对象写入、读取和 hash。不是只对一个 HTTP 端口做连通测试。
最终收尾记录:
| 项目 | 09-20 14:37 状态 |
|---|---|
| Ceph | 18 OSD up/in、745 PG clean,正常 scrub 可在进行 |
| MON | k/l/m,三个节点地址,3/3 quorum |
| FS / RGW | 两套 FS 主备恢复,RGW Ready |
| 生产挂载 | 68 个 RBD 映射、23 个 CephFS 内核客户端,均 v2 |
| 数据身份 | 128 个原 PVC/PV 的 UID 与绑定、OSD UUID、池与 CRUSH 未变 |
| 业务 | 21 套数据库角色/复制、10 个 VMI、预期工作负载通过检查 |
| 新异常 | 最后 18 个 OSD 日志样本 101495 行,无 CRC 匹配、未触及每 Pod 20000 行上限;无新增 crash |
| 临时对象 | 三只测试 PVC/PV、namespace、观察 Pod、S3 探针对象已回收 |
| 恢复资料 | 新业务备份与变更前后 etcd 快照均有经过校验的第二份 |
68 是当时的 RBD 映射数,23 是 CephFS 内核客户端数,都不是 PVC 总数。
全窗口的测试日志也保留着。服务端轮换前后共 731 轮测试通过,三卷中最大单轮耗时约 2.9 秒;host 阶段另有最长 9.706 秒的空闲客户端首次只读访问。这些是各自测试路径的实测值,不能外推成所有业务的抖动上限。
三条 09-17 的历史 crash 没有归档,客户端兼容性静默也没有全部解除,集群仍有 HEALTH_WARN。有限时间的日志样本和测试通过,不能写成 CRC 根因已经永久消除,更不能用清空告警制造 HEALTH_OK。
剩下的三个告警
服务端完成后,仍有这三项:
AUTH_INSECURE_CLIENT_KEY_TYPEAUTH_INSECURE_KEYS_ALLOWEDAUTH_INSECURE_KEYS_CREATABLE旧类型身份剩 5 个:四个 client.csi-* 和 client.rbd-mirror-peer。第一项对应这些旧客户端密钥;后两项对应集群仍允许使用、创建旧类型。它们当前保持静默,尚未整改。
当前是 CSI 3.16.2、节点内核 6.8.0-138-generic。在当前 3.16 分支,3.16.3 补入支持 AES256K 的 Ceph 客户端;内核还要有 7.0+ 的支持或经验证的供应商回补。现场没有证明这些 Ubuntu 6.8 模块已回补,所以继续保留 CSI aes。CSI 3.16.3 发布说明、Ceph 兼容性说明
本次只读检查了 HWE 候选并做安装模拟,没有安装新内核,也没有重启宿主机。三台机器同时承担控制面、etcd、Ceph 和业务,客户端整改需要另开节点维护窗口:
- 确认 CSI 镜像、目标内核、驱动和启动回退通道。
- 保持旧类型兼容,逐台完成内核维护,每台恢复后再处理下一台。
- 隔离测试新类型后轮换 CSI,暂留前代密钥,按业务分批重挂已有卷;另核实 mirror peer 的消费者。
- 确认旧挂载不再依赖前代密钥,才清理旧代,并把允许类型收紧到 aes256k。
不能现在直接禁用 aes。 新挂载会拿到新凭据,但已有内核挂载未必已经换过;前代保留也不等于迁移完成。
本轮到服务端收尾为止,客户端另定窗口。Ceph 20.2.4 和 Rook 1.20 也保留为独立升级动作:它们各有版本门槛、CSI 管理迁移和恢复边界,后续开窗重新检查,不沿用这一夜的健康快照。