Rook-Ceph¶
Distributed block storage using Rook as the Kubernetes
operator for Ceph. It is the component that still has your
data after a node dies, and the one with the steepest learning curve. Check
ceph status more often than seems necessary and never ignore a WARN.
At a glance¶
| Namespace | rook-ceph |
| Stage | 03-controllers for rook-ceph-operator; 04-storage for rook-ceph (CSI driver, RBAC, dashboards) and rook-ceph-cluster |
| Depends on | A raw rook-osd partition on every node, written at install time |
| If it is down | Every pod with a volume — openbao first, which turns a storage problem into a cluster problem |
| Health check | make storage-check — it binds a real PVC, which ceph status alone does not prove |
| UI | rook.infra.k8s.wlkr.ch |
| Files | payload/platform/rook-ceph-operator/, payload/platform/rook-ceph-cluster/, payload/platform/rook-ceph/ |
Configuration¶
Disks¶
Each node has a raw partition labelled rook-osd, created by Ignition as the
last partition with whatever the root leaves, which Rook adds as an OSD;
data is replicated three ways across them. Its contents are wherever Ceph last
wrote them, so inserting a partition ahead of it shifts its offset and takes
the OSD data with it: changing the partition table is a
reinstall, not an edit.
Ignition declares it with format: none and wipe_filesystem: true, because
ceph-volume reads the BlueStore signature, not the partition table, and a
reinstalled node that kept the old bytes would present an OSD from a cluster
that no longer exists. Ceph declines any device that already carries a
filesystem — the first thing to check when an OSD refuses to appear, see
No OSDs after reprovisioning.
Storage classes¶
| StorageClass | Provides | Notes |
|---|---|---|
rook-ceph-block |
RBD block volumes, cluster default | ReadWriteOnce only; ReadWriteMany needs CephFS, which is not enabled. Block PVs are deleted with their claim, and every Application prunes, so removing a PVC from Git removes the volume — see Backups |
ceph-bucket |
S3 buckets from the object store | Reclaim Retain, so deleting a claim cannot delete the bucket |
Object storage¶
A CephObjectStore provides an S3 endpoint at
http://rook-ceph-rgw-object-store.rook-ceph.svc, so backups and log chunks
live somewhere other than the block pool they protect. Two RGW instances keep
a node reboot from stalling a backup; the data pool is erasure coded 2+1 —
1.5x overhead rather than 3x, safe at failureDomain: host with six nodes.
Monitoring¶
| Setting | Why |
|---|---|
cephClusterSpec.monitoring.enabled |
Turns on the mgr prometheus module and lets the operator maintain a ServiceMonitor. Without it a degraded pool, a down OSD or a near-full cluster is invisible to Prometheus, and a full Ceph cluster stops accepting writes |
monitoring.createPrometheusRules |
Ships Ceph's own alerting rules as a PrometheusRule. Its CRD comes from prometheus-operator-crds in 01-crds so the kind exists when 04-storage syncs — see Monitoring |
cephClusterSpec.cephConfig mgr/dashboard/* |
Sets the dashboard's Prometheus and Grafana links through the mon config store, which the operator reapplies on every reconcile, rather than a one-off ceph dashboard set-* Job |
mon_cluster_log_level: info |
The Ceph default is debug, and the mons copy the cluster log to stderr, so Rook's probes and the mgr's pgmap ticks became most of the mon log volume in Loki. Warnings and errors are unaffected |
Rook's Grafana dashboards are vendored in grafana-dashboards.yaml — see
Monitoring → Dashboards.
CSI driver¶
From Rook v1.20 the RBD driver is deployed by the ceph-csi-operator subchart,
which deploys nothing until an OperatorConfig and a Driver CR tell it what
to run. Neither chart ships them, so they live in csi-driver.yaml; without
them every PVC stays Pending on ExternalProvisioning.
| Setting | Why |
|---|---|
csi-driver.yaml in rook-ceph, with SkipDryRunOnMissingResource |
The CRs need the ceph-csi-operator's CRDs, which arrive with the operator chart one stage earlier |
OperatorConfig pointing at the chart's image-set ConfigMap |
A chart bump moves every sidecar and the cephcsi image together |
csi.<sidecar>.tag pins in rook-ceph-operator |
The sidecars Rook ships link a gRPC with an authorization bypass; the pins move ahead of the chart under # renovate: annotations and go once the chart catches up |
Driver name rook-ceph.rbd.csi.ceph.com |
Derived from the namespace and must match the StorageClasses' provisioner. RBD is the only driver; CephFS is not enabled |
csi-rbac.yaml |
The ceph-csi-operator creates no ServiceAccounts, and the names are fixed by CSI_SERVICE_ACCOUNT_PREFIX (empty here): rbd-ctrlplugin-sa and rbd-nodeplugin-sa. Without them the Deployment and DaemonSet exist but create no pods |
csi values on the operator chart do nothing, except the images
Helm accepts values a chart no longer reads. Driver settings go in csi-driver.yaml; only csi.<sidecar>.tag still renders into the image-set ConfigMap.
csi-rbac.yaml is the RBD half of the ceph-csi-operator's
deploy/multifile/csi-rbac.yaml at the version the rook-ceph chart pulls in;
Renovate does not see it. To refresh it:
-
Fetch and rename:
-
Keep only the rbd documents (CephFS, NFS and NVMe-oF are not enabled).
- Re-indent the sequences for yamllint and prefix the cluster-scoped names.
cephx keys stay on aes¶
Ceph 20 warns about the legacy aes cipher, and every key here is
deliberately on it: the kernel client krbd does not implement aes256k, so
CSI keys moved to it make every rbd map fail with
map failed: (22) Invalid argument while Ceph reports the rotation a success.
Two ciphers in one cluster trigger AUTH_EMERGENCY_CIPHERS_SET and buy no
security.
| Setting | Why |
|---|---|
keyType: aes |
krbd cannot use anything else |
keyRotationPolicy: KeyGeneration |
Changing keyType alone never rotates existing keys while the policy is Disabled |
keyGeneration |
Rotation only triggers on an increase, so a generation can never be lowered to undo one |
healthCheck.muteHealthWarning |
The AUTH_* warnings would otherwise keep the Application Degraded while the data is healthy. Temporary: remove once RBD moves to the userspace rbd-nbd mounter and the keys can leave aes |
Usage¶
Requesting a volume¶
Any workload can claim the default class:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-app-data
namespace: my-app
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
Add storageClassName: rook-ceph-block to say so explicitly. A Deployment
with two replicas sharing one RWO PVC schedules one pod and leaves the other in
ContainerCreating with a multi-attach error.
Requesting a bucket¶
Ask for an ObjectBucketClaim; Rook creates the bucket and writes a
ConfigMap (BUCKET_NAME, BUCKET_HOST, BUCKET_PORT) and a Secret
(AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY) sharing the claim's name:
apiVersion: objectbucket.io/v1alpha1
kind: ObjectBucketClaim
metadata:
name: my-app-bucket
namespace: my-app
spec:
generateBucketName: my-app
storageClassName: ceph-bucket
Health check¶
Is storage ready?¶
Run it after the GitOps handover, before trusting anything that mounts a
volume: a CephCluster reports Ready with no OSDs, and a StorageClass
exists whether or not a CSI driver registered for it. The script walks the
chain in the order it breaks and stops at the first missing link:
| Check | What its absence means |
|---|---|
CephCluster is Ready |
the cluster Application has not synced yet |
an OSD pod is Running |
Rook took no disk — see No OSDs after reprovisioning |
ceph health is not HEALTH_ERR |
Ceph itself is unwell; HEALTH_WARN is allowed through |
| the CSI driver is registered | no Driver CR, so the ceph-csi-operator deployed nothing |
| node plugin and provisioner are up | usually a ServiceAccount the DaemonSet cannot find |
| a 1 GiB PVC binds and is cleaned up | the only check that proves the other five |
The last one asks for a volume the way a workload would and waits TIMEOUT
seconds (120 by default); NAMESPACE and CLASS override where and what it
asks for.
Pitfalls¶
Ceph warnings block the rollout
ArgoCD maps HEALTH_WARN to Degraded, so while Ceph warns no change reaches anything after 04-storage — see GitOps.
Recovery¶
No OSDs after reprovisioning¶
A node whose rook-osd partition still carries a previous cluster's BlueStore
signature gets no OSD: the CephCluster reports Ready with HEALTH_WARN,
kubectl -n rook-ceph get pods -l app=rook-ceph-osd is empty, and the first
PVC — data-openbao-0 on a fresh bootstrap — stays Pending.
The old cluster's data is already gone
Once the mons that held the cluster map left with the old control plane, those OSDs cannot be re-adopted by anything. Take a backup off the disks before a rebuild if you need one.
-
Confirm the cause:
-
Rebuild the node; the installer wipes the disk:
-
If the node cannot be rebuilt now, wipe the partition by hand over SSH.
wipefsremoves the signature Rook skips on; theddremoves the BlueStore label behind it. Address it by label — the partition number differs between control-plane and worker nodes: -
Restart the operator so it recreates the
osd-preparejobs: -
Confirm an OSD per node and a binding PVC: