The Twelve-Factors Kubernetes

“Kubernetes est le Linux du cloud” cette phrase prononcée par Kelsey Hightower lors de la Kubecon 2017 à Austin montre bien l’émergence de Kubernetes dans les infrastructures cloud modernes. Cette émergence est tirée d’une part par l’ensemble de la communauté de développeurs mais aussi par des géants du web comme : Google, Amazon, Alibaba ou encore Red Hat qui misent beaucoup sur cette technologie, contribuent à son amélioration et à son intégration avec leurs écosystèmes respectifs. EKS pour AWS, GKE pour Google et AKS pour Azure en sont les premiers exemples.

Cet article liste les 12 règles de base ainsi que les bonnes pratiques à avoir en tête lorsque l’on souhaite utiliser Kubernetes au mieux. Cette liste s’adresse à toutes les personnes, développeurs ou bien administrateurs système qui utilisent K8s au quotidien.

I. 1 Pod = 1 ou n conteneurs

La terminologie est importante pour bien aligner tout le monde sur le même langage. Dans le monde Kubernetes, un Pod est la plus petite unité de calcul déployable sur un cluster. Il se compose de 1 ou plusieurs conteneurs. Les conteneurs au sein d’un Pod partagent la même IP, le même stockage et sont colocalisés sur le même noeud du cluster.

Pour aller plus loin : https://kubernetes.io/docs/concepts/workloads/pods/pod/

II. Labels everywhere

La plupart des ressources Kubernetes peuvent être labellisées : les Pods, les Nodes, les Namespaces etc … Cette labellisation se présente sous la forme d’un champ clé valeur injecté dans les métadonnées. Labelliser ses composants a 2 vertues :

  • Un usage technique, en effet de nombreuses ressources inter-dépendantes se basent sur les labels pour s’identifier. Exemple : je labellise une partie de mes noeuds (Nodes) “zone: front” car ces noeuds sont susceptibles d’accueillir des applications de type web. Je donne une affinité à mes Pods de type frontend de sorte à ce qu’ils se placent sur les noeuds portant le label “zone: front”.
  • Un usage organisationnel : poser des labels permet de mieux identifier les ressources et surtout permet de les requêter efficacement.

Exemple : Je souhaite avoir la liste de mes noeuds qui sont en zone front.

$> kubectl get nodes -l zone=front

Pour aller plus loin : https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/

III. Infrastructure as Code et versionnement

L’ensemble des ressources Kubernetes peut être décrit sous forme de fichier YAML ou JSON. La création de ressources à partir d’un fichier se fait via la commande :

$> kubectl apply -f MONFICHIER.{yaml,json}

La commande apply réalise un diff intelligent, en effet : elle ajoute la ressource si celle-ci n’est pas présente, la modifie si le fichier a été changé et ne fait rien si elle existe déjà.

L’utilisation des fichiers permet de tracer, de versionner et de reproduire le système complet à chaque instant. Il est donc d’usage de versionner les fichiers de description des ressources K8s au même titre que du code.

Pour aller plus loin : https://kubernetes.io/docs/concepts/cluster-administration/manage-deployment/#kubectl-apply

IV. Un Service pour exposer

Les Pods ne communiquent jamais entre eux directement, ils passent par un Service car les Pods sont volatiles et éphémères au travers du cluster. Il peut arriver que pour des opérations de maintenance certains Pods migrent d’un noeud à un autre. Il peut également arriver que ces Pods redémarrent, passent à l’échelle (scaling) ou même soient détruits en cas de montée de version notamment. Dans chacun des cas, l’IP du Pod change ainsi que son nom. Le Service est une ressource Kubernetes à part entière se plaçant devant les Pods et permettant d’exposer certains ports des Pods sur le réseau. Les Services ont un nom fixe et une IP propre qui est fixe elle aussi. Ainsi nous pouvons accéder à nos Pods quelles que soient leurs IPs ou noms. Le matching entre les Services et les Pods se fait au travers des labels. Lorsque le Service match plusieurs Pods celui-ci loadbalance le trafic en round-robin.

Pour aller plus loin : https://kubernetes.io/docs/tutorials/kubernetes-basics/expose-intro/

V. ConfigMap et secret pour configurer

Les ConfigMaps et les Secrets sont des ressources Kubernetes permettant de gérer la configuration des Pods. La configuration y est décrite sous forme de paires clé-valeur. Ces configurations sont ensuite injectées dans les Pods soit sous forme de variables d’environnement soit sous forme de fichiers de configuration directement montés dans les conteneurs.

L’utilisation de ces ressources permet de décorréler la description des Pods de toute configuration. Pouvant être décrit en YAML ou JSON, les configurations sont versionables (sauf les Secrets qui contiennent des données sensibles).

Pour aller plus loin : https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/

VI. Limit et Request pour maitriser l’utilisation des ressources

Dans les nombreuses options de configurations des Pods, il est possible de définir les ressources utilisées et utilisables par le Pod (cpu, et mémoire) :

  • requests, cette configuration est applicable au cpu ou à la mémoire, elle définit les ressources minimum dont le Pod a besoin pour fonctionner. Ces valeurs seront utilisées par le scheduler au moment du choix du placement.
  • limits, comme pour la request cette configuration s’applique sur le cpu et la mémoire. Elle définit la quantité maximale de ressources utilisables par le Pod. Définir ces paramètres permet d’éviter qu’un Pod défaillant mette en péril tout le cluster en consommant l’intégralité des ressources. Cela permet également de mettre en place de l’autoscaling. L’autoscaler de Pods – qui est également une ressource Kubernetes – se base sur la limit de cpu et peut scaler automatiquement si par exemple 80% de la limit est atteinte.

Si l’administrateur du cluster Kubernetes a défini des quotas de ressources pour les Namespaces, il devient alors obligatoire de définir les valeurs de requests et de limits, sinon le Pod ne sera pas schedulé. Il se peut également qu’en l’absence de ces valeurs l’administrateur en définisse par défaut dans une ressource K8s appelée LimitRange.

Pour aller plus loin : https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/

VII. Penser au cycle de vie des Pods

Il est possible de déployer un Pod en décrivant directement sa configuration en YAML/JSON et de l’injecter dans K8s avec le client kubectl. Attention, passer par cette méthode nous prive de la résilience des Pods proposée par design par K8s. Si le Pod tombe, il ne sera pas relancé automatiquement. Il est recommandé de passer par un Deployment. Cet objet K8s permet de décrire un Pod avec toute sa configuration mais il cache également la complexité de la résilience. En effet le Deployment va générer un ReplicaSet. Le seul rôle du ReplicaSet est d’assurer que le nombre de Pods présents correspond au nombre de Pods souhaités, il permet également de faire scaler les Pods à la demande. Le Deployment permet de configurer les stratégies de déploiement. Il est par exemple possible de définir une stratégie de rolling-update en cas de montée de version d’un conteneur d’un Pod.

La commande suivante permet de démarrer des Pods (ex: pour un nginx) :

$> kubectl run nginx image=nginx --replicas=2

Cette commande va générer un Deployment avec la configuration d’un Pod exécutant le conteneur nginx, ce même Deployment va également générer le ReplicaSet qui va s’assurer qu’il y a bien 2 Pods qui tournent à chaque instant.

Pour aller plus loin : https://kubernetes.io/docs/concepts/workloads/controllers/deployment/

VIII. LivenessProbe pour surveiller

Nous l’avons vu, le rôle du ReplicaSet est de garantir que le nombre de Pods qui tournent est conforme au nombre de Pods souhaité. Il relance donc les Pods qui tombent en erreur. Il est possible de configurer les Pods pour assurer la résilience mais au niveau fonctionnel cette fois. Pour cela il existe l’option : LivenessProbe. Elle permet de vérifier grâce à une URL (/healthz) ou un script si l’application est fonctionnelle. Cela permet de relancer automatiquement le Pod si la condition n’est pas vérifiée.

De la même façon que l’option LivenessProbe vérifie le statut d’une application, il existe l’option ReadinessProbe qui teste le moment où une application est disponible suite à son démarrage. Cela est utile pour une application qui fait des opérations avant de démarrer (Injection de données).

Pour aller plus loin : https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/

IX. Latest n’est pas une version

K8s est un orchestrateur de conteneurs et le nom du conteneur à déployer est spécifié dans la configuration du Pod. La nomenclature d’un nom d’une image est la suivante :
<Nom de la registry>/<Nom du conteneur>:<tag ou version>


Il est d’usage d’incrémenter la version d’une image au même titre que nous incrémentons les versions du code, mais aussi de poser le tag “latest” sur la dernière image construite.

Configurer un Pod pour déployer une image avec le tag “latest” n’est pas une bonne pratique pour plusieurs raisons :

  • Pas de maîtrise de la version déployée, potentiellement des effets de bords liés aux nouvelles versions des composants de l’image.
  • Latest peut contenir des bugs.
  • Il est possible de configurer les Pods pour choisir une stratégie de “pull” des images. C’est l’option “ImagePullPolicy” cette option peut prendre 3 valeurs:
    • IfNotPresent : Pull l’image seulement si elle n’est pas disponible localement sur le noeud
    • Always : Pull l’image systématiquement
    • Never : Ne pull jamais l’image

IfnotPresent est l’option par défaut, donc si nous utilisons une image avec le tag latest, au premier déploiement Kubernetes ira pull l’image en version latest, puis comme elle est déjà présente sur le noeud il ne re-téléchargera pas l’image en cas de nouveau déploiement, même si une nouvelle image latest à été créée et est présente sur la registry.

Pour aller plus loin : https://kubernetes.io/docs/concepts/configuration/overview/#container-images

X. Les Pods sont stateless

Les Pods sont éphémères et volatiles, ils peuvent être amenés à changer de noeud en cas de maintenance, de nouveau déploiement ou s’ils sont redémarrés. Ils peuvent également – et c’est un des gros intérêts d’un système comme K8s – scaler à la demande. Le flux entrant dans les Pods est alors load-balancé par le Service devant les Pods. C’est la raison pour laquelle les applications hébergées sur K8s doivent utiliser une solution tierce pour réaliser du stockage. Par exemple un site de e-commerce qui persiste des données de session par fichier au sein du conteneur (au hasard un panier d’achat). En cas de scaling ou de redémarrage du Pod les données seront perdues.

Les solutions pour palier ce problème diffèrent suivant les usecases. Un service de stockage clé/valeur (redis, memcache) peut être envisagé pour stocker les données de sessions par exemple. Pour une application hébergeant des fichiers et autres pièces jointes on privilégiera une solution de stockage objet comme AWS S3.

Pour aller plus loin : https://kubernetes.io/docs/tasks/run-application/run-stateless-application-deployment/

XI. Les Volumes sur stockage distribué

Nous l’avons vu, nos applications doivent être stateless. Il arrive cependant de déployer des composants stateful nécessitant une couche de stockage, les bases de données par exemple. Kubernetes permet de monter des volumes au sein des Pods. Il est alors possible de monter un volume issu du block storage AWS, Azure ou Google. Le stockage est alors externe à notre cluster et suit le Pod en cas de redéploiement sur un noeud différent. Il est également possible de monter un volume entre le noeud sur lequel est déployé le Pod et le Pod. Ce mode de montage ne doit à aucun moment être envisagé. Notre Pod avec son volume rattaché sur l’hôte est susceptible de migrer et de perdre ainsi toutes ses données.

Pour aller plus loin : https://kubernetes.io/docs/concepts/storage/volumes/

XII. Mes applications sont 12 factors app

Le code applicatif qui à terme sera déployé sur un cluster Kubernetes se doit de respecter un certain nombre de règles. Les 12 factors apps sont un ensemble de conseils/bonnes pratiques créé par Heroku. Heroku est un fournisseur de plateforme PaaS hébergeant des applications sous forme de conteneurs et ces principes sont un moyen d’opérer au mieux du code à vocation d’être conteneurisé.

Les principales recommandations sont:

  • Versionnement du code (GIT)
  • Exposition d’une URL de healthcheck
  • Application stateless
  • Configuration par variables d’environnement
  • La sortie des logs sur la sortie standard ou la sortie d’erreur
  • Gestion du mode dégradé.
  • Gestion des arrêts/relances de manière propre.

Pour aller plus loin : https://blog.octo.com/applications-node-js-a-12-facteurs-partie-1-une-base-de-code-saine/

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *


Ce formulaire est protégé par Google Recaptcha