Tutoriel : Comment installer Longhorn ?

Publié le

Dans un précédent article, nous avons eu un aperçu de Longhorn. Ce tutoriel a pour but de montrer comment installer Longhorn, pour gérer les volumes de données d'un cluster Kubernetes. Nous installerons également le nécessaire pour gérer des snapshots CSI.

Prérequis

Nous avons besoin d'un cluster Kubernetes fonctionnel et de Helm 3 pour installer Longhorn à partir de sa chart officielle. Nous partons du principe que les nœuds du cluster sont sous Debian ou Ubuntu.

Étant donné que Longhorn repose sur iSCSI, et que le support des volumes ReadWriteMany utilise NFS, open-iscsi et le client NFS doivent être disponibles sur chaque nœud du cluster.

L'idéal est de préparer des images de machines virtuelles avec ces outils préinstallés, avec Packer par exemple. Dans un environnement de test, ces outils peuvent être installés avec ces commandes :

# mise à jour des dépôts
apt update -y

# installation d'open-iscsi et du client NFS
apt-get install -y open-iscsi nfs-common"

Installation d'un backend de sauvegarde

Pour stocker les backups et les snapshots CSI des volumes, Longhorn s'appuie sur un stockage compatible S3 ou sur un serveur NFS. Dans ce tutoriel, nous allons déployer un serveur Minio, qui fournit un stockage compatible S3.

Attention : Ce déploiement a pour unique but de tester les fonctionnalités de backups et de snapshots CSI. Ce n'est PAS une solution valable en production...

En production, il est recommandé d'utiliser un serveur NFS, ou le stockage objet compatible S3 d'un fournisseur Cloud. Ces moyens de stockage doivent être localisés dans une autre zone géographique que celle du cluster de production (par exemple : ne pas tout mettre chez OVH à Strasbourg).

Personnellement, je privilégie l'utilisation d'un serveur NFS, tout en le déployant dans une autre ville et chez un autre Cloud provider.

Le déploiement d'un serveur Minio de test se fait avec la commande :

kubectl apply -f https://gist.githubusercontent.com/PhilippeChepy/3e64754a802e52f1f7b61f3c34771778/raw/575bbd04472f981f1abbd06d27fc446f0d4a6804/minio.yaml

Installation de Longhorn

La première étape est d'ajouter le dépôt de Longhorn à Helm :

sudo helm repo add longhorn https://charts.longhorn.io

Nous créons ensuite un namespace dédié aux add-ons du cluster, s'il n'est pas déjà créé :

sudo kubectl create ns kube-platform

L'installation de Longhorn se fait ensuite avec cette commande :

sudo helm upgrade -i --set=backupTarget=s3://backupbucket@us-east-1/ \
    --set=backupTargetCredentialSecret=minio-secret \
    --namespace=kube-platform longhorn longhorn/longhorn

Pour que les sauvegardes et les snapshots CSI fonctionnent, il faut paramétrer Longhorn pour utiliser le backend Minio que nous avons mis en place précédemment. Ces paramètres sont indiqués par les variables backupTarget et backupTargetCredentialSecret.

Très rapidement, le contenu du namespace kube-platform évolue. La commande kubectl -n kube-platform get pods -o wide renvoie :

NAME                                        READY   STATUS    RESTARTS   AGE     IP             NODE       NOMINATED NODE   READINESS GATES
csi-attacher-5dcdcd5984-79pcz               1/1     Running   0          2m36s   10.112.2.167   worker-2   <none>           <none>
csi-attacher-5dcdcd5984-ntrtz               1/1     Running   0          2m36s   10.112.2.234   worker-2   <none>           <none>
csi-attacher-5dcdcd5984-qtgg9               1/1     Running   0          2m36s   10.112.2.150   worker-2   <none>           <none>
csi-provisioner-5c9dfb6446-6jd58            1/1     Running   0          2m36s   10.112.2.127   worker-2   <none>           <none>
csi-provisioner-5c9dfb6446-c6k2c            1/1     Running   0          2m36s   10.112.2.48    worker-2   <none>           <none>
csi-provisioner-5c9dfb6446-snpz5            1/1     Running   0          2m36s   10.112.2.93    worker-2   <none>           <none>
csi-resizer-54d484bf8-clt5f                 1/1     Running   0          2m35s   10.112.2.142   worker-2   <none>           <none>
csi-resizer-54d484bf8-v9fmb                 1/1     Running   0          2m35s   10.112.2.58    worker-2   <none>           <none>
csi-resizer-54d484bf8-zkzqs                 1/1     Running   0          2m35s   10.112.2.200   worker-2   <none>           <none>
csi-snapshotter-96bfff7c9-47vcp             1/1     Running   0          2m35s   10.112.1.14    worker-1   <none>           <none>
csi-snapshotter-96bfff7c9-7g5bs             1/1     Running   0          2m35s   10.112.2.102   worker-2   <none>           <none>
csi-snapshotter-96bfff7c9-n8sck             1/1     Running   0          2m35s   10.112.1.2     worker-1   <none>           <none>
engine-image-ei-cf743f9c-6qlbb              1/1     Running   0          3m59s   10.112.1.58    worker-1   <none>           <none>
engine-image-ei-cf743f9c-wk4b6              1/1     Running   0          106s    10.112.3.242   worker-3   <none>           <none>
engine-image-ei-cf743f9c-zxdvt              1/1     Running   0          3m11s   10.112.2.132   worker-2   <none>           <none>
instance-manager-e-55b4d7b4                 1/1     Running   0          3m58s   10.112.1.155   worker-1   <none>           <none>
instance-manager-e-653541d8                 1/1     Running   0          57s     10.112.3.216   worker-3   <none>           <none>
instance-manager-e-7a6a6dc5                 1/1     Running   0          2m9s    10.112.2.113   worker-2   <none>           <none>
instance-manager-r-4f99cc85                 1/1     Running   0          3m57s   10.112.1.70    worker-1   <none>           <none>
instance-manager-r-77b02121                 1/1     Running   0          56s     10.112.3.233   worker-3   <none>           <none>
instance-manager-r-8e510d8a                 1/1     Running   0          2m9s    10.112.2.117   worker-2   <none>           <none>
longhorn-csi-plugin-7s675                   2/2     Running   0          2m34s   10.112.1.41    worker-1   <none>           <none>
longhorn-csi-plugin-lqtpj                   2/2     Running   0          2m34s   10.112.2.118   worker-2   <none>           <none>
longhorn-csi-plugin-z78rj                   2/2     Running   0          106s    10.112.3.208   worker-3   <none>           <none>
longhorn-driver-deployer-666c84fbb7-88mzx   1/1     Running   0          6m19s   10.112.1.71    worker-1   <none>           <none>
longhorn-manager-8zslv                      1/1     Running   0          4m39s   10.112.1.236   worker-1   <none>           <none>
longhorn-manager-b5t8c                      1/1     Running   0          106s    10.112.3.187   worker-3   <none>           <none>
longhorn-manager-z95xc                      1/1     Running   0          3m11s   10.112.2.177   worker-2   <none>           <none>
longhorn-ui-5576b69d6d-9g2qr                1/1     Running   0          6m19s   10.112.1.35    worker-1   <none>           <none>

Installation du gestionnaire de snapshots CSI

Pour que les fonctionnalités de gestion des snapshots CSI fonctionnent correctement, il est nécessaire d'avoir installé :

  • les CRDs concernées (VolumeSnapshotClass, VolumeSnapshotContent, et VolumeSnapshot) et
  • un déploiement de Snapshot Controller.

Voici les étapes d'installation de ces éléments :

# récupération des définitions des CRDs

curl -o snapshot.storage.k8s.io_volumesnapshotclasses.yaml https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotclasses.yaml

curl -o snapshot.storage.k8s.io_volumesnapshotcontents.yaml https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshotcontents.yaml

curl -o snapshot.storage.k8s.io_volumesnapshots.yaml https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/client/config/crd/snapshot.storage.k8s.io_volumesnapshots.yaml

# récupération des définitions du déploiement de "Snapshot Controller"
curl -o rbac-snapshot-controller.yaml https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/rbac-snapshot-controller.yaml

curl -o deployment-snapshot-controller.yaml https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/master/deploy/kubernetes/snapshot-controller/setup-snapshot-controller.yaml

# installation des CRDs
kubectl apply -f snapshot.storage.k8s.io_volumesnapshotclasses.yaml
kubectl apply -f snapshot.storage.k8s.io_volumesnapshotcontents.yaml
kubectl apply -f snapshot.storage.k8s.io_volumesnapshots.yaml

# installation de "Snapshot Controller"
kubectl apply -f rbac-snapshot-controller.yaml
kubectl apply -f deployment-snapshot-controller.yaml

Une fois les CRDs et Snapshot Controller installés, il reste à définir un objet de type VolumeSnapshotClass pour faire le lien entre Snapshot Controller et Longhorn.

Nous créons un fichier vsc-longhorn.yaml avec ce contenu :

kind: VolumeSnapshotClass
apiVersion: snapshot.storage.k8s.io/v1beta1
metadata:
  name: longhorn
driver: driver.longhorn.io
deletionPolicy: Delete

Nous appliquons la définition avec cette commande :

kubectl apply -f vsc-longhorn.yaml

Nous avons maintenant tous les éléments pour tester la plupart des fonctionnalités de Longhorn.

Accès au Dashboard

Le Dashboard de Longhorn est exposé sur le port 80 du service longhorn-frontend. Pour y accéder, il faut rendre ce port accessible en local, avec cette commande :

kubectl -n kube-platform port-forward svc/longhorn-frontend 8000:80

Il suffit ensuite de se rendre sur http://localhost:8000 avec un navigateur internet.

Nous obtenons alors ce type de résultat :

Capture d’écran 2021-03-13 à 22.12.12.png

Test des volumes

Nous choisissons de créer un déploiement avec plusieurs replicas. Tous ces replicas partagent un même volume en mode ReadWriteMany.

Nous créons donc un fichier test.yaml contenant ces définitions :

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data-pvc
spec:
  accessModes:
    - ReadWriteMany
  storageClassName: longhorn
  resources:
    requests:
      storage: 2Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: data
  labels:
    app: data
spec:
  selector:
    matchLabels:
      app: data
  replicas: 10
  template:
    metadata:
      labels:
        app: data
    spec:
      restartPolicy: Always
      containers:
      - image: nginx:1.19
        name: data
        livenessProbe:
          exec:
            command:
              - ls
              - /data/lost+found
          initialDelaySeconds: 5
          periodSeconds: 5
        volumeMounts:
        - name: data-volume
          mountPath: /data
      volumes:
      - name: data-volume
        persistentVolumeClaim:
          claimName: data-pvc

Nous appliquons la configuration avec la commande kubectl apply -f test.yaml, ce qui nous renvoie :

persistentvolumeclaim/data-pvc created
deployment.apps/data created

Les Pods sont rapidement déployés, la commande kubectl get pods -o wide nous donne ceci :

NAME                    READY   STATUS    RESTARTS   AGE   IP             NODE       NOMINATED NODE   READINESS GATES
data-64d4d4c486-42cpr   1/1     Running   0          51s   10.112.3.147   worker-3   <none>           <none>
data-64d4d4c486-dch8b   1/1     Running   0          51s   10.112.2.128   worker-2   <none>           <none>
data-64d4d4c486-lxvt2   1/1     Running   0          51s   10.112.2.193   worker-2   <none>           <none>
data-64d4d4c486-mz256   1/1     Running   0          33s   10.112.1.98    worker-1   <none>           <none>
data-64d4d4c486-sjqgz   1/1     Running   0          31s   10.112.2.30    worker-2   <none>           <none>
data-64d4d4c486-t68ps   1/1     Running   0          33s   10.112.3.215   worker-3   <none>           <none>
data-64d4d4c486-thtzt   1/1     Running   0          51s   10.112.3.185   worker-3   <none>           <none>
data-64d4d4c486-ttrg7   1/1     Running   0          30s   10.112.2.19    worker-2   <none>           <none>
data-64d4d4c486-w4d77   1/1     Running   0          51s   10.112.3.92    worker-3   <none>           <none>
data-64d4d4c486-x9cw4   1/1     Running   0          30s   10.112.2.12    worker-2   <none>           <none>

Le PersistentVolumeClaim est bien créé, et un volume est reservé. La commande kubectl get pv,pvc indique :

NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM              STORAGECLASS   REASON   AGE
persistentvolume/pvc-af30f964-79e5-4616-b0ba-c6db9f280f8c   2Gi        RWX            Delete           Bound    default/data-pvc   longhorn                2m47s

NAME                             STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/data-pvc   Bound    pvc-af30f964-79e5-4616-b0ba-c6db9f280f8c   2Gi        RWX            longhorn       2m51s

D'autres exemples d'utilisation de Longhorn sont disponibles dans la documentation de Longhorn.

Test des snapshots CSI

Voici les tâches que nous allons exécuter pour tester les snapshots CSI :

  • accéder au shell d'un Pod
  • créer du contenu dans le dossier /data
  • créer un snapshot
  • modifier le contenu du dossier /data
  • restaurer le snapshot

Nous commençons par écrire un fichier dans le volume :

# choisir un pod dans la sortie suivante :
kubectl get pods

# accéder au shell de ce Pod
kubectl exec -it data-64d4d4c486-42cpr -- /bin/bash

# modifier le volume
touch /data/test

Le fichier "test" est bien présent, la commande ls -lah /data renvoie :

drwxr-xr-x 3 root root 4.0K Mar 13 22:56 .
drwxr-xr-x 1 root root 4.0K Mar 13 22:56 ..
drwx------ 2 root root  16K Mar 13 22:55 lost+found
-rw-r--r-- 1 root root    0 Mar 13 22:56 test

Nous pouvons quitter le container avec la commande exit.

Nous créons un snapshot. Pour cela il faut créer un fichier data-snapshot.yaml :

apiVersion: snapshot.storage.k8s.io/v1beta1
kind: VolumeSnapshot
metadata:
  name: data-snapshot-pvc
spec:
  volumeSnapshotClassName: longhorn
  source:
    persistentVolumeClaimName: data-pvc

Nous appliquons ce snapshot avec la commande kubectl apply -f data-snapshot.yaml, nous obtenons une confirmation :

volumesnapshot.snapshot.storage.k8s.io/data-snapshot-pvc created

Le snapshot prend un certain temps pour être prêt. Nous pouvons voir l'état de celui-ci avec la commande kubectl get volumesnapshot :

NAME                READYTOUSE   SOURCEPVC   SOURCESNAPSHOTCONTENT   RESTORESIZE   SNAPSHOTCLASS   SNAPSHOTCONTENT                                    CREATIONTIME   AGE
data-snapshot-pvc   true         data-pvc                            2Gi           longhorn        snapcontent-ada841aa-2cdb-4d3e-b527-c9e694ece129   17s            66s

Dans ce tableau, l'information importante est la valeur true dans la colonne READYTOUSE.

Nous pouvons retourner dans le pod et supprimer le fichier :

# accéder au shell de ce Pod
kubectl exec -it data-64d4d4c486-42cpr -- /bin/bash

# modifier le volume
rm /data/test

La suppression du fichier est effective : la commande ls -lah /data renvoie :

total 24K
drwxr-xr-x 3 root root 4.0K Mar 13 22:57 .
drwxr-xr-x 1 root root 4.0K Mar 13 22:56 ..
drwx------ 2 root root  16K Mar 13 22:55 lost+found

La restauration d'un volume se fait en déployant un nouveau PersistantVolumeClaim, qui a pour source de données (dataSource) le snapshot que nous venons de faire. Nous créons pour ça un fichier restored-pvc.yaml :

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: restored-data-pvc
spec:
  storageClassName: longhorn
  dataSource:
    name: data-snapshot-pvc
    kind: VolumeSnapshot
    apiGroup: snapshot.storage.k8s.io
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 2Gi

Pour compléter la restauration, il nous reste à modifier le Deployment pour référencer ce nouveau PersistentVolumeClaim à la place de l'original :

kubectl edit deployment data

Nous cherchons la portion qui référence data-pvc (le nom de l'ancien PersistentVolumeClaim) :

...
      volumes:
      - name: data-volume
        persistentVolumeClaim:
          claimName: data-pvc
...

Nous remplaçons data-pvc par restored-data-pvc, puis nous sauvegardons la modification.

Si nous lançons la commande kubectl get pods, nous remarquons que le déploiement se met à jour pour utiliser le volume restauré.

Si nous retournons dans un Pod (commande kubectl exec -it data-6465d5567c-d45fn -- /bin/bash), et que nous listons le contenu du dossier /data (ls -lah /data), nous retrouvons bien le fichier "test" qui avait été supprimé :

drwxr-xr-x 3 root root 4.0K Mar 13 22:57 .
drwxr-xr-x 1 root root 4.0K Mar 13 22:56 ..
drwx------ 2 root root  16K Mar 13 22:55 lost+found
-rw-r--r-- 1 root root    0 Mar 13 22:56 test

Un autre workflow de restauration possible est celui-ci :

  • ouvrir un shell dans un Pod qui monte les deux PersistentVolumeClaim : celui qu'il faut restaurer, et celui qui a été créé à partir du VolumeSnapshot
  • restaurer l'ancien volume à partir du snapshot avec un outil comme rsync

Cette solution est plus simple à exécuter, mais n'est pas atomique : le temps que la synchronisation se fasse, l'état de l'ancien volume est temporairement inconsistant.

Conclusion

Longhorn est assez simple à mettre en place. L'essentiel de son utilisation se fait au moyen des mécanismes standards de Kubernetes, ce qui est appréciable : ce sont principalement les objets de type PersistentVolume, PersistentVolumeClaim, et VolumeSnapshot.

La documentation est très complète et explique très bien le fonctionnement global de Longhorn. Elle couvre même les cas de figures exceptionnels, comme la perte d'un nœud du cluster.

Autres références

Longhorn - Install with Helm
Longhorn - Enable CSI Snapshot Support on a Cluster
CSI Snapshotter
Longhorn - Support for ReadWriteMany (RWX) workloads

PHILIPPE CHEPY

Administrateur Système et Développeur