Comment mettre en place une gestion de la persistance des données dans Kubernetes ? Quelles solutions sont à ma disposition ?
Dans Kubernetes, un volume peut être considéré comme un répertoire accessible aux conteneurs d'un pod. Il existe différents types de volumes dans Kubernetes, ce type va définir la façon dont le volume va être créé ainsi que son contenu.
Kubernetes prend en charge différents types de stockage dans lesquels le pod peut utiliser plusieurs d'entre eux en même temps.
Voici une liste - non exhaustive - des principaux types de stockage :
- hostPath : Ce type de volume monte un fichier ou un répertoire du système de fichiers du nœud hôte dans votre pod.
- emptyDir : Il s'agit d'un type de volume créé lors de la première affectation d'un pod à un nœud. Il reste actif tant que le pod s'exécute sur ce nœud. Le volume est initialement vide et les conteneurs du pod peuvent lire et écrire les fichiers dans le volume emptyDir. Une fois le pod retiré du nœud, les données du répertoire vide sont effacées.
- secret : Un volume secret est utilisé pour transmettre des informations sensibles, telles que des mots de passe, à des pods.
- ConfigMap : La ressource ConfigMap fournit un moyen d’injecter des données de configuration dans les pods.
- persistentVolumeClaim : Un volume persistentVolumeClaim est utilisé pour monter un PersistentVolume dans un pod. Les volumes persistants sont un moyen pour les utilisateurs de «revendiquer» un stockage durable (tel qu'un disque persistant GCE ou un volume iSCSI) sans connaître les détails de l'environnement cloud en particulier.
Vous pouvez trouver une liste plus exhaustive des types de volumes disponibles sur Kubernetes à l'adresse suivante : https://kubernetes.io/fr/docs/concepts/storage/volumes/#types-de-volumes
Utiliser des volumes avec les déploiements
Il est possible de déclarer vos volumes directement depuis votre deployment. Ce type de déclaration est simple à mettre en oeuvre. Si vous suivez régulièrement le blog, j'ai déjà utilisé ce type de volume avec Traefik :
---
kind: Deployment
apiVersion: apps/v1
metadata:
namespace: default
name: traefik
labels:
app: traefik
spec:
replicas: 1
selector:
matchLabels:
app: traefik
template:
metadata:
labels:
app: traefik
spec:
serviceAccountName: traefik-ingress-controller
containers:
- name: traefik
image: traefik:v2.2
volumeMounts:
- name: letsencrypt
mountPath: "/letsencrypt/"
volumes:
- name: letsencrypt
hostPath:
path: "/home/debian/traefik/letsencrypt"
Dans ce fichier, vous pouvez constater 2 choses :
- On déclare le point de montage du volume dans le conteneur avec volumeMounts
- Ensuite on définit le volume lui même avec les instructions volumes.
Vous pouvez également voir que j'utilise le type de volume hostPath. Le dossier précisé à la suite de l'argument sera donc partagé entre mon noeud hôte et le conteneur.
On comprend rapidement qu'en cas d'installation type Cluster, cette façon de procéder va avoir des limites. Il faudrait répliquer le dossier sur tout mes noeuds.
Notons qu'il est également possible de passer un argument type
pour un volume hostPath
. Voici les valeurs supportées par le champ :
- Une chaîne de caractères vide (par défaut) sert à la rétrocompatibilité, ce qui signifie qu’aucune vérification ne sera effectuée avant de monter le volume hostPath.
DirectoryOrCreate
: Si rien n’existe au chemin fourni, un dossier vide y sera créé au besoin avec les permissions définies à 0755, avec le même groupe et la même possession que Kubelet.Directory
: Un dossier doit exister au chemin fourniFileOrCreate
: Si rien n’existe au chemin fourni, un fichier vide y sera créé au besoin avec les permissions définies à 0644, avec le même groupe et la même possession que Kubelet.File
: Un fichier doit exister au chemin fourniSocket
: Un socket UNIX doit exister au chemin fourniCharDevice
: Un périphérique en mode caractère doit exister au chemin fourniBlockDevice
: Un périphérique en mode bloc doit exister au chemin fourni
Dans le cadre des exemples avec Traefik, nous avions également pu voir un autre type de volume : ConfigMap.
Par exemple pour le fichier traefik.yml :
apiVersion: v1
kind: ConfigMap
metadata:
name: traefik-config-static
namespace: default
labels:
app: traefik
data:
traefik.yml: |
entryPoints:
web:
address: :80
On déclare ici une ressource ConfigMap pour le fichier de configuration de Traefik. Cette utilisation permet de rendre le fichier disponible sur l'intégralité de mon cluster. Et non uniquement présent dans un fichier plat d'un nœud en particulier. J'utilise ensuite cette ressource lors du déploiement :
kind: Deployment
apiVersion: apps/v1
metadata:
namespace: default
name: traefik
labels:
app: traefik
spec:
replicas: 1
selector:
matchLabels:
app: traefik
template:
metadata:
labels:
app: traefik
spec:
serviceAccountName: traefik-ingress-controller
containers:
- name: traefik
image: traefik:v2.2
volumeMounts:
- name: traefik-config-static
mountPath: /etc/traefik/
volumes:
- name: traefik-config-static
configMap:
name: traefik-config-static
La déclaration dans ce fichier ressemble fortement à celle que nous avons rencontré précédemment pour le hostPath
.
Enfin il vous sera peut-être nécessaire de partager vos volumes sur plusieurs noeuds et un nombre conséquent de pods. Il devient alors complexe de tout maintenir dans des fichiers de déploiement. Afin de répondre à ce problème, vous pouvez utiliser les PersistentVolume
et PersistentVolumeClaim
.
Persistant Volume et Persistant Volume Claim
Tout d'abord expliquons ces deux notions :
Un PersistentVolume
(PV) est un élément de stockage dans le cluster qui a été provisionné par un administrateur ou provisionné dynamiquement. Il s’agit d’une ressource dans le cluster, tout comme un nœud est une ressource de cluster. Ils ont un cycle de vie indépendant de tout pod.
Un PersistentVolumeClaim
(PVC) est une demande de stockage réalisée par un utilisateur ( Claim ) car comme à l'image d'un pod qui consomme des ressources de noeud, il peut également demander des niveaux spécifiques de ressources ( CPU/mémoire ). Le PVC consomme des ressources PV et peut exiger une taille particulière, des modes d'accès spécifiques ( Lecture seule, lecture/ecriture, etc ).
Cette solution proposée par Kubernetes va nous permettre de gérer de façon plus centralisée nos volumes.
Un exemple va vous permettre de comprendre rapidement ces deux notions !
Créons tout d'abord un volume pour gérer nos access logs de Traefik :
kind: PersistentVolume
apiVersion: v1
metadata:
name: access-log-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
hostPath:
path: /var/log/traefik
type: DirectoryOrCreate
Déclarer un PV nous permet d'utiliser les ressources comme hostPath. Bien évidemment au sein d'un vrai cluster, l'utilisation d'une ressource réseau sera plus appropriée ! J'ai également précisé ici que ce volume pouvait être accessible en lecture/écriture mais uniquement pour un seul nœud.
Vous allez pouvoir créer le volume avec la commande suivante :
kubectl apply -f access-log-pv.yaml
Vous pouvez vérifier son existence avec la commande suivante :
kubectl get pv
Résultat :
Maintenant cette ressource est disponible pour tous les utilisateurs de votre cluster. Et bien sûr vous-même ! 😂
Réclamons donc ce volume ! Pour cela créons une ressource PVC avec un fichier comme suit :
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: access-log-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 500Mi
Appliquons ce template à notre cluster :
kubectl apply -f access-log-pvc.yaml
Vérifions que tout s'est bien passé :
kubectl get pvc
Résultat :
Nous allons pouvoir utiliser cette ressource dans notre deployment :
kind: Deployment
apiVersion: apps/v1
metadata:
namespace: default
name: traefik
labels:
app: traefik
spec:
replicas: 1
selector:
matchLabels:
app: traefik
template:
metadata:
labels:
app: traefik
spec:
serviceAccountName: traefik-ingress-controller
containers:
- name: traefik
image: traefik:v2.2
volumeMounts:
- name: access-log-pvc
mountPath: "/letsencrypt/"
volumes:
- name: access-log-pvc
persistentVolumeClaim:
claimName: access-log-pvc
Nous venons de remplacer notre déploiement de Traefik avec l'utilisation d'un Persistent Volume Claim
pour stocker nos fichiers access logs.
Nous avons pu voir ensemble qu'il était essentiel de sauvegarder vos données persistantes afin qu'elles soient toujours disponibles après l'arrêt de votre conteneur.
Afin de répondre à ce besoin avec Kubernetes, nous avons vu comment déclarer vos volumes aussi bien au sein même du fichier de déploiement de votre conteneur. Mais également avec l'utilisation des PersistentVolume
et PersistentVolumeClaim
.
Dans le cadre d'un serveur standalone
vous pouvez utiliser la déclaration directement dans votre fichier de déploiement. Mais ce type d'utilisation possède quelques limitations, notamment si votre but est de réaliser une installation en mode cluster.
Effectivement dans le cadre de ce type d'installation, il sera bien plus intéressant de passer par une déclaration de volume et un claim. Votre volume devra également être disponible pour chacun de vos noeuds, il faudra surement utiliser une ressource réseau pour ça.
Et-vous, déclarez-vous vos volumes dans vos fichiers de déploiement ou au travers d'un PV/PVC ? Utilisez-vous des volumes réseaux ou votre infrastructure ne le nécessite pas ?
En tout cas n'hésitez pas à m'apporter des remarques ou des commentaires sur Twitter !