All Products
Search
Document Center

Container Service for Kubernetes:Use a statically provisioned disk volume

Last Updated:Mar 25, 2026

Mount an existing cloud disk to a pod as a statically provisioned persistent volume (PV). Unlike dynamic provisioning, static provisioning requires you to manually create a PV and a persistent volume claim (PVC) before starting the application — this guarantees the storage is ready before the container starts.

Use cases

Static disk provisioning fits workloads that need high IOPS, low latency, and no data sharing:

  • Databases and caching services such as MySQL and Redis

  • Applications that write logs at high speed

  • Any workload that requires data to outlive its pod

Prerequisites

Before you begin, make sure that:

  • The Container Storage Interface (CSI) plug-in is installed in your cluster. To check, go to Operations > Add-ons > Storage and verify that csi-plugin and csi-provisioner are listed. If your cluster uses FlexVolume, migrate to CSI before proceeding — FlexVolume is no longer supported.

  • The cloud disk meets the following conditions:

    • Billing method is pay-as-you-go and the disk is in the Pending state.

    • The disk is in the same zone as the Elastic Compute Service (ECS) node you plan to mount it to, and the disk type is compatible with the ECS instance type.

Important

Disks cannot be mounted across zones. If the zone or disk type is incompatible with the node, the mount fails. For compatibility rules, see Overview of instance families.

Usage notes

  • One pod per disk: Unless multi-attach is enabled, each disk can be mounted to only one pod at a time. For multi-attach details, see Use the multi-attach and NVMe reservation features of NVMe disks.

  • Zone constraint: A disk can only be mounted to a pod in the same zone as the disk.

  • Pod rescheduling: When a pod is rebuilt, the original disk is re-attached. If the pod cannot be scheduled to the original zone, it stays in the Pending state.

  • Use StatefulSets, not Deployments: Deployments can have multiple replicas, but a disk (without multi-attach) can only be bound to one pod. Restart policies in Deployments can also cause mount failures during rolling updates. Use StatefulSets for disk-backed workloads.

  • `fsGroup` impact on mount speed: If securityContext.fsGroup is set, kubelet runs chmod and chown on the volume after mounting, which can slow startup significantly when the volume contains many files. For clusters running Kubernetes 1.20 or later, set fsGroupChangePolicy: OnRootMismatch in the pod spec — this limits ownership changes to the first time the pod starts, skipping them on subsequent restarts. If that doesn't meet your needs, use an init container to handle permissions instead.

Mount a statically provisioned disk volume using kubectl

Step 1: create a PV

  1. Connect to your cluster. For instructions, see Obtain the kubeconfig file of a cluster and use kubectl to connect to the cluster or Manage Kubernetes clusters via kubectl in Cloud Shell.

  2. Create a file named disk-pv.yaml with the following content, replacing the placeholders with your disk's actual values:

    PlaceholderDescriptionExample
    <YOUR-DISK-ID>ID of the existing diskd-uf628m33r5rsbi******
    <YOUR-DISK-SIZE>Size of the existing disk20Gi
    <YOUR-DISK-ZONE-ID>Zone where the disk is locatedcn-shanghai-f
    <YOUR-DISK-CATEGORY>Disk typecloud_essd

    Supported values for <YOUR-DISK-CATEGORY>:

    ValueDisk type
    cloud_essd_entryEnterprise SSD (ESSD) Entry
    cloud_autoESSD AutoPL
    cloud_essdESSD
    cloud_ssdStandard SSD
    cloud_efficiencyUltra disk
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: "<YOUR-DISK-ID>"   # e.g. d-uf628m33r5rsbi******
      annotations:
        csi.alibabacloud.com/volume-topology: '{"nodeSelectorTerms":[{"matchExpressions":[{"key":"node.csi.alibabacloud.com/disktype.<YOUR-DISK-CATEGORY>","operator":"In","values":["available"]}]}]}'
    spec:
      capacity:
        storage: "<YOUR-DISK-SIZE>"   # e.g. 20Gi
      claimRef:
        apiVersion: v1
        kind: PersistentVolumeClaim
        namespace: default
        name: disk-pvc
      accessModes:
        - ReadWriteOnce
      persistentVolumeReclaimPolicy: Retain
      csi:
        driver: diskplugin.csi.alibabacloud.com
        volumeHandle: "<YOUR-DISK-ID>"   # e.g. d-uf628m33r5rsbi******
      nodeAffinity:
        required:
          nodeSelectorTerms:
          - matchExpressions:
            - key: topology.diskplugin.csi.alibabacloud.com/zone
              operator: In
              values:
              - "<YOUR-DISK-ZONE-ID>"   # e.g. cn-shanghai-f
      storageClassName: alicloud-disk-topology-alltype
      volumeMode: Filesystem

    Parameters:

    ParameterRequiredDescription
    csi.alibabacloud.com/volume-topologyRecommendedRestricts scheduling to nodes that support the disk type. Specify the disk category to avoid mounting failures caused by incompatible node types.
    claimRefOptionalBinds this PV to a specific PVC. Set this to prevent another PVC from claiming the PV before yours does. Delete this field to allow any PVC to bind.
    accessModesRequiredMust be ReadWriteOnce — the disk can only be mounted to one pod in read-write mode.
    persistentVolumeReclaimPolicyRequiredRetain keeps the disk when the PVC is deleted. Delete removes the PV and disk automatically.
    driverRequireddiskplugin.csi.alibabacloud.com — identifies the Alibaba Cloud CSI plug-in.
    nodeAffinityRequiredPins pod scheduling to the zone where the disk is located. Disks cannot be mounted across zones.
    storageClassNameRequiredMust match the storageClassName in the PVC exactly (including case). For static provisioning, no StorageClass resource needs to exist — this field is used only as a matching label.
  3. Create the PV:

    kubectl create -f disk-pv.yaml
  4. Verify the PV is available:

    kubectl get pv

    Expected output:

    NAME                     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM              STORAGECLASS   VOLUMEATTRIBUTESCLASS   REASON   AGE
    d-uf628m33r5rsbi******   20Gi       RWO            Retain           Available   default/disk-pvc   disk           <unset>                          1m36s

Step 2: create a PVC

The PVC references the PV by name, and the PV references the PVC via claimRef. Both sides of this binding must match to prevent other workloads from claiming the volume.

  1. Create a file named disk-pvc.yaml:

    ParameterRequiredDescription
    accessModesRequiredMust be ReadWriteOnce.
    storageRequiredThe capacity to allocate. Cannot exceed the actual disk size.
    storageClassNameRequiredMust match the storageClassName in the PV exactly.
    volumeNameOptionalBinds the PVC to a specific PV by name. Delete this field to allow the system to bind any available PV.
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: disk-pvc
    spec:
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: "<YOUR-DISK-SIZE>"   # e.g. 20Gi — must not exceed disk capacity
      storageClassName: alicloud-disk-topology-alltype
      volumeName: "<YOUR-DISK-ID>"      # e.g. d-uf628m33r5rsbi****** — must match the PV name
  2. Create the PVC:

    kubectl create -f disk-pvc.yaml
  3. Verify the PVC is bound:

    kubectl get pvc

    Expected output — the PVC status should be Bound:

    NAME       STATUS   VOLUME                   CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
    disk-pvc   Bound    d-uf628m33r5rsbi******   20Gi       RWO            disk           <unset>                 64s

Step 3: deploy an application with the disk mounted

  1. Create a file named disk-test.yaml. The following configuration defines a StatefulSet with one pod that mounts the disk-pvc PVC at /data:

    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: disk-test
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: nginx
      template:
        metadata:
          labels:
            app: nginx
        spec:
          containers:
          - name: nginx
            image: anolis-registry.cn-zhangjiakou.cr.aliyuncs.com/openanolis/nginx:1.14.1-8.6
            ports:
            - containerPort: 80
            volumeMounts:
            - name: pvc-disk
              mountPath: /data
          volumes:
            - name: pvc-disk
              persistentVolumeClaim:
                claimName: disk-pvc
  2. Deploy the StatefulSet:

    kubectl create -f disk-test.yaml
  3. Verify the pod is running:

    kubectl get pod -l app=nginx

    Expected output:

    NAME          READY   STATUS    RESTARTS   AGE
    disk-test-0   1/1     Running   0          14s
  4. Verify the disk is mounted at /data:

    kubectl exec disk-test-0 -- df -h /data

    Expected output:

    Filesystem      Size  Used Avail Use% Mounted on
    /dev/vdb         20G   24K   20G   1% /data

Mount a statically provisioned disk volume in the ACK console

Step 1: create a PV

  1. Log on to the ACK console. In the left-side navigation pane, click Clusters.

  2. Click the name of your cluster. In the left-side pane, choose Volumes > Persistent Volumes.

  3. On the Persistent Volumes page, click Create.

  4. Configure the parameters and click Create:

    ParameterDescriptionExample
    PV typeSelect Cloud Disk.Cloud Disk
    Access modeOnly ReadWriteOnce is supported.ReadWriteOnce
    Disk IDClick Select Disk and choose a disk in the same zone as your node.d-uf628m33r5rsbi******
    File system typeSelect the file system for the disk. Options: ext4, ext3, xfs, vfat. Default: ext4.ext4

After the PV is created, it appears on the Persistent Volumes page.

Step 2: create a PVC

  1. In the left-side pane, choose Volumes > Persistent Volume Claims.

  2. Click Create in the upper-right corner.

  3. Configure the parameters and click Create:

    ParameterDescriptionExample
    PVC typeSelect Cloud Disk.Cloud Disk
    NameEnter a name for the PVC.disk-pvc
    Allocation modeSelect Existing Volumes.Existing Volumes
    Existing storage classSelect the PV created in Step 1.d-uf628m33r5rsbi****, 20 GiB
    CapacityStorage capacity to allocate. Cannot exceed the disk size.20 GiB

After the PVC is created, it appears on the Persistent Volume Claims page with a Bound status, linked to the PV from Step 1.

Step 3: deploy an application with the disk mounted

  1. In the left-side pane, choose Workloads > StatefulSets.

  2. Click Create from Image in the upper-right corner.

  3. Configure the StatefulSet parameters and click Create. The following table covers the volume-related settings — configure other parameters based on your requirements. For details, see Use a StatefulSet to create a stateful application.

    PageParameterDescriptionExample
    Basic informationNameName of the StatefulSet.disk-test
    Basic informationReplicasNumber of pod replicas.1
    ContainerImage nameContainer image address.anolis-registry.cn-zhangjiakou.cr.aliyuncs.com/openanolis/nginx:1.14.1-8.6
    ContainerRequired resourcesCPU, memory, and ephemeral storage.CPU: 0.25 vCore; Memory: 512 MiB
    VolumeAdd PVCClick Add PVC, then set Mount source to the PVC from Step 2 and Container path to the mount path.Mount source: disk-pvc; Container path: /data
  4. After the StatefulSet is created, click its name on the StatefulSets page and check the Pods tab to confirm the pod is in the Running state.

Verify data persistence

The StatefulSet keeps the disk attached across pod restarts. To confirm that data survives a pod deletion:

  1. Verify the disk is mounted by checking files in the mount path:

    kubectl exec disk-test-0 -- ls /data

    Expected output:

    lost+found
  2. Create a file on the disk:

    kubectl exec disk-test-0 -- touch /data/test
  3. Delete the pod:

    kubectl delete pod disk-test-0

    The StatefulSet automatically recreates the pod with the same name.

  4. Wait for the pod to return to Running:

    kubectl get pod -l app=nginx

    Expected output:

    NAME          READY   STATUS    RESTARTS   AGE
    disk-test-0   1/1     Running   0          27s
  5. Verify the original disk is mounted to the new pod and the file is retained:

    kubectl exec disk-test-0 -- ls /data

    Expected output — the test file is retained on the disk:

    lost+found
    test

What's next