Méthodes de stockage persistées dans Kubernetes

Méthodes de stockage persistées dans Kubernetes

By SAUVAGE Pierre

28 oct. 2017

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.

Canada - Morocco - France

International locations

10 rue de la Kasbah
2393 Rabbat
Canada

Nous sommes une équipe passionnées par l'Open Source, le Big Data et les technologies associées telles que le Cloud, le Data Engineering, la Data Sciencem le DevOps…

Nous fournissons à nos clients un savoir faire reconnu sur la manière d'utiliser les technologies pour convertir leurs cas d'usage en projets exploités en production, sur la façon de réduire les coûts et d'accélérer les livraisons de nouvelles fonctionnalités.

Si vous appréciez la qualité de nos publications, nous vous invitons à nous contacter en vue de coopérer ensemble.