NVMe-oF Block Storage¶
This feature is experimental
NVMe over Fabrics (NVMe-oF) allows RBD volumes to be exposed and accessed via the NVMe/TCP protocol. This enables both Kubernetes pods within the cluster and external clients outside the cluster to connect to Ceph block storage using standard NVMe-oF initiators, providing high-performance block storage access over the network.
Goals¶
The NVMe-oF integration in Rook serves two primary purposes:
-
In-Cluster Consumption: Pods inside the Kubernetes cluster can consume storage via the NVMe-oF protocol, providing an alternative to traditional RBD mounts with potential performance benefits for certain workloads.
-
External Client Access: Rook serves as a backend for external clients outside the cluster, enabling non-Kubernetes workloads to access Ceph block storage through standard NVMe-oF initiators. This allows organizations to leverage their Ceph storage infrastructure for both containerized and traditional workloads.
Both use cases share a common foundation (gateway, CSI driver, and data pool) and then diverge. Follow the Common Setup first, then continue with either In-Cluster Deployment or External Client Deployment (or both).
For more background and design details, see the NVMe-oF gateway design doc. For the Ceph-CSI NVMe-oF design proposal, see the ceph-csi NVMe-oF proposal.
Prerequisites¶
This guide assumes a Rook cluster as explained in the Quickstart Guide.
Requirements¶
- Ceph Version: Ceph v20 (Tentacle) or later
Common Setup¶
These steps are required for both in-cluster and external deployments.
Step 1: Create the NVMe-oF Gateway¶
The CephNVMeOFGateway CRD manages the NVMe-oF gateway infrastructure. The operator will automatically create the following resources:
- Service: One per gateway instance for service discovery
- Deployment: One per gateway instance running the NVMe-oF gateway daemon
NVMe-oF uses two separate pools:
- Metadata pool (
.nvmeof): An internal pool that stores gateway state (subsystem and namespace definitions). Created withspec.name: .nvmeof. - Data pool: A standard CephBlockPool that holds the actual RBD image data. See Step 3.
Create the gateway and the .nvmeof metadata pool:
Apply the gateway configuration:
Verify the gateway is running:
Example Output
Step 2: Deploy the NVMe-oF CSI Driver via CSI Operator¶
The NVMe-oF CSI driver is deployed via the ceph-csi operator.
Apply the Driver CR for NVMe-oF that will trigger the creation of the Ceph-CSI/NVMe-oF deployment and daemonset:
Verify the CSI operator created the controller and node plugins:
Example Output
Step 3: Create the Data Pool¶
Create a CephBlockPool that will be used by the StorageClass to store data:
In-Cluster Deployment (for Pods)¶
This section covers consuming NVMe-oF volumes from pods inside the Kubernetes cluster via the NVMe-oF CSI driver.
Step 4: Create the StorageClass¶
Create a StorageClass that uses the NVMe-oF CSI driver. The subsystemNQN parameter places all in-cluster PVCs into a shared subsystem, and the listeners list tells the CSI node plugin which gateways to connect to for I/O.
Note
The provisioner name rook-ceph.nvmeof.csi.ceph.com is prefixed with the operator namespace.
Step 5: Create a PersistentVolumeClaim¶
Create a PVC using the NVMe-oF storage class:
Verify the PVC is bound:
Example Output
Step 6: Create a Pod¶
Create a pod that consumes the NVMe-oF volume:
Verify the pod is running:
Example Output
External Client Deployment (via LoadBalancer)¶
This section covers exposing NVMe-oF volumes to clients outside the Kubernetes cluster. External clients connect using standard NVMe-oF initiators (nvme-cli) through LoadBalancer services.
Prerequisites for External Clients¶
- nvme-cli: The
nvmecommand-line tool must be installed on the client - Network Access: The client must be able to reach the gateway LoadBalancer IP
- Host NQN: The client's host NQN must be listed in the
VolumeAttributesClass
Install nvme-cli and load the required kernel module on the external client:
Step 4: Create a VolumeAttributesClass¶
A VolumeAttributesClass defines mutable volume parameters such as host access control. It specifies which host NQNs are allowed to access the volume.
Create the VolumeAttributesClass with the allowed host NQNs:
Step 5: Create the External StorageClass and PVC¶
The external StorageClass does not include subsystemNQN. Each PVC is automatically placed in its own subsystem, and external clients discover the subsystem NQN dynamically via nvme discover.
Create the external StorageClass and PVC:
Verify the PVC is bound:
Example Output
Step 6: Create LoadBalancer Services¶
External clients need LoadBalancer services to reach the NVMe-oF gateways from outside the cluster. Create one LoadBalancer per gateway instance so each gateway has a dedicated external endpoint.
Create the services:
Get the external IPs:
Example Output
Step 7: Connect from the External Client¶
The connection process has two stages: first discover the available subsystems via the discovery port (8009), then connect to the discovered subsystem via the I/O port (4420).
Discover subsystems¶
Run a discovery against either gateway LoadBalancer address using the discovery port (8009):
Replace <GW-A-EXTERNAL-IP> with one of the LoadBalancer external IPs from the service output above.
The discovery log returns the target subsystem NQN and available transport addresses:
Connect to the LoadBalancer¶
Use the subsystem NQN from the discovery output to connect through all gateway LoadBalancers in the gateway group. The host must connect to every gateway to see all namespaces:
Verify the connection:
Format and Mount¶
Teardown¶
Warning
Deleting a PVC will also delete the underlying RBD image and NVMe namespace. Ensure you have backups if needed.
In-Cluster Teardown¶
External Teardown¶
Disconnect the NVMe-oF volume from the external client:
Then delete the Kubernetes resources:
Common Teardown¶
After removing the mode-specific resources above, clean up the shared infrastructure: