TT Lab
博客

한국어 English 日本語 中文

开始
博客

博客

Rook-Ceph 解析 2:Rook operator 做的事情与不做的事情

状态:草稿(2026-10-29),基于源码与官方文档,未进行实测。 基准版本是 Rook v1.21.1(标签指向的提交 09d2f0d8d883b70b30de796c4390371be725ff3b,2026-10-26 发布)以及 Rook Chart 作为子 Chart 引入的 ceph-csi-operator v1.1.0 (提交 98eee8980fc69e5c9c02c80958abe06c3faba5eb)。文中所有的源码链接均已固定到此提交。在 TT Lab 实验集群中直接清点 operator 创建的资源将在第 4 篇(收集到原始数据后编写)中进行。 编写规则:以实测数据为基础,无法测量的标记为「未实测」,在原文中未找到的标记为「未确认」。

目录

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 하나가 만드는 것 (Rook v1.21.1) CR 위: Ceph 안 · 아래: 쿠버네티스 CephCluster MON·MGR·OSD 를 세운다 Deployment·Service·Job·Secret CephBlockPool 풀과 CRUSH 규칙 쿠버네티스 자원 없음 CephFilesystem 풀과 파일시스템 MDS Deployment(활성의 두 배) CephObjectStore 풀·realm·zone RGW Deployment·Service CephObjectStoreUser RGW 사용자 S3 키 Secret ObjectBucketClaim 버킷 같은 이름의 Secret·ConfigMap CephFilesystemSubVolumeGroup 서브볼륨 그룹 CSI ClientProfile CephClient cephx 사용자 키 Secret StorageClass 는 operator 가 아니라 클러스터 차트가 만든다
图:一个 CR 在 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 分钟后替换。

mon 하나가 쿼럼에서 빠졌을 때 Rook v1.21.1 기본값: 45초마다 검사, 10분 뒤 교체 기다리는 10분(MonOutTimeout) 0 눈금 한 칸 = 45초 10분 처음 발견 시각을 적고 기다린다 그사이 파드 재시작을 기대 10분 뒤에도 없으면 새 mon-d 를 canary 로 배치 뜨면 mon-b 를 지운다 예외 timeout 0: 바꾸지 않는다(계획된 정비용) mon 이 있던 노드가 지워졌다: 기다리지 않고 바로 파드가 노드에 배치조차 안 됨: 한 번 더 기다린다(2배) 한 번의 검사에서 바꾸는 mon 은 하나뿐
图:当单个 mon 退出仲裁时,Rook 的等待与替换顺序

health.go中包含两个常量: HealthCheckInterval = 45 * time.Second 和 MonOutTimeout = 10 * time.Minute 。流程如下。

  1. 每 45 秒向 Ceph 查询一次仲裁状态。
  2. 首次发现不在仲裁中的 mon 时,记录该时间点。在此期间,Kubernetes 会尝试通过 Deployment 重新启动该 Pod。文档中写道:“恢复现有 mon 总是优于替换为新 mon”。
  3. 如果 10 分钟后仍未恢复,则使用新名称(在现有最大编号之后,例如如果之前是 mon-a、b、c,则为 mon-d)建立新 mon。旧 mon 的 Deployment 会被缩减为 0(使用主机网络的 mon 不会缩减,以防止新 mon 使用相同的主机 IP 启动),如果新 mon 失败则会回滚 (failoverMon, health.go#L742).
  4. 单次检查中只会替换一个 mon (health.go#L331).

)。 存在三个例外情况 (health.go#L286-L323).

这两个值可以通过 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-<번호> 。

OSD prepare Job 이 디스크를 고르는 길 Rook operator: 노드마다 Job 을 띄운다 Job rook-ceph-osd-prepare-<노드> ① lsblk 로 장치 목록 rbd·nbd·drbd·zram 은 처음부터 뺀다 ② 이미 쓰이는 장치 거르기 마운트·파일시스템 서명·기존 OSD 는 건너뜀 ③ ceph-volume inventory 에 묻기 쓸 수 없다는 답이면 이유를 로그에 남김 ④ 설정과 맞추기 useAllDevices·deviceFilter·devices ⑤ 준비: ceph-volume raw prepare 암호화·장치당 OSD 여럿·메타 장치면 lvm batch ConfigMap rook-ceph-osd-<노드>-status OSD Deployment rook-ceph-osd-<번호> ④ 에서 LVM 논리 볼륨과 loop 장치는 useAllDevices 로 고르지 않는다
图:OSD prepare Job 筛选设备并创建 OSD 的路径

按源码顺序跟踪 prepare Job 内部发生的事情,流程如下。

  1. 设备列表。 lsblk 使用 rbd·nbd·drbd·zram 查找节点的块设备。此时,会将设备完全从列表中排除。因为前三者是网络存储,可能会停止读取,而 zram 是易失性内存(disk.go#L37-L39
  2. ,v1.21.1 的更改事项)。(getAvailableDevices, daemon.go#L347-L410)。 跳过已挂载的设备、带有文件系统签名的设备以及已经包含 BlueStore OSD 的设备。
  3. 询问 ceph-volume。 对剩下的每个设备运行 ceph-volume inventory --format json <장치> ,只保留回答“可用”的设备。拒绝原因会直接记录在日志中(device.go#L399-L428).
  4. 匹配用户设置。 useAllDevices: true 如果是,则使用所有剩余设备,但不选择 LVM 逻辑卷和 loop 设备。如果有 deviceFilter (设备名称正则表达式)、 devicePathFilter、按节点划分的 devices 列表,则只使用与之匹配的设备。
  5. 准备。 默认是 ceph-volume raw prepare 。如果开启加密、在一个设备上放置多个 OSD(osdsPerDevice 大于或等于 2),或单独设置元数据设备,则无法使用 raw 模式,因此会转为 ceph-volume lvm batch 模式 (allowRawMode, volume.go#L497-L525).
  6. 报告结果。 将创建的 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 中,这项工作由三个主体分工完成。由于这是初次安装者最容易踩坑的地方,因此我们按顺序进行记录。

v1.21 의 CSI: 셋이 나눠 맡는다 Rook operator mon 이 뜬 뒤 ceph-csi-drivers 차트 + Rook values rook-ceph 차트 하위 차트로 설치 CephConnection ClientProfile Driver(rbd·cephfs) OperatorConfig ceph-csi-operator operator 파드 mon 주소·키 이름 ceph-csi-operator 가 Driver 마다 만든다 <드라이버>-ctrlplugin Deployment 볼륨을 만든다(provisioner) <드라이버>-nodeplugin DaemonSet, 모든 노드 노드에 붙인다(map·mount) PVC → ctrlplugin 이 RBD 이미지·CephFS 서브볼륨을 만들고 파드가 뜨면 kubelet 이 그 노드의 nodeplugin 으로 붙인다
图:v1.21 中的 CSI:由 Rook、ceph-csi-drivers Chart 和 ceph-csi-operator 分工协作
  1. ceph-csi-operator。 rook-ceph 作为子 Chart(ceph-csi-operator 1.1.0)随同安装 (Chart.yaml, 条件 csi.installCsiOperator)。该 operator 负责创建实际的驱动 Pod。
  2. ceph-csi-drivers Chart。 用于创建指定启用哪些驱动的 Driver CR、 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).
  3. Rook operator。 在 mon 启动后,会在 operator 命名空间中创建并修改 CephConnection CR(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 的破坏性变更共有三项。

此外,在 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。

登录后即可点赞

评论

还没有评论。

登录后即可发表评论