All Products
Search
Document Center

Container Service for Kubernetes:Migrate statically provisioned NAS volumes from Flexvolume to CSI

Last Updated:Mar 26, 2026

FlexVolume is deprecated, and new Container Service for Kubernetes (ACK) clusters no longer support it. This topic describes how to migrate statically provisioned Object Storage Service (OSS) volumes from FlexVolume to Container Storage Interface (CSI) on an existing ACK cluster. The CSI driver for OSS is fully supported in ACK.

This topic applies only to clusters where FlexVolume manages statically provisioned OSS volumes. If your cluster also has disk volumes managed by FlexVolume, see Use csi-compatible-controller to migrate from FlexVolume to CSI.

How CSI and FlexVolume differ

Plug-in Components kubelet parameter
CSI CSI-Provisioner (Deployment): handles automatic volume creation, snapshot creation, Container Network File System (CNFS) storage, and data restoration after accidental deletion. CSI-Plugin (DaemonSet): handles automatic volume mounting and unmounting for disk, File Storage NAS (NAS), and OSS volumes. Set enable-controller-attach-detach to true on each node.
FlexVolume Disk-Controller (Deployment): handles automatic volume creation. FlexVolume (DaemonSet): handles volume mounting and unmounting for disk, NAS, and OSS volumes. Set enable-controller-attach-detach to true on each node.

Usage notes

During migration, persistent volume claims (PVCs) are recreated, which causes pods to restart and interrupts your workloads. Perform the migration and any pod-restart operations during off-peak hours.

Prerequisites

Before you begin, make sure you have:

  • An ACK cluster with FlexVolume installed and at least one statically provisioned OSS volume managed by FlexVolume

  • kubectl access with permissions to create, get, list, and delete PVCs, persistent volumes (PVs), Deployments, and DaemonSets in the kube-system namespace

  • Access to the OpenAPI Explorer console to call the UnInstallClusterAddons and InstallClusterAddons operations

Migrate statically provisioned OSS volumes from FlexVolume to CSI

This example migrates a statically provisioned OSS volume from FlexVolume to CSI using a StatefulSet workload. The workflow is:

oss

Step 1: Install CSI manually

Before starting the migration, deploy the CSI components alongside the existing FlexVolume installation.

  1. Create two files: csi-plugin.yaml and csi-provisioner.yaml. csi-plugin.yaml

    ---
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: csi-admin
      namespace: kube-system
    ---
    kind: ClusterRole
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: alicloud-csi-plugin
    rules:
      - apiGroups: [""]
        resources: ["secrets"]
        verbs: ["get", "create", "list"]
      - apiGroups: [""]
        resources: ["persistentvolumes"]
        verbs: ["get", "list", "watch", "update", "create", "delete", "patch"]
      - apiGroups: [""]
        resources: ["persistentvolumeclaims"]
        verbs: ["get", "list", "watch", "update"]
      - apiGroups: [""]
        resources: ["persistentvolumeclaims/status"]
        verbs: ["get", "list", "watch", "update", "patch"]
      - apiGroups: ["storage.k8s.io"]
        resources: ["storageclasses"]
        verbs: ["get", "list", "watch"]
      - apiGroups: ["storage.k8s.io"]
        resources: ["csinodes"]
        verbs: ["get", "list", "watch"]
      - apiGroups: [""]
        resources: ["events"]
        verbs: ["get", "list", "watch", "create", "update", "patch"]
      - apiGroups: [""]
        resources: ["endpoints"]
        verbs: ["get", "watch", "list", "delete", "update", "create"]
      - apiGroups: [""]
        resources: ["configmaps"]
        verbs: ["get", "watch", "list", "delete", "update", "create"]
      - apiGroups: [""]
        resources: ["nodes"]
        verbs: ["get", "list", "watch", "update"]
      - apiGroups: ["csi.storage.k8s.io"]
        resources: ["csinodeinfos"]
        verbs: ["get", "list", "watch"]
      - apiGroups: ["storage.k8s.io"]
        resources: ["volumeattachments"]
        verbs: ["get", "list", "watch", "update", "patch"]
      - apiGroups: ["snapshot.storage.k8s.io"]
        resources: ["volumesnapshotclasses"]
        verbs: ["get", "list", "watch", "create"]
      - apiGroups: ["snapshot.storage.k8s.io"]
        resources: ["volumesnapshotcontents"]
        verbs: ["create", "get", "list", "watch", "update", "delete"]
      - apiGroups: ["snapshot.storage.k8s.io"]
        resources: ["volumesnapshots"]
        verbs: ["get", "list", "watch", "update", "create"]
      - apiGroups: ["apiextensions.k8s.io"]
        resources: ["customresourcedefinitions"]
        verbs: ["create", "list", "watch", "delete", "get", "update", "patch"]
      - apiGroups: ["coordination.k8s.io"]
        resources: ["leases"]
        verbs: ["get", "create", "list", "watch", "delete", "update"]
      - apiGroups: ["snapshot.storage.k8s.io"]
        resources: ["volumesnapshotcontents/status"]
        verbs: ["update"]
      - apiGroups: ["storage.k8s.io"]
        resources: ["volumeattachments/status"]
        verbs: ["patch"]
      - apiGroups: ["snapshot.storage.k8s.io"]
        resources: ["volumesnapshots/status"]
        verbs: ["update"]
      - apiGroups: ["storage.k8s.io"]
        resources: ["storageclasses"]
        verbs: ["get", "list", "watch"]
      - apiGroups: [""]
        resources: ["namespaces"]
        verbs: ["get", "list"]
      - apiGroups: [""]
        resources: ["pods","pods/exec"]
        verbs: ["create", "delete", "get", "post", "list", "watch", "patch", "update"]
      - apiGroups: ["storage.alibabacloud.com"]
        resources: ["rules"]
        verbs: ["get"]
      - apiGroups: ["storage.alibabacloud.com"]
        resources: ["containernetworkfilesystems"]
        verbs: ["get","list", "watch"]
    ---
    kind: ClusterRoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: alicloud-csi-plugin
    subjects:
      - kind: ServiceAccount
        name: csi-admin
        namespace: kube-system
    roleRef:
      kind: ClusterRole
      name: alicloud-csi-plugin
      apiGroup: rbac.authorization.k8s.io
    ---
    apiVersion: storage.k8s.io/v1
    kind: CSIDriver
    metadata:
      name: diskplugin.csi.alibabacloud.com
    spec:
      attachRequired: true
      podInfoOnMount: true
    ---
    apiVersion: storage.k8s.io/v1
    kind: CSIDriver
    metadata:
      name: nasplugin.csi.alibabacloud.com
    spec:
      attachRequired: false
      podInfoOnMount: true
    ---
    apiVersion: storage.k8s.io/v1
    kind: CSIDriver
    metadata:
      name: ossplugin.csi.alibabacloud.com
    spec:
      attachRequired: false
      podInfoOnMount: true
    ---
    kind: DaemonSet
    apiVersion: apps/v1
    metadata:
      name: csi-plugin
      namespace: kube-system
    spec:
      selector:
        matchLabels:
          app: csi-plugin
      template:
        metadata:
          labels:
            app: csi-plugin
        spec:
          tolerations:
            - operator: Exists
          affinity:
            nodeAffinity:
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                - matchExpressions:
                  - key: type
                    operator: NotIn
                    values:
                    - virtual-kubelet
          nodeSelector:
            kubernetes.io/os: linux
          serviceAccount: csi-admin
          priorityClassName: system-node-critical
          hostNetwork: true
          hostPID: true
          dnsPolicy: ClusterFirst
          containers:
            - name: disk-driver-registrar
              image: registry.cn-hangzhou.aliyuncs.com/acs/csi-node-driver-registrar:v2.3.1-038aeb6-aliyun
              resources:
                requests:
                  cpu: 10m
                  memory: 16Mi
                limits:
                  cpu: 500m
                  memory: 1024Mi
              args:
                - "--v=5"
                - "--csi-address=/var/lib/kubelet/csi-plugins/diskplugin.csi.alibabacloud.com/csi.sock"
                - "--kubelet-registration-path=/var/lib/kubelet/csi-plugins/diskplugin.csi.alibabacloud.com/csi.sock"
              volumeMounts:
                - name: kubelet-dir
                  mountPath: /var/lib/kubelet
                - name: registration-dir
                  mountPath: /registration
            - name: nas-driver-registrar
              image: registry.cn-hangzhou.aliyuncs.com/acs/csi-node-driver-registrar:v2.3.1-038aeb6-aliyun
              resources:
                requests:
                  cpu: 10m
                  memory: 16Mi
                limits:
                  cpu: 500m
                  memory: 1024Mi
              args:
                - "--v=5"
                - "--csi-address=/var/lib/kubelet/csi-plugins/nasplugin.csi.alibabacloud.com/csi.sock"
                - "--kubelet-registration-path=/var/lib/kubelet/csi-plugins/nasplugin.csi.alibabacloud.com/csi.sock"
              volumeMounts:
                - name: kubelet-dir
                  mountPath: /var/lib/kubelet/
                - name: registration-dir
                  mountPath: /registration
            - name: oss-driver-registrar
              image: registry.cn-hangzhou.aliyuncs.com/acs/csi-node-driver-registrar:v2.3.1-038aeb6-aliyun
              resources:
                requests:
                  cpu: 10m
                  memory: 16Mi
                limits:
                  cpu: 500m
                  memory: 1024Mi
              args:
                - "--v=5"
                - "--csi-address=/var/lib/kubelet/csi-plugins/ossplugin.csi.alibabacloud.com/csi.sock"
                - "--kubelet-registration-path=/var/lib/kubelet/csi-plugins/ossplugin.csi.alibabacloud.com/csi.sock"
              volumeMounts:
                - name: kubelet-dir
                  mountPath: /var/lib/kubelet/
                - name: registration-dir
                  mountPath: /registration
            - name: csi-plugin
              securityContext:
                privileged: true
                allowPrivilegeEscalation: true
              image: registry.cn-hangzhou.aliyuncs.com/acs/csi-plugin:v1.24.6-55c95dd-aliyun
              args:
                - "--endpoint=$(CSI_ENDPOINT)"
                - "--v=2"
                - "--driver=oss,nas,disk"
              env:
                - name: KUBE_NODE_NAME
                  valueFrom:
                    fieldRef:
                      apiVersion: v1
                      fieldPath: spec.nodeName
                - name: CSI_ENDPOINT
                  value: unix://var/lib/kubelet/csi-plugins/driverplugin.csi.alibabacloud.com-replace/csi.sock
                - name: MAX_VOLUMES_PERNODE
                  value: "15"
                - name: SERVICE_TYPE
                  value: "plugin"
              resources:
                requests:
                  cpu: 100m
                  memory: 128Mi
                limits:
                  cpu: 500m
                  memory: 1024Mi
              livenessProbe:
                httpGet:
                  path: /healthz
                  port: healthz
                  scheme: HTTP
                initialDelaySeconds: 10
                periodSeconds: 30
                timeoutSeconds: 5
                failureThreshold: 5
              readinessProbe:
                httpGet:
                  path: /healthz
                  port: healthz
                initialDelaySeconds: 10
                periodSeconds: 30
                timeoutSeconds: 5
                failureThreshold: 5
              ports:
                - name: healthz
                  containerPort: 11260
              volumeMounts:
                - name: kubelet-dir
                  mountPath: /var/lib/kubelet/
                  mountPropagation: "Bidirectional"
                - name: etc
                  mountPath: /host/etc
                - name: host-log
                  mountPath: /var/log/
                - name: ossconnectordir
                  mountPath: /host/usr/
                - name: container-dir
                  mountPath: /var/lib/container
                  mountPropagation: "Bidirectional"
                - name: host-dev
                  mountPath: /dev
                  mountPropagation: "HostToContainer"
                - mountPath: /var/addon
                  name: addon-token
                  readOnly: true
                - mountPath: /host/var/run/
                  name: fuse-metrics-dir
          volumes:
            - name: fuse-metrics-dir
              hostPath:
                path: /var/run/
                type: DirectoryOrCreate
            - name: registration-dir
              hostPath:
                path: /var/lib/kubelet/plugins_registry
                type: DirectoryOrCreate
            - name: container-dir
              hostPath:
                path: /var/lib/container
                type: DirectoryOrCreate
            - name: kubelet-dir
              hostPath:
                path: /var/lib/kubelet
                type: Directory
            - name: host-dev
              hostPath:
                path: /dev
            - name: host-log
              hostPath:
                path: /var/log/
            - name: etc
              hostPath:
                path: /etc
            - name: ossconnectordir
              hostPath:
                path: /usr/
            - name: addon-token
              secret:
                defaultMode: 420
                optional: true
                items:
                - key: addon.token.config
                  path: token-config
                secretName: addon.csi.token
      updateStrategy:
        rollingUpdate:
          maxUnavailable: 30%
        type: RollingUpdate

    csi-provisioner.yaml

    ---
    kind: Deployment
    apiVersion: apps/v1
    metadata:
      name: csi-provisioner
      namespace: kube-system
    spec:
      selector:
        matchLabels:
          app: csi-provisioner
      strategy:
        rollingUpdate:
          maxSurge: 0
          maxUnavailable: 1
        type: RollingUpdate
      replicas: 2
      template:
        metadata:
          labels:
            app: csi-provisioner
        spec:
          affinity:
            nodeAffinity:
              preferredDuringSchedulingIgnoredDuringExecution:
              - weight: 1
                preference:
                  matchExpressions:
                  - key: node-role.kubernetes.io/master
                    operator: Exists
              requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                - matchExpressions:
                  - key: type
                    operator: NotIn
                    values:
                    - virtual-kubelet
            podAntiAffinity:
              preferredDuringSchedulingIgnoredDuringExecution:
              - weight: 100
                podAffinityTerm:
                  labelSelector:
                    matchExpressions:
                    - key: app
                      operator: In
                      values:
                      - csi-provisioner
                  topologyKey: kubernetes.io/hostname
          tolerations:
          - effect: NoSchedule
            operator: Exists
            key: node-role.kubernetes.io/master
          - effect: NoSchedule
            operator: Exists
            key: node.cloudprovider.kubernetes.io/uninitialized
          serviceAccount: csi-admin
          hostPID: true
          priorityClassName: system-node-critical
          containers:
            - name: external-disk-provisioner
              image: registry.cn-hangzhou.aliyuncs.com/acs/csi-provisioner:v3.0.0-080f01e64-aliyun
              resources:
                requests:
                  cpu: 10m
                  memory: 16Mi
                limits:
                  cpu: 500m
                  memory: 1024Mi
              args:
                - "--csi-address=$(ADDRESS)"
                - "--feature-gates=Topology=True"
                - "--volume-name-prefix=disk"
                - "--strict-topology=true"
                - "--timeout=150s"
                - "--leader-election=true"
                - "--retry-interval-start=500ms"
                - "--extra-create-metadata=true"
                - "--default-fstype=ext4"
                - "--v=5"
              env:
                - name: ADDRESS
                  value: /var/lib/kubelet/csi-provisioner/diskplugin.csi.alibabacloud.com/csi.sock
              volumeMounts:
                - name: disk-provisioner-dir
                  mountPath: /var/lib/kubelet/csi-provisioner/diskplugin.csi.alibabacloud.com
            - name: external-disk-attacher
              image: registry.cn-hangzhou.aliyuncs.com/acs/csi-attacher:v3.3-72dd428b-aliyun
              resources:
                requests:
                  cpu: 10m
                  memory: 16Mi
                limits:
                  cpu: 500m
                  memory: 1024Mi
              args:
                - "--v=5"
                - "--csi-address=$(ADDRESS)"
                - "--leader-election=true"
              env:
                - name: ADDRESS
                  value: /var/lib/kubelet/csi-provisioner/diskplugin.csi.alibabacloud.com/csi.sock
              volumeMounts:
                - name: disk-provisioner-dir
                  mountPath: /var/lib/kubelet/csi-provisioner/diskplugin.csi.alibabacloud.com
            - name: external-disk-resizer
              image: registry.cn-hangzhou.aliyuncs.com/acs/csi-resizer:v1.3-ca84e84-aliyun
              resources:
                requests:
                  cpu: 10m
                  memory: 16Mi
                limits:
                  cpu: 500m
                  memory: 8Gi
              args:
                - "--v=5"
                - "--csi-address=$(ADDRESS)"
                - "--leader-election"
              env:
                - name: ADDRESS
                  value: /var/lib/kubelet/csi-provisioner/diskplugin.csi.alibabacloud.com/csi.sock
              volumeMounts:
                - name: disk-provisioner-dir
                  mountPath: /var/lib/kubelet/csi-provisioner/diskplugin.csi.alibabacloud.com
            - name: external-nas-provisioner
              image: registry.cn-hangzhou.aliyuncs.com/acs/csi-provisioner:v3.0.0-080f01e64-aliyun
              resources:
                requests:
                  cpu: 10m
                  memory: 16Mi
                limits:
                  cpu: 500m
                  memory: 1024Mi
              args:
                - "--csi-address=$(ADDRESS)"
                - "--volume-name-prefix=nas"
                - "--timeout=150s"
                - "--leader-election=true"
                - "--retry-interval-start=500ms"
                - "--default-fstype=nfs"
                - "--v=5"
              env:
                - name: ADDRESS
                  value: /var/lib/kubelet/csi-provisioner/nasplugin.csi.alibabacloud.com/csi.sock
              volumeMounts:
                - name: nas-provisioner-dir
                  mountPath: /var/lib/kubelet/csi-provisioner/nasplugin.csi.alibabacloud.com
            - name: external-nas-resizer
              image: registry.cn-hangzhou.aliyuncs.com/acs/csi-resizer:v1.3-ca84e84-aliyun
              resources:
                requests:
                  cpu: 10m
                  memory: 16Mi
                limits:
                  cpu: 500m
                  memory: 8Gi
              args:
                - "--v=5"
                - "--csi-address=$(ADDRESS)"
                - "--leader-election"
              env:
                - name: ADDRESS
                  value: /var/lib/kubelet/csi-provisioner/nasplugin.csi.alibabacloud.com/csi.sock
              volumeMounts:
                - name: nas-provisioner-dir
                  mountPath: /var/lib/kubelet/csi-provisioner/nasplugin.csi.alibabacloud.com
            - name: external-oss-provisioner
              args:
                - --csi-address=$(ADDRESS)
                - --volume-name-prefix=oss
                - --timeout=150s
                - --leader-election=true
                - --retry-interval-start=500ms
                - --default-fstype=ossfs
                - --v=5
              env:
              - name: ADDRESS
                value: /var/lib/kubelet/csi-provisioner/ossplugin.csi.alibabacloud.com/csi.sock
              image: registry.cn-hangzhou.aliyuncs.com/acs/csi-provisioner:v3.0.0-080f01e64-aliyun
              resources:
                limits:
                  cpu: 500m
                  memory: 1Gi
                requests:
                  cpu: 10m
                  memory: 16Mi
              volumeMounts:
              - mountPath: /var/lib/kubelet/csi-provisioner/ossplugin.csi.alibabacloud.com
                name: oss-provisioner-dir
            - name: external-csi-snapshotter
              image: registry.cn-hangzhou.aliyuncs.com/acs/csi-snapshotter:v4.0.0-a230d5b3-aliyun
              resources:
                requests:
                  cpu: 10m
                  memory: 16Mi
                limits:
                  cpu: 500m
                  memory: 1024Mi
              args:
                - "--v=5"
                - "--csi-address=$(ADDRESS)"
                - "--leader-election=true"
                - "--extra-create-metadata=true"
              env:
                - name: ADDRESS
                  value: /csi/csi.sock
              volumeMounts:
                - name: disk-provisioner-dir
                  mountPath: /csi
            - name: external-snapshot-controller
              image: registry.cn-hangzhou.aliyuncs.com/acs/snapshot-controller:v4.0.0-a230d5b3-aliyun
              resources:
                requests:
                  cpu: 10m
                  memory: 16Mi
                limits:
                  cpu: 500m
                  memory: 1024Mi
              args:
                - "--v=5"
                - "--leader-election=true"
            - name: csi-provisioner
              securityContext:
                privileged: true
              image: registry.cn-hangzhou.aliyuncs.com/acs/csi-plugin:v1.24.6-55c95dd-aliyun
              args:
                - "--endpoint=$(CSI_ENDPOINT)"
                - "--v=2"
                - "--driver=nas,disk,oss"
              env:
                - name: CSI_ENDPOINT
                  value: unix://var/lib/kubelet/csi-provisioner/driverplugin.csi.alibabacloud.com-replace/csi.sock
                - name: MAX_VOLUMES_PERNODE
                  value: "15"
                - name: SERVICE_TYPE
                  value: "provisioner"
                - name: "CLUSTER_ID"
                  value: "CLUSTER_ID"
              livenessProbe:
                httpGet:
                  path: /healthz
                  port: healthz
                  scheme: HTTP
                initialDelaySeconds: 10
                periodSeconds: 30
                timeoutSeconds: 5
                failureThreshold: 5
              readinessProbe:
                httpGet:
                  path: /healthz
                  port: healthz
                initialDelaySeconds: 5
                periodSeconds: 20
              ports:
                - name: healthz
                  containerPort: 11270
              volumeMounts:
                - name: host-log
                  mountPath: /var/log/
                - name: disk-provisioner-dir
                  mountPath: /var/lib/kubelet/csi-provisioner/diskplugin.csi.alibabacloud.com
                - name: nas-provisioner-dir
                  mountPath: /var/lib/kubelet/csi-provisioner/nasplugin.csi.alibabacloud.com
                - name: oss-provisioner-dir
                  mountPath: /var/lib/kubelet/csi-provisioner/ossplugin.csi.alibabacloud.com
                - mountPath: /var/addon
                  name: addon-token
                  readOnly: true
                - mountPath: /mnt
                  mountPropagation: Bidirectional
                  name: host-dev
                - mountPath: /host/etc
                  name: etc
              resources:
                limits:
                  cpu: 500m
                  memory: 1024Mi
                requests:
                  cpu: 100m
                  memory: 128Mi
          volumes:
            - name: disk-provisioner-dir
              emptyDir: {}
            - name: nas-provisioner-dir
              emptyDir: {}
            - name: oss-provisioner-dir
              emptyDir: {}
            - name: host-log
              hostPath:
                path: /var/log/
            - name: etc
              hostPath:
                path: /etc
                type: ""
            - name: host-dev
              hostPath:
                path: /mnt
                type: ""
            - name: addon-token
              secret:
                defaultMode: 420
                optional: true
                items:
                - key: addon.token.config
                  path: token-config
                secretName: addon.csi.token
  2. Deploy both components:

    kubectl apply -f csi-plugin.yaml -f csi-provisioner.yaml
  3. Verify that CSI is running:

    kubectl get pods -nkube-system | grep csi

    Expected output:

    csi-plugin-577mm                              4/4     Running   0          3d20h
    csi-plugin-k9mzt                              4/4     Running   0          41d
    csi-provisioner-6b58f46989-8wwl5              9/9     Running   0          41d
    csi-provisioner-6b58f46989-qzh8l              9/9     Running   0          6d20h

Step 2: Check the current volume status

Before converting PVCs and PVs, record the current state of your workload.

  1. Check pod status:

    kubectl get pod

    Expected output:

    NAME       READY   STATUS    RESTARTS   AGE
    oss-sts-1  1/1     Running   0          11m
  2. Identify the PVC used by the pod:

    kubectl describe pod oss-sts-1 |grep ClaimName

    Expected output:

    ClaimName:  oss-pvc
  3. Check the PVC status:

    kubectl get pvc

    Expected output:

    NAME      STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    oss-pvc   Bound    oss-pv   5Gi        RWX                           7m23s

Step 3: Create CSI-managed PVC and PV for the OSS volume

Two methods are available. Choose based on your situation:

Method 1: Automated (Flexvolume2CSI CLI) Method 2: Manual YAML editing
Best for Clusters with many PVs to convert Clusters with a small number of PVs, or when you want full control
Effort Low — the CLI generates the CSI YAML Higher — edit YAML definitions by hand
Risk Low — logical conversion only; original PV/PVC objects remain intact until you delete them Low — same data, new PV/PVC definitions; original objects remain until cleanup
Downtime Pod restarts when you update the application reference Pod restarts when you update the application reference

Method 1: Automated conversion with the Flexvolume2CSI CLI

  1. Use the Flexvolume2CSI CLI to convert your FlexVolume PVs and PVCs to CSI-managed definitions. The CLI generates oss-pv-pvc-csi.yaml.

  2. Apply the generated file:

    kubectl apply -f oss-pv-pvc-csi.yaml
  3. Verify that both the old and new PVCs are bound:

    kubectl get pvc

    Expected output:

    NAME          STATUS   VOLUME       CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    oss-pvc-csi   Bound    oss-pv-csi   5Gi        RWO                           7m15s
    oss-pvc       Bound    oss-pv       5Gi        RWX                           52m

Method 2: Manual conversion by editing PVC and PV configurations

  1. Save the existing FlexVolume PVC and PV definitions. Save the PVC:

    kubectl get pvc oss-pvc -oyaml > oss-pvc-flexvolume.yaml
    cat oss-pvc-flexvolume.yaml

    Expected output:

    apiVersion: v1
    kind: PersistentVolumeClaim
      name: oss-pvc
      namespace: default
    spec:
      accessModes:
      - ReadWriteMany
      resources:
        requests:
          storage: 5Gi
      volumeMode: Filesystem
      volumeName: oss-pv

    Save the PV:

    kubectl get pv oss-pv -oyaml > oss-pv-flexvolume.yaml
    cat oss-pv-flexvolume.yaml

    Expected output:

    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: oss-pv
    spec:
      accessModes:
      - ReadWriteMany
      capacity:
        storage: 5Gi
      claimRef:
        apiVersion: v1
        kind: PersistentVolumeClaim
        name: oss-pvc
        namespace: default
      flexVolume:
        driver: alicloud/oss
        nodePublishSecretRef:
          name: oss-secret
          namespace: default
        options:
          bucket: xxx
          otherOpts: -o max_stat_cache_size=0 -o allow_other
          url: xxx.aliyuncs.com
      persistentVolumeReclaimPolicy: Retain
      volumeMode: Filesystem
  2. Create oss-pv-pvc-csi.yaml with CSI-managed PVC and PV definitions. Replace *** with your actual OSS bucket name and endpoint.

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: oss-pvc-csi
    spec:
      accessModes:
      - ReadWriteMany
      resources:
        requests:
          storage: 5Gi
      selector:
        matchLabels:
          alicloud-pvname: oss-pv-csi
    ---
    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: oss-pv-csi
      labels:
        alicloud-pvname: oss-pv-csi
    spec:
      capacity:
        storage: 5Gi
      accessModes:
        - ReadWriteMany
      persistentVolumeReclaimPolicy: Retain
      csi:
        driver: ossplugin.csi.alibabacloud.com
        volumeHandle: oss-pv-csi
        nodePublishSecretRef:
          name: oss-secret
          namespace: default
        volumeAttributes:
          bucket: "***"
          url: "***.aliyuncs.com"
          otherOpts: "-o max_stat_cache_size=0 -o allow_other"
  3. Apply the file:

    kubectl apply -f oss-pv-pvc-csi.yaml
  4. Verify that both PVCs are bound:

    kubectl get pvc

    Expected output:

    NAME          STATUS   VOLUME       CAPACITY   ACCESS MODES   STORAGECLASS   AGE
    oss-pvc-csi   Bound    oss-pv-csi   5Gi        RWO                           7m15s
    oss-pvc       Bound    oss-pv       5Gi        RWX                           52m

Step 4: Update the application to use the CSI-managed PVC

  1. Edit the StatefulSet configuration:

    kubectl edit sts oss-sts
  2. Update the volume reference to point to the CSI PVC:

          volumes:
          - name: oss
            persistentVolumeClaim:
              claimName: oss-pvc-csi
  3. Verify that the pod restarted successfully:

    kubectl get pod

    Expected output:

    NAME       READY   STATUS    RESTARTS   AGE
    oss-sts-1  1/1     Running   0          70s
  4. Confirm the volume is mounted via CSI (ossfs):

    kubectl exec oss-sts-1 -- mount |grep ossfs

    Expected output:

    ***:/ on /var/lib/kubelet/pods/ac02ea3f-125f-4b38-9bcf-9b117f62eaf0/volumes/kubernetes.io~csi/oss-pv-csi/mount type ossfs (rw,relatime,max_stat_cache_size=0,allow_other)

    The mount path kubernetes.io~csi confirms the pod is now using the CSI driver.

Step 5: Uninstall FlexVolume

  1. Log on to the OpenAPI Explorer console and call the UnInstallClusterAddons operation to uninstall FlexVolume. For details, see Uninstall components from a cluster.

    • ClusterId: Your cluster ID. Find it on the Basic Information tab of the cluster details page.

    • name: Set to flexvolume.

  2. Delete the FlexVolume controller components:

    kubectl delete deploy -n kube-system alicloud-disk-controller alicloud-nas-controller
  3. Verify that FlexVolume is fully removed:

    kubectl get pods -n kube-system | grep 'flexvolume\|alicloud-disk-controller\|alicloud-nas-controller'

    No output means FlexVolume has been removed successfully.

  4. Delete the FlexVolume StorageClasses (provisioner: alicloud/disk):

    kubectl delete storageclass alicloud-disk-available alicloud-disk-efficiency alicloud-disk-essd alicloud-disk-ssd

    Expected output:

    storageclass.storage.k8s.io "alicloud-disk-available" deleted
    storageclass.storage.k8s.io "alicloud-disk-efficiency" deleted
    storageclass.storage.k8s.io "alicloud-disk-essd" deleted
    storageclass.storage.k8s.io "alicloud-disk-ssd" deleted

Step 6: Install CSI via the API

Replace the manually deployed CSI components with the officially managed add-on.

  1. Log on to the OpenAPI Explorer console and call the InstallClusterAddons operation. For details, see Install a component in an ACK cluster.

    • ClusterId: Your cluster ID.

    • name: csi-provisioner

    • version: The latest version is applied automatically. For available versions, see csi-provisioner.

  2. Verify that the CSI plug-in is running:

    kubectl get pods -nkube-system | grep csi

    Expected output:

    csi-plugin-577mm                              4/4     Running   0          3d20h
    csi-plugin-k9mzt                              4/4     Running   0          41d
    csi-provisioner-6b58f46989-8wwl5              9/9     Running   0          41d
    csi-provisioner-6b58f46989-qzh8l              9/9     Running   0          6d20h

Step 7: Update kubelet parameters on existing nodes

CSI requires the kubelet parameter --enable-controller-attach-detach to be set to true on each node. The following DaemonSet updates this parameter automatically.

Important

Deploying this DaemonSet restarts kubelet on each node, which may briefly interrupt pod scheduling. To minimize impact, drain nodes one at a time before deploying, or verify that your workloads can tolerate a brief kubelet restart. Delete the DaemonSet after this step is complete.

kind: DaemonSet
apiVersion: apps/v1
metadata:
  name: kubelet-set
spec:
  selector:
    matchLabels:
      app: kubelet-set
  template:
    metadata:
      labels:
        app: kubelet-set
    spec:
      tolerations:
        - operator: "Exists"
      hostNetwork: true
      hostPID: true
      containers:
        - name: kubelet-set
          securityContext:
            privileged: true
            capabilities:
              add: ["SYS_ADMIN"]
            allowPrivilegeEscalation: true
          image: registry.cn-hangzhou.aliyuncs.com/acs/csi-plugin:v1.26.5-56d1e30-aliyun
          imagePullPolicy: "Always"
          env:
          - name: enableADController
            value: "true"
          command: ["sh", "-c"]
          args:
          - echo "Starting kubelet flag set to $enableADController";
            ifFlagTrueNum=`cat /host/etc/systemd/system/kubelet.service.d/10-kubeadm.conf | grep enable-controller-attach-detach=$enableADController | grep -v grep | wc -l`;
            echo "ifFlagTrueNum is $ifFlagTrueNum";
            if [ "$ifFlagTrueNum" = "0" ]; then
                curValue="true";
                if [ "$enableADController" = "true" ]; then
                    curValue="false";
                fi;
                sed -i "s/enable-controller-attach-detach=$curValue/enable-controller-attach-detach=$enableADController/" /host/etc/systemd/system/kubelet.service.d/10-kubeadm.conf;
                restartKubelet="true";
                echo "current value is $curValue, change to expect "$enableADController;
            fi;
            if [ "$restartKubelet" = "true" ]; then
                /nsenter --mount=/proc/1/ns/mnt systemctl daemon-reload;
                /nsenter --mount=/proc/1/ns/mnt service kubelet restart;
                echo "restart kubelet";
            fi;
            while true;
            do
                sleep 5;
            done;
          volumeMounts:
          - name: etc
            mountPath: /host/etc
      volumes:
        - name: etc
          hostPath:
            path: /etc

After deploying and confirming all nodes have enable-controller-attach-detach=true, delete the DaemonSet:

kubectl delete daemonset kubelet-set

What's next