Dynamic Provisioning (Enterprise)
Dynamic provisioning allows Kubernetes to automatically create a new flexFS volume when a PersistentVolumeClaim is submitted. The CSI controller calls the admin server REST API to create the volume; later, at mount time, the node driver retrieves a volume token and mounts the volume for pods.
Prerequisites
Section titled “Prerequisites”- The flexFS CSI driver is installed
- An Enterprise admin server is running and reachable from the cluster
- You have the account token
Step 1: Create a Secret
Section titled “Step 1: Create a Secret”The CSI driver needs the admin server address and an account token to create volumes. Store these in a Kubernetes Secret:
apiVersion: v1kind: Secretmetadata: name: flexfs-secret namespace: defaultstringData: adminAddr: <admin-addr> token: <account-token>Replace <admin-addr> with the address of your admin server (for example, admin.example.com:443) and <account-token> with a valid account token.
kubectl apply -f secret.yamlStep 2: Create a StorageClass
Section titled “Step 2: Create a StorageClass”The StorageClass tells Kubernetes to use the flexFS CSI provisioner and specifies volume creation parameters:
apiVersion: storage.k8s.io/v1kind: StorageClassmetadata: name: flexfs-dynamicprovisioner: csi.flexfs.ioallowVolumeExpansion: trueparameters: csi.storage.k8s.io/provisioner-secret-namespace: default csi.storage.k8s.io/provisioner-secret-name: flexfs-secret csi.storage.k8s.io/node-publish-secret-namespace: default csi.storage.k8s.io/node-publish-secret-name: flexfs-secret csi.storage.k8s.io/controller-expand-secret-namespace: default csi.storage.k8s.io/controller-expand-secret-name: flexfs-secret provider: aws region: <region>provider and region are not optional decoration: the admin server needs them (or an explicit metaStore + blockStore) to place the volume, and rejects a request carrying neither with a 400. For provider: aws, blockAPI defaults to s3; for gcp, azure, and oci you must also set blockAPI (gcs, azure, or oci respectively) or the request is rejected with a 400. See StorageClass parameters for the full list.
You can add optional parameters to control volume settings at creation time. See the StorageClass parameters reference for all options.
Example with custom parameters
Section titled “Example with custom parameters”apiVersion: storage.k8s.io/v1kind: StorageClassmetadata: name: flexfs-encryptedprovisioner: csi.flexfs.ioallowVolumeExpansion: trueparameters: csi.storage.k8s.io/provisioner-secret-namespace: default csi.storage.k8s.io/provisioner-secret-name: flexfs-secret csi.storage.k8s.io/node-publish-secret-namespace: default csi.storage.k8s.io/node-publish-secret-name: flexfs-secret csi.storage.k8s.io/controller-expand-secret-namespace: default csi.storage.k8s.io/controller-expand-secret-name: flexfs-secret provider: aws region: <region> blockSize: 4Mi compression: "true" compressionAlgo: lz4 encryption: "true" maxInodes: "1000000"Mounting a volume created with encryption: "true" requires its encryption secret, at least 8 characters long. Add it as the secret field of the Secret named by node-publish-secret-name:
stringData: adminAddr: <admin-addr> token: <account-token> secret: <encryption-secret>Step 3: Create a PersistentVolumeClaim
Section titled “Step 3: Create a PersistentVolumeClaim”apiVersion: v1kind: PersistentVolumeClaimmetadata: name: flexfs-dynamic namespace: defaultspec: storageClassName: flexfs-dynamic accessModes: - ReadWriteMany resources: requests: storage: 100Gikubectl apply -f dynamic-volume.yamlOnce the PVC is created, the CSI provisioner will:
- Call
CreateVolumeon the flexFS CSI controller - The controller sends a
POST /v1/volumesrequest to the admin server to create a new volume - A PersistentVolume is automatically created and bound to the PVC
Step 4: Use the volume in a pod
Section titled “Step 4: Use the volume in a pod”apiVersion: v1kind: Podmetadata: name: my-appspec: containers: - name: app image: ubuntu:latest command: ["sleep", "infinity"] volumeMounts: - name: data mountPath: /data volumes: - name: data persistentVolumeClaim: claimName: flexfs-dynamicVolume lifecycle
Section titled “Volume lifecycle”One volume per claim. The provisioner names each volume pvc-<PVC UID>, so every claim gets its own flexFS volume. Volumes are never reused across claims: delete a PVC and create another with the same name and you get a new UID, and therefore a new, empty volume. Binding to an existing volume is what static provisioning is for.
Retries adopt rather than duplicate. If provisioning is retried after a lost response, the admin server returns the volume it already created for that name instead of making a second one. If a volume of that name exists but has a smaller quota than the claim asks for, the driver reports ALREADY_EXISTS rather than binding the claim to something too small.
Deletion retires the volume. When the claim is deleted and the reclaim policy is Delete, the driver calls DELETE /v1/volumes/for-name/<volume-name>, which retires the volume: it is marked retired, its volume tokens are removed, and the metadata server then deletes its files and blocks. The volume’s retention does not apply once it is retired, so a deleted claim’s data cannot be recovered or mounted at an earlier point in time. Deleting a claim whose volume is already gone succeeds rather than failing. See Retiring a Volume.
With reclaimPolicy: Retain, the driver is never asked to delete anything: the volume, its tokens, and its data stay until someone retires them with configure.flexfs delete volume.
Storage quotas
Section titled “Storage quotas”The claim’s resources.requests.storage is enforced on the created volume, not merely recorded (if the claim also sets resources.limits.storage, the limit is what the quota is set to, since that is the ceiling the claim says must not be exceeded):
- It is rounded up to a whole block, so a 1 GiB claim on a 4 MiB block size becomes 256 blocks.
dfinside the pod reports the quota, and writing past it fails with ENOSPC.- The limit counts blocks, not bytes. Each stored block uses one unit of the quota whether it is full or partly filled, and compression does not change the count, so many small files can reach the quota well before their combined size does.
dfcomputes its used figure from file sizes, so it can show less usage than the quota enforces. maxInodeson the StorageClass caps inodes; it is unlimited unless set, since a claim expresses only bytes.
Growing a volume. Set allowVolumeExpansion: true on the StorageClass and edit the claim’s request. The csi-resizer sidecar calls the driver, which raises the volume’s quota. The new limit is recorded at once, but a mount that is already running picks it up on its next volume-settings refresh — about a minute in practice — so for up to that long an already-full volume keeps returning ENOSPC and df keeps showing the old size. No remount or pod restart is needed; it simply is not instant.
Expansion only ever raises a quota. Kubernetes refuses to shrink a claim below its recorded capacity, so a dynamically provisioned volume’s limit is whatever its claim last asked for.
One request is refused: expanding a volume that has no quota. A volume with no limit already permits more than any request, so there is nothing to raise, and reporting an unlimited capacity — exabytes — for the claim would be worse than an error. Any volume without a storage quota is in this state, including one bound through a static PV. Set a limit deliberately with configure.flexfs update volume --maxBlocks/--maxInodes if you want one.
A pre-existing volume bound through a static PV is not affected by any of this: its PV capacity is informational, and quotas are set with configure.flexfs update volume --maxBlocks/--maxInodes.
Access modes
Section titled “Access modes”FlexFS supports the following CSI access modes:
| Access mode | Supported | Description |
|---|---|---|
ReadWriteMany | Yes | Multiple pods on any nodes can read and write simultaneously |
ReadWriteOnce | Yes | Read-write from a single node. Any number of pods on that node can use the volume at the same time (see the note below). |
ReadWriteOncePod | Yes | Read-write for a single pod in the cluster (see the note below) |
ReadOnlyMany | Yes | Multiple pods on any nodes with read-only access |
Next steps
Section titled “Next steps”- StorageClass parameters reference
- Static provisioning for pre-existing volumes
- Troubleshooting