Préparation à la CKA - 05 - Volumes

Publié le 10/09/2024 par Ali Sanhaji

Volumes

Dans mon article sur les pods, j’avais présenté cet objet dans Kubernetes comme étant constitué d’un ou plusieurs containers, et de 0 ou plusieurs volumes. Il est temps de parler des volumes.

Une application simple peut avoir tous les fichiers qu’il lui faut dans son image de container pour répondre au service qu’elle rend, pas besoin d’éléments à ajouter en plus. Mais comme nous l’avons vu dans l’article précédent, dans les applications plus complexes, nous avons souvent besoin d’ajouter de la configuration ou des identifiants qui seront lus par l’application dans le pod. Les volumes servent à ajouter des dossiers dans l’arborescence du système de fichiers du pod afin de permettre à l’application de lire ou d’écrire des données.

Nous allons voir comment utiliser un volume en commençant par les types de volumes les plus simples : EmptyDir et HostPath.

EmptyDir

L’EmptyDir est comme son nom l’indique un dossier vide, tout simplement. Un container dans le pod peut l’utiliser pour mettre des fichiers dedans afin qu’ils soient relus plus tard en cas de besoin, ou si jamais le pod redémarre à cause d’une erreur (le pod ne change pas de noeud dans ce cas-là) et qu’on ne veut pas perdre les données du traitement fait par l’application, ou s’il y a un second container dans le pod qui a besoin d’utiliser ce qu’a fait le premier container du pod. En revanche, l’EmptyDir n’est pas persistant, donc si le pod change de noeud, les données sont supprimées.

Pour créer un volume et l’utiliser dans un pod, il y a deux choses à faire : le déclarer dans la spécification du pod (au même niveau que les containers) avec volumes, et définir dans chaque container du pod qui va l’utiliser où est-ce qu’il doit être monté avec volumeMounts. Voici un exemple de pod qui monte un volume de type EmptyDir :

apiVersion: v1
kind: Pod
metadata:
  name: emptydir-pod
spec:
  containers:
  - image: busybox:latest
    name: emptydir-container
    args:
      - sleep
      - "3600"
    volumeMounts:
    - mountPath: /path/emptydir
      name: empty
  volumes:
  - name: empty
    emptyDir: {}

Le nom du volume est empty, on le monte dans le pod qui s’appelle emptydir-container dans le chemin /path/emptydir.

Si on lance le pod, on peut voir à l’intérieur du pod qu’il y a bien un dossier qui s’appelle emptydir dans /path :

$ kubectl apply -f emptydir.yaml
$ kubectl exec emptydir-pod -- ls -l /path
total 4
drwxrwxrwx    2 root     root          4096 Dec 27 14:20 emptydir

HostPath Le HostPath est un autre type de volume qui prend un dossier sur le nœud sur lequel tourne le pod pour le monter sur le pod. On comprend très vite que ce n’est pas très viable pour une application de dépendre du nœud sur lequel elle tourne, ça ne la rend pas du tout portable, et c’est fortement déconseillé de faire ça. Mais HostPath est utilisé pour des applications très spécifiques où on veut rendre accessible au pod des dossiers spéciaux qui lui permettent par exemple d’accéder aux informations systèmes du nœud, ou s’il a besoin d’accéder au container runtime (p.ex., docker, containerd) ou s’il a besoin d’accéder à une socket ou un matériel spécial sur le nœud.

Voici un exemple d’un pod qui monte le chemin /etc du noeud sur lequel il tourne dans un dossier /hostpath :

apiVersion: v1
kind: Pod
metadata:
  name: hostpath-pod
spec:
  containers:
  - image: busybox:latest
    name: hostpath
    args:
      - sleep
      - "3600"
    volumeMounts:
    - mountPath: /hostpath/etc
      name: ecetera
  volumes:
  - name: ecetera
    hostPath:
      path: /etc/
      type: Directory

On peut le lancer et voir que, comme on est sur un noeud kubernetes, on peut voir dans le dossier /etc/kubernetes/certs les certificats utilisés par le cluster pour chiffrer les appels API :

$ kubectl apply -f hostpath.yaml
$ kubectl exec hostpath-pod -- ls /hostpath/etc/kubernetes/certs
apiserver.crt
ca.crt
client.key
kubeletserver.crt
kubeletserver.key

ConfigMap

Revenons enfin aux ConfigMaps et aux Secrets qu’on a vu dans l’article précédent. Nous avons vu qu’on pouvait monter le contenu d’une ConfigMap en tant que variables d’environnements, mais parfois on a besoin d’avoir tout un fichier de configuration en tant que ConfigMap et le monter en entier dans un container pour qu’il soit lu par l’application. On va donc monter la ConfigMap en tant que volume de type configMap.

On va déjà créer le fichier de configuration et créer la ConfigMap à partir de ce fichier :

$ cat << EOF > system.conf
[colors]
planet=blue
moon=white
EOF
kubectl create configmap space-system --from-file=system.conf

Maintenant, on peut créer le pod qui va utiliser cette ConfigMap :

apiVersion: v1
kind: Pod
metadata:
  labels:
    run: confvolume
  name: confvolume
spec:
  containers:
  - image: busybox:latest
    name: confvolume
    args:
      - sleep
      - "3600"
    volumeMounts:
      - name: system
        mountPath: /etc/system.conf
        subPath: system.conf
  volumes:
  - name: system
    configMap:
      name: space-system

On peut lancer le pod et voir qu’on retrouve bien le contenu de la ConfigMap :

$ kubectl apply -f confvolume.yaml
$ kubectl exec confvolume -- cat /etc/system.conf
[colors]
planet=blue
moon=white

La mention subPath permet de dire au pod de ne monter que le fichier system.conf de la ConfigMap dans le pod et de le mettre directement avec comme nom /etc/system.conf. C’est le seul qu’on a dans notre cas, mais si on avait omis subPath, on aurait retrouvé le fichier system.conf dans /etc/system.conf/system.conf. Ce qu’on ne veut pas. Et si on avait plusieurs fichiers dans la ConfigMap, par exemple system1.conf et system2.conf, sans le subPath, on aurait retrouvé tout le contenu de la ConfigMap montée dans le container donc on aurait /etc/system.conf/system1.conf et /etc/system.conf/system2.conf. Le subPath permet d’avoir un contrôle plus fin de ce qui est monté dans le container à partir du volume.

Secret

Pour les Secrets, c’est la même chose que pour les ConfigMap. On peut monter un Secret en tant que volume de type secret.

On reprend les mêmes identifiants qu’on avait dans l’article précédent :

$ echo -n 'admin' > username
$ echo -n 'admin-pass' > password

$ kubectl create secret generic admin-creds --from-file=username --from-file=password

Et maintenant, on les monte dans un pod en tant que volume :

apiVersion: v1
kind: Pod
metadata:
  labels:
    run: secretvolume
  name: secretvolume
spec:
  containers:
  - image: busybox:latest
    name: secretvolume
    args:
      - sleep
      - "3600"
    volumeMounts:
      - name: admincred
        mountPath: /etc/admin-creds
        readOnly: true
  volumes:
  - name: admincreds
    secret:
      secretName: admin-creds

Quand on créé le pod, on voit qu’on retrouve les identifiants en tant que fichiers dans le dossier /etc/admin-creds :

$ kubectl apply -f secretvolume.yaml
$ kubectl exec secretvolume -- ls /etc/admin-cred
password
username

$ kubectl exec secretvolume -- cat /etc/admin-cred/username
admin
$ kubectl exec secretvolume -- cat /etc/admin-cred/password
admin-pass

Conclusion

Nous avons vu comment utiliser des volumes pour apporter des données au pod. Les types de volumes que nous avons vus jusqu’ici sont éphémères, c’est-à-dire que lorsque le pod s’arrête définitivement de tourner, les données sont perdues. Pour certaines applications, comme par exemple des bases de données, nous ne voulons pas perdre les données dès que le pod s’arrête de tourner. Nous avons besoin de la persistance des données. Pour cela, nous allons avoir recours à des volumes persistants, le sujet du prochain article.