Kubernetes Operator for declarative management of simplyblock storage clusters
π Read the full documentation at docs.simplyblock.io
simplyblock-operator is the official Kubernetes operator for simplyblock storage. It turns the
simplyblock control plane into a set of native Kubernetes Custom Resources, so you can provision and
operate high-performance NVMe/TCP block storage the same way you manage any other Kubernetes object.
The operator watches simplyblock CRs in its namespace and reconciles the desired state into actual storage-system state by calling the simplyblock Web API. Observed results are written back into each CR's status, giving you a declarative, GitOps-friendly, status-driven view of your storage estate.
Where the simplyblock CSI driver provisions and attaches volumes to workloads, the operator manages the lifecycle of the storage platform itself β clusters, nodes, pools, backups, restores, and replication.
π For full documentation, see the Simplyblock Kubernetes Deployment Guide.
| Feature | Benefit |
|---|---|
| Declarative Storage Clusters | Create, activate, expand, and lifecycle-manage clusters via Kubernetes CRs |
| Storage Node Management | Reconciles the storage-node DaemonSet, node labels, and per-namespace RBAC |
| Pool Provisioning | Manages storage pools, QoS, dhchap security, and host allow-lists |
| Backup, Restore & Import | First-class backups with cross-cluster import and policy-driven retention |
| Snapshot Replication | Replicates snapshots between clusters/pools, including failback support |
| Drain-Aware Node Lifecycle | Coordinates storage-node shutdown/restart with Kubernetes drains via PDBs |
| Standard Kubernetes RBAC | Authorization delegated entirely to native K8s RBAC (no custom identity model) |
| mTLS to the Control Plane | Optional cert-manager-issued mTLS between the operator and the Web API |
The operator manages the following storage.simplyblock.io/v1alpha1 resources:
| Kind | Purpose |
|---|---|
ControlPlane |
Singleton gating the system on control-plane readiness |
StorageCluster |
Create/activate/expand clusters and provision per-pool StorageClasses |
StorageNodeSet |
Manage storage nodes and the storage-node DaemonSet |
Pool |
Create storage pools with QoS, dhchap security, and host affinity |
Task |
Observe long-running cluster tasks |
StorageBackup |
Snapshot a PVC's backing volume and create a cluster backup |
BackupRestore |
Restore a backup into a cluster/pool/node |
BackupImport |
Import a backup from a source cluster's backend into a target cluster |
BackupPolicy |
Define retention (maxVersions, maxAge) attached to PVCs |
SnapshotReplication |
Replicate snapshots between clusters/pools |
VolumeMigration |
Migrate volumes between clusters |
See ARCHITECTURE.md for a detailed component and reconciliation overview.
The operator is installed as part of a simplyblock deployment via the official Helm charts. Follow
the documentation for the supported, end-to-end installation flow β it wires up the control plane, the
ControlPlane CR, cert-manager, and the CSI driver alongside the operator:
π Simplyblock Kubernetes Deployment Guide
The manual and from-source paths below exist for development and troubleshooting only. For any real deployment, use the documentation and Helm charts above.
The operator delegates user authorisation entirely to standard Kubernetes RBAC.
It does not ship per-CR admin/editor/viewer ClusterRoles or any
identity-bearing fields on its CRs; cluster admins write Roles,
RoleBindings and ClusterRoleBindings using the normal K8s primitives.
Each StorageCluster is namespace-scoped, and a Pool must live in the same
namespace as the StorageCluster it references via spec.clusterName. The
Pool controller enforces this: if a Pool references a StorageCluster that
does not exist in the Pool's namespace, the controller refuses to call the
backend, sets status.status = "InvalidClusterReference", and emits a
InvalidClusterReference Event on the Pool.
This converts "admin of cluster foo" into "admin of the namespace where
StorageCluster foo lives" β a problem standard K8s RBAC already solves
cleanly. The recommended layout is one namespace per logical storage cluster
(e.g. cluster-prod, cluster-staging).
The operator installs two ClusterRoles labelled to aggregate into the
standard Kubernetes ClusterRoles:
| Operator ClusterRole | Aggregates into | Grants on simplyblock CRs |
|---|---|---|
simplyblock-aggregate-to-view |
view |
get, list, watch |
simplyblock-aggregate-to-edit |
edit, admin |
get, list, watch, create, update, patch, delete |
Effect: anyone already bound to the built-in view, edit, or admin
ClusterRole in a namespace automatically gets the corresponding access to the
StorageClusters and Pools in that namespace. No further configuration is
needed for the common case.
For example, to make alice an admin of cluster prod (assuming
StorageCluster/prod lives in namespace cluster-prod):
kubectl create rolebinding alice-admin \
--clusterrole=admin \
--user=alice \
--namespace=cluster-prodFor finer-grained delegation β e.g. admin only of StorageCluster/prod, not
any other StorageCluster in the same namespace β write a Role with
resourceNames:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: prod-cluster-admin
namespace: cluster-prod
rules:
- apiGroups: ["storage.simplyblock.io"]
resources: ["storageclusters"]
resourceNames: ["prod"]
verbs: ["get", "update", "patch", "delete"]
- apiGroups: ["storage.simplyblock.io"]
resources: ["storageclusters/status"]
resourceNames: ["prod"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: alice-prod-cluster-admin
namespace: cluster-prod
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: prod-cluster-admin
subjects:
- kind: User
name: aliceK8s RBAC limitation:
resourceNamesonly filters verbs that target a named object (get,update,patch,delete). It is silently ignored forlist,watch, andcreate. A user with only the Role above cankubectl get storagecluster prod(a named GET) but notkubectl get storagecluster(a LIST) β they will need a separate, broader binding (e.g. theviewClusterRole) if you want them to enumerate. This is a property of K8s RBAC, not the operator.
There is no shipped "platform admin" ClusterRole β choose your own gate. Two common patterns:
- Gate by namespace ownership. Whoever has the built-in
adminClusterRole in a namespace can create and fully manageStorageClusters there (the aggregation role makes that work). To stop arbitrary users from creating namespaces, restrictcreate namespacesat the cluster scope. - Gate by SA. Reserve
create storageclustersfor a small set of service accounts (e.g. your platform automation) and have them stand up tenant namespaces on demand.
To let a "cluster owner" delegate admin to teammates without giving them
escalate on RBAC, grant them the bind verb on the specific Role they're
allowed to hand out:
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["roles"]
resourceNames: ["prod-cluster-admin"]
verbs: ["bind"]See the upstream docs on privilege escalation prevention for the full mechanism.
The operator's pod is the sole caller of the simplyblock webapi; user identities are not propagated to the backend. K8s RBAC governs what users can do to the CRs, the operator then talks to webapi using its own service account token.
β οΈ These paths are for local development and troubleshooting. For real deployments, use the documentation and Helm charts.
Prerequisites
- Go v1.24.6+, Docker v17.03+, kubectl v1.11.3+
- Access to a Kubernetes v1.11.3+ cluster
- A reachable simplyblock control plane (Web API)
Apply the pre-built installer bundle (CRDs, RBAC, and the manager deployment):
kubectl apply -f https://raw.githubusercontent.com/simplyblock/simplyblock-operator/main/dist/install.yaml
kubectl -n simplyblock-operator get pods # manager pod should be Running
kubectl apply -k config/samples/ # try the bundled samplesBuild and deploy from source:
# Build and push the operator image
make docker-build docker-push IMG=<some-registry>/simplyblock-operator:tag
# Install CRDs and deploy the manager with your image
make install
make deploy IMG=<some-registry>/simplyblock-operator:tag
# Regenerate the install bundle (dist/install.yaml)
make build-installer IMG=<some-registry>/simplyblock-operator:tag
# Tear everything down
make undeploy # remove the controller
make uninstall # remove the CRDsNOTE: The pushed image must be reachable from the cluster β ensure the registry is accessible and that you have pull permissions. If you hit RBAC errors during
make deploy, you may need cluster-admin privileges. Runmake helpfor the full list of targets.
This project is licensed under the Apache 2.0 License β see the LICENSE file for details.
We welcome contributions!
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Commit your changes with a clear message
- Push to your fork and open a Pull Request
Run make help for the full list of available make targets.
- π Documentation
- π GitHub Issues
- π Simplyblock Website
Maintained by the simplyblock team.
Manage NVMe-grade simplyblock storage the Kubernetes-native way.