Storage Class
A Kubernetes StorageClass defines the way dynamic storage provisioning is handled within a cluster. When a
PersistentVolumeClaim (PVC) references a StorageClass, Kubernetes provisions a PersistentVolume (PV) according to the
class's parameters. With simplyblock, a StorageClass draws from one StoragePool of one StorageCluster, and its
parameters describe the volumes the CSI driver creates in that pool: QoS ceilings, encryption, filesystem, and more.
How Simplyblock Uses StorageClass
The CSI driver csi.simplyblock.io reads the class parameters at volume creation and asks the simplyblock control
plane for a logical volume in the named cluster and pool. Kubernetes does not allow the parameters of a StorageClass
to change after creation, so the settings of a class hold for every volume provisioned from it.
A class and a pool are joined by three labels on the class. All three are required, because a pool name is only unique within one namespace and one cluster:
| Label | Value |
|---|---|
storage.simplyblock.io/namespace |
Namespace of the StoragePool. |
storage.simplyblock.io/cluster |
Name of the StorageCluster of the pool. |
storage.simplyblock.io/pool |
Name of the StoragePool. |
A pool may have any number of classes, and each class may be named freely. The pool lists the classes assigned to it
in status.storageClassNames.
StorageClass Created by a Storage Pool
The operator writes exactly one StorageClass itself: the class of a cluster's default pool <cluster>-default, named
simplyblock-<namespace>-<cluster>. It carries the three labels and the label
storage.simplyblock.io/managed-by=storagecluster, uses volumeBindingMode: WaitForFirstConsumer,
reclaimPolicy: Delete, and allowVolumeExpansion: true, and is not marked as the default class of the Kubernetes
cluster. Its name is recorded in the pool's status.defaultStorageClassName.
The class is written once. When it is deleted, it is not recreated. When a class of that name already exists and is
not the operator's, the pool gets no default class and a StorageClassNameTaken event is emitted.
Authoring a StorageClass for a Pool
For every other pool, the StorageClass is written by an administrator. It carries the three labels, cluster_id
(the StorageCluster.status.uuid), and pool_name, and the parameters that correspond to the pool's volume defaults.
apiVersion: storage.simplyblock.io/v1alpha2
kind: StoragePool
metadata:
name: tenant-a
namespace: simplyblock
spec:
clusterRef: production
limits:
capacity: 10T
iops: 200000
volumeDefaults:
iops: 20000
throughput:
readWrite: 512
filesystem: xfs
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: tenant-a-fast
labels:
storage.simplyblock.io/namespace: simplyblock
storage.simplyblock.io/cluster: production
storage.simplyblock.io/pool: tenant-a
provisioner: csi.simplyblock.io
parameters:
cluster_id: <CLUSTER_UUID>
pool_name: tenant-a
max_iops: "20000"
max_mbytes_per_sec: "512"
csi.storage.k8s.io/fstype: xfs
reclaimPolicy: Delete
volumeBindingMode: WaitForFirstConsumer
allowVolumeExpansion: true
kubectl get storagecluster production -n simplyblock -o jsonpath='{.status.uuid}'
A class without the labels still provisions volumes, but the pool does not know about it. Such a class is not listed
in status.storageClassNames, and it cannot be chosen when the operator needs a class for a pool, for example, for a
restored backup.
Pool Volume Defaults and Class Parameters
StoragePool.spec.volumeDefaults states what every volume of the pool is created with. The block is immutable once
set, because the parameters of a StorageClass are. A pool with different defaults is a new pool. The operator writes
the defaults of the default pool into its class with the mapping below, and an authored class for a pool uses the same
keys:
volumeDefaults field |
StorageClass parameter |
|---|---|
iops |
max_iops |
throughput.readWrite |
max_mbytes_per_sec |
throughput.read |
max_read_mbytes_per_sec |
throughput.write |
max_write_mbytes_per_sec |
enableEncryption |
encryption |
enableCompression |
compression |
enableClientCompression |
client_compression |
enableClientDeduplication |
client_deduplication |
enableReplication |
replicate |
maxNamespacesPerSubsystem |
max_namespace_per_subsys |
priorityClass |
priority_class |
filesystem |
csi.storage.k8s.io/fstype |
fabric |
fabric |
tune2fsReservedBlocks |
tune2fs_reserved_blocks |
enableDHCHAP |
dhchap_node_selector |
filesystem defaults to xfs. For enableDHCHAP, the parameter value is the node label key of the pool's allowed
nodes, storage.simplyblock.io/storage-pool.<POOL_UUID>, and it is only written when allowedNodes is non-empty (see
Host Authentication and Encryption).
Note
compression, replicate, and priority_class are written into the class for completeness. The CSI driver
does not forward them when it creates a volume.
StorageClass Parameters
The erasure coding schema (the number of data and parity chunks per stripe) is set once at cluster creation and applies to all volumes in the cluster. It cannot be configured per volume or through a StorageClass. See the Erasure Coding Configuration for more details.
See Defining Quality of Service for QoS limits and their per-claim overrides.
Namespace Volumes
For a definition of namespace volumes, as well as the advantages and disadvantages of NVMe namespaces versus NVMe subsystems, see Logical Volumes.
Namespace volumes are enabled through max_namespace_per_subsys alone. A value above one makes every volume of the
storage class a namespace volume, sharing an NVMe subsystem with up to <n> siblings. At the default of one, each
volume receives its own subsystem.
Warning
A namespace volume cannot be migrated or rebalanced on its own, because moving it would disturb every other volume sharing its subsystem.
Available Parameters
The parameters below are read by the CSI driver. A parameter that is omitted leaves the choice to the control plane, for example, the cluster's fabric or no QoS ceiling.
Warning
tune2fs_reserved_blocks is skipped only when it is absent. A value of 0 is not a no-op, it runs
tune2fs -m 0 on every volume and removes the reserve that mkfs would have kept.
| Parameter Name | Value Type | Description |
|---|---|---|
| cluster_id | string | UUID of the backing storage cluster. Required unless zone_cluster_map or region_cluster_map is used. |
| zone_cluster_map | string | JSON map of Kubernetes zone to simplyblock cluster UUID (for topology-aware multi-cluster provisioning). |
| region_cluster_map | string | JSON map of Kubernetes region to simplyblock cluster UUID (for topology-aware multi-cluster provisioning). |
| pool_name | string | Name of the simplyblock storage pool. Required. |
| fabric | string | Fabric type to connect to the storage cluster, tcp or rdma. Defaults to the cluster's fabric. |
| csi.storage.k8s.io/fstype | string | Filesystem to format the logical volume with. If not specified, a raw block device is given to the container. |
| max_size | string | Caps the size a logical volume of this storage class can grow to. Accepts size suffixes, for example, 10G. |
| max_iops | int | Maximum read and write IOPS of a volume. 0 means no maximum. |
| max_mbytes_per_sec | int | Maximum read and write throughput of a volume, in MB/s. 0 means no maximum. |
| max_read_mbytes_per_sec | int | Maximum read throughput of a volume, in MB/s. 0 means no maximum. |
| max_write_mbytes_per_sec | int | Maximum write throughput of a volume, in MB/s. 0 means no maximum. |
| encryption | bool | Whether the volumes of this storage class are encrypted. Defaults to false. |
| client_compression | bool | Compresses each volume on the consuming node (VDO). The volume is pinned to a node that can run dm-vdo. |
| client_deduplication | bool | Deduplicates each volume on the consuming node (VDO). The volume is pinned to a node that can run dm-vdo. |
| max_namespace_per_subsys | int | Number of namespaces per NVMe subsystem. Defaults to 1. |
| tune2fs_reserved_blocks | int | Reserved-blocks percentage applied through tune2fs -m when an ext4 volume is staged. Left unset, tune2fs is skipped entirely. |
| dhchap_node_selector | string | Node label key of the allowed nodes of a DHCHAP pool. The PersistentVolume's node affinity is restricted to nodes with that label set to allowed. |
Legacy Parameter Keys
StorageClasses written for earlier operator versions keep working, because their parameters cannot be changed. The following older keys are still read. When a class carries both spellings of one ceiling, the new key wins.
| Legacy parameter | Current parameter |
|---|---|
qos_rw_iops |
max_iops |
qos_rw_mbytes |
max_mbytes_per_sec |
qos_r_mbytes |
max_read_mbytes_per_sec |
qos_w_mbytes |
max_write_mbytes_per_sec |
The parameter name dhchap_node_label, which appeared in earlier documentation, is not read. A DHCHAP class names
the node label through dhchap_node_selector.
The per-pool StorageClass simplyblock-<namespace>-<cluster>-<pool> that earlier operator versions generated for
every pool is no longer created. Such classes keep provisioning, and new pools get authored classes.