Quantcast
Channel: Publicis Sapient Engineering – Engineering Done Right
Viewing all articles
Browse latest Browse all 1865

Autour des conteneurs : Qu’est-ce qu’un conteneur ?

$
0
0
Autour des conteneurs  : Qu’est-ce qu’un conteneur ?

Pour débuter la thématique Autour des conteneurs, il nous paraît évident de commencer par la question suivante :

Qu’est-ce qu’un conteneur ?

Dans sa définition la plus simple on pourrait dire qu’un conteneur est un processus. C’est une définition certes un peu simpliste mais tout de même vraie.

Nous pouvons même le vérifier rapidement en exécutant les commandes suivantes

docker run -d alpine sleep 60
ps aux | grep sleep
# root     23456  1.7  0.0   1564     4 ?        Ss   12:58   0:00 sleep 60

Pour être plus précis, il s’agit également d’une instance d’une image de conteneur. L’image d’un conteneur va décrire un état statique de départ. Le conteneur, une fois démarré, va partir de cet état et « vivre ».

Enfin, un conteneur est souvent associé à la notion de portabilité.

Pour voir plus en détail les mécanismes de fonctionnement d’un conteneur, partons de ce schéma

Que nous apprend ce schéma ?

Un conteneur est composé d’un binaire et de ses dépendances et va s’exécuter de façon isolée des autres applications du système.

Il est lié à un environnement d’exécution de conteneurs (container runtime), lui même lié au système d’exploitation de l’hôte.

Que ne nous apprend pas ce schéma ?

Ce schéma masque toute la mécanique mise en œuvre pour assurer le bon fonctionnement des conteneurs.

En tant que développeur, peut-être avez-vous déjà vécu ce moment pénible lorsqu’une application se dit que ça pourrait être cool de s’approprier une quantité de mémoire dépassant la capacité du système (et donc fatalement d’entraîner un peu de bazar) ou de consommer l’ensemble des ressources CPU ?

Et bien en fait, Linux dispose déjà du nécessaire pour éviter (au maximum) ces situations. Et dans le cas des conteneurs, ce nécessaire est utilisé, ce que nous allons maintenant voir.

Une histoire de conteneur

Avant d’entrer dans ces détails, voyons comment se déroule l’allocation de ressources d’un conteneur au sein de votre système

Cette histoire nous apprend que le noyau de notre système d’exploitation (kernel) ne va mettre à disposition de notre conteneur qu’un sous-ensemble restreint de ressources. De plus, au sein de ce sous-ensemble, il sera impossible pour le processus conteneur de voir le reste du système (tout du moins tant que l’on ne l’y autorise pas explicitement, ce que nous verrons dans un futur article).

Cela est mis en place par deux mécanismes qui sont :

  • les espaces de noms (namespaces)
  • les groupes de contrôles (Control Groups ou CGroups)

Les espaces de noms (namespaces)

Un espace de noms est une fonction du système d’exploitation qui permet le partage de ressources globales comme le réseau ou le disque. Les ressources associées à un espace de nom ne seront visibles que par les processus qui ont accès à cet espace de nom.

Les espaces de noms sont au nombre de 7 au sein d’un système Linux et pour les connaître, rien de mieux que la commande : man unshare

L’espace de nom des points de montage (mount namespace)

Mounting and unmounting filesystems will not affect the rest of the filesystem, except for filesystems which are explicitly marked as shared

Ici, cela va permettre à notre conteneur de pouvoir monter et démonter des systèmes de fichiers, sans que cela n’affecte le reste du système. Dans notre cas, un conteneur est également sujet à la notion de chroot, ou changement de racine, qui permet de lui faire croire que le point de montage qu’on lui donne est la racine du système.

L’espace de nom réseau (network namespace)

The process will have independent IPv4 and IPv6 stacks, IP routing tables, firewall rules.

Cela permet à chaque conteneur de disposer librement de sa stack réseau. Ainsi, plusieurs conteneurs peuvent écouter sur le même port sur un même système hôte (ce qui est totalement impossible dans un environnement non conteneurisé).

L’espace de nom des identifiants de processus (PID namespace)

Children will have a distinct set of PID-to-process mappings from their parent

Le processus exécuté au sein du conteneur aura donc le PID 1 (qui est le premier processus s’exécutant sur une machine) et chacun de ses enfants aura un ID totalement décorrélé de ceux du système hôte.

L’espace de nom Utilisateur (user namespace)

The process will have a distinct set of UIDs, GIDs and capabilities

Ici encore, l’ensemble des droits à l’intérieur du conteneur seront décorrélés de ceux du système hôte.
Ce point peut d’ailleurs donner lieu à quelques points de complexité, sur lesquels nous reviendrons dans un futur article.

L’espace de nom UTS (Unix Timesharing System)

Setting hostname and domainname will not affect the rest of the system.

Notre processus/conteneur pourra donc changer le nom du système à sa guise.

L’espace de nom des communications inter processus (InterProcess Communication ou IPC namespace)

The process will have an independent namespace for POSIX message queues as well as System V messages queues, semaphore sets and shared memory segments.

Cet espace de noms permet d’isoler la communication au sein des groupes de processus. Ainsi, plusieurs groupes qui appartiennent à des espaces de noms différents pourront se servir de moyens de communications ayant le même nom, sans pour autant entrer en conflit ni avoir d’interférences.

L’espace de nom des groupes de contrôle (Control Groups ou CGroups namespace)

The process will have a virtualized view of /proc/self/cgroup, and new cgroup mounts will be rooted at the namespace cgroup root.

Ici, il s’agit de fournir au processus une vue virtuelle de la notion des groupes de contrôles. Mais qu’est-ce qu’un groupe de contrôle ?

Et bien, c’est tout simplement le deuxième mécanisme qui va nous intéresser. Pour le moment, nous avons vu que le noyau de notre système dispose de moyens pour isoler les différents groupes de processus. En revanche, nous n’avons rien vu qui permette de limiter la consommation des ressources physiques.

S’il y avait uniquement les espaces de noms, un seul processus pourrait utiliser l’ensemble du disque ou encore monopoliser la mémoire vive. C’est là qu’interviennent les …

Groupes de contrôles (Control groups ou CGroups)

Les groupes de contrôle constituent une autre fonctionnalité du noyau qui isole et gère les ressources du système.

Ces groupes ont plusieurs fonctions :

La priorisation

Les accès Entrées/Sorties (I/O), CPU sont partagés par tous les processus du système.
Ce partage n’est pas égalitaire ; ainsi un groupe peut recevoir une plus grande part de ces ressources. Il aura donc une priorité plus élevée.

La limitation de ressources

Un groupe de processus peut se voir affecter une limite d’utilisation des ressources à ne pas dépasser (cache disque, CPU ou encore mémoire). Si limite il y a, les groupes de contrôles assurent également…

La comptabilité

Il s’agit de mesurer pour chaque groupe de processus les quantités de ressources consommées.
Ainsi, si cette mesure vient à s’avérer être supérieure au quota alloué, le groupe de contrôle peut être amené à utiliser…

Le contrôle (ça alors …)

Il s’agit de la possibilité de suspendre, arrêter ou redémarrer un processus. Si vous remontez un peu plus sur la BD, vous verrez que les deux amis du kernel dans la dernière bulle s’appellent CPU Throttler et OOM Killer. Il s’agit de deux fonctionnements internes du noyau Linux qui consistent à ne plus donner d’accès au CPU à un process (qui aura donc l’impression de ralentir) ou encore même de le tuer s’il consomme trop de mémoire.

Voilà, on sait maintenant ce que sont les conteneurs et comment ils sont mis en œuvre.

En revanche, il reste un point à préciser et qui ne vous a peut-être pas échappé à la lecture du premier schéma : c’est …

L’environnement d’exécution des conteneurs (container runtime)

Bien que l’exécution d’un conteneur repose sur des mécanismes d’isolation, il n’est pas nécessaire de se préoccuper soi-même de leur gestion pour exécuter un conteneur. Et heureusement, car s’il fallait prendre en charge les appels système pour créer une interface réseau, créer un point de montage à chaque fois que l’on souhaite exécuter quelque chose, cela serait de suite beaucoup plus compliqué.

Les différentes initialisations seront prises en charge et abstraites par l’environnement d’exécution des conteneurs (container runtime), qui devra être installé sur la machine hôte.

Disposer d’un environnement d’exécution de conteneurs est donc le seul prérequis à l’exécution de conteneurs.

Et c’est donc lui qui va assurer la portabilité de notre conteneur, en s’assurant que les différentes initialisations soient correctement réalisées selon le système hôte sur lequel il s’exécute.

Nous reviendrons plus en détail sur cette notion dans un futur article.

Conclusion

Nous pouvons donc constater que la définition simpliste était bien vraie. Même si son exécution requiert des mécanismes qui peuvent être complexes (isolation, contrôle), nous n’avons pas besoin de nous en soucier pour créer nos applications conteneurisées.

Il restera un simple processus qui s’exécutera où bon nous semble (ou presque) et n’aura besoin que de ressources pour fonctionner. Il pourra donc ainsi être dupliqué, exporté, relancé et toujours fonctionner de la même manière.


Viewing all articles
Browse latest Browse all 1865

Trending Articles