Using preexisting persistent disks as PersistentVolumes

This page explains how to create a PersistentVolume using an existing Compute Engine persistent disk populated with data, and how to use the PersistentVolume in a Pod.

Before you begin

Before you start, make sure you have performed the following tasks:

Set up default gcloud settings using one of the following methods:

  • Using gcloud init, if you want to be walked through setting defaults.
  • Using gcloud config, to individually set your project ID, zone, and region.

Using gcloud init

If you receive the error One of [--zone, --region] must be supplied: Please specify location, complete this section.

  1. Run gcloud init and follow the directions:

    gcloud init

    If you are using SSH on a remote server, use the --console-only flag to prevent the command from launching a browser:

    gcloud init --console-only
  2. Follow the instructions to authorize gcloud to use your Google Cloud account.
  3. Create a new configuration or select an existing one.
  4. Choose a Google Cloud project.
  5. Choose a default Compute Engine zone for zonal clusters or a region for regional or Autopilot clusters.

Using gcloud config

  • Set your default project ID:
    gcloud config set project PROJECT_ID
  • If you are working with zonal clusters, set your default compute zone:
    gcloud config set compute/zone COMPUTE_ZONE
  • If you are working with Autopilot or regional clusters, set your default compute region:
    gcloud config set compute/region COMPUTE_REGION
  • Update gcloud to the latest version:
    gcloud components update

Creating the PersistentVolume and PersistentVolumeClaim

There are several ways to bind a PersistentVolumeClaim to a specific PersistentVolume. For example, the following YAML manifest creates a new PersistentVolume and PersistentVolumeClaim, and then binds the claim to the volume using a claimRef, which ensures that the PersistentVolume can only be bound to that PersistentVolumeClaim.

To bind a PersistentVolume to a PersistentVolumeClaim, the storageClassName of the two resources must match, as well as capacity, accessModes, and volumeMode. You can omit the storageClassName, but you must specify "" to prevent Kubernetes from using the default StorageClass.

The storageClassName does not need to refer to an existing StorageClass object. If all you need is to bind the claim to a volume, you can use any name you want. However, if you need extra functionality configured by a StorageClass, like volume resizing, then storageClassName must refer to an existing StorageClass object.

For more details, see the Kubernetes documentation on PersistentVolumes.

  1. Save the following YAML manifest:

    apiVersion: v1
    kind: PersistentVolume
    metadata:
      name: PV_NAME
    spec:
      storageClassName: "STORAGE_CLASS_NAME"
      capacity:
        storage: DISK_SIZE
      accessModes:
        - ReadWriteOnce
      claimRef:
        namespace: default
        name: PV_CLAIM_NAME
      gcePersistentDisk:
        pdName: DISK_NAME
        fsType: ext4
    ---
    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: PV_CLAIM_NAME
    spec:
      storageClassName: "STORAGE_CLASS_NAME"
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: DISK_SIZE
    

    Replace the following:

    • PV_NAME: the name of your new PersistentVolume.
    • STORAGE_CLASS_NAME: the name of your new StorageClass.
    • DISK_SIZE: the size of your preexisting persistent disk. For example, 500G.
    • PV_CLAIM_NAME: the name of your new PersistentVolumeClaim.
    • DISK_NAME: the name of your preexisting persistent disk.
  2. Create the PersistentVolume and PersistentVolumeClaim:

    kubectl apply -f FILE_PATH
    

    Replace FILE_PATH with the path to the YAML file.

Using the PersistentVolume in a Pod

After you create and bind the PersistentVolume and PersistentVolumeClaim, you can give a Pod's containers access to the volume by specifying values in the volumeMounts field.

The following YAML configuration creates a new Pod and a container running an nginx image, and then mounts the PersistentVolume on the Pod:

kind: Pod
apiVersion: v1
metadata:
  name: POD_NAME
spec:
  volumes:
    - name: VOLUME_NAME
      persistentVolumeClaim:
       claimName: PV_CLAIM_NAME
  containers:
    - name: CONTAINER_NAME
      image: nginx
      ports:
        - containerPort: 80
          name: "http-server"
      volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: VOLUME_NAME

Replace the following:

  • POD_NAME: the name of your new Pod.
  • VOLUME_NAME: the name of the volume.
  • PV_CLAIM_NAME: the name of the PersistentVolumeClaim you created in the previous step.
  • CONTAINER_NAME: the name of your new container.

Apply the configuration:

kubectl apply -f FILE_PATH

Replace FILE_PATH with the path to the YAML file.

To verify that the volume was mounted, run the following command:

kubectl describe pods POD_NAME

In the output, check that the PersistentVolumeClaim was mounted:

...
Volumes:
  VOLUME_NAME:
    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
    ClaimName:  PV_CLAIM_NAME
    ReadOnly:   false
Events:
  Type    Reason                  Age   From                     Message
  ----    ------                  ----  ----                     -------
  Normal  Scheduled               29s   default-scheduler        Successfully assigned default/POD_NAME to gke-cluster-1-default-pool-d5cde866-o4g4
  Normal  SuccessfulAttachVolume  21s   attachdetach-controller  AttachVolume.Attach succeeded for volume "PV_NAME"
  Normal  Pulling                 19s   kubelet                  Pulling image "nginx"
  Normal  Pulled                  19s   kubelet                  Successfully pulled image "nginx"
  Normal  Created                 18s   kubelet                  Created container CONTAINER_NAME
  Normal  Started                 18s   kubelet                  Started container CONTAINER_NAME

Using a preexisting disk in a StatefulSet

You can use preexisting Compute Engine persistent disks in a StatefulSet using PersistentVolumes. The StatefulSet automatically generates a PersistentVolumeClaim for each replica. You can predict the names of the generated PersistentVolumeClaims and bind them to the PersistentVolumes using claimRef.

In the following example, you take two preexisting persistent disks, create PersistentVolumes to use the disks, and then mount the volumes on a StatefulSet with two replicas in the default namespace.

  1. Decide on a name for your new StatefulSet, a name for your PersistentVolumeClaim template, and the number of replicas in the StatefulSet.
  2. Work out the names of the automatically generated PersistentVolumeClaims. The StatefulSet uses the following format for PersistentVolumeClaim names:

    VC_TEMPLATE_NAME-STATEFULSET_NAME-REPLICA_INDEX
    

    Replace the following:

    • VC_TEMPLATE_NAME: the name of your new PersistentVolumeClaim template.
    • STATEFULSET_NAME: the name of your new StatefulSet.
    • REPLICA_INDEX: the index of the StatefulSet's replica. For this example, use 0 and 1.
  3. Create the PersistentVolumes. You must create a PersistentVolume for each replica in the StatefulSet.

    1. Save the following YAML manifest:

      apiVersion: v1
      kind: PersistentVolume
      metadata:
        name: pv-ss-demo-0
      spec:
        storageClassName: "example-storageclass"
        capacity:
          storage: DISK1_SIZE
        accessModes:
          - ReadWriteOnce
        claimRef:
          namespace: default
          name: PVC1_NAME
        gcePersistentDisk:
          pdName: DISK1_NAME
          fsType: ext4
       ---
      apiVersion: v1
      kind: PersistentVolume
      metadata:
        name: pv-ss-demo-1
      spec:
        storageClassName: "example-storageclass"
        capacity:
          storage: DISK2_SIZE
        accessModes:
          - ReadWriteOnce
        claimRef:
          namespace: default
          name: PVC2_NAME
        gcePersistentDisk:
          pdName: DISK2_NAME
          fsType: ext4
      

      Replace the following:

      • DISK1_SIZE and DISK2_SIZE: the sizes of your preexisting persistent disks.
      • DISK1_NAME and DISK2_NAME: the names of your preexisting persistent disks.
      • PVC1_NAME and PVC2_NAME: the names of the automatically generated PersistentVolumeClaims.
    2. Apply the configuration:

      kubectl apply -f FILE_PATH
      

      Replace FILE_PATH with the path to the YAML file.

  4. Create a StatefulSet using the values you chose in step 1. Ensure that the storage you specify in the volumeClaimTemplates is less than or equal to the total capacity of your PersistentVolumes.

    1. Save the following YAML manifest:

      apiVersion: apps/v1
      kind: StatefulSet
      metadata:
        name: STATEFULSET_NAME
      spec:
        selector:
          matchLabels:
            app: nginx
        serviceName: "nginx"
        replicas: 2
        template:
          metadata:
            labels:
              app: nginx
          spec:
            terminationGracePeriodSeconds: 10
            containers:
            - name: nginx
              image: k8s.gcr.io/nginx-slim:0.8
              ports:
              - containerPort: 80
                name: web
              volumeMounts:
              - name: VC_TEMPLATE_NAME
                mountPath: /usr/share/nginx/html
        volumeClaimTemplates:
        - metadata:
            name: VC_TEMPLATE_NAME
          spec:
            accessModes: [ "ReadWriteOnce" ]
            resources:
              requests:
                storage: 100Gi
      

      Replace the following:

      • STATEFULSET_NAME: the name of your new StatefulSet.
      • VC_TEMPLATE_NAME: the name of your new PersistentVolumeClaim template.
    2. Apply the configuration:

      kubectl apply -f FILE_PATH
      

      Replace FILE_PATH with the path to the YAML file.

What's next