状态:草稿(2026-10-29),基于源码与官方文档,未进行实测。 基准版本是 Rook
v1.21.1(标签指向的提交09d2f0d8d883b70b30de796c4390371be725ff3b,2026-10-26 发布)以及 Rook Chart 作为子 Chart 引入的 ceph-csi-operatorv1.1.0(提交98eee8980fc69e5c9c02c80958abe06c3faba5eb)。文中所有的源码链接均已固定到此提交。在 TT Lab 实验集群中直接清点 operator 创建的资源将在第 4 篇(收集到原始数据后编写)中进行。 编写规则:以实测数据为基础,无法测量的标记为「未实测」,在原文中未找到的标记为「未确认」。
目录
- 1. operator 与 Rook 的定位
- 2. CRD 列表与 reconcile 创建的内容
- 3. mon 的部署与故障转移 (failover)
- 4. OSD prepare Job 选择磁盘的方式
- 5. CSI 部署:v1.21 中由三方分担
- 6. 升级顺序
- 7. 集群清理:cleanupPolicy
- 8. toolbox
- 9. operator 不做的事情
- 参考
1. operator 与 Rook 的定位
Kubernetes 的 operator 是将人类运维知识转化为控制器的程序。如果用户通过自定义资源 (CR, Custom Resource) 记下「希望处于这种状态」,operator 就会将其与当前状态进行对比,创建不足的部分并删除多余的部分。这种对比并使之匹配的单次循环称为 reconcile 。
Rook 的官方文档(Storage Architecture)将 Rook 的工作分为三个层级:自动化 Ceph 配置的 Rook operator,创建并挂载卷的 Ceph-CSI 驱动,以及实际负责存储的 Ceph 守护进程。同一文档中有一句最重要的话: 「Rook is not in the Ceph data path.」 应用程序的读写操作正如我们在第 1 篇中所见,直接在应用与 OSD 之间往返,operator 并步站在该路径上。因此,即使 operator Pod 暂时停止,已挂载卷的 I/O 也不会经过 operator。当 operator 停止时被延迟的是诸如新 OSD、mon 替换等由 operator 负责的「变更工作」。
2. CRD 列表与 reconcile 创建的内容
v1.21.1 的 deploy/examples/crds.yaml中包含 21 种 CRD(定义 CR 类别的架构):
CephBlockPool, CephBlockPoolRadosNamespace, CephBucketNotification, CephBucketTopic, CephCOSIDriver, CephClient, CephCluster, CephFilesystem, CephFilesystemMirror, CephFilesystemSubVolumeGroup, CephNFS, CephNVMeOFGateway, CephObjectRealm, CephObjectStore, CephObjectStoreAccount, CephObjectStoreUser, CephObjectZone, CephObjectZoneGroup, CephRBDMirror, ObjectBucket, ObjectBucketClaim
将常用项在 reconcile 中创建的内容划分为 Ceph 内部与 Kubernetes 侧,如下所示:
| CR | 在 Ceph 内部 | 在 Kubernetes 中创建的内容 | 依据 |
|---|---|---|---|
| CephCluster | 启动 MON·MGR·OSD 并创建集群 | 每个 mon 对应 Deployment 与 Service、mgr Deployment、每个节点对应 OSD prepare Job、每个 OSD 对应 Deployment、每个节点对应 crash collector·exporter、 rook-ceph-mon-endpoints ConfigMap、 rook-ceph-mon·rook-ceph-admin-keyring Secret、CSI 将使用的 4 个 cephx 密钥 Secret(rook-csi-rbd-provisioner·rook-csi-rbd-node·rook-csi-cephfs-provisioner·rook-csi-cephfs-node, cluster.go#L541)、PDB |
快速入门的 Pod 列表, CephCluster CRD 文档 |
| CephBlockPool | 存储池与 CRUSH 规则。若更改故障域,则创建新规则并挂载到存储池 | 无(StorageClass 不是由 operator 创建,而是由 Chart 或用户创建) | CephBlockPool CRD |
| CephFilesystem | 元数据池、数据池、文件系统 | MDS Deployment。创建 activeCount 的两倍,多余的作为备用 |
CephFilesystem CRD |
| CephObjectStore | RGW 的存储池、realm·zonegroup·zone | RGW Deployment(rook-ceph-rgw-<이름>-a)和 Service |
CephObjectStore CRD |
| CephObjectStoreUser | RGW 用户 | 包含访问密钥的 Secret rook-ceph-object-user-<스토어>-<이름> |
CephObjectStoreUser CRD |
| ObjectBucketClaim | 用于创建新存储桶或向现有存储桶授予权限 | 与 OBC 同名、同命名空间的 Secret(密钥)和 ConfigMap(地址),以及集群范围的 ObjectBucket | Object Bucket Claim |
| CephFilesystemSubVolumeGroup | CephFS 的子卷组 | CSI 专用的 ClientProfile | controller.go#L337 |
| CephBlockPoolRadosNamespace | 存储池中的 RADOS 命名空间 | CSI 专用的 ClientProfile | controller.go#L353 |
| CephClient | cephx 用户和权限 (caps) | 包含密钥的 Secret rook-ceph-client-<이름> |
CephClient CRD |
其余各项仅简要介绍其用途:CephNFS(NFS 网关)、CephRBDMirror 与 CephFilesystemMirror(向其他集群镜像)、CephObjectRealm、ZoneGroup 与 Zone(RGW 多站点)、CephBucketTopic 与 Notification(存储桶事件通知)、CephCOSIDriver(COSI 驱动)、CephNVMeOFGateway(NVMe over Fabrics 网关)以及 CephObjectStoreAccount(RGW 账户)。
CephCluster 的 reconcile 具有固定的顺序。 cluster.go 的 reconcileCephDaemons,会创建配置覆盖 ConfigMap, 启动 mon (L121),检查集群标识是否已生成,启动 mgr (L149),并启动 OSD (L164)。MDS 和 RGW 不在此函数中。它们分别由负责 CephFilesystem 和 CephObjectStore 的独立控制器进行 reconcile。
3. mon 的部署与故障转移 (failover)
正如第 1 篇中所见,MON 必须有过半存活,集群才能运作。因此,Rook 最费心维护的就是 mon。结合 Rook 文档 Monitor Health与源码阅读,情况如下。
标识即 Service 的 IP。 在 Ceph 中,mon 的标识是 IP 地址且不能改变。由于 Pod IP 每次重启都会改变,Rook 会为每个 mon 创建一个 Kubernetes Service,并将该 ClusterIP 用作固定标识。
数据绑定到节点。 mon 的元数据存储在 CephCluster 的 dataDirHostPath(默认 /var/lib/rook)下方的宿主机磁盘或由
volumeClaimTemplate 创建的卷中。因此,mon Pod 重启时必须返回同一节点,从而能够利用现有数据继续启动。
通过 canary 决定部署位置。 确定将新 mon 放置在哪个节点时,Rook 会先启动一个名称以 -canary 结尾的测试用 Deployment,让 Kubernetes 调度器选择节点,并将该结果用作 mon 的位置
(scheduleMonitor, mon.go#L731-L740).
allowMultiplePerNode: false 如果为 ,则防止 mon 部署在同一节点的 anti-affinity 将成为强制规则;如果为 true ,则变为权重为 50 的首选规则(参见将在第 3 篇中看到的集群 chart values 注释)。
每 45 秒检查一次,10 分钟后替换。
health.go中包含两个常量: HealthCheckInterval = 45 * time.Second 和 MonOutTimeout = 10 * time.Minute 。流程如下。
- 每 45 秒向 Ceph 查询一次仲裁状态。
- 首次发现不在仲裁中的 mon 时,记录该时间点。在此期间,Kubernetes 会尝试通过 Deployment 重新启动该 Pod。文档中写道:“恢复现有 mon 总是优于替换为新 mon”。
- 如果 10 分钟后仍未恢复,则使用新名称(在现有最大编号之后,例如如果之前是 mon-a、b、c,则为 mon-d)建立新 mon。旧 mon 的 Deployment 会被缩减为 0(使用主机网络的 mon 不会缩减,以防止新 mon 使用相同的主机 IP 启动),如果新 mon 失败则会回滚
(
failoverMon, health.go#L742). - 单次检查中只会替换一个 mon (health.go#L331).
)。 存在三个例外情况 (health.go#L286-L323).
- 超时为 0:不进行故障转移。文档指出这在计划性维护时很有用。
- 如果 mon 所在的节点已被删除 :无需等待 10 分钟,立即迁移 (
shouldFailoverMonImmediately). - 如果超过超时时间但 mon Pod 甚至尚未调度到节点上(例如节点被驱逐 (drain)),则再等待一个超时时间。用文档的话来说就是“等待时间加倍”。
这两个值可以通过 CephCluster 的 healthCheck.daemonHealth.mon.interval·timeout 进行修改。如果存在 operator 的环境变量
ROOK_MON_OUT_TIMEOUT·ROOK_MON_HEALTHCHECK_INTERVAL ,则优先使用该变量(为了向后兼容旧行为,
health.go#L58-L103)。文档警告称,如果间隔太短,更换 mon 的频率会过高;如果间隔太长,则存在另一个 mon 在新 mon 启动之前死亡并失去仲裁的风险。
mon 更改时,其地址也会改变。Rook 在每次启动 mon 后,都会将新的 mon 地址重新通知给 CSI 侧(第 5 节的 CephConnection、 mon.go#L1261-L1269).
4. OSD prepare Job 选择磁盘的方式
OSD 的创建分为两个阶段:“查找并准备磁盘,以及启动守护进程”。前一个阶段由每个节点运行一次的 Job 执行,后一个阶段由 operator 执行。其名称定义在 osd.go#L64-L69中。Job 是 rook-ceph-osd-prepare-<노드>,而 OSD 守护进程是 rook-ceph-osd-<번호> 。
按源码顺序跟踪 prepare Job 内部发生的事情,流程如下。
- 设备列表。
lsblk使用rbd·nbd·drbd·zram查找节点的块设备。此时,会将设备完全从列表中排除。因为前三者是网络存储,可能会停止读取,而 zram 是易失性内存(disk.go#L37-L39 - ,v1.21.1 的更改事项)。(
getAvailableDevices, daemon.go#L347-L410)。 跳过已挂载的设备、带有文件系统签名的设备以及已经包含 BlueStore OSD 的设备。 - 询问 ceph-volume。 对剩下的每个设备运行
ceph-volume inventory --format json <장치>,只保留回答“可用”的设备。拒绝原因会直接记录在日志中(device.go#L399-L428). - 匹配用户设置。
useAllDevices: true如果是,则使用所有剩余设备,但不选择 LVM 逻辑卷和 loop 设备。如果有deviceFilter(设备名称正则表达式)、devicePathFilter、按节点划分的devices列表,则只使用与之匹配的设备。 - 准备。 默认是
ceph-volume raw prepare。如果开启加密、在一个设备上放置多个 OSD(osdsPerDevice大于或等于 2),或单独设置元数据设备,则无法使用 raw 模式,因此会转为ceph-volume lvm batch模式 (allowRawMode, volume.go#L497-L525). - 报告结果。 将创建的 OSD 列表写入 ConfigMap
rook-ceph-osd-<노드>-status中 (status.go#L47)。 operator 读取该内容并为每个 OSD 创建 Deployment。
官方文档的要求(Prerequisites)也表达了同样的意思。可以用作 OSD 的包括:既没有分区也没有文件系统的 raw 设备、没有文件系统的 raw 分区、没有文件系统的 LVM 逻辑卷,以及 block 模式的 PV。因此,如果遇到“新连接了磁盘但没有生成 OSD”的情况,首先应使用 lsblk -f 检查是否残留有文件系统签名。反之,没有任何签名的空磁盘在 useAllDevices: true 中甚至不会被询问就会直接变成 OSD。这就是我们在第 3 篇中警告该默认值的原因。
prepare Job 会根据设备大小占用一次较多的内存。文档提到在大磁盘上大约需要 1 到 2Gi,并建议不要为该 Job 设置内存上限。由于已经准备好的 OSD 在下一次 prepare 时会因为“已经存在”而被跳过,因此这种高内存使用只发生一次。
5. CSI 部署:在 v1.21 中由三方分工
CSI(Container Storage Interface)是 Kubernetes 调用存储厂商驱动的标准规范。当 PVC 创建时,CSI 驱动的 provisioner(控制器端)会在存储中创建卷,而当 Pod 在节点上启动时,该节点的 nodeplugin 会将该卷挂载(map·mount)上去。
以前的 Rook 直接部署 CSI 驱动。而在 v1.21 中,这项工作由三个主体分工完成。由于这是初次安装者最容易踩坑的地方,因此我们按顺序进行记录。
- ceph-csi-operator。
rook-ceph作为子 Chart(ceph-csi-operator 1.1.0)随同安装 (Chart.yaml, 条件csi.installCsiOperator)。该 operator 负责创建实际的驱动 Pod。 - ceph-csi-drivers Chart。 用于创建指定启用哪些驱动的
DriverCR、OperatorConfig以及驱动 Pod 将使用的 RBAC。该 Chart 不是从 Rook 仓库获取,而是从 ceph-csi-operator 的 Helm 仓库获取,并在应用 Rook 准备好的 values 文件 (deploy/charts/ceph-csi-drivers/values.yaml)后进行安装。默认启用 RBD 和 CephFS,禁用 NFS 和 NVMe-oF。驱动名称会像rook-ceph.rbd.csi.ceph.com这样在前面加上 operator 的命名空间。升级文档 指出如果不安装此 Chart,将因为缺少服务账号而导致 CSI 驱动处于失败状态(自 v1.20 起的要求, Rook Upgrades). - Rook operator。 在 mon 启动后,会在 operator 命名空间中创建并修改
CephConnectionCR(mon 地址列表、read affinity、RBD 镜像数量)以及默认的ClientProfile(RBD 和 CephFS 将使用的 Secret 名称) (ceph_connection.go#L37-L103, config.go#L149-L190)。 两者的名称均为 CephCluster 的命名空间。正如在第 2 节中所见,为每个子卷组(subvolume group)和 RADOS 命名空间单独创建 ClientProfile。
ceph-csi-operator 在收到一个 Driver CR 后,会创建 CSIDriver 对象、以 -ctrlplugin 结尾的 Deployment(包含 provisioner 和边车)、以 -nodeplugin 结尾的 DaemonSet(覆盖所有节点)、liveness Service、NetworkPolicy 以及配置 ConfigMap
(driver_controller.go#L254-L264)。
因此,在 Rook quickstart 的 Pod 列表中可以看到 rook-ceph.rbd.csi.ceph.com-ctrlplugin-… 和 rook-ceph.rbd.csi.ceph.com-nodeplugin-… 。
StorageClass 的 provisioner 必须与该驱动名称完全相同。文档警告称,如果想要更改名称的前半部分(CSI_DRIVER_NAME_PREFIX),请仅在初次安装时进行。如果在已使用的集群中进行更改,将会产生意料之外的行为
(Ceph CSI Drivers).
6. 升级顺序
Rook 将升级分为两部分: Rook 本身(管理层) 和 Ceph(数据层) 。
Rook 升级。 按照 rook-ceph → ceph-csi-drivers → rook-ceph-cluster 的顺序升级 Chart。文档指出的 v1.21 的破坏性变更共有三项。
- Helm OCI 차트 태그에
v不附加(1.21.0). - Ceph msgr2 协议已成为默认的强制要求。msgr2 需要内核 5.11 或更高版本,如果是旧内核,则通过
requireMsgr2: false关闭。 已经挂载的卷在重新挂载之前会继续使用 msgr1,而 mon 则从因故障转移(failover)而重新创建时起仅接受 msgr2。 - 支持的 Kubernetes 最低版本为 v1.32。
此外,在 v1.21 中,块 StorageClass 新增了 CSI Secret 参数,由于 StorageClass 的 parameters 无法更改,因此官方建议在升级 Chart 之前删除 ceph-block StorageClass。文档提到,由于会重新生成同名的 StorageClass,因此现有的 PV 不会受到影响。
Ceph 升级。 更改 CephCluster 的镜像即可启动升级
(Ceph Upgrades)。
Rook 会确认是否可以停止一个守护进程、对其进行更改,并在等待其状态稳定(mon 达到仲裁、OSD 的 PG 处于 clean 状态、MDS 处于 up 状态)后,再进入下一个守护进程。按照第 2 节中见到的协调(reconcile)顺序,在 CephCluster 内部依次为 mon、mgr、OSD,而 MDS 和 RGW 则由各自的控制器负责。等待单个 OSD 变为可停止状态的超时时间是 waitTimeoutForHealthyOSDInMinutes(默认 10 分钟)。支持的 Ceph 版本为 Squid v19.2.0 及以上和 Tentacle v20.2.1 及以上;由于已知 v20.2.0 在开启 read affinity 时存在数据损坏问题,因此不推荐使用。Rook 会在 v20.2.0 中自动关闭 read affinity
(ceph_connection.go#L105-L116).
7. 集群清理:cleanupPolicy
删除 CephCluster 并不意味着数据会被删除。这是默认行为,旨在防止因误删 CR 而导致数据丢失。Chart 文档中也指出,即使删除了 rook-ceph-cluster , /var/lib/rook 和 OSD 磁盘上的数据仍然会保留,因此如果想要重复利用磁盘,必须手动将其清除。
如果确实要删除,请先在 CephCluster 中写入确认短语 (Cleanup, CephCluster CRD: Cleanup policy).
| 字段 | 默认 | 含义 |
|---|---|---|
confirmation |
空字符串 | yes-really-destroy-data 仅在为该值时进行清理。一旦写入该值,operator 将停止应用新配置,并等待删除。如果在删除 CR 之前,可以撤消此操作 |
sanitizeDisks.method |
quick |
quick 仅清除 Ceph 元数据, complete 清除整个磁盘 |
sanitizeDisks.dataSource |
zero |
要覆盖的字节。 random 比 |
sanitizeDisks.iteration |
1 | 慢得多 |
allowUninstallWithVolumes |
false |
覆盖次数 |
如果在存在确认短语的状态下删除 CephCluster,operator 会记住曾有 Ceph 守护进程 Pod 运行的节点,并以 5 秒为间隔等待这些 Pod 全部消失,然后在这每个节点上启动一个 Job(cluster-cleanup-job-<노드>,标签 rook-ceph-cleanup)来删除 dataDirHostPath 并擦除 OSD 磁盘(cleanup.go#L62-L100)。
文档强调应先清理应用程序的卷和 PVC。否则,卷可能会挂起,并需要重启节点。CSI operator 的 CR(operatorconfigs·drivers·clientprofiles·clientprofilemappings·cephconnections)也必须单独删除。
如果在没有确认短语的情况下删除,则必须手动删除每个节点上的 dataDirHostPath 并擦除磁盘(参见文档中的 sgdisk --zap-all, dd, blkdiscard、LVM 与 device mapper 清理步骤)。Chart values 的注释警告称,特别是如果保留了 dataDirHostPath 而重新安装,mon 将无法清新集群中启动。
8. toolbox
用于窥探 Ceph 的基本工具是 ceph 命令。Rook 将包含此命令的容器作为名为 toolbox 的 Deployment(rook-ceph-tools)提供
(Toolbox).
在集群 Chart 中, toolbox.enabled 是默认的 false ,启用后会使用与 Ceph 镜像相同的镜像启动。还有一种 Job 方式,即只运行一次命令并将结果保留在日志中;也有不使用 toolbox、直接使用的 kubectl 插件。
kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- ceph status
kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- ceph osd tree
kubectl -n rook-ceph exec -it deploy/rook-ceph-tools -- ceph df
我们计划在第 4 篇中,将 TT Lab 实践集群的这三个输出与第 1 篇的概念结合起来阅读。
9. operator 不做的事情
引入 operator 后,人们很容易抱有“现在一切都可以自动搞定了”的期待。以下是在原文中确认的、Rook 不会做或默认不会做的事情。
| 不做的事情 | 谁来做 | 依据 |
|---|---|---|
| 位于数据路径上 | 应用程序与 OSD 直接交互 | 存储架构 “Rook 不在 Ceph 数据路径中” |
| 创建 StorageClass 和 VolumeSnapshotClass | 集群 Chart(或用户) | Chart 模板 cephblockpool.yaml |
| 创建 CSI 驱动 Driver CR | ceph-csi-drivers Chart、Pod 为 ceph-csi-operator |
第 5 节 |
| 将故障 OSD 从集群中移除 | 默认是人工操作。 removeOSDsIfOutAndSafeToRemove: true 如果状态为 out 且可以安全删除,operator 会移除 OSD |
CephCluster CRD 文档 |
| 数据重平衡 | Ceph(down 持续 10 分钟则设为 out,见第 1 篇) | Ceph 文档 |
| 修改 PG 数量 | Ceph 的 PG 自动缩放器(第 1 篇) | Ceph 文档 |
| 删除 CR 时清理磁盘和主机路径 | 默认是人工操作。 cleanupPolicy 仅当出现确认提示时 operator 才会 |
第 7 节 |
lvm2 安装,匹配内核版本 |
节点管理器 | Prerequisites, Rook Upgrades |
| mon 元数据与 keyring 备份 | 外部工具。Rook 仅 clusterMetadata 通过注解让此类工具 rook-ceph-mon-endpoints·rook-ceph-mon·rook-ceph-admin-keyring 能够进行选择 |
CephCluster CRD 文档 |
还有一点需要指出。 useAllNodes: true 如果在此期间更改了节点的 taint 或 affinity,导致节点被排除在 Rook 的目标之外,该节点的 OSD 需要通过删除并重新创建 operator Pod 来进行「确认」才能被移除。这是一种防止意外丢失数据的机制,官方文档建议在生产环境中
useAllNodes: false 使用节点明确指定。
TT 的总结。 Rook 是一个细心的管理员。如果 mon 出现故障,它会等待 10 分钟;看到空磁盘就会将其制作为 OSD;当被要求删除时,它一定会反复确认“真的吗?”。但是,它不会亲自搬运数据,那是 Ceph 守护进程的工作。因此,即使 operator 稍作休息,已经挂载的卷也完好无损!
参考
源码与文档均于 2026-10-29 查看上述 commit。
- Rook v1.21.1 发布: https://github.com/rook/rook/releases/tag/v1.21.1
- Storage Architecture: https://github.com/rook/rook/blob/09d2f0d8d883b70b30de796c4390371be725ff3b/Documentation/Getting-Started/storage-architecture.md
- Monitor Health: https://github.com/rook/rook/blob/09d2f0d8d883b70b30de796c4390371be725ff3b/Documentation/Storage-Configuration/Advanced/ceph-mon-health.md
- CephCluster CRD: https://github.com/rook/rook/blob/09d2f0d8d883b70b30de796c4390371be725ff3b/Documentation/CRDs/Cluster/ceph-cluster-crd.md
- Prerequisites: https://github.com/rook/rook/blob/09d2f0d8d883b70b30de796c4390371be725ff3b/Documentation/Getting-Started/Prerequisites/prerequisites.md
- Ceph CSI Drivers: https://github.com/rook/rook/blob/09d2f0d8d883b70b30de796c4390371be725ff3b/Documentation/Storage-Configuration/Ceph-CSI/ceph-csi-drivers.md
- Ceph-CSI Driver Helm Chart: https://github.com/rook/rook/blob/09d2f0d8d883b70b30de796c4390371be725ff3b/Documentation/Helm-Charts/csi-drivers-chart.md
- Rook Upgrades: https://github.com/rook/rook/blob/09d2f0d8d883b70b30de796c4390371be725ff3b/Documentation/Upgrade/rook-upgrade.md
- Ceph Upgrades: https://github.com/rook/rook/blob/09d2f0d8d883b70b30de796c4390371be725ff3b/Documentation/Upgrade/ceph-upgrade.md
- Cleanup: https://github.com/rook/rook/blob/09d2f0d8d883b70b30de796c4390371be725ff3b/Documentation/Storage-Configuration/ceph-teardown.md
- Toolbox: https://github.com/rook/rook/blob/09d2f0d8d883b70b30de796c4390371be725ff3b/Documentation/Troubleshooting/ceph-toolbox.md
- ceph-csi-operator v1.1.0 Driver 控制器: https://github.com/ceph/ceph-csi-operator/blob/98eee8980fc69e5c9c02c80958abe06c3faba5eb/internal/controller/driver_controller.go