Méthodes de stockage persistées dans Kubernetes
28 oct. 2017
Ne ratez pas nos articles sur l'open source, le big data et les systèmes distribués, fréquence faible d’un email tous les deux mois.
Cet article est basé sur la présentation “Introduction to Kubernetes Storage Primitives for Stateful Workloads” par the {Code} team à la conférence OSS 2017 à Prague. Commençons par qu’est-ce que Kubernetes ?
Kubernetes
Kubernetes vient du mot grec “Helmsman” qui est également la racine du mot “Gouverneur”
Kubernetes …
- Est un orchestrateur de conteneurs
- Supporte plusieurs environnements de conteneur (dont runC de Docker)
- Supporte des clusters bare-metal et dans le cloud
- Est inspiré et profite de l’expérience de Google
- Est Open Source et écrit en Go
Kubernetes gère des applications, pas des machines !
Separation of Concerns
On peut séparer un système d’information en 4 couches :
- Applicatif
- Cluster (Kubernetes se place ici)
- Kernel/OS
- Hardware
Idéalement, chacune des couches devrait être remplaçable de manière transparente. Kubernetes applique cette philosophie en se basant principalement sur des APIs.
Les objectifs de Kubernetes
- Des APIs ouvertes et implémentées
- Modulaire / remplaçable
- Ne force pas les applications à connaître les concepts
- Spécifiques à un cloud provider
- Spécifiques à Kubernetes
- Permet aux utilisateurs de
- Écrire un code exécutable partout
- Éviter les verrouillages fournisseur (vendor lock-in)
- Éviter un couplage lourd de l’applicatif avec l’infrastructure
Étudions maintenant le concept de “pod” dans Kubernetes. C’est l’équivalent d’un “node” dans Docker Swarm.
Les Pods
Un pod est un élément atomique à déployer dans Kubernetes. Il est composé dans un ensemble de conteneurs et volumes interconnectés.
Les principales propriétés d’un pod sont :
- Un namespace partagé
- les conteneurs partagent une adresse IP et un localhost
- ils partagent des communications interprocess (IPC), …
- Un cycle de vie
- un pod est lié à un noeud, s’il doit être relancé c’est sur le même noeud
- un pod peut mourir et ne peut être relancé avec le même ID
Par exemple :
# pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: filepuller
image: saadali/filepuller:v2
- name: webserver
image: saadali/webserver:v3
Les modifications de fichier dans un conteneur sont liées à l’instance du conteneur uniquement, ainsi si un container crash ou est supprimé on a une perte de données. C’est particulièrement problématique pour des applications avec un stockage persistant (stateful apps) ou si des conteneurs ont des fichiers partagés.
L’abstraction de volumes dans Kubernetes permet de résoudre ces deux problèmes.
Les Volumes Kubernetes
Un volume Kubernetes n’est pas la même chose qu’un volume Docker. Dans Docker, un volume est simplement un dossier sur disque ou dans un autre conteneur. Jusque récemment, les cycles de vie n’étaient pas gérés et il s’agissait uniquement de volumes persistés sur disque local. A l’inverse, un volume Kubernetes a un cycle de vie défini.
Un volume Kubernetes est :
- Un dossier, pouvant contenir des données
- Accessible par tous les conteneurs d’un pod
Les plugins de volume définissent :
- Comment un dossier est mis en place
- Le média qui le stocke
- Le contenu du dossier
La durée de vie d’un volume est celle d’un pod ou plus long.
Le plus important est que Kubernetes supporte énormément de types de volumes.
Les plugins de volume Kubernetes
Kubernetes propose beaucoup de plugins de volume :
- Stockage distant :
- Disque persistant GCE
- AWS
- Azure (FileSystem et disque de données)
- Dell EMC ScaleIO
- iSCSI
- Flocker
- NFS
- vSphere
- GlusterFS
- Ceph File et RBD
- Cinder
- Quobyte Volume
- FibreChannel
- VMware Photon PD
- Sockage éphémère :
- Dossier vide (tmpfs)
- API Kubernetes exposées :
- Secret
- ConfigMap
- DownwardAPI
- Stockage local (Alpha)
- Conteneurs exposant un stockage basé sur du software
- “Out-of-Tree” (ne faisant pas partie du tronc Kubernetes)
- Flex (exécution d’un binaire, permet d’utiliser des driver externes)
- CSI (Cloud Storage Interface, spécification d’API générique définissant un accès de stockage pour des conteneurs, disponinbles dans une release future)
- Autre :
- Path sur la machine hôte
Kubernetes étant ouvert, des stockages de tierce partie sont disponibles avec les plugins “out-of-tree”.
Afin d’assurer l’inter-opérabilité des différents orchestrateurs, CloudFoundry, Mesos et Kubernetes travaillent à la standardisation des APIs “out-of-tree” pour le stockage des données d’un containeur avec le CSI.
Exemple GCE PD
Un volume peut être référencé directement, par exemple :
# pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: sleepypod
spec:
volumes:
- name: data
- gcePersistentDisk:
pdName: panda-disk
fsType: ext4
containers:
- name: sleepycontainer
image: ...
command:
- sleep
- "6000"
volumeMounts:
- name: data
mountPath: /data
readonly: false
Cependant, référencer directement un volume est “comme se tatouer le prénom de sa copine sur le bras à 16 ans”, ça peut paraître une bonne idée au début car vous pensez que cela durera toujours, mais ce n’est généralement pas le cas !
Persistent volume claim (PVC)
Le principe est de séparer la déclaration d’un volume persistant de son pod.
On déclare d’abord les volumes persistants avec un processus spécifique, puis on rattache le pod à un volume disponible avec le persistent volume claim.
Exemple PV
Créons un volume persistant pv1 avec 10GB et un pv2 avec 100GB, voici la définition de pv2 :
# pv2.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name : mypv2
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 100Gi
persistentVolumeReclaimPolicy: Retain
gcePersistentDisk:
fsType: ext4
pdName: panda-disk2
Et voici comment on le créé :
$ kubectl create -f pv1.yaml
persistentvolume "pv1" created
$ kubectl create -f pv2.yaml
persistentvolume "pv2" created
$ kubectl get pv
NAME CAPACITY ACCESSMODES STATUS CLAIM REASON AGE
pv1 10Gi RWO Available 1m
pv2 100Gi RWO Available 1m
Exemple PVC
Maintenant que nous avons un volume persistant, nous pouvons le rattacher à un container avec PVC :
# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mypvc
namespace: testns
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Gi
Lorsqu’un PVC est créé, Kubernetes va rattacher le pod à un volume persistant disponible :
$ kubectl create -f pvc.yaml
persistentvolumeclaim "mypvc" created
$ kubectl get pv
NAME CAPACITY ACCESSMODES STATUS CLAIM REASON AGE
pv1 10Gi RWO Available 3m
pv2 100Gi RWO Bound testns/mypvc 3m
Vous pouvez configurer un PVC directement dans la déclaration d’un pod :
# pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: sleepypod
spec:
volumes:
- name: data
persistentVolumeClaim:
claimName: mypvc
containers:
- name: sleepycontainer
image: gcr.io/google_containers/busybox
command:
- sleep
- "6000"
volumeMounts:
- name: data
mountPath: /data
readOnly: false
Dynamic Provisioning et StorageClasses
- Permet de créer un stockage à la demande (lorsque le user en a besoin)
- Élimine le besoin de pré-création de stockage par un administrateur
- Les administrateurs de cluster / stockage autorisent le “dynamic provisioning” en créant des “StorageClass”
- Un StorageClass défini les paramètres à utiliser à la création du volume
- Les paramètres StorageClass sont découplés de Kubernetes, ainsi les fournisseurs de stockage peuvent exposer autant de paramètres paramètres personnalisés que nécessaire
Voici comment déclarer un StorageClass :
# sc.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: slow
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-standard
--
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: fast
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-ssd
- L’utilisateur consomme le stockage de la même manière qu’avec un PVC
- La sélection d’un StorageClass dans un PVC déclenche la création dynamique du volume
Voici comment créer un PVC avec un StorageClass :
# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mypvc
namespace: testns
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Gi
storageClassName: fast
$ kubectl create -f storage_class.yaml
storageclass "fast" created
$ kubectl create -f pvc.yaml
persistentvolumeclaim "mypvc" created
$ kubectl get pvc --all-namespaces
NAMESPACE NAME STATUS VOLUME CAPACITY ACCESSMODES AGE
testns mypvc Bound pvc-331d7407-fe18-11e6-b7cd-42010a8000cd 100Gi RWO 6s
$ kubectl get pv pvc-331d7407-fe18-11e6-b7cd-42010a8000cd
NAME CAPACITY ACCESSMODES RECLAIMPOLICY STATUS CLAIM REASON AGE
pvc-331d7407-fe18-11e6-b7cd-42010a8000cd 100Gi RWO Delete Bound testns/mypvc 13m
L’utilisateur peut ensuite référencer le volume avec un PVC :
# pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: sleepypod
spec:
volumes:
- name: data
persistentVolumeClaim:
claimName: mypvc
containers:
- name: sleepycontainer
image: gcr.io/google_containers/busybox
command:
- sleep
- "6000"
volumeMounts:
- name: data
mountPath: /data
readOnly: false
Les StorageClass par défaut
Les StorageClass par défault permettent la création dynamique d’un volume même si le StorageClass n’est pas spécifié dans le PVC.
Les StorageClasses pré-installées par défaut sont :
- Amazon AWS - EBS volume
- Google Cloud (GCE/GKE) - GCE PD
- Openstack - Cinder Volume
La fonctionnalité de StorageClass par défaut a été introduite en alpha dans Kubernetes 1.2 (GA depuis la 1.6)
Quel futur pour le stockage Kubernetes ?
Le stockage Kubernetes tend vers :
- Container Storage Interface (CSI)
- Standardisation de plugins de volume block et fichier “Out-of-Tree”
- Stockage local
- Rendre le stockage local d’un noeud disponible en tant que volume persistant
- Isolation des capacités
- Mettre en place des limites afin qu’un unique pod ne puisse consommer toutes les ressources de stockage d’un noeud via overlay FS, des logs, etc.
Impressions
Kubernetes fournit, à travers des APIs et des drivers / plugis, une méthode propre, standardisée et agnostique de déclarer et utiliser un volume dans un conteneur Kubernetes. Avec cette fonctionnalité, vous pouvez facilement et proprement migrer votre FS backend. L’effort de convergence et standardisation de ces plugins avec la CSI poussé par CloudFoundry, Kubernetes est un bon point cependant il y a encore peu d’informations disponibles.