The module lifecycle stage: Experimental
The module has requirements for installation
Before enabling the module, you need to allow the use of experimental modules in the deckhouse ModuleConfig. To do this, set the allowExperimentalModules parameter to true.
Deckhouse Platform installs CRDs but does not remove them when a module is disabled. If you no longer need the created CRDs, delete them.
ElasticCluster
Short names: ec
Scope: Namespaced
Version: v1alpha1
ElasticCluster describes the desired state of a Ceph cluster managed by the sds-elastic module. The controller bootstraps a Rook CephCluster (mon/mgr/osd) on top of LVM-based local storage discovered via blockDeviceSelector.
Pool configuration (RBD/CephFS, replication strategy) is defined separately in ElasticStorageClass resources of the same namespace that reference this ElasticCluster by name.
The namespace is what separates one Ceph cluster from another. Rook allows a single CephCluster per namespace, and the module derives the namespace it deploys Rook into from the namespace of this resource, so at most one ElasticCluster per namespace is allowed (enforced by the validating webhook).
-
objectspecDesired state of the Ceph cluster managed by the sds-elastic module.
-
objectspec.network
Optional network configuration. If unset, Rook listens on every host IP of storage nodes (host networking). When set, both
publicandclustermust be provided together (all-or-nothing pair).Ceph runs with
network.provider: host, so both fields must list host subnets — every daemon (mon/mgr/osd) binds directly to a host interface and must own an IP inside one of the given ranges. The Pod network is not a valid value for either field. Each field accepts one or more CIDRs, so a range set can be given when storage nodes span several host subnets.Both fields are immutable after creation.
-
array of stringsspec.network.cluster
Required value
One or more CIDRs of the cluster (host) network used only for OSD-to-OSD traffic (replication, backfill, recovery, heartbeat). Set it to a dedicated storage-NIC subnet, or to the same value aspublicwhen there is no separate one (for example,["10.0.1.0/24"]).-
stringspec.network.cluster.Element of the array
Length:
9..18Pattern:
^([0-9]{1,3}\.){3}[0-9]{1,3}\/[0-9]{1,2}$
-
-
array of stringsspec.network.public
Required value
One or more CIDRs of the public (host) network carrying client traffic (Pods reaching mon/osd) plus all inter-daemon traffic except OSD replication (for example,["10.0.0.0/24"]).-
stringspec.network.public.Element of the array
Length:
9..18Pattern:
^([0-9]{1,3}\.){3}[0-9]{1,3}\/[0-9]{1,2}$
-
-
-
objectspec.storage
Required value
OSD backing storage configuration. The controller selects matching BlockDevices on matching nodes and provisions one LVMVolumeGroup per device, one LVMLogicalVolume per group, and one local PersistentVolume per LVMLogicalVolume that backs a Rook OSD.-
objectspec.storage.blockDeviceSelector
Required value
Selector that matches BlockDevice CRs (managed by sds-node-configurator) eligible for OSD provisioning. Selected devices must reside on nodes that match
storage.nodeSelectorand must be unused (consumable).The field is required, so the key is always present: selecting every device is written as an explicit empty selector (
{}), which stands for every consumable BlockDevice on the nodes matched bystorage.nodeSelector. Either selector is re-evaluated on every reconcile, so with an empty one a disk that appears on a matching node later joins the cluster with no edit to this resource.-
array of objectsspec.storage.blockDeviceSelector.matchExpressionsList of label selector requirements.
-
stringspec.storage.blockDeviceSelector.matchExpressions.key
-
stringspec.storage.blockDeviceSelector.matchExpressions.operator
Allowed values:
In,NotIn,Exists,DoesNotExist -
array of stringsspec.storage.blockDeviceSelector.matchExpressions.values
-
-
objectspec.storage.blockDeviceSelector.matchLabelsMap of label selectors.
-
-
booleanspec.storage.encrypted
Turns on dm-crypt (LUKS2) underneath every OSD of the cluster: Rook hands each OSD block device to
cryptsetupbefore ceph-volume touches it, so both the data and the Ceph metadata next to it are unreadable once the disk leaves the node. It protects a pulled or replaced disk, not a running cluster — anything able to read through a live OSD sees plaintext.Key encryption keys are stored in Kubernetes Secrets of the module namespace unless
encryptionKeyStorepoints them at an external key manager. Keys in Secrets live in the same cluster as the data they unlock, and losing them makes the OSDs unrecoverable.Immutable. Rook encrypts an OSD only while creating it, so the flag cannot be flipped on an existing cluster — every OSD would have to be drained and re-created. Expect a measurable cost on the data path: every read and write of every OSD goes through dm-crypt.
Default:
false -
objectspec.storage.encryptionKeyStore
Where the key encryption key of every OSD is kept. Unset means Kubernetes Secrets of the cluster’s namespace, which is Rook’s own default.
Immutable, for the same reason
encryptedis: Rook writes the key to the store it was told about while creating the OSD and never migrates keys between stores, so a swap on a live cluster would leave the existing OSDs unable to unlock.-
objectspec.storage.encryptionKeyStore.strongholdCoordinates of the Vault-API store. Required when
typeisStronghold, rejected otherwise. The module never provisions anything inside Stronghold: the KV mount, its policy and the role below are created by whoever runs it.-
stringspec.storage.encryptionKeyStore.stronghold.address
Required value
Base URL of the Stronghold API, reachable from the storage nodes: Ceph daemons run with host networking, so the name has to resolve and route from the node, not only from the Pod network.Length:
8..253Pattern:
^https?://[a-zA-Z0-9._-]+(:[0-9]{1,5})?/?$ -
stringspec.storage.encryptionKeyStore.stronghold.authMountPathWhere the Kubernetes auth method is mounted. Deckhouse Stronghold mounts the one for its own cluster at
kubernetes_local, which is why that, and not Vault’skubernetes, is the default.Default:
kubernetes_localLength:
1..128Pattern:
^[a-zA-Z0-9._-]+$ -
stringspec.storage.encryptionKeyStore.stronghold.caCertSecretNameName of a Secret in the cluster’s namespace holding the CA bundle that signs the Stronghold certificate, under the key
cert. Rook takes a Secret name here rather than a path or an inline PEM.Maximum length:
253 -
booleanspec.storage.encryptionKeyStore.stronghold.insecureSkipTLSVerifyTurns off certificate verification towards Stronghold. It exists for test clusters with a self-signed certificate and no CA bundle to hand over; anything holding real data should ship the CA instead.
-
stringspec.storage.encryptionKeyStore.stronghold.kvPath
Required value
Mount path of the KV v2 engine holding the keys.Length:
1..128Pattern:
^[a-zA-Z0-9._-]+$ -
stringspec.storage.encryptionKeyStore.stronghold.role
Required value
Kubernetes-auth role Rook logs in as. It has to bind therook-ceph-osdandrook-ceph-systemservice accounts of the cluster’s namespace and carry a policy overkvPath.Length:
1..128
-
-
stringspec.storage.encryptionKeyStore.type
Required value
Picks the store.
KubernetesSecretkeeps a key per OSD in a Secret of the module namespace: no external dependency, but the keys share the cluster with the data they unlock and disappear with it.Strongholdkeeps them in Deckhouse Stronghold (or any store serving the same Vault API) over its KV v2 engine. The key is read on every OSD start, not once at creation, so the store has to be reachable and unsealed whenever OSDs start — and it must not be backed by the very ElasticCluster it protects, or a cold start deadlocks.Default:
KubernetesSecretAllowed values:
KubernetesSecret,Stronghold
-
-
objectspec.storage.keyRotation
Asks Rook to replace the key encryption key of every OSD on a schedule. What changes is the key that unlocks the volume, not the key the data itself is encrypted with, so nothing is re-encrypted and the OSDs keep serving.
Unlike
encryptedandencryptionKeyStorethis is mutable: rotation is an operational policy, and turning it on or off — or moving the schedule — has to be possible on a running cluster.-
booleanspec.storage.keyRotation.enabled
Required value
Turns the rotation on. Requiresencrypted: true— without encryption there are no OSD keys to rotate.Default:
false -
stringspec.storage.keyRotation.scheduleHow often the keys are replaced: a cron expression (
0 3 * * 0) or one of the usual descriptors (@weekly,@monthly). Unset leaves Rook’s own default, which is weekly.Length:
1..64
-
-
objectspec.storage.nodeSelector
Required value
Selector that matches Kubernetes Nodes considered as storage nodes. Each matching node hosts mon/mgr/mds (as needed) and OSD daemons if it has eligible BlockDevices.
The field is required, so the key is always present: selecting every Node is written as an explicit empty selector (
{}), which matches the whole cluster, control-plane Nodes included.-
array of objectsspec.storage.nodeSelector.matchExpressionsList of label selector requirements.
-
stringspec.storage.nodeSelector.matchExpressions.key
-
stringspec.storage.nodeSelector.matchExpressions.operator
Allowed values:
In,NotIn,Exists,DoesNotExist -
array of stringsspec.storage.nodeSelector.matchExpressions.values
-
-
objectspec.storage.nodeSelector.matchLabelsMap of label selectors.
-
-
-
-
objectstatusCurrent state of resources managed by ElasticCluster.
-
objectstatus.capacityCluster-wide raw storage usage as observed by Rook (
CephCluster.status.ceph.capacity). Empty until Rook publishes the first capacity probe.-
integer or stringstatus.capacity.availableFree raw capacity, as a Kubernetes Quantity (BinarySI). Usable client capacity depends on pool-level replication (~
available / pool.sizefor replicated pools).Pattern:
^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ -
stringstatus.capacity.lastUpdatedTimestamp of the latest capacity probe Rook successfully observed against the cluster.
-
integer or stringstatus.capacity.totalTotal raw capacity of the cluster (sum over all OSDs), as a Kubernetes Quantity (BinarySI), e.g.
500Gior1.2Ti.Pattern:
^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ -
integer or stringstatus.capacity.usedConsumed raw capacity (data + metadata + Ceph overhead), as a Kubernetes Quantity (BinarySI), e.g.
200Gi.Pattern:
^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ -
stringstatus.capacity.usedPercent
used / total * 100, formatted with two decimals.
-
-
stringstatus.cephFSIDFSID/UUID of the deployed Ceph cluster, populated from the
rook-ceph-monSecret once Rook starts the cluster. -
objectstatus.cephTopologyEffective Ceph daemon counts the controller has applied to the underlying CephCluster. Promotion is sticky (monotonic): once an
ElasticStorageClasswithreplication: HighRedundancyraises the counts to the high-availability profile (monCount=5,mgrCount=3), the controller never lowers them back, even when the trigger ESC is removed. Operators can force a recompute by clearing this field via the status subresource.-
stringstatus.cephTopology.lastPromotedAtTimestamp of the most recent count increase. Empty until the cluster first leaves the standard profile.
-
integerstatus.cephTopology.mgrCount
Required value
EffectiveCephCluster.spec.mgr.count.Allowed values:
1 <= X -
integerstatus.cephTopology.monCount
Required value
EffectiveCephCluster.spec.mon.count.Allowed values:
1 <= X -
stringstatus.cephTopology.reasonMachine-readable explanation of the latest topology decision. Known values:
Standard(defaults are in effect),HighRedundancyESCPresent(a HighRedundancy ESC currently demands the high-availability profile),StickyHighWaterMark(the recorded counts exceed what the live ESC inventory currently demands; the prior promotion is being preserved).
-
-
objectstatus.cephVersionCeph version state. The desired version is hardcoded in the module image; bumping the module triggers a rolling upgrade in Rook.
-
stringstatus.cephVersion.requestedCeph version requested by the controller (matches the module image).
-
stringstatus.cephVersion.runningCeph version currently running across the cluster, derived from
CephCluster.status.ceph.versions. -
objectstatus.cephVersion.upgradeProgressPer-stage progress while a rolling upgrade is in flight.
-
integerstatus.cephVersion.upgradeProgress.completedNumber of daemons in the current stage already running the requested version.
Allowed values:
0 <= X -
stringstatus.cephVersion.upgradeProgress.lastTransitionTimeTimestamp of the last upgrade-stage transition.
-
stringstatus.cephVersion.upgradeProgress.phaseCurrent upgrade stage.
Allowed values:
MonUpgrading,MgrUpgrading,MdsUpgrading,OsdUpgrading,Completed -
integerstatus.cephVersion.upgradeProgress.totalTotal number of daemons in the current stage.
Allowed values:
0 <= X
-
-
-
array of objectsstatus.conditionsPer-component conditions:
StorageReady,CephClusterReady,CredentialsReady,CsiCephReady,UpgradeReady,UpgradeInProgress, and the aggregateReady.-
stringstatus.conditions.lastTransitionTimeTimestamp of the last status transition for this condition.
-
stringstatus.conditions.messageHuman-readable explanation of the current status.
Maximum length:
32768 -
integerstatus.conditions.observedGenerationValue of
metadata.generationthe condition was last set against.Allowed values:
0 <= X -
stringstatus.conditions.reasonMachine-readable reason for the current status, suitable for log filtering.
Length:
1..1024Pattern:
^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ -
stringstatus.conditions.statusCurrent status of the condition.
Allowed values:
True,False,Unknown -
stringstatus.conditions.typeCondition type, for example
Ready,StorageReady, orCephClusterReady.Maximum length:
316Pattern:
^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
-
-
objectstatus.credentialsRefReference to the ElasticClusterCredentials resource that backs up cluster identity (FSID, mon-secret, admin-secret) outside the cluster’s namespace. Named after the cluster; outside the module namespace the name is prefixed with the cluster’s namespace.
-
stringstatus.credentialsRef.nameName of the ElasticClusterCredentials resource (1:1 with this ElasticCluster’s
metadata.name).
-
-
stringstatus.dataDirHostPath
Host directory the Ceph daemons of this cluster keep their state in on every node (
CephCluster.spec.dataDirHostPath): the mon store, the Ceph configuration and keyring the daemons read, and crash reports. There is no volume behind the monitors, so this directory is the monitor data.Set once, when the controller first renders the CephCluster, and read from here on every reconcile after that; it never changes. A cluster created by this release or later gets
/var/lib/deckhouse/sds/elastic/<namespace>/<name>. A cluster created by an earlier release keeps the path its CephCluster already has —/opt/deckhouse/sds/elastic/<name>ind8-sds-elastic,/opt/deckhouse/sds/elastic/<namespace>/<name>elsewhere — for good: the module does not move the mon store, and there is no migration.The value can be cleared through the status subresource but not changed; a cleared value is recorded again from the running CephCluster. The directory is not removed when the cluster is deleted, so read it before deleting a cluster you intend to re-create under the same name.
-
objectstatus.healthLatest Ceph health summary surfaced by Rook (
CephCluster.status.ceph.healthand the matching detail blocks). Drives the top-level OK / Warn / Err indicator on the UI dashboard.-
array of objectsstatus.health.checksActive health checks (warnings/errors) reported by Ceph. Empty on
HEALTH_OK.-
stringstatus.health.checks.messageHuman-readable description of the check.
-
stringstatus.health.checks.nameCeph check identifier, for example
MON_DOWN,OSD_NEARFULL,POOL_NO_REDUNDANCY. -
stringstatus.health.checks.severityPer-check severity, mirroring Rook (
HEALTH_WARN/HEALTH_ERR).
-
-
stringstatus.health.lastCheckedTimestamp of the latest health probe Rook successfully observed against the cluster.
-
stringstatus.health.messageShort human-readable summary Rook attaches to the health probe.
-
stringstatus.health.statusOne of
HEALTH_OK,HEALTH_WARN,HEALTH_ERR. Empty until Rook publishes the first health probe.Allowed values:
HEALTH_OK,HEALTH_WARN,HEALTH_ERR
-
-
objectstatus.mgrsCeph manager daemon count and per-version histogram, derived from
CephCluster.status.ceph.versions.mgr. Rook deploys a single active manager by default (with optional standbys), soknownToCeph == 1is the typical healthy value.-
array of objectsstatus.mgrs.byVersionPer-version histogram for mgr daemons. Same ordering as
osds.byVersion.-
integerstatus.mgrs.byVersion.countNumber of mgr daemons reporting this version.
Allowed values:
0 <= X -
stringstatus.mgrs.byVersion.versionCeph version string Rook publishes for this bucket.
-
-
integerstatus.mgrs.knownToCephSum of
CephCluster.status.ceph.versions.mgrvalues.Allowed values:
0 <= X
-
-
array of stringsstatus.monEndpointsList of
ceph-monendpoints in the form<host>:<port>, parsed from therook-ceph-mon-endpointsConfigMap. -
stringstatus.monMaxIdHighest mon id reached so far, copied verbatim from
data.maxMonIdof therook-ceph-mon-endpointsConfigMap. Always a non-negative integer encoded as string for parity with the raw ConfigMap value. Reserved for use by the disaster-recovery flow in a future release. -
objectstatus.monsCeph monitor daemon count and per-version histogram, derived from
CephCluster.status.ceph.versions.mon. UI usesknownToCephto render a coarse quorum indicator (knownToCeph >= floor(spec.mon.count/2)+1).-
array of objectsstatus.mons.byVersionPer-version histogram for mon daemons. Same ordering as
osds.byVersion.-
integerstatus.mons.byVersion.countNumber of mon daemons reporting this version.
Allowed values:
0 <= X -
stringstatus.mons.byVersion.versionCeph version string Rook publishes for this bucket.
-
-
integerstatus.mons.knownToCephSum of
CephCluster.status.ceph.versions.monvalues.Allowed values:
0 <= X
-
-
integerstatus.observedGenerationThe last value of
metadata.generationprocessed by the controller. -
objectstatus.osdsOSD daemon count and per-version histogram. Sourced from
CephCluster.status.ceph.versions.osd(withdesiredcoming from the controller’s selected BlockDevice count).-
array of objectsstatus.osds.byVersionPer-version histogram for OSD daemons. Sorted by
countdesc, thenversionasc. Empty until at least one OSD has reported a version.-
integerstatus.osds.byVersion.countNumber of OSD daemons reporting this version.
Allowed values:
0 <= X -
stringstatus.osds.byVersion.versionCeph version string Rook publishes for this bucket (verbatim — e.g.
ceph version 19.2.5 (...) squid (stable)).
-
-
integerstatus.osds.desiredNumber of OSDs the controller asked Rook for (== matched BlockDevices for this ElasticCluster).
Allowed values:
0 <= X -
integerstatus.osds.knownToCephSum of
CephCluster.status.ceph.versions.osdvalues — OSD daemons that have reported a version to Ceph since the last heartbeat. Approximation of “alive in Ceph’s eyes”, not Pod readiness.Allowed values:
0 <= X
-
-
stringstatus.phaseAggregate phase:
Pending,InProgress,Ready, orError.Allowed values:
Pending,InProgress,Ready,Error
-
ElasticStorageClass
Short names: esc
Scope: Namespaced
Version: v1alpha1
ElasticStorageClass declares a single Ceph pool plus the corresponding
Kubernetes StorageClass (provisioned via the csi-ceph module). The
controller maps spec.replication to a production-tested pool layout
and creates a 1:1-named CephStorageClass in csi-ceph.
The resource is namespaced and spec.clusterRef is resolved inside its
own namespace: a storage class belongs to the ElasticCluster that shares
its namespace. The StorageClass the pool ends up backing is still
cluster-wide, so its name (== the name of this resource) has to be unique
across the cluster, not only within the namespace.
The ErasureCodedCompact replication mode is temporarily disabled and
cannot be selected.
-
objectspec
-
stringspec.clusterRef
Required value
Name of the ElasticCluster this storage class belongs to. The referenced cluster must exist in the SAME namespace as this resource and be inReadyphase before pool provisioning starts.Length:
1..30Pattern:
^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ -
stringspec.replication
High-level replication strategy. The controller translates this into pool-level settings:
AvailabilityWithoutConsistency— 2 replicas,min_size=1,requireSafeReplicaSize=false. Maximum availability at the cost of split-brain risk; suitable for non-critical workloads.ConsistencyAndAvailability(default) — 3 replicas,min_size=2. Recommended production setting.HighRedundancy— 4 replicas,min_size=2,requireSafeReplicaSize=true. Tolerates two simultaneous host failures with continued I/O and one extra failure as a recovery margin (data loss only at the fourth simultaneous failure). Requires at least 5 storage nodes; the controller auto-promotes the underlying CephCluster to a 5-mon, 3-mgr topology while at least one HighRedundancy ESC exists, and keeps the promotion sticky after the trigger ESC is removed. SeeElasticCluster.status.cephTopologyfor the audit trail.
The
ErasureCodedCompactmode is temporarily disabled and is not an accepted value.Default:
ConsistencyAndAvailabilityAllowed values:
AvailabilityWithoutConsistency,ConsistencyAndAvailability,HighRedundancy -
stringspec.type
Required value
Type of the underlying Ceph pool / Kubernetes StorageClass.RBDprovisions block volumes via Rados Block Device,CephFSprovisions shared-filesystem volumes via CephFS subvolumes.Allowed values:
RBD,CephFS
-
-
objectstatusCurrent state of resources managed by ElasticStorageClass.
-
array of objectsstatus.conditionsPer-component conditions:
PoolReady,CsiStorageClassReady, and the aggregateReady.-
stringstatus.conditions.lastTransitionTimeTimestamp of the last status transition for this condition.
-
stringstatus.conditions.messageHuman-readable explanation of the current status.
Maximum length:
32768 -
integerstatus.conditions.observedGenerationValue of
metadata.generationthe condition was last set against.Allowed values:
0 <= X -
stringstatus.conditions.reasonMachine-readable reason for the current status, suitable for log filtering.
Length:
1..1024Pattern:
^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ -
stringstatus.conditions.statusCurrent status of the condition.
Allowed values:
True,False,Unknown -
stringstatus.conditions.typeCondition type, for example
Ready,PoolReady, orCsiStorageClassReady.Maximum length:
316Pattern:
^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
-
-
integerstatus.observedGenerationObservedGeneration is the most recent .metadata.generation reconciled by the controller.
-
stringstatus.phasePhase is a coarse-grained summary derived from Conditions.
Allowed values:
Pending,InProgress,Ready,Error
-