TT Lab
Blog

한국어 English 日本語 中文

Get started
Blog

Blog

Rook-Ceph Guide 2: What the Rook Operator Does and Does Not Do

Status: Draft (2026-10-29), based on source code and official documentation, no actual measurements taken. The baseline version is Rook v1.21.1(commit pointed to by the tag 09d2f0d8d883b70b30de796c4390371be725ff3b, released on 2026-10-26) and ceph-csi-operator included as a subchart by the Rook chart v1.1.0 (commit 98eee8980fc69e5c9c02c80958abe06c3faba5eb). All source code links in this article are fixed to this commit. Counting the resources created by the operator directly in the TT Lab practice cluster will be covered in Part 4 (written once the raw data is gathered). Writing rule: Measured data is the default; unmeasured items are marked as "[Unmeasured]", and items not found in the original text are marked as "[Unconfirmed]".

Table of Contents

1. The Role of Operators and Rook

An Kubernetes operator is a program that translates human operational knowledge into a controller. When a user writes down "desired state" as a Custom Resource (CR), the operator compares it with the current state, creating what is missing and deleting what is excess. This cycle of comparison and alignment is called a reconcile .

Rook's official documentation (Storage Architecture) divides Rook's work into three layers: the Rook operatorwhich automates Ceph configuration, the Ceph-CSI driverwhich creates and attaches volumes, and the Ceph daemonswhich perform the actual storage. The same document contains the most important single line: "Rook is not in the Ceph data path." As seen in Part 1, application reads and writes travel directly between the application and OSDs, and the operator does not stand in that path. Therefore, even if the operator pod temporarily stops, the I/O of already attached volumes does not pass through the operator. When the operator stops, the things that are delayed are "change operations" handled by the operator, such as creating new OSDs or replacing mons.

2. CRD List and What Reconcile Creates

In v1.21.1, there are deploy/examples/crds.yaml21 CRDs (schemas defining types of CRs).

CephBlockPool, CephBlockPoolRadosNamespace, CephBucketNotification, CephBucketTopic, CephCOSIDriver, CephClient, CephCluster, CephFilesystem, CephFilesystemMirror, CephFilesystemSubVolumeGroup, CephNFS, CephNVMeOFGateway, CephObjectRealm, CephObjectStore, CephObjectStoreAccount, CephObjectStoreUser, CephObjectZone, CephObjectZoneGroup, CephRBDMirror, ObjectBucket, ObjectBucketClaim

Dividing what frequently used ones create during a reconcile into the Ceph side and the Kubernetes side yields the following.

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 가 아니라 클러스터 차트가 만든다
Figure: What a single CR creates inside Ceph and Kubernetes
CR Inside Ceph Created in Kubernetes Reference
CephCluster Provisions MONs, MGRs, and OSDs to create a cluster Deployments and Services for each mon, mgr Deployment, OSD prepare Job per node, Deployment per OSD, crash collector and exporter per node, rook-ceph-mon-endpoints ConfigMap, rook-ceph-mon·rook-ceph-admin-keyring Secret, 4 cephx key Secrets used by CSI (rook-csi-rbd-provisioner·rook-csi-rbd-node·rook-csi-cephfs-provisioner·rook-csi-cephfs-node, cluster.go#L541), PDB Pod list from quickstart, CephCluster CRD documentation
CephBlockPool Pools and CRUSH rules. Creates new rules and applies them to pools if failure domains change None (StorageClasses are created by charts or users, not the operator) CephBlockPool CRD
CephFilesystem Metadata pools, data pools, and filesystem MDS Deployments. activeCount Creates twice as many and holds extras as standby CephFilesystem CRD
CephObjectStore Pools for RGW, realm, zonegroup, and zone RGW Deployment (rook-ceph-rgw-<이름>-a) and Service CephObjectStore CRD
CephObjectStoreUser RGW user Secret containing access keys rook-ceph-object-user-<스토어>-<이름> CephObjectStoreUser CRD
ObjectBucketClaim Creates a new bucket or grants permissions to an existing bucket Secret (keys) and ConfigMap (address) with the same name and namespace as the OBC, cluster-scope ObjectBucket Object Bucket Claim
CephFilesystemSubVolumeGroup CephFS subvolume group ClientProfile for CSI controller.go#L337
CephBlockPoolRadosNamespace RADOS namespace within the pool ClientProfile for CSI controller.go#L353
CephClient cephx user and capabilities (caps) Secret containing keys rook-ceph-client-<이름> CephClient CRD

The rest are briefly noted by usage: CephNFS (NFS gateway), CephRBDMirror and CephFilesystemMirror (mirroring to other clusters), CephObjectRealm, ZoneGroup, and Zone (RGW multisite), CephBucketTopic and Notification (bucket event notifications), CephCOSIDriver (COSI driver), CephNVMeOFGateway (NVMe over Fabrics gateway), and CephObjectStoreAccount (RGW account).

The reconcile of CephCluster follows a fixed order. cluster.go Following the reconcileCephDaemonsof , it creates the configuration override ConfigMap, brings up mons (L121), verifies that cluster identity has been established, brings up mgrs (L149), and brings up OSDs (L164). MDS and RGW are not in this function; they are reconciled by separate controllers handling CephFilesystem and CephObjectStore respectively.

3. Mon Placement and Failover

As seen in Part 1, a majority of MONs must be alive for the cluster to operate. Therefore, mon is what Rook takes the most care to protect. Reading the Rook documentation Monitor Healthalong with the source code yields the following.

Identity is a Service IP. In Ceph, a mon's identity is an IP address and must not change. Since pod IPs change every time they restart, Rook creates a separate Kubernetes Service for each mon and uses its ClusterIP as a fixed identity.

Data is bound to nodes. Mon metadata is stored in host directories under the CephCluster's dataDirHostPath(default /var/lib/rook) or volumes created with volumeClaimTemplate . Therefore, mon pods must return to the same node when restarting, which allows them to resume using existing data.

Placement is determined by canaries. When deciding which node to place a new mon on, Rook first spins up a test Deployment with -canary appended to its name to let the Kubernetes scheduler choose a node, and uses that result as the mon's location (scheduleMonitor, mon.go#L731-L740). allowMultiplePerNode: false ). If , anti-affinity preventing mons from residing on the same node is enforced as a mandatory rule, and if , it becomes a preference rule with a weight of 50 (comments in the cluster chart values to be seen in Part 3). true Checks are every 45 seconds, and replacement happens after 10 minutes.

Figure: Sequence of Rook waiting and replacing when a single mon drops out of quorum

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

health.go: HealthCheckInterval = 45 * time.Second and MonOutTimeout = 10 * time.Minute . The flow is as follows.

  1. Every 45 seconds, it queries Ceph for the quorum status.
  2. When it encounters a mon missing from the quorum for the first time, it records that timestamp. Meanwhile, Kubernetes tries to restart that pod via its Deployment. The documentation notes that "saving an existing mon is always better than replacing it with a new mon."
  3. If it does not return even after 10 minutes, a new mon is provisioned with a new name (the next highest number after the existing ones, e.g., mon-d if mon-a, b, and c existed). The old mon's Deployment is scaled down to 0 (mons using host networking are not scaled down to prevent the new mon from starting on the same host IP), and if the new mon fails, it rolls back (failoverMon, health.go#L742).
  4. ). Only one mon is replaced per inspection (health.go#L331).

). There are three exceptions (health.go#L286-L323).

Both values can be changed via the CephCluster's healthCheck.daemonHealth.mon.interval·timeout . If the operator environment variable ROOK_MON_OUT_TIMEOUT·ROOK_MON_HEALTHCHECK_INTERVAL is present, it takes precedence (for backward compatibility with older behavior, health.go#L58-L103). The documentation warns that if the interval is too short, mons will be cycled too frequently, and if it is too long, another mon might die and quorum be lost before a new mon is established.

When a mon changes, its address also changes. Every time Rook spins up a mon, it re-notifies the CSI side of the mon address (CephConnection in Section 5, mon.go#L1261-L1269).

4. How the OSD Prepare Job Selects Disks

OSDs are created in two phases: "discovering, preparing, and spinning up the daemon." The first phase is handled by a Job running once per node, and the second phase is handled by the operator. The names are defined in osd.go#L64-L69. The Job is rook-ceph-osd-prepare-<노드>, and the OSD daemon is 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 로 고르지 않는다
Figure: The path where the OSD prepare Job filters devices and creates OSDs

Following the events occurring inside the prepare Job in source code order, they proceed as follows.

  1. Device list. lsblk Finds the block devices on the node using rbd·nbd·drbd·zram . At this time, devices such as are excluded entirely from the list. This is because the first three are network storages where reads can hang, and zram is volatile memory (disk.go#L37-L39, changes in v1.21.1).
  2. Filtering out already used devices(getAvailableDevices, daemon.go#L347-L410). Skips mounted devices, devices with filesystem signatures, and devices that already contain a BlueStore OSD.
  3. Querying ceph-volume. For each remaining device, runs ceph-volume inventory --format json <장치> and keeps only those that answer "usable." The rejection reasons are logged as-ically (device.go#L399-L428).
  4. Matching user configuration. useAllDevices: true If it is, all remaining devices are used, but LVM logical volumes and loop devices are not selected. deviceFilter (device name regex), devicePathFilter, and per-node devices lists, only matching ones are used.
  5. Preparation. The default is ceph-volume raw prepare . If encryption is enabled, multiple OSDs are placed on a single device (osdsPerDevice 2 or greater), or metadata devices are separated, raw mode cannot be used, so it falls back to ceph-volume lvm batch (allowRawMode, volume.go#L497-L525).
  6. Reporting results. Writes the list of created OSDs to the ConfigMap rook-ceph-osd-<노드>-status (status.go#L47). The operator reads this and creates a Deployment for each OSD.

The official documentation's requirements (Prerequisites) say the same thing. Things usable as OSDs are raw devices without partitions or filesystems, raw partitions without filesystems, LVM logical volumes without filesystems, and block mode PVs. Therefore, if you "attached a new disk but the OSD was not created," you should first check with lsblk -f to see if any filesystem signatures remain. Conversely, empty disks with no signatures become OSDs without even being queried by useAllDevices: true . This is why Part 3 warns about this default value.

The prepare Job uses a significant amount of memory once depending on the device size. The documentation mentions it is around 1 to 2 Gi on large disks and advises not to set a memory limit for this Job. Once an OSD is prepared, future prepares skip it as "already present," so the heavy usage happens only once.

5. CSI Deployment: Split Among Three in v1.21

CSI(Container Storage Interface) is the standard Kubernetes specification for storage vendor drivers. When a PVC is created, the CSI driver's provisioner(controller side) creates a volume on the storage, and when a pod is scheduled onto a node, that node's nodeplugin attaches (maps and mounts) the volume.

Historically, Rook directly deployed CSI drivers. In v1.21, this responsibility is split among three entities. Since this is the part where first-time installers are most likely to stumble, we will list them in order.

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 으로 붙인다
Figure: CSI in v1.21: Responsibilities divided among Rook, the ceph-csi-drivers chart, and ceph-csi-operator
  1. ceph-csi-operator. rook-ceph Installed together as a subchart (ceph-csi-operator 1.1.0) by the chart (Chart.yaml, condition csi.installCsiOperator). This operator actually creates the driver pods.
  2. ceph-csi-drivers chart. Creates the Driver CR specifying which drivers to enable, OperatorConfig, and the RBAC to be used by the driver pods. This chart is downloaded from the ceph-csi-operator Helm repository rather than the Rook repository and is installed with the values file prepared by Rook (deploy/charts/ceph-csi-drivers/values.yaml). By default, RBD and CephFS are enabled while NFS and NVMe-oF are disabled. Driver names are prefixed with the operator namespace, such as rook-ceph.rbd.csi.ceph.com . The upgrade documentation notes that if this chart is not installed, the CSI driver will fail due to missing service accounts(requirement since v1.20, Rook Upgrades).
  3. Rook operator. After mons are up, creates and modifies in the operator namespace the CephConnection CR (list of mon addresses, read affinity, number of RBD mirrors) and the default ClientProfile(Secret name to be used by RBD and CephFS) (ceph_connection.go#L37-L103, config.go#L149-L190). Both are named after the CephCluster namespace. As seen in Section 2, creating separate ClientProfiles for each subvolume group and RADOS namespace remains the same.

When ceph-csi-operator receives a Driver CR, it creates a CSIDriver object, a Deployment ending with -ctrlplugin (provisioner and sidecars), -nodeplugin ending DaemonSet (all nodes), liveness Service, NetworkPolicy, and configuration ConfigMap (driver_controller.go#L254-L264). Thus, you can see rook-ceph.rbd.csi.ceph.com-ctrlplugin-… and rook-ceph.rbd.csi.ceph.com-nodeplugin-… in the Rook quickstart pod list.

The provisioner of the StorageClass must match this driver name exactly. If you want to change the prefix part (CSI_DRIVER_NAME_PREFIX), the documentation warns to do so only during initial installation. It notes that changing it on an already running cluster may cause unexpected behavior (Ceph CSI Drivers).

6. Upgrade Procedure

Rook divides upgrades into two parts: Rook itself (management plane) and Ceph (data plane) .

Rook upgrade. Upgrade the charts in the order of rook-ceph → ceph-csi-drivers → rook-ceph-cluster . The breaking changes in v1.21 specified by the documentation are three:

Additionally, a new CSI Secret parameter was introduced for block StorageClasses in v1.21. Since the parameters of a StorageClass cannot be changed, it guides you to delete the ceph-block StorageClass before upgrading the chart. It notes that existing PVs are unaffected because a StorageClass with the same name will be recreated.

Ceph upgrade. Initiated by changing the CephCluster image (Ceph Upgrades). Rook checks if each daemon is safe to stop, updates it, waits for stabilization (quorum for mons, clean PGs for OSDs, up for MDS), and then proceeds to the next daemon. Following the reconcile order seen in Section 2, within CephCluster it goes mon, mgr, OSD in order, while MDS and RGW are handled by their respective controllers. The time waited for an OSD to become safe to stop is waitTimeoutForHealthyOSDInMinutes(default 10 minutes). Supported Ceph versions are Squid v19.2.0 or higher and Tentacle v20.2.1 or higher; v20.2.0 is not recommended due to a known data corruption issue when read affinity is enabled. Rook automatically disables read affinity on v20.2.0 (ceph_connection.go#L105-L116).

7. Cluster Cleanup: cleanupPolicy

Deleting a CephCluster does not delete the data. This is the default behavior, designed as a safeguard to prevent accidentally deleted CRs from wiping out data. The chart documentation also notes that even if you delete rook-ceph-cluster , data in /var/lib/rook and OSD disks remains, so you must delete them manually if you want to reuse the disks.

To truly delete them, first add a confirmation phrase to the CephCluster (Cleanup, CephCluster CRD: Cleanup policy).

Field Default Meaning
confirmation empty string yes-really-destroy-data Clean up only when set to . The moment this value is entered, the operator stops applying new configurations and waits solely for deletion. This can be reverted before deleting the CR
sanitizeDisks.method quick quick deletes only Ceph metadata, while complete wipes the entire disk
sanitizeDisks.dataSource zero Bytes to overwrite. random is much slower
sanitizeDisks.iteration 1 Number of overwrite passes
allowUninstallWithVolumes false Waits for any PVs created in that cluster to be deleted

If you delete the CephCluster while the confirmation phrase is present, the operator remembers the nodes where Ceph daemon pods were located, waits in 5-second intervals until all those pods have disappeared, and then spawns a Job (cluster-cleanup-job-<노드>, label rook-ceph-cleanup) on each of those nodes to delete dataDirHostPath and scrub the OSD disks (cleanup.go#L62-L100). The documentation emphasizes cleaning up application volumes and PVCs first. Failing to do so may leave volumes dangling and require a node restart. The CSI operator's CR (operatorconfigs·drivers·clientprofiles·clientprofilemappings·cephconnections) must also be deleted separately.

If you deleted it without the confirmation phrase, you must manually delete dataDirHostPath on each node and scrub the disks (the documentation's sgdisk --zap-all, dd, blkdiscard, LVM and device mapper cleanup procedure). In particular, chart values comments warn that reinstalling while leaving dataDirHostPath behind will prevent mons from starting in the new cluster.

8. Toolbox

The primary tool for inspecting Ceph is the ceph command. Rook provides a container containing this command as a Deployment ( toolbox ) namedrook-ceph-tools(Toolbox). In the cluster chart, toolbox.enabled is the default false , and when enabled, it runs with the same image as Ceph. There is also a Job approach that runs a command once and logs the results, as well as kubectl plugins that work without the toolbox.

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

In Part 4, we plan to read these three outputs from the TT Lab practice cluster in alignment with the concepts from Part 1.

9. What the Operator Does Not Do

When adopting an operator, it is easy to expect, "Now it will take care of everything automatically." These are things verified from the original text that Rook does not do, or does not do by default.

What it doesn't do Who does it Basis
Standing in the data path Applications and OSDs directly Storage Architecture "Rook is not in the Ceph data path"
Creating StorageClass and VolumeSnapshotClass Cluster Chart (or User) Chart Template cephblockpool.yaml
Creating CSI Driver Driver CR ceph-csi-drivers Chart, Pod is ceph-csi-operator Section 5
Removing Failed OSDs from the Cluster The default is human. removeOSDsIfOutAndSafeToRemove: true The operator removes OSDs that are marked out and safe to delete CephCluster CRD Documentation
Data Relocation Ceph (out if down for 10 minutes, Part 1) Ceph Documentation
Adjusting the Number of PGs Ceph's PG Autoscale (Part 1) Ceph Documentation
Wiping Disk and Host Paths When Deleting CR The default is human. cleanupPolicy The operator proceeds only when there is a confirmation phrase Section 7
lvm2 Installation, Matching Kernel Versions Node Manager Prerequisites, Rook Upgrades
Backing Up Mon Metadata and Keyring External tools. Rook only lets clusterMetadata annotations allow such tools to rook-ceph-mon-endpoints·rook-ceph-mon·rook-ceph-admin-keyring choose CephCluster CRD Documentation

There is one more thing to note. useAllNodes: true When a node's taint or affinity is changed so that it is excluded from Rook's targets, the OSD on that node must be removed by "confirming" it through deleting and restarting the operator pod. This is a safeguard to prevent accidental data loss, and the documentation recommends specifying nodes by useAllNodes: false in production environments.

A word from Titi. Rook is a meticulous manager. If a mon is unhealthy, it waits for 10 minutes; if it sees an empty disk, it turns it into an OSD; and when told to delete, it always asks, "Are you sure?" However, it does not transport data itself. That is the job of the Ceph daemons. Therefore, even if the operator rests for a moment, already attached volumes remain perfectly fine!

Reference

Sources and documentation were accessed from the above commit on 2026-10-29.

Sign in to like

Comments

No comments yet.

Sign in to leave a comment