technique:docker:general:presentation

Présentation de Docker et Compose

D’innombrables introductions et tutoriels sont disponibles sur Internet, c’est pourquoi cette page ne vise qu’à donner une intuition des concepts utilisés dans le reste de la documentation. En particulier, on n’expliquera pas comment fonctionne Docker, mais plutôt ce qu’il permet de faire.

Docker est un logiciel libre permettant de lancer des applications dans des conteneurs.

Question:

Qu’est ce qu’un conteneur ?

Un conteneur peut être vu comme une sorte de machine virtuelle légère.

Question:

Qu’est ce qu’une machine virtuelle ?

C’est la simulation d’une machine physique. Ainsi, sur une même machine physique, plusieurs machines virtuelles peuvent tourner simultanément et indépendamment.

Chaque machine virtuelle a l’impression d’être seule, avec son matériel propre (disques, CPU, RAM), son système d’exploitation, et ses applications.

En pratique, ce qui fait l’interface entre la machine physique et les machines virtuelles est un logiciel qui se nomme hyperviseur. L’hyperviseur est chargé de simuler le matériel utilisé par les machines virtuelles, par exemple.

L’intérêt d’une machine virtuelle est donc :

  • De pouvoir allouer les ressources d’une machine physique de manière flexible
  • De pouvoir isoler les machines entre elles (sécurité, performance…)

On peut représenter les machines virtuelles comme suit :

Les conteneurs reprennent un peu le même principe, mais pour les applications. En réalité, il n’y a pas un système d’exploitation complet qui tourne dans chaque machine, et le matériel n’est pas simulé.

Note:

En particulier, le noyau du système d’exploitation utilisé par chaque conteneur est le même, et c’est celui de la machine hôte.

Un conteneur est donc une boîte auto-suffisante, isolée, contenant une application, pouvant s’exécuter n’importe où.

Note:

Docker est tout simplement un gestionnaire de conteneurs. Il y en a d’autres, comme Podman.

En comparaison avec une machine virtuelle :

Historiquement, c’est Docker qui a fait exploser la popularité des conteneurs, parce qu’il fournit :

  • Un outil en ligne de commande permettant de gérer facilement le cycle de vie des conteneurs
  • Un espace permettant de partager ses applications conteneurisées avec les autres (le Docker Hub)
  • Tout un écosystème permettant de faire de la haute disponibilité, de la répartition de charge, etc.

Note:

Ce qu’il faut retenir, c’est qu’un conteneur permet de lancer une application isolée des autres applications, par rapport à un système Linux basique, où toutes les applications écrivent dans les mêmes dossiers (/etc…) et peuvent communiquer les unes avec les autres par défaut.

Une implication subsidiaire est qu’il est très facile de migrer un conteneur d’une machine à une autre, puisqu’un conteneur peut s’exécuter n’importe où.

La philosophie de Docker rejoint celle des micro-services : créer des briques ré-utilisables, qui font une chose et le font bien, et sont capables de communiquer avec d’autres briques. Cette approche s’oppose à celle dite monolithique.

La brique, dans ce cas, est un conteneur.

Important:

En suivant cette philosophie, pour un service et sa base de données, on créera deux conteneurs différents, de sorte qu’on puisse facilement changer de base de données sans devoir manipuler complètement le conteneur applicatif.

Une image Docker peut être vue comme une grosse archive inerte, qui contient tout ce qu’il faut pour lancer une application. L’intérêt principal d’une image est qu’elle est utilisable sur n’importe quel système d’exploitation et réutilisable.

Note:

Pour faire une analogie, pour installer un paquet sur une distribution Linux, les commandes vont varier (apt-get, pacman, yum…), tout comme le nom des paquets. La configuration distribuée sera différente, l’emplacement des fichiers aussi. Avec Docker, il suffit d’une seule commande pour lancer une application sur n’importe quel système d’exploitation et obtenir le même résultat partout.

Une image Docker se construit à partir d’un Dockerfile, qui est en quelque sorte la recette de cuisine de l’image.

Les images peuvent hériter d’autres images.

Note:

En continuant la métaphore, ça reviendrait à dire : pour préparer une sauce au poivre, partez d’un roux, puis […].

Plus concrètement, un Dockerfile pourrait ressembler à ceci :

FROM debian:buster
RUN apt-get install wget
COPY code /code
EXPOSE 8000
CMD [ "/code/app" ]

Note:

FROM debian:buster

debian est le nom de l’image et buster est son tag. Le tag est une déclinaison de l’image, et est souvent utilisé en tant que version.

Note:

RUN apt-get install wget

On installe wget dans l’image.

Note:

COPY code /code

On copie le dossier code, situé sur la machine, dans l’image, à l’emplacement /code.

Note:

EXPOSE 8000

L’image pourra écouter sur le port 8000.

Note:

CMD [ "/code/app" ]

La commande à exécuter pour démarrer l’application est /code/app

On dit que l’on construit une image à partir d’un Dockerfile. Une fois l’image construite, on peut lui donner un nom et un tag, par exemple app:v1.

Une commande typique pour construire cette image serait :

snippet.bash
# Construire l'image avec le Dockerfile
# et les fichiers du dossier courant
docker build -t app:v1 .

Dans notre micro exemple, on obtient une image redistribuable, qui contient wget, est prête à écouter sur le port 8000, a le code de l’application et sait quoi exécuter. On peut donc la lancer de n’importe où : elle s’auto-suffit.

Important:

Un conteneur Docker est une instance d’une image Docker. C’est sa version vivante.

En d’autres termes, plusieurs conteneurs peuvent être instanciés depuis la même image et vivre leur vie indépendamment. Ce principe de réutilisation est très utile.

Note:

Exemple : on prépare une image nginx, qui permet de lancer un site web. On a cinq sites web. On lance 5 conteneurs à partir de cette image, sans avoir à se taper cinq fois la configuration.

En reprenant l’exemple de l’image nginx, on sent que les conteneurs ne sont pas exactement dans le même état. Chaque conteneur va :

  • Avoir ses propres fichiers à servir
  • Être relié à un port différent de la machine hôte, par exemple
  • Être isolé d’un point de vue réseau, parce qu’ils n’ont pas de raisons de communiquer ensemble.

Docker permet de gérer tout ceci.

Attention:

Lorsqu’un conteneur est éteint puis supprimé, toutes les modifications apportées par rapport à l’image de base sont supprimées également. C’est pourquoi il est utile d’utiliser les volumes, qui stockent de manière persistante les modifications à certains fichiers du conteneur (contenu d’une base de donnée, etc).

On l’a vu, Docker est assez simple d’utilisation : un docker build, un docker run, et notre conteneur est lancé.

Cependant, sur une activité d’hébergement de services, il y a parfois plusieurs dizaines de services à lancer.

Or, la configuration des volumes, des ports, des réseaux, etc, se fait au démarrage de chaque conteneur et n’est pas référencé dans l’image.

Ceci veut dire qu’à chaque démarrage de conteneur, il faudrait écrire des commandes à rallonge du type :

snippet.bash
docker run -v <volume>:<destination> -p <port source>:<port conteneur> --network <nom du réseau> <image>

C’est inimaginable.

C’est pourquoi Docker Compose a vu le jour : c’est un système d’orchestration de conteneurs.

Compose se base sur des fichiers écrits au format YAML, qui décrivent comment lancer les différents conteneurs.

Pour donner une intuition, voici un fichier docker-compose.yml fictif qui vise à faciliter l’administration d’un service et de sa base de données :

snippet.yaml
version: '3.7'

volumes:
  db:

networks:
  app:
  
services:
  app:
    image: mattermost_app
    depends_on: mattermost_db
    ports:
      - 80:8080
    networks:
      - app
    environment:
      ADMIN_PASSWORD: admin
 
  db:
    image: postgres
    networks:
      - app
    environment:
      POSTGRES_PASSWORD: password 
    volumes:
      - db:/var/lib/postgresql

Sans comprendre tous les éléments de syntaxe, on comprend que :

  • app utilise l’image mattermost, la met dans un réseau dédié, se relie au port 80 de la machine hôte, et dépend du service db.
  • db utilise l’image postgres, la met dans le même réseau que Mattermost, indique qu’il faut conserver le contenu du dossier /var/lib/postgresql, etc.

Note:

Ce fichier peut être distribué à n’importe qui, qui pourra alors lancer le service et sa base de données avec une simple commande qui se charge de configurer et de démarrer tous les services :

snippet.bash
docker-compose up

  • technique/docker/general/presentation.txt
  • de qduchemi