Le Docker container

La solution de virtualisation Docker a modifié en profondeur la manière dont le software est construit, distribué et piloté au cours des dix dernières années. Contrairement aux machines virtuelles (VM) précédemment établies, Docker virtualise des applications individuelles. Un conteneur Docker est donc un conteneur d’application ou de logiciel.

Le terme « conteneur logiciel » vient des conteneurs physiques, comme ceux utilisés sur les navires de transport. En logistique, les conteneurs, en tant qu’unités standardisées, ont rendu les chaînes commerciales modernes possibles. Un conteneur peut ainsi être transporté sur n’importe quel navire, camion ou train conçu à cette fin. Ceci fonctionne en grande partie indépendamment du contenu des conteneurs. À l’extérieur, le conteneur est équipé d’interfaces standardisées. Ce fonctionnement est, à plus d’un titre, analogue à celui des conteneurs Docker.

Domaine Internet pas cher

Bien plus qu'un simple domaine !

Personnalisez votre présence en ligne avec un nom de domaine pertinent.

Email
Certificat SSL
Assistance 24/7

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

Alors, de quoi parle-t-on exactement quand on parle de « conteneur Docker » ? Les développeurs de Dockers sont sans doute les mieux placés pour en parler :

Citation

„Containers are a standardized unit of software that allows developers to isolate their app from its environment.“ – Source : https://www.docker.com/why-docker

« Les conteneurs constituent une unité standardisée de software qui permet aux développeurs d’isoler leurs applications de leur environnement » - (traduction : IONOS)

Contrairement à un conteneur physique, un conteneur Docker existe dans un environnement virtuel. Un conteneur physique est assemblé sur la base de spécifications standardisées. On peut observer quelque chose de similaire avec les conteneurs virtuels. Un conteneur Docker est créé à partir d’un modèle immuable qu’on appelle une « image ». Une image Docker contient les dépendances et paramètres de configuration nécessaires pour créer un conteneur.

De même que de nombreux conteneurs physiques peuvent être produits à partir d’un modèle unique, il est possible de créer autant de conteneurs Docker qu’on veut à partir d’une seule et même image. Les conteneurs Docker constituent dès lors la base pour des services évolutifs et des environnements d’applications reproductibles. Nous pouvons créer un conteneur depuis une image et sauvegarder é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 différence d’une machine virtuelle (VM), un conteneur Docker ne contient pas son propre système d’exploitation (OS). À la place, tous les conteneurs 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 hyperviseur ou une machine virtuelle.

Une certaine part des ressources système sont allouées à chaque conteneur pendant l’exécution. Ceci comprend la RAM, les cœurs du processeur, le stockage de masse et les appareils réseau (virtuels). Techniquement, les « cgroups » (version raccourcie de « groupes de contrôle ») limitent l’accès d’un conteneur Docker aux ressources système. Des « espaces de nom de noyau » sont utilisés pour séparer les ressources du noyau et distinguer les processus les uns des autres.

Les conteneurs Docker communiquent en externe à travers le réseau. Pour ce faire, des services spécifiques guettent les ports exposés. Ceux-ci sont en général des serveurs Web ou de bases de données. Les conteneurs eux-mêmes sont contrôlés depuis leur hôte Docker respectif via l’API Docker. Les conteneurs peuvent être lancés, interrompus et supprimés. Le client Docker fournit une interface de ligne de commande (CLI) qui comporte les commandes appropriées.

Qu’est-ce qui différencie les Docker containers des images Docker?

Les deux termes « conteneur Docker » et « image Docker» sont souvent source de confusion. Il n’y a rien de surprenant à 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 sauvegardé en tant que nouvelle image. Jetons un œil aux diffé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 comportement ; il interagit avec son environnement. 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 programmation 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 similaires 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 « Dockerfile », lequel, à son tour, crée un grand nombre d’instances :

  Texte source Modèle Instance
Concept Docker Dockerfile Image Docker Conteneur Docker
Analogie de programmation 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 « instancier » sont encore assez abstraits à ce stade. Si vous avez du mal à comprendre, utilisons un moyen mnémotechnique. Remplacez « instancier » par « découper » dans votre esprit. Même s’il n’y a aucun lien entre ces mots, il existe une forte correspondance entre leurs sens dans le langage propre à la science informatique. Représentez-vous le principe comme ceci : de même que nous utilisons un emporte-pièce pour découper de nombreux biscuits identiques d’une couche de pâte, nous instancions de nombreux objets identiques à partir d’un même modèle. Par conséquent, l’instanciation est ce qui a lieu lorsqu’un modèle crée un objet.

Comment un Docker container est-il construit ?

Pour comprendre comment un conteneur Docker est construit, cela peut aider de jeter un œil à la « méthodologie 12 facteurs » . Il s’agit du regroupement de douze principes fondamentaux propres à la construction et au pilotage de logiciels orientés vers le service. Docker est né en 2011, tout comme l’application 12 facteurs qui aide les développeurs à concevoir des applications logiciel en tant que service (SaaS) conformément à certains standards spécifiques. Ces derniers comprennent :

  • Le recours à des formats déclaratifs pour l’automatisation des installations en vue de minimiser les coûts en temps et en argent pour les nouveaux développeurs qui rejoignent le projet
  • L’observation du système d’exploitation sous-jacent et l’assurance d’une portabilité maximale entre les niveaux d’exécution 
  • Le déploiement sur des plateformes de Cloud modernes, rendant obsolète le recours à des serveurs et à une administration système 
  • La minimisation des divergences entre le développement et la production afin de permettre un déploiement en continu pour une agilité maximale 
  • Et l’évolutivité sans devoir changer d’outil, d’architecture ou de processus de développement

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

  1. Système d’exploitation de conteneur et Union File System
  2. Composants logiciel et configuration
  3. Variables d’environnement et configuration d’exécution
  4. Ports et volumes
  5. Processus et journaux

Système d’exploitation de conteneur et Union File System

À la différence d’une machine virtuelle, un conteneur Docker ne renferme pas son propre système d’exploitation. À la place, tous les conteneurs 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 implémentation de la bibliothèque standard du C et d’un shell Linux pour les processus en cours d’exécution. Voici un aperçu des composants dans l’image officielle « Alpine Linux » :

Noyau Linux Bibliothè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 changements au système fichier dans la couche inférieure. À l’aide d’un union file system spécifique tel que overlay2, les couches sont superposées et unifiées en une interface cohérente. Une autre couche inscriptible est ajoutée aux couches en lecture seule lorsque vous créez un conteneur Docker depuis une image. Tous les changements apportés au système fichier sont incorporés à la couche scriptible à l’aide de la méthode « copy-on-write » (« copie sur écriture »).

Composants logiciel et configuration

S’appuyant sur le système d’exploitation minimal du conteneur, des composants logiciel supplémentaires sont installés dans un conteneur Docker. Ceci est en général suivi par des étapes supplémentaires d’installation et de configuration. Les méthodes standard sont utilisées pour l’installation :

  • via un gestionnaire de paquets système tel que apt, apk, yum, brew, etc.
  • via un gestionnaire de paquets de langage de programmation tel que pip, npm, composer, gem, cargo, etc.
  • Par compilation dans le conteneur avec make, mvn, etc.

Voici quelques exemples de composants logiciel communément utilisés dans les conteneurs Docker :

Champ d’application Composants logiciel
Langages de programmation PHP, Python, Ruby, Java, JavaScript
Outils de développement 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’environnement et configuration d’exécution

Observant la méthodologie d’application 12 facteurs, la configuration de conteneur Docker est stockée dans des variables d’environnement. Nous entendons ici par configuration toutes les valeurs qui évoluent d’un environnement à l’autre, telles que le système de développement vs production. On retrouve souvent parmi elles les noms d’hôtes et les identifiants de bases de données.

Les valeurs des variables d’environnement influencent la manière dont le conteneur se comporte. Deux méthodes primaires sont employées pour rendre les variables d’environnement disponibles au sein d’un conteneur :

1. Définition dans Dockerfile

La déclaration ENV déclare une variable d’environnement dans le Dockerfile. Une valeur par défaut optionnelle peut lui être assignée. Ceci prend effet si la variable d’environnement est vide au moment où le conteneur est lancé.

2. Transmission lors du lancement du conteneur

Pour accéder à la variable d’environnement 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 fonctionne pour les variables uniques via les paramètres de ligne de commande. Par ailleurs, un « fichier env », qui définit plusieurs variables d’environnement ainsi que leurs valeurs, peut être transmis.

Voici comment passer une variable d’environnement 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’environnement 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’environnement présentes dans le conteneur accompagnées de leurs valeurs. Par conséquent, vous devez faire attention lorsque vous utilisez des données confidentielles dans les variables d’environnement.

Lors du lancement d’un conteneur depuis une image, les paramètres de configuration peuvent être transmis. Parmi ceux-ci, on retrouve la quantité de ressources système allouées, qui sont par ailleurs illimitées. Les paramètres de démarrage sont également utilisés pour définir les ports et les volumes pour le conteneur. Nous en apprendrons plus à ce sujet dans la section suivante. Il est possible que les paramètres de démarrage écrasent toutes les valeurs par défaut dans le Dockerfile. Voici quelques exemples.

Allouer un cœur de processeur 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 Dockerfile 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 application qui est isolée du monde extérieur. Pour que cela soit utile, il est nécessaire de pouvoir interagir avec l’environnement. Par conséquent, il existe des manières d’échanger des données entre l’hôte et le conteneur, ainsi qu’entre plusieurs conteneurs. Les interfaces standardisées permettent aux conteneurs d’être utilisés dans différents environnements.

La communication avec les processus en cours d’exécution dans le conteneur depuis l’extérieur passe par les ports réseau activés. Les protocoles 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 Dockerfile 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 communiquer 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 différents types de volumes :

Les différences entre les types de volume sont subtiles. Le choix du type approprié dépend en grande partie du cas d’usage particulier. Une description détaillée dépasserait de loin le cadre de cet article.

Les processus et journaux

Un conteneur Docker contient en général une application 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 conteneurs ou le système hôte. Les processus peuvent être lancés, interrompus 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 produisent en permanence des informations de statut. Observant la méthodologie 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 journalisation peut également être utilisée. Il rédige des logs au format JSON par défaut.

Comment et où les Dockers containers sont-ils utilisés ?

On a recours à Docker à tous les stades du cycle de vie du software de nos jours. Ceci comprend le développement, le testage et le pilotage. Des conteneurs 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écifiques d’orchestration sont utilisés pour contrôler les clusters de conteneurs Docker.

La marche à suivre basique pour déployer des conteneurs Docker peut être résumée ainsi :

  1. L’hôte Docker télécharge l’image Docker depuis le répertoire.
  2. Le conteneur Docker est créé et lancé depuis cette image.
  3. L’application dans le conteneur est exécutée jusqu’à ce que le conteneur soit interrompu ou supprimé.

Jetons un œil à deux exemples de déploiement de conteneurs Docker :

Déployer des conteneurs Docker dans l’environnement de développement local

Le recours à des conteneurs Docker jouit d’une popularité particulière dans le monde du développement logiciel. En général, le software est développé par une équipe de spécialistes. À cette fin, on a recours à tout un éventail d’outils de développement qu’on appelle une chaîne de compilation (« toolchain » en anglais). Chaque outil existe dans une version spécifique, et la chaîne complète fonctionne uniquement si les versions sont compatibles les unes avec les autres. Par ailleurs, les outils doivent être configurés correctement.

Pour s’assurer que l’environnement de développement soit cohérent, les développeurs ont recours à Docker. Une image Docker est créée une seule fois, et elle contient l’ensemble de la chaîne de compilation correctement développée. Chaque développeur au sein de l’équipe charge l’image sur sa machine locale et lance un conteneur depuis cette dernière. Le développement a ensuite lieu au sein du conteneur. L’image est mise à jour de manière centralisée si la chaîne de compilation subit un changement.

Déployer des conteneurs Docker dans clusters orchestrés

Les data centers des fournisseurs d’hébergement et des fournisseurs de Plateforme en tant que service (PaaS) ont recours à des clusters de conteneurs 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’orchestration supervisent les conteneurs, ainsi que leur charge et leur condition. Les orchestrateurs lancent des conteneurs additionnels lorsque la charge augmente. Cette approche assure aux services une extensibilité rapide pour répondre à des changements de conditions.

Les avantages et inconvénients de la virtualisation basée sur les conteneurs Docker

Les avantages de la virtualisation via Docker sont notamment à considérer par rapport à l’utilisation des machines virtuelles (VMs). Les conteneurs Docker sont beaucoup plus légers que les VMs. Ils peuvent être lancés plus rapidement et consomment moins de ressources. Les images sous-jacentes aux conteneurs 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 virtualisation basée sur le conteneurs à l’aide de Docker présente également quelques limites. Compte tenu du fait qu’un conteneur ne contient pas son propre système d’exploitation, l’isolation des processus en cours d’exécution en son sein n’est pas parfaite. Avoir recours à de nombreux conteneurs a pour conséquence un haut degré de complexité. Par ailleurs, Docker constitue un système évolué, et la plateforme Docker en fait désormais trop. Les développeurs travaillent donc d’arrache-pied pour séparer les composants individuels.