5693 字
28 分钟

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:12Ceph 19.2.3 → 19.2.6,Rook 仍为 1.17.9cluster 33
09-17 11:06 起Rook 1.17.9 → 1.18.11,随后适配 StorageClassoperator 7;cluster 34、35
09-17 14:23 起Rook 1.18.11 → 1.19.10;CSI operator 0.6.0、CSI 3.16.2operator 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
→ abort

09-20 00:15 的日志采样里,还能看到来自同一个 v1 客户端的 bad crc in data。这说明错误仍在发生,但仅凭这段栈和 CRC 日志,不能把根因直接定成网卡、MTU 或 Ceph 某一个缺陷。

所以先停在 Squid 19.2.6,把协议、网络和密钥分别处理,再看后续版本。


三件事分开做#

动作改的是什么对已有业务的主要影响
msgr2Ceph 连接协议、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 Readyhelm --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-crc

RBD 另外核对了 client.csi-rbd-noderbd_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 客户端,检查 monmaposdmap;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 就按启动键。

数据库各有自己的控制循环#

对象实际遇到的问题后续处理
CNPGcordon 节点触发额外主库切换改用更窄的临时调度控制,逐组核对 primary、复制和 rw 入口
MariaDB一组提升副本时遇到历史 GTID/binlog 冲突;另一组副本竟是可写状态停止扩大批次,恢复原主、正确只读状态和复制,补备份
Redis首组“暂停写入 + Sentinel FAILOVER”没有收敛,短时实际双 master停下其他组,恢复拓扑;后续协调 Operator/Sentinel 后再做原生交接
GitLab KASRedis 恢复后仍缓存旧 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/24br0MTU 1500。没有新增独立复制网,没有改 Calico、网卡、交换机或 jumbo frame。

开窗时发现 HDD 文件系统的两个 MDS 都在 .22,先把这个冗余问题处理掉,再迁网络。

阶段动作通过后再继续
A1,rev 38HDD MDS 按文件系统设置硬反亲和active/standby-replay 分居不同节点
A2,rev 39SSD MDS 同样分散两套 FS 主备正常,测试读写正常
B,rev 40OSD 并发 1、要求 PG clean,暂缓 MON 自动迁移原 MON 名称、地址、仲裁不变
C,rev 41provider=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最终节点地址
jk.23:3300
gl.22:3300
im.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: aes

CSI 的 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 缓存:

Terminal window
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_TYPE
AUTH_INSECURE_SERVICE_TICKETS
AUTH_INSECURE_ROTATING_SERVICE_KEY_TYPE

原生服务票据没有被强制擦除。后续若出现正常轮转过渡,需要跟踪自然更新,不能为了立刻消告警把旧票据清空。


最后怎样验收#

三种测试卷都检查原持久文件,再写新数据、fsync、读回。下面只列各窗口的连续验收段,不把整段维护时长当成持续稳态:

窗口三卷连续校验覆盖范围
msgr2121 轮 / 1808 秒跨越最后几个业务迁移批次,并非全部结束后另等 30 分钟
host358 轮 / 1821 秒全部生产迁移收敛后
服务端 CephX364 轮 / 1849.747 秒核心轮换验收后

host 和 CephX 两个窗口都在观察后,额外把测试卷放到三个节点重新挂载,先读回上一节点落盘的 hash,再开始新写入。这样才覆盖“现有连接能继续用”和“以后新挂载也能成功”两条路径。

数据库另外查真实角色、复制和入口;GitLab 查完整 readiness/Gitaly,Harbor 查 health 和实际镜像内容,监控查查询结果,RGW 查真实对象写入、读取和 hash。不是只对一个 HTTP 端口做连通测试。

最终收尾记录:

项目09-20 14:37 状态
Ceph18 OSD up/in、745 PG clean,正常 scrub 可在进行
MONk/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_TYPE
AUTH_INSECURE_KEYS_ALLOWED
AUTH_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 aesCSI 3.16.3 发布说明Ceph 兼容性说明

本次只读检查了 HWE 候选并做安装模拟,没有安装新内核,也没有重启宿主机。三台机器同时承担控制面、etcd、Ceph 和业务,客户端整改需要另开节点维护窗口:

  1. 确认 CSI 镜像、目标内核、驱动和启动回退通道。
  2. 保持旧类型兼容,逐台完成内核维护,每台恢复后再处理下一台。
  3. 隔离测试新类型后轮换 CSI,暂留前代密钥,按业务分批重挂已有卷;另核实 mirror peer 的消费者。
  4. 确认旧挂载不再依赖前代密钥,才清理旧代,并把允许类型收紧到 aes256k。

不能现在直接禁用 aes。 新挂载会拿到新凭据,但已有内核挂载未必已经换过;前代保留也不等于迁移完成。

本轮到服务端收尾为止,客户端另定窗口。Ceph 20.2.4 和 Rook 1.20 也保留为独立升级动作:它们各有版本门槛、CSI 管理迁移和恢复边界,后续开窗重新检查,不沿用这一夜的健康快照。