Dans cet article nous allons voir comment il est possible de mettre en place un montage persistant dynamique de type NFS avec accès en ReadWriteMany:
La figure suivante résume schématiquement ce que nous allons obtenir en terme de ressources Kubernetes :
Pour réaliser une attribution dynamique d'un espace de stockage externe (PersistentVolume ou PV) de type NFS, nous avons besoin d'un contrôleur de stockage NFS (aussi appelé "provisioner").
En effet, un contrôleur de stockage peut recevoir, par l'intermédiaire d'une classe de stockage (StorageClass), des demandes d'espaces de stockage (PersistentVolumeClaim, ou PVC). Il y répond en créant un PV et en l'attribuant (Bind) à la PVC.
En termes de ressources Kubernetes, on obtient un workflow tel que celui-ci :
Note : selon l'infrastructure Kubernetes mise en place, il est possible qu'un contrôleur de stockage NFS soit déjà nativement présent. Si ce n'est pas le cas, alors il est nécessaire d'en installer un.
Le moyen le plus simple de procéder est d'utiliser un Chart Helm. Cela repose donc sur :
Vous pouvez alors installer le contrôleur comme suit :
$ helm install stable/nfs-server-provisioner --name nfs-provisioner -f values.yaml
Cette commande permet d'installer un nouveau contrôleur NFS dans votre cluster Kubernetes, nommé nfs-provisioner, à partir du fichier de variables values.yaml.
Il est donc bien sûr nécessaire de créer au préalable ce fichier de variables. L'exemple suivant devrait suffire dans la plupart des cas :
Cet exemple de configuration précise le nom à donner à la StorageClass (celui qui sera utilisé pour la référencer depuis les PVC), et active la persistance, avec une taille demandée de 1 Gio.
Attention : comme les pods, le contrôleur a lui-même besoin d'un stockage persistant.
Car en effet, comme je l'ai déjà mentionné, nous sommes en train de déployer le contrôleur dans notre cluster. Il sera donc, comme tout déploiement dans Kubernetes, déployé sous forme de Deployment, ReplicaSet et Pod. De fait, par défaut il n'aura pas de stockage persistant.
En réalité, par défaut, le Chart Helm est quand même prévu pour utiliser un volume externe persistant, de type EmptyDir. Mais EmptyDir n’est persistant que pour la durée de vie du pod courant. Que se passe-t-il si le pod est détruit et reconstruit sur un autre noeud du cluster ? Dans ce cas, toutes les données du contrôleur sont perdues...
Et qu'advient-il des applications qui des volumes gérés par ce contrôleur ? C'est un problème sur lequel nous reviendront un peu plus tard, mais elle n’auront plus accès à leur stockage. Il convient donc d'éviter cette situation.
Pour l'heure, l'important c'est qu'il faut TOUJOURS définir de la persistance pour le contrôleur NFS au-delà de la terminaison du pod, via la définition des trois paramètres suivants :
Vous pouvez ensuite suite exécuter la commande Helm citée un peu plus tôt.
Le contrôleur monte son volume externe dans /export/ et crée à l'intérieur une arborescence du type :
Ici le répertoire pvc-*
correspond à un PV, exporté en NFS et attaché à une PVC. Ce contrôleur n'en a qu'un, mais il pourrait bien sûr en avoir d'autres.
Et le fichier nfs-provisioner.identity
contient un identifiant unique qui est associé sous forme d'annotation à l'objet Kubernetes de type PV. C'est lui qui permet à un contrôleur de savoir quels PVs lui appartiennent, et à quels PVs il ne doit pas toucher.
Grâce à ça il peut gérer le cycle de vie de ses PVs, et laisser tranquille les PVs des autres.
Un dernier point à noter ici est que le contrôleur, lorsqu'il s'arrête, et ce quelle qu'en soit la raison, ne modifie pas les objets PV qu'il a créé. L'intérêt c'est que quand il redémarrera (potentiellement sur un autre nœud, on ne l'oublie pas), alors il pourra poursuivra la gestion de ces PV, dont nos applications auront toujours besoin.
J'ai déjà bien insisté précédemment, le stockage externe du contrôleur doit être persistant au-delà de la durée de vie du pod.
Pour bien l'avoir en tête nous allons ici imaginer que ce ne soit pas le cas, et que le contrôleur soit configuré pour stocker ses données via un PV de type EmptyDir, et qu'il soit détruit et reconstruit sur un autre nœud.
Il redémarre donc "vierge", mais les objets PVs n'ont pas disparu.
Comme les PV n'ont pas disparus, les PVC associées sont toujours là et nos applications vont essayer de les utiliser. Ce qui va simplement échouer...
En effet, ces PV référencent maintenant la nouvelle instance de notre provisioner (car au niveau de Kubernetes, le provisioner s'appelle pareil que l'ancien), lequel a perdu son stockage, et donc toute sa "mémoire des PVs".
Ainsi, non seulement le répertoire que les applications vont essayer de monter n'existe plus, ce qui va empêcher la création des pods associés. Mais en plus le contrôleur a maintenant changé d'identifiant (puisque celui-ci est généré aléatoirement), et il ne reconnaît donc même pas le PV actuel, qui ne sera donc pas détruit...
C'est pour éviter ce genre de scénario qu'il est impératif que le stockage du contrôleur soit persistant.
Si vous vous retrouvez dans la situation mentionnée ci-dessus, il n'y a pas grand chose d'autre à faire que de recréer la PVC ("delete" puis "create" donc) afin de forcer l'allocation d'un nouveau PV, lequel sera reconnu par le contrôleur.
Sauf que pour des raisons de sécurité Kubernetes empêche la suppression d’une PVC si elle est en cours d'utilisation par un conteneur...
Vous allez donc devoir procéder comme suit :
Comme vous pouvez le constater, c'est une situation qu'il convient d'éviter si on peut :-)
Après toutes ces explications, on commence à avoir hâte d’utiliser notre nouveau contrôleur de stockage pour déployer des ressources Kubernetes qui l’utilisent.
Pour cela c'est simple car avec le déploiement du Chart Helm une StorageClass a été créée automatiquement, et s'appelle "nfs" dans notre cas (mais vous pouvez bien sûr changer ce nom).
Il suffit donc de créer un fichier yaml pour la PVC, tel que :
Puis de générer l'objet Kubernetes associé :
$ kubectl create -f nfs_claim.yaml
Et finalement de l'utiliser dans la définition d'un objet Kubernetes qui inclut la définition d'un pod, par exemple :
Puis là encore de créer l'objet Kubernetes :
$ kubectl create -f mon_objet_kube.yaml
Vous pouvez à présent vérifier avec un “kubectl describe” que votre objet s’est bien vu affecter un volume persistant NFS, lequel sera accessible en écriture par des conteneurs appartenant à plusieurs pods.
Vous disposez à présent dans votre cluster Kubernetes d’un contrôleur de stockage NFS. Celui-ci permet de créer facilement des volumes persistant NFS accessible en écriture par plusieurs pods simultanément.
Ce contrôleur est par ailleurs configuré pour utiliser lui-même un stockage persistant externe au cluster afin de ne pas perdre les données en cas de destruction de son pod, par exemple lors d’une mise à jour de Kubernetes.