6.4. Using Persistent Volumes for Kubernetes Pods

Kubernetes allows using compute volumes as persistent storage for pods. Persistent volumes (PV) exist independently of pods, meaning that such a volume persists after the pod it is mounted to is deleted. This PV can be mounted to other pods for accessing data stored on it. You can provision PVs dynamically, without having to create them manually, or statically, using volumes that exist in the compute cluster.

6.4.1. Creating Storage Classes

In Virtuozzo Hybrid Cloud, storage classes map to compute storage policies defined in the admin panel. Creating a storage class is required for all storage operations in a Kubernetes cluster.

6.4.1.1. Creating Storage Class

Сlick + Create on the Kubernetes dashboard and specify a YAML file that defines this object. For example:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: mysc
provisioner: cinder.csi.openstack.org
parameters:
  type: default

This manifest describes the storage class mysc with the storage policy default. The storage policy must exist in the compute cluster and be specified in the storage quotas to the current project.

6.4.2. Dynamically Provisioning Persistent Volumes

Persistent volumes can be dynamically provisioned via persistent volume claims (PVC). A PVC requests for a PV of a specific storage class, access mode, and size. If a suitable PV exists in the cluster, it is bound to the claim. If suitable PVs do not exist but can be provisioned, a new volume is created and bound to the claim. Kubernetes uses a PVC to obtain the PV backing it and mounts it to the pod.

Prerequisites:

  • A pod and the persistent volume claim it uses must exist in the same namespace.

6.4.2.1. Provisioning PV to Pod Dynamically

  1. Access the Kubernetes cluster via the dashboard. Click Kubernetes access for instructions.

  2. On the Kubernetes dashboard, create a storage class, as described in Creating Storage Classes.

  3. Create a persistent volume claim. To do it, click + Create and specify the following YAML file:

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: mypvc
    spec:
      accessModes:
      - ReadWriteOnce
      resources:
          requests:
            storage: 10Gi
      storageClassName: mysc
    

    This manifest specifies the persistent volume claim mypvc that requests from the storage class mysc a volume of at least 10 GiB that can be mounted in the read/write mode by a single node.

    Creation of the PVC triggers dynamic provisioning of a persistent volume that satisfies the claim’s requirements. Kubernetes then binds it to the claim.

    ../_images/vhc-dynamically-provisioning-persistent-volumes.png
  4. Create a pod and specify the PVC as its volume. To do it, click + Create and enter the following YAML file:

    apiVersion: v1
    kind: Pod
    metadata:
      name: nginx
    spec:
      containers:
      - image: nginx
        imagePullPolicy: IfNotPresent
        name: nginx
        ports:
        - containerPort: 80
          protocol: TCP
        volumeMounts:
          - mountPath: /var/lib/www/html
            name: mydisk
      volumes:
      - name: mydisk
        persistentVolumeClaim:
          claimName: mypvc
          readOnly: false
    

This configuration file describes the pod nginx that uses the persistent volume claim mypvc. The persistent volume bound to the claim will be accessible at /var/lib/www/html inside the nginx container.

6.4.3. Statically Provisioning Persistent Volumes

You can mount existing compute volumes to pods using static provisioning of persistent volumes.

6.4.3.1. Mounting Compute Volume

  1. In the self-service panel, obtain the ID of the desired volume.

    ../_images/vhc-statically-provisioning-persistent-volumes-1.png
  2. Access the Kubernetes cluster via the dashboard. Click Kubernetes access for instructions.

  3. On the Kubernetes dashboard, create a storage class, as described in Creating Storage Classes.

  4. Create a persistent volume. To do it, click + Create and specify the following YAML file:

    apiVersion: v1
    kind: PersistentVolume
    metadata:
      annotations:
        pv.kubernetes.io/provisioned-by: cinder.csi.openstack.org
      name: mypv
    spec:
      accessModes:
      - ReadWriteOnce
      capacity:
        storage: 10Gi
      csi:
        driver: cinder.csi.openstack.org
        fsType: ext4
        volumeHandle: c5850e42-4f9d-42b5-9bee-8809dedae424
      persistentVolumeReclaimPolicy: Delete
      storageClassName: mysc
    

    This manifest specifies the persistent volume mypv from the storage class mysc that has 10 GiB of storage and access mode that allows it to be mounted in the read/write mode by a single node. The PV mypv uses the compute volume with the ID c5850e42-4f9d-42b5-9bee-8809dedae424 as backing storage.

  5. Create a persistent volume claim. Before you define the PVC, make sure the PV is created and has the status “Available”. The existing PV must meet the claim’s requirements to storage size, access mode and storage class. Click + Create and specify the following YAML file:

    apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      name: mypvc
    spec:
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 10Gi
      storageClassName: mysc
    

    Once the persistent volume claim mypvc is created, the volume mypv is bound to it.

    ../_images/vhc-statically-provisioning-persistent-volumes-2.png
  6. Create a pod and specify the PVC as its volume. Use the example from Step 4 in Dynamically Provisioning Persistent Volumes.

    In the self-service panel, the compute volume will be mounted to the virtual machine running the Kubernetes pod.

    ../_images/vhc-statically-provisioning-persistent-volumes-3.png

6.4.4. Making Kubernetes Deployments Highly Available

If a node that hosts a Kubernetes pod fails or becomes unreachable over the network, the pod is stuck in a transitional state. In this case, the pod’s persistent volumes are not automatically detached, and it prevents the pod redeployment on another worker node. To make your Kubernetes applications highly available, you need to enforce the pod termination in the event of node failure by adding rules to the pod deployment.

6.4.4.1. Terminating Stuck Pod

Add the following lines to the spec section of the deployment configuration file:

terminationGracePeriodSeconds: 0
tolerations:
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 2
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 2

If the node’s state changes to “NotReady” or “Unreachable”, the pod will be automatically terminated in 2 seconds.

The entire YAML file of a deployment may look as follows:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      terminationGracePeriodSeconds: 0
      tolerations:
      - effect: NoExecute
        key: node.kubernetes.io/unreachable
        operator: Exists
        tolerationSeconds: 2
      - effect: NoExecute
        key: node.kubernetes.io/not-ready
        operator: Exists
        tolerationSeconds: 2
      containers:
      - image: nginx
        imagePullPolicy: IfNotPresent
        name: nginx
        ports:
        - containerPort: 80
          protocol: TCP
        volumeMounts:
          - mountPath: /var/lib/www/html
            name: mydisk
      volumes:
        - name: mydisk
          persistentVolumeClaim:
            claimName: mypvc

The manifest above describes the deployment nginx with one pod that uses the persistent volume claim mypvc and will be automatically terminated in 2 seconds in the event of node failure.