Restoring Volumes for Kubernetes StatefulSets

    Longhorn supports restoring backups, and one of the use cases for this feature is to restore data for use in a Kubernetes StatefulSet, which requires restoring a volume for each replica that was backed up.

    To restore, follow the below instructions. The example below uses a StatefulSet with one volume attached to each Pod and two replicas.

    1. Connect to the Longhorn UI page in your web browser. Under the Backup tab, select the name of the StatefulSet volume. Click the dropdown menu of the volume entry and restore it. Name the volume something that can easily be referenced later for the Persistent Volumes.

      • Repeat this step for each volume you need restored.
      • For example, if restoring a StatefulSet with two replicas that had volumes named pvc-01a and pvc-02b, the restore could look like this:
      Backup NameRestored Volume
      pvc-01astatefulset-vol-0
      pvc-02bstatefulset-vol-1
    2. In Kubernetes, create a Persistent Volume for each Longhorn volume that was created. Name the volumes something that can easily be referenced later for the Persistent Volume Claims. storage capacity, numberOfReplicas, storageClassName, and volumeHandle must be replaced below. In the example, we’re referencing statefulset-vol-0 and statefulset-vol-1 in Longhorn and using longhorn as our storageClassName.

      1. apiVersion: v1
      2. kind: PersistentVolume
      3. metadata:
      4. name: statefulset-vol-0
      5. spec:
      6. capacity:
      7. storage: <size> # must match size of Longhorn volume
      8. volumeMode: Filesystem
      9. accessModes:
      10. - ReadWriteOnce
      11. persistentVolumeReclaimPolicy: Delete
      12. csi:
      13. driver: driver.longhorn.io # driver must match this
      14. fsType: ext4
      15. volumeAttributes:
      16. numberOfReplicas: <replicas> # must match Longhorn volume value
      17. staleReplicaTimeout: '30' # in minutes
      18. volumeHandle: statefulset-vol-0 # must match volume name from Longhorn
      19. storageClassName: longhorn # must be same name that we will use later
      20. ---
      21. apiVersion: v1
      22. kind: PersistentVolume
      23. metadata:
      24. name: statefulset-vol-1
      25. spec:
      26. capacity:
      27. storage: <size> # must match size of Longhorn volume
      28. volumeMode: Filesystem
      29. accessModes:
      30. - ReadWriteOnce
      31. persistentVolumeReclaimPolicy: Delete
      32. csi:
      33. driver: driver.longhorn.io # driver must match this
      34. fsType: ext4
      35. volumeAttributes:
      36. numberOfReplicas: <replicas> # must match Longhorn volume value
      37. staleReplicaTimeout: '30'
      38. volumeHandle: statefulset-vol-1 # must match volume name from Longhorn
      39. storageClassName: longhorn # must be same name that we will use later

    2.1 In the case of encrypted volume, make sure you are specifying the nodePublishSecretRef, and nodeStageSecretRef while creating the PV.

    1. kind: PersistentVolume
    2. metadata:
    3. name: statefulset-encrypted-vol-0
    4. spec:
    5. capacity:
    6. storage: <size>
    7. volumeMode: Filesystem
    8. accessModes:
    9. - ReadWriteOnce
    10. persistentVolumeReclaimPolicy: Delete
    11. csi:
    12. driver: driver.longhorn.io
    13. fsType: ext4
    14. nodePublishSecretRef:
    15. name: <secret-name>
    16. namespace: <namespace>
    17. nodeStageSecretRef:
    18. name: <secret-name>
    19. namespace: <namespace>
    20. volumeAttributes:
    21. numberOfReplicas: <replicas>
    22. staleReplicaTimeout: "30"
    23. volumeHandle: statefulset-encrypted-vol-0
    24. storageClassName: longhorn
    1. In the namespace the StatefulSet will be deployed in, create PersistentVolume Claims for each Persistent Volume. The name of the Persistent Volume Claim must follow this naming scheme:

      1. <name of Volume Claim Template>-<name of StatefulSet>-<index>

      StatefulSet Pods are zero-indexed. In this example, the name of the Volume Claim Template is data, the name of the StatefulSet is webapp, and there are two replicas, which are indexes 0 and 1.

      1. apiVersion: v1
      2. kind: PersistentVolumeClaim
      3. metadata:
      4. name: data-webapp-0
      5. spec:
      6. accessModes:
      7. - ReadWriteOnce
      8. resources:
      9. requests:
      10. storage: 2Gi # must match size from earlier
      11. storageClassName: longhorn # must match name from earlier
      12. volumeName: statefulset-vol-0 # must reference Persistent Volume
      13. ---
      14. apiVersion: v1
      15. kind: PersistentVolumeClaim
      16. metadata:
      17. name: data-webapp-1
      18. spec:
      19. accessModes:
      20. - ReadWriteOnce
      21. resources:
      22. requests:
      23. storage: 2Gi # must match size from earlier
      24. storageClassName: longhorn # must match name from earlier
      25. volumeName: statefulset-vol-1 # must reference Persistent Volume
    2. Create the StatefulSet:

      1. apiVersion: apps/v1beta2
      2. kind: StatefulSet
      3. metadata:
      4. name: webapp # match this with the PersistentVolumeClaim naming scheme
      5. spec:
      6. selector:
      7. matchLabels:
      8. app: nginx # has to match .spec.template.metadata.labels
      9. serviceName: "nginx"
      10. replicas: 2 # by default is 1
      11. template:
      12. metadata:
      13. labels:
      14. app: nginx # has to match .spec.selector.matchLabels
      15. spec:
      16. terminationGracePeriodSeconds: 10
      17. containers:
      18. - name: nginx
      19. image: registry.k8s.io/nginx-slim:0.8
      20. ports:
      21. - containerPort: 80
      22. name: web
      23. volumeMounts:
      24. - name: data
      25. mountPath: /usr/share/nginx/html
      26. volumeClaimTemplates:
      27. - metadata:
      28. name: data # match this with the PersistentVolumeClaim naming scheme
      29. spec:
      30. accessModes: [ "ReadWriteOnce" ]
      31. storageClassName: longhorn # must match name from earlier
      32. resources:
      33. requests:
      34. storage: 2Gi # must match size from earlier

    Result: The restored data should now be accessible from inside the StatefulSet Pods.