Avec l’explosion de l’utilisation des conteneurs logiciels, principalement popularisés par Docker, arrivent de nouvelles problématiques. Docker Compose répond en partie à certaines de ces problématiques, en regroupant un ensemble de conteneurs sur un même host. Mais comment automatiser et industrialiser le déploiement, la montée en charge et la gestion des applications fonctionnant dans des conteneurs ? C’est à cette question que Kubernetes se propose de répondre.

Kubernetes ?

Kubernetes, souvent abrégé k8s, est un orchestrateur de conteneurs conçu par Google et sorti en 2014. C’est un outil Open Source (code source disponible sur github), écrit en Go, qui trouve ses racines dans Borg le système interne de Google qui gère l’infrastructure du géant de Mountain View.

Ce n’est pas le seul orchestrateur de conteneurs sur le marché, il en existe d’autres comme Docker Swarm, Apache Marathon, Nomad ou Kontena. Cependant, à l’heure où j’écris cet article, il apparaît comme celui qui domine le marché, en témoigne l’intégration native de Kubernetes à la suite logicielle Docker et sa présence chez les fournisseurs de cloud, notamment les 3 géants : Amazon, Google et Microsoft.

Le mot Kubernetes vient du grec ancien et signifie “timonier”, soit celui qui tient la barre d’un bateau. Si l’on reprend l’analogie de Docker avec les conteneurs maritimes, Kubernetes est le capitaine qui dirige le porte-conteneurs.

un porte conteneur représentant Kubernetes

Kubernetes définit un certain nombre d’abstractions qui représentent l’état du système que l’on souhaite mettre en place. Ces abstractions s’appellent des objets, elles sont de deux types :

  • Objets simples : conteneurs, interfaces réseaux, espaces de stockage ou autres informations utiles pour le système visé.
  • Contrôleurs : abstractions de plus haut niveau qui sont basées sur les objets simples et leur ajoutent des fonctionnalités.

Tâchons d’y voir plus clair.

Un peu de terminologie

Avant de voir un peu plus en détails les concepts qu’introduit Kubernetes, il nous faut connaître deux termes : cluster et node. Nous ne rentrerons pas ici dans les détails de l’architecture de Kubernetes mais ces deux notions sont indispensables à la compréhension de la philosophie derrière k8s.

Un node représente un “host”, soit une machine virtuelle ou physique connue. Cette abstraction fait de chaque machine un ensemble de ressources (CPU, RAM…) utilisable par Kubernetes. C’est sur un ou plusieurs nodes que vont s’exécuter les ressources que nous allons détailler par la suite.

Un cluster est quant à lui une agrégation de nodes, pour former une machine plus puissante. Kubernetes va gérer pour nous, de manière transparente, l’ajout et la suppression de nodes au sein du cluster.

Ainsi Kubernetes nous abstrait la gestion des machines en faisant des nodes substituables entre eux. La philosophie derrière ces concepts est qu’il n’est souvent pas nécessaire de savoir où s’exécute une application, l’important étant qu’elle s’exécute, peu importe où.

Définition des objets

Les quatres principaux objets utilisés par Kubernetes sont les pods, les services, les volumes et les namespaces.

Le Pod, pour exécuter les conteneurs

Un pod représente la plus petite unité déployable et exécutable au sein du cluster. C’est la brique de base de Kubernetes qui encapsule un ou plusieurs conteneurs. Un pod a son propre réseau interne.

Le cas le plus commun est un pod qui exécute un seul conteneur. Kubernetes va donc gérer le pod plutôt que le conteneur directement.

Le cas le plus rare et qui représente un usage plus avancé est celui où un pod encapsule plusieurs conteneurs qui sont fortement liés et partagent des ressources communes (comme un espace de stockage par exemple).

Un pod a donc pour objectif d’exécuter une instance d’une application. Si l’on veut scaler horizontalement, il suffit d’avoir plusieurs instances de l’application, chacune sur un pod différent.

Dans la réalité, on ne travaille que rarement directement sur les pods mais on va utiliser des contrôleurs à qui on va confier la gestion de nos pods.

Le Service, pour regrouper les Pods

Un service est un ensemble logique de pods ouverts au reste du cluster Kubernetes.

Imaginons que mon application est composée d’un back-end et d’un front-end qui communiquent en HTTP. Le back-end ainsi que le front-end sont représentés au sein du cluster par trois pods chacun, avec leurs propres réseaux et leurs cycles de vie (un pod peut mourir). Comment faire en sorte que mes pods front-end aient connaissance des pods back-end disponibles ?

Le service vient répondre à cette problématique en regroupant logiquement des pods ensemble et en leur fournissant une interface vers le reste du cluster, au moyen d’une adresse IP, d’un port et d’un protocole de communication (par exemple HTTP dans le cas de notre application). C’est le service qui va se charger de router les requêtes vers les pods qu’il gère.

Contrairement aux pods, un service est persistant et représente le service exposé au reste du cluster. Il permet d’être découvert via le mécanisme de service discovery fourni par Kubernetes et peut aller plus loin en fournissant du load balancing par exemple.

Le Volume, pour stocker les données

Un pod étant stateless, il faut un moyen de stocker de manière persistante de la donnée. Un volume est fait pour ça. C’est le même concept qu’un volume Docker en un peu plus poussé, mais pour le moment partons du fait que c’est une sorte de répertoire accessible à un conteneur au sein d’un pod.

Le Namespace, pour séparer les clusters

Un namespace est un moyen de séparer logiquement un cluster physique en plusieurs clusters virtuels. On peut ainsi faire un découpage par projet et procéder à une gestion des droits plus fine. Des utilisateurs Kubernetes ne peuvent avoir accès qu’à un seul namespace, avec des quotas définis en terme de nombre de pods, de CPU, de mémoire… Un namespace par défaut existe et est utilisé quand on ne renseigne pas le namespace utilisé, il s’appelle “default”.

Contrôleurs

Un contrôleur est donc une abstraction de plus haut niveau qu’un objet simple et est basé sur ces derniers. Un contrôleur va venir piloter des objets de plus bas niveau. Les cinq principaux contrôleurs sont le RepicaSet, le Deployment, le StatefulSet, le DaemonSet et le Job.

ReplicaSet

Un ReplicaSet pilote des pods et s’assure qu’un certain nombre de pods sont lancés. Si un pod vient à mourir, le ReplicaSet va lancer un nouveau pod. Dans la réalité, on ne se sert que rarement directement des ReplicaSet, au profit d’un autre contrôleur, qui vient piloter un ReplicaSet : le Deployment.

Deployment

Un Deployment contrôle et pilote des ReplicaSets et des pods. Il ajoute des fonctionnalités aux ReplicaSet comme :

  • Déployer (rollout) des ReplicaSet : les ReplicaSets vont créer des pods en tâche de fond et le Deployment va s’assurer que le déploiement (rollout) se passe bien.
  • Déclarer un nouvel état des pods : le Deployment va piloter la mise à jour des pods, des anciens ReplicaSet vers les nouveaux. Ce déploiement va créer une nouvelle révision du Deployment.
  • Revenir à une ancienne version du Deployment : si le déploiement engendre des instabilités, le Deployment nous permet de revenir à une précédente révision.
  • Mettre à l’échelle le Deployment pour gérer les montées en charge par exemple.
  • Mettre en pause un déploiement (rollout) : pour fixer des éventuelles erreurs et reprendre le rollout.
  • Nettoyer les anciens ReplicaSets qui ne servent plus.
  • Et d’autres fonctionnalités pour des cas d’utilisation plus avancés

kubernetes

StatefulSet

Contrairement aux contrôleurs précédents qui sont stateless, le StatefulSet permet de piloter des pods et d’en conserver l’état, comme son nom l’indique il est stateful. Au sein d’un StatefulSet, chaque pod est unique et a sa propre identité. Ce type de contrôleur est utilisé pour avoir un stockage persistant, que ce soit au moyen d’une base de données ou du système de fichier.

DaemonSet

Un DeamonSet est un contrôleur qui va s’assurer qu’un seul et unique pod s’exécute sur un node. C’est utile pour faire du monitoring serveur ou collecter des logs par exemple. Ainsi quand un node est ajouté au cluster, le DaemonSet va lancer lui même le pod qu’il définit. À l’inverse quand le DaemonSet est supprimé, il supprime avec lui le pod qu’il gère.

Job

Un Job est un contrôleur qui va gérer un ou plusieurs pods qui ont une fin déterminée. Le Job va suivre l’exécution des pods afin de vérifier qu’ils se sont achevés avec succès et ainsi pouvoir supprimer les pods. C’est utilisé pour lancer des scripts. On retrouve la même notion, mais planifié à l’avance, avec le CronJob.

Et concrètement ça donne quoi ?

Tous ces objets et contrôleurs que nous venons d’introduire sont souvent décrits par des fichiers yaml, qui représentent l’état des pods dans lequel on veut arriver. Pour discuter avec un cluster Kubernetes, il nous faut un client afin de lui envoyer des commandes et lui demander de piloter les ressources dans l’état désiré.

Cette partie pratique sera couverte dans un prochain billet de blog.