La solution de vir­tua­li­sa­tion Docker a modifié en pro­fon­deur la manière dont le software est construit, distribué et piloté au cours des dix dernières années. Con­trai­re­ment aux machines vir­tuelles (VM) pré­cé­dem­ment établies, Docker vir­tua­lise des ap­pli­ca­tions in­di­vi­duelles. Un conteneur Docker est donc un conteneur d’ap­pli­ca­tion ou de logiciel.

Le terme « conteneur logiciel » vient des con­te­neurs physiques, comme ceux utilisés sur les navires de transport. En lo­gis­tique, les con­te­neurs, en tant qu’unités stan­dar­di­sées, ont rendu les chaînes com­mer­ciales modernes possibles. Un conteneur peut ainsi être trans­porté sur n’importe quel navire, camion ou train conçu à cette fin. Ceci fonc­tionne en grande partie in­dé­pen­dam­ment du contenu des con­te­neurs. À l’extérieur, le conteneur est équipé d’in­ter­faces stan­dar­di­sées. Ce fonc­tion­ne­ment est, à plus d’un titre, analogue à celui des con­te­neurs Docker.

Nom de domaine
Votre domaine en un clic
  • 1 cer­ti­fi­cat SSL Wildcard par contrat
  • Fonction incluse Domain Connect pour une con­fi­gu­ra­tion DNS sim­pli­fiée

Un Docker container : qu’est-ce que c’est ?

Alors, de quoi parle-t-on exac­te­ment quand on parle de « conteneur Docker » ? Les dé­ve­lop­peurs de Dockers sont sans doute les mieux placés pour en parler :

Citation

„Con­tai­ners are a stan­dar­di­zed unit of software that allows de­ve­lo­pers to isolate their app from its en­vi­ron­ment.“ – Source : https://www.docker.com/why-docker

« Les con­te­neurs cons­ti­tuent une unité stan­dar­di­sée de software qui permet aux dé­ve­lop­peurs d’isoler leurs ap­pli­ca­tions de leur en­vi­ron­ne­ment » - (tra­duc­tion : IONOS)

Con­trai­re­ment à un conteneur physique, un conteneur Docker existe dans un en­vi­ron­ne­ment virtuel. Un conteneur physique est assemblé sur la base de spé­ci­fi­ca­tions stan­dar­di­sées. On peut observer quelque chose de similaire avec les con­te­neurs virtuels. Un conteneur Docker est créé à partir d’un modèle immuable qu’on appelle une « image ». Une image Docker contient les dé­pen­dances et pa­ra­mètres de con­fi­gu­ra­tion né­ces­saires pour créer un conteneur.

De même que de nombreux con­te­neurs physiques peuvent être produits à partir d’un modèle unique, il est possible de créer autant de con­te­neurs Docker qu’on veut à partir d’une seule et même image. Les con­te­neurs Docker cons­ti­tuent dès lors la base pour des services évolutifs et des en­vi­ron­ne­ments d’ap­pli­ca­tions re­pro­duc­tibles. Nous pouvons créer un conteneur depuis une image et sau­ve­gar­der également un conteneur existant dans une nouvelle image. Les processus peuvent être exécutés, mis en pause et terminés dans le conteneur.

À la dif­fé­rence d’une machine virtuelle (VM), un conteneur Docker ne contient pas son propre système d’ex­ploi­ta­tion (OS). À la place, tous les con­te­neurs en cours d’exécution sur un hôte Docker ont accès au même noyau OS. Lorsque Docker est déployé sur un hôte Linux, le noyau Linux existant est utilisé. Si le logiciel Docker est exécuté sur un système différent de Linux, on a recours à une image système Linux minimale via un hy­per­vi­seur ou une machine virtuelle.

Une certaine part des res­sources système sont allouées à chaque conteneur pendant l’exécution. Ceci comprend la RAM, les cœurs du pro­ces­seur, le stockage de masse et les appareils réseau (virtuels). Tech­ni­que­ment, les « cgroups » (version rac­cour­cie de « groupes de contrôle ») limitent l’accès d’un conteneur Docker aux res­sources système. Des « espaces de nom de noyau » sont utilisés pour séparer les res­sources du noyau et dis­tin­guer les processus les uns des autres.

Les con­te­neurs Docker com­mu­ni­quent en externe à travers le réseau. Pour ce faire, des services spé­ci­fiques guettent les ports exposés. Ceux-ci sont en général des serveurs Web ou de bases de données. Les con­te­neurs eux-mêmes sont contrôlés depuis leur hôte Docker respectif via l’API Docker. Les con­te­neurs peuvent être lancés, in­ter­rom­pus et supprimés. Le client Docker fournit une interface de ligne de commande (CLI) qui comporte les commandes ap­pro­priées.

Qu’est-ce qui dif­fé­ren­cie les Docker con­tai­ners des images Docker?

Les deux termes « conteneur Docker » et « image Docker» sont souvent source de confusion. Il n’y a rien de sur­pre­nant à cela, puisqu’il s’agit peu ou prou du dilemme de l’œuf ou de la poule. Un conteneur est créé à partir d’une image ; mais un conteneur peut également être sau­ve­gardé en tant que nouvelle image. Jetons un œil aux dif­fé­rences qui existent entre ces deux concepts en détail.

Une image Docker constitue un modèle inerte. L’image ne fait que prendre un peu de place sur un disque dur et rien d’autre. Par contraste, le conteneur Docker constitue une instance « vivante ». Un conteneur Docker en cours d’exécution a un com­por­te­ment ; il interagit avec son en­vi­ron­ne­ment. Par ailleurs, un conteneur dispose d’un état qui change avec le temps, à l’aide d’une quantité variable de RAM.

Vous avez peut-être déjà entendu parler des concepts de « classe » et d’« objet » issus de la pro­gram­ma­tion orientée objet (OOP). La relation entre un conteneur Docker et une image Docker est en quelques sortes similaire à la relation entre un objet et la classe qui lui est associée. Une classe n’existe qu’une seule fois ; plusieurs objets si­mi­laires peuvent être créés à partir de celle-ci. La classe elle-même est chargée depuis un fichier de code source. Il existe une tendance analogue dans l’univers Docker. Un modèle est créé depuis une unité source, un « Do­cker­file », lequel, à son tour, crée un grand nombre d’instances :

Texte source Modèle Instance
Concept Docker Do­cker­file Image Docker Conteneur Docker
Analogie de pro­gram­ma­tion Code source de classe Classe chargée Objet instancié
Conseil

On appelle le conteneur Docker une « instance en cours d’exécution » de l’image qui lui est associée. Les termes « instance » et « ins­tan­cier » sont encore assez abstraits à ce stade. Si vous avez du mal à com­prendre, utilisons un moyen mné­mo­tech­nique. Remplacez « ins­tan­cier » par « découper » dans votre esprit. Même s’il n’y a aucun lien entre ces mots, il existe une forte cor­res­pon­dance entre leurs sens dans le langage propre à la science in­for­ma­tique. Re­pré­sen­tez-vous le principe comme ceci : de même que nous utilisons un emporte-pièce pour découper de nombreux biscuits iden­tiques d’une couche de pâte, nous ins­tan­cions de nombreux objets iden­tiques à partir d’un même modèle. Par con­sé­quent, l’ins­tan­cia­tion est ce qui a lieu lorsqu’un modèle crée un objet.

Comment un Docker container est-il construit ?

Pour com­prendre comment un conteneur Docker est construit, cela peut aider de jeter un œil à la « mé­tho­do­lo­gie 12 facteurs » . Il s’agit du re­grou­pe­ment de douze principes fon­da­men­taux propres à la cons­truc­tion et au pilotage de logiciels orientés vers le service. Docker est né en 2011, tout comme l’ap­pli­ca­tion 12 facteurs qui aide les dé­ve­lop­peurs à concevoir des ap­pli­ca­tions logiciel en tant que service (SaaS) con­for­mé­ment à certains standards spé­ci­fiques. Ces derniers com­pren­nent :

  • Le recours à des formats dé­cla­ra­tifs pour l’au­to­ma­ti­sa­tion des ins­tal­la­tions en vue de minimiser les coûts en temps et en argent pour les nouveaux dé­ve­lop­peurs qui re­joig­nent le projet
  • L’ob­ser­va­tion du système d’ex­ploi­ta­tion sous-jacent et l’assurance d’une por­ta­bi­lité maximale entre les niveaux d’exécution 
  • Le dé­ploie­ment sur des pla­te­formes de Cloud modernes, rendant obsolète le recours à des serveurs et à une ad­mi­nis­tra­tion système 
  • La mi­ni­mi­sa­tion des di­ver­gences entre le dé­ve­lop­pe­ment et la pro­duc­tion afin de permettre un dé­ploie­ment en continu pour une agilité maximale 
  • Et l’évo­lu­ti­vité sans devoir changer d’outil, d’ar­chi­tec­ture ou de processus de dé­ve­lop­pe­ment

La structure d’un conteneur Docker est basée sur ces principes. De plus, il comprend les com­po­sants suivants, que nous allons examiner en détails ci-dessous :

  1. Système d’ex­ploi­ta­tion de conteneur et Union File System
  2. Com­po­sants logiciel et con­fi­gu­ra­tion
  3. Variables d’en­vi­ron­ne­ment et con­fi­gu­ra­tion d’exécution
  4. Ports et volumes
  5. Processus et journaux

Système d’ex­ploi­ta­tion de conteneur et Union File System

À la dif­fé­rence d’une machine virtuelle, un conteneur Docker ne renferme pas son propre système d’ex­ploi­ta­tion. À la place, tous les con­te­neurs en cours d’exécution sur un hôte Docker accèdent à un noyau Linux partagé. Seule une couche d’exécution minimale est incluse dans le conteneur. Ceci comprend en général une im­plé­men­ta­tion de la bi­blio­thèque standard du C et d’un shell Linux pour les processus en cours d’exécution. Voici un aperçu des com­po­sants dans l’image of­fi­cielle « Alpine Linux » :

Noyau Linux Bi­blio­thèque standard C Commandes unix
from host musl libc BusyBox

Une image Docker consiste en une pile de couches système de fichiers en lecture seule. Une couche décrit les chan­ge­ments au système fichier dans la couche in­fé­rieure. À l’aide d’un union file system spé­ci­fique tel que overlay2, les couches sont su­per­po­sées et unifiées en une interface cohérente. Une autre couche ins­crip­tible est ajoutée aux couches en lecture seule lorsque vous créez un conteneur Docker depuis une image. Tous les chan­ge­ments apportés au système fichier sont in­cor­po­rés à la couche scrip­tible à l’aide de la méthode « copy-on-write » (« copie sur écriture »).

Com­po­sants logiciel et con­fi­gu­ra­tion

S’appuyant sur le système d’ex­ploi­ta­tion minimal du conteneur, des com­po­sants logiciel sup­plé­men­taires sont installés dans un conteneur Docker. Ceci est en général suivi par des étapes sup­plé­men­taires d’ins­tal­la­tion et de con­fi­gu­ra­tion. Les méthodes standard sont utilisées pour l’ins­tal­la­tion :

  • via un ges­tion­naire de paquets système tel que apt, apk, yum, brew, etc.
  • via un ges­tion­naire de paquets de langage de pro­gram­ma­tion tel que pip, npm, composer, gem, cargo, etc.
  • Par com­pi­la­tion dans le conteneur avec make, mvn, etc.

Voici quelques exemples de com­po­sants logiciel com­mu­né­ment utilisés dans les con­te­neurs Docker :

Champ d’ap­pli­ca­tion Com­po­sants logiciel
Langages de pro­gram­ma­tion PHP, Python, Ruby, Java, Ja­vaS­cript
Outils de dé­ve­lop­pe­ment node/npm, React, Laravel
Systèmes de base de données MySQL, Postgres, MongoDB, Redis
Serveurs Web Apache, nginx, lighttpd
Caches et proxies Varnish, Squid
Systèmes de gestion de contenu WordPress, Magento, Ruby on Rails

Variables d’en­vi­ron­ne­ment et con­fi­gu­ra­tion d’exécution

Observant la mé­tho­do­lo­gie d’ap­pli­ca­tion 12 facteurs, la con­fi­gu­ra­tion de conteneur Docker est stockée dans des variables d’en­vi­ron­ne­ment. Nous entendons ici par con­fi­gu­ra­tion toutes les valeurs qui évoluent d’un en­vi­ron­ne­ment à l’autre, telles que le système de dé­ve­lop­pe­ment vs pro­duc­tion. On retrouve souvent parmi elles les noms d’hôtes et les iden­ti­fiants de bases de données.

Les valeurs des variables d’en­vi­ron­ne­ment in­fluen­cent la manière dont le conteneur se comporte. Deux méthodes primaires sont employées pour rendre les variables d’en­vi­ron­ne­ment dis­po­nibles au sein d’un conteneur :

1. Dé­fi­ni­tion dans Do­cker­file

La dé­cla­ra­tion ENV déclare une variable d’en­vi­ron­ne­ment dans le Do­cker­file. Une valeur par défaut op­tion­nelle peut lui être assignée. Ceci prend effet si la variable d’en­vi­ron­ne­ment est vide au moment où le conteneur est lancé.

2. Trans­mis­sion lors du lancement du conteneur

Pour accéder à la variable d’en­vi­ron­ne­ment dans le conteneur qui n’a pas été déclaré dans le Dokerfile, on passe la variable au moment où on lance le conteneur. Ceci fonc­tionne pour les variables uniques via les pa­ra­mètres de ligne de commande. Par ailleurs, un « fichier env », qui définit plusieurs variables d’en­vi­ron­ne­ment ainsi que leurs valeurs, peut être transmis.

Voici comment passer une variable d’en­vi­ron­ne­ment au moment du lancement du conteneur :

docker run --env <env-var> <image-id></image-id></env-var>

Passer un fichier env pour de multiples variables d’en­vi­ron­ne­ment peut être utile :

docker run --env-file /path/to/.env <image-id></image-id>
Note

La commande 'docker inspect' peut être utilisée pour exposer les variables d’en­vi­ron­ne­ment présentes dans le conteneur ac­com­pag­nées de leurs valeurs. Par con­sé­quent, vous devez faire attention lorsque vous utilisez des données con­fi­den­tielles dans les variables d’en­vi­ron­ne­ment.

Lors du lancement d’un conteneur depuis une image, les pa­ra­mètres de con­fi­gu­ra­tion peuvent être transmis. Parmi ceux-ci, on retrouve la quantité de res­sources système allouées, qui sont par ailleurs il­li­mi­tées. Les pa­ra­mètres de démarrage sont également utilisés pour définir les ports et les volumes pour le conteneur. Nous en ap­pren­drons plus à ce sujet dans la section suivante. Il est possible que les pa­ra­mètres de démarrage écrasent toutes les valeurs par défaut dans le Do­cker­file. Voici quelques exemples.

Allouer un cœur de pro­ces­seur et 10 mégabytes de RAM au conteneur Docker au démarrage :

docker run --cpus="1" --memory="10m" <image-id></image-id>

Exposer les ports définis dans le Do­cker­file lors du lancement du conteneur :

docker run -P <image-id></image-id>

Mapper le port TCP 80 de l’hôte Docker au port 80 du conteneur Docker :

docker run -p 80:80/tcp <image-id></image-id>

Ports et volumes

Un conteneur Docker contient une ap­pli­ca­tion qui est isolée du monde extérieur. Pour que cela soit utile, il est né­ces­saire de pouvoir interagir avec l’en­vi­ron­ne­ment. Par con­sé­quent, il existe des manières d’échanger des données entre l’hôte et le conteneur, ainsi qu’entre plusieurs con­te­neurs. Les in­ter­faces stan­dar­di­sées per­met­tent aux con­te­neurs d’être utilisés dans dif­fé­rents en­vi­ron­ne­ments.

La com­mu­ni­ca­tion avec les processus en cours d’exécution dans le conteneur depuis l’extérieur passe par les ports réseau activés. Les pro­to­coles standards TCP et UCP sont utilisés ici. À titre d’exemple, imaginons un conteneur Docker qui contient un serveur Web ; il écoute sur le port TCP 8080. Le Do­cker­file de l’image Docker contient également la ligne « EXPOSE 8080/tcp ». Nous lançons le conteneur avec « docker run -P » et accédons au serveur Web via http://localhost:8080.

Les ports sont employés pour com­mu­ni­quer avec les services en cours d’exécution dans le conteneur. Quoi qu’il en soit, dans de nombreux cas, il peut être pertinent d’avoir recours à un fichier partagé entre le conteneur et le système hôte pour échanger des données. C’est la raison pour laquelle Docker connaît dif­fé­rents types de volumes :

Les dif­fé­rences entre les types de volume sont subtiles. Le choix du type approprié dépend en grande partie du cas d’usage par­ti­cu­lier. Une des­crip­tion détaillée dé­pas­se­rait de loin le cadre de cet article.

Les processus et journaux

Un conteneur Docker contient en général une ap­pli­ca­tion ou un service. Le logiciel exécuté à l’intérieur du conteneur forme un ensemble de processus en cours d’exécution. Les processus présents dans un conteneur Docker sont isolés des processus présents dans d’autres con­te­neurs ou le système hôte. Les processus peuvent être lancés, in­ter­rom­pus et listés à l’intérieur du conteneur Docker. Il est contrôlé via la ligne de commande ou via l’API Docker.

Les processus en cours d’exécution pro­dui­sent en per­ma­nence des in­for­ma­tions de statut. Observant la mé­tho­do­lo­gie 12 facteurs, les flux de données standards STDOUT et STDERR sont utilisés à titre d’output. L’output sur ces deux flux de données peut être lu à l’aide de la commande 'docker logs'. Un pilote de jour­na­li­sa­tion peut également être utilisée. Il rédige des logs au format JSON par défaut.

Comment et où les Dockers con­tai­ners sont-ils utilisés ?

On a recours à Docker à tous les stades du cycle de vie du software de nos jours. Ceci comprend le dé­ve­lop­pe­ment, le testage et le pilotage. Des con­te­neurs exécutés sur un hôte Docker sont contrôlés via l’API Docker. Le client Docker accepte les commandes sur la ligne de commande ; des outils spé­ci­fiques d’or­ches­tra­tion sont utilisés pour contrôler les clusters de con­te­neurs Docker.

La marche à suivre basique pour déployer des con­te­neurs Docker peut être résumée ainsi :

  1. L’hôte Docker té­lé­charge l’image Docker depuis le ré­per­toire.
  2. Le conteneur Docker est créé et lancé depuis cette image.
  3. L’ap­pli­ca­tion dans le conteneur est exécutée jusqu’à ce que le conteneur soit in­ter­rompu ou supprimé.

Jetons un œil à deux exemples de dé­ploie­ment de con­te­neurs Docker :

Déployer des con­te­neurs Docker dans l’en­vi­ron­ne­ment de dé­ve­lop­pe­ment local

Le recours à des con­te­neurs Docker jouit d’une po­pu­la­rité par­ti­cu­lière dans le monde du dé­ve­lop­pe­ment logiciel. En général, le software est développé par une équipe de spé­cia­listes. À cette fin, on a recours à tout un éventail d’outils de dé­ve­lop­pe­ment qu’on appelle une chaîne de com­pi­la­tion (« toolchain » en anglais). Chaque outil existe dans une version spé­ci­fique, et la chaîne complète fonc­tionne uni­que­ment si les versions sont com­pa­tibles les unes avec les autres. Par ailleurs, les outils doivent être con­fi­gu­rés cor­rec­te­ment.

Pour s’assurer que l’en­vi­ron­ne­ment de dé­ve­lop­pe­ment soit cohérent, les dé­ve­lop­peurs ont recours à Docker. Une image Docker est créée une seule fois, et elle contient l’ensemble de la chaîne de com­pi­la­tion cor­rec­te­ment dé­ve­lop­pée. Chaque dé­ve­lop­peur au sein de l’équipe charge l’image sur sa machine locale et lance un conteneur depuis cette dernière. Le dé­ve­lop­pe­ment a ensuite lieu au sein du conteneur. L’image est mise à jour de manière cen­tra­li­sée si la chaîne de com­pi­la­tion subit un chan­ge­ment.

Déployer des con­te­neurs Docker dans clusters or­ches­trés

Les data centers des four­nis­seurs d’hé­ber­ge­ment et des four­nis­seurs de Pla­te­forme en tant que service (PaaS) ont recours à des clusters de con­te­neurs Docker. Chaque service (load balancer, serveur Web, serveur de base de données, etc.) exécute son propre conteneur Docker. Cela étant, un conteneur unique ne peut gérer qu’une certaine charge. Les logiciels d’or­ches­tra­tion su­per­vi­sent les con­te­neurs, ainsi que leur charge et leur condition. Les or­ches­tra­teurs lancent des con­te­neurs ad­di­tion­nels lorsque la charge augmente. Cette approche assure aux services une ex­ten­si­bi­lité rapide pour répondre à des chan­ge­ments de con­di­tions.

Les avantages et in­con­vé­nients de la vir­tua­li­sa­tion basée sur les con­te­neurs Docker

Les avantages de la vir­tua­li­sa­tion via Docker sont notamment à con­si­dé­rer par rapport à l’uti­li­sa­tion des machines vir­tuelles (VMs). Les con­te­neurs Docker sont beaucoup plus légers que les VMs. Ils peuvent être lancés plus ra­pi­de­ment et con­som­ment moins de res­sources. Les images sous-jacentes aux con­te­neurs Docker sont également plus petites de plusieurs ordres de grandeur. Si, d’un côté, la taille des images VM s’élève en général à quelques centaines de MB, voire quelques GB, celle des images Docker s’élève à quelques MB au minimum.

Cela dit, la vir­tua­li­sa­tion basée sur le con­te­neurs à l’aide de Docker présente également quelques limites. Compte tenu du fait qu’un conteneur ne contient pas son propre système d’ex­ploi­ta­tion, l’isolation des processus en cours d’exécution en son sein n’est pas parfaite. Avoir recours à de nombreux con­te­neurs a pour con­sé­quence un haut degré de com­plexité. Par ailleurs, Docker constitue un système évolué, et la pla­te­forme Docker en fait désormais trop. Les dé­ve­lop­peurs tra­vail­lent donc d’arrache-pied pour séparer les com­po­sants in­di­vi­duels.

Aller au menu principal