Généralités

Docker est un utilitaire permettant d’embarquer, distribuer et exécuter des images au sein de conteneurs. Ce faisant, on isole application munies de ses dépendances (l’image) au sein d’un processus dédié (le conteneur).

Danger

Il faut garder en tête que le kernel demeure partagé, et que l’isolation est relative ; en effet, la surface exposée est largement plus importante que dans le cadre d’une machine virtuelle (VM).

Installation de Docker

Le noyau dur de Docker, sa substance logicielle, est portée par le projet open-source Docker Engine. Ce logiciel permet de gérer les conteneurs par le biais d’un processus daemon, interfacé depuis le front-end CLI docker.

Sur Arch Linux, il suffit d’installer le paquet docker

Service vs Socket

Via systemd, il existe deux manières de gérer Docker au démarrage de l’ordinateur :

  1. L’approche statique avec systemctl enable docker.service : Le démon Docker se lance pleinement dès le démarrage de la machine.
  2. L’approche dynamique avec systemctl enable docker.socket : Au lieu de lancer Docker au démarrage, le système écoute sur la socket, et lance Docker lorsque nécessaire.

Le choix n’est pas sans importances sur les performances1:

MéthodeTemps au démarrageImpact sur les ressources
Service~2000ms (2 secondes)Consomme de la RAM dès le début
Socket~1msConsomme quasiment 0 jusqu’à l’usage

Toutefois, le choix de la méthode de démarrage est à balancer avec les conditions d’usage :

  • Sur un ordinateur personnel, on préfère démarrer le Socket, gagner en performance au boot, notamment si l’usage de Docker n’est pas immédiat.
  • Sur un serveur, quitte à perdre deux secondes au démarrage, on préfère assurer la disponibilité immédiate des applications déployées via Docker, et donc se tourne vers le Service.

Échec du lancement

Il est possible que le lancement du service Docker échoue dans le cas d’un conflit d’IP entre un quelconque VPN actif et le réseau privé créé par Docker.2

Il est possible de vérifier le status de Docker, et la capacité à lancer un conteneur avec les commandes :

docker info
docker run -it --rm archlinux bash -c "echo hello world"

États

  • created : Les ressources sont allouées, le layer R/W existe, mais aucun processus ne tourne.
  • running
  • paused : Le processus est suspendu (signal SIGSTOP). Le conteneur reste en mémoire, mais ne consomme plus de CPU.
  • stopped/exited : Le conteneur et son layer R/W existent toujours, mais le processus principal s’est terminé.
docker logs myapp              # Voir les derniers logs
docker inspect myapp | grep -i oom  # Vérifier si OOM
dmesg | grep -i "killed process"    # Vérifier OOM killer
  • dead
docker rm -f myapp

Layers

L’utilisation de couches permet d’optimiser l’utilisation de la mémoire dans le cadre du stockage des images, ainsi que le téléchargement. En effet, les images partagent les couches qui leurs sont identiques ; et subséquemment, le téléchargement d’images s’en trouve d’autant moins couteux (en octets), que les couches sont déjà présentes sont la machine hôte.

docker pull ubuntu:22.04        # Télécharge Ubuntu (~77 Mo)
docker pull nginx:ubuntu        # Réutilise Ubuntu, télécharge nginx (~30 Mo)

Chaque instruction d’un Dockerfile crée une couche.

FROM ubuntu:22.04          # Layer 1 : image de base
RUN apt-get update         # Layer 2 : cache apt
RUN apt-get install nginx  # Layer 3 : nginx installé
COPY app/ /var/www/        # Layer 4 : fichiers application

Info

Dans l’optique de concevoir des images en utilisant Docker, il est judicieux d’installer docker-buildx.

Copy-on-Write

Lorsqu’une un fichier au sein d’un couche lecture seule est modifié, Docker copie ce dernier au sein du layer R/> du conteneur et y applique les modifications. Ainsi, la couche originale intacte demeure intacte.

Performances

Les écritures fréquente dans les fichiers de l’image (logs, bdd) peuvent être lentes à cause du CoW. Il est préférable d’utiliser des volumes docker].

Persistance

Le layer RW du conteneur est temporaire. Lors de la suppression du conteneur (docker rm), toutes les données qu’il contient disparaissent avec lui. Pour rendre les données persistantes, on peut utiliser :

  • Volume monté : -v mydata:/var/lib/data
  • Bind mount : -v ./src:/app
  • docker commit

Inspection

Pour inspecter les couches d’un image on utilise la commande docker history

Isolation

Docker utilise les namespaces et les control groups afin d’isoler les conteneurs. Ce faisant, le processus principal au sein de ces derniers devient PID 1. De plus, ce processus :

Gestion des signaux

Si l’application ne gère pas bien les signaux, utiliser tini comme init minimaliste (docker run --init ... ).

docker run -d --memory 128m --cpus 0.5 nginx:alpine
docker stats --no-stream

Sécuriser Docker

https://blog.stephane-robert.info/docs/conteneurs/moteurs-conteneurs/docker/securite/

Architecture Docker

Docker utilise une architecture client-serveur où le client communique avec le daemon via une API REST.

dockerd

Le daemon Docker (dockerd) est le cœur du système :

  • Écoute sur le socket Unix/var/run/docker.sock
  • Gère les images, conteneurs, réseaux et volumes
  • S’exécute en root par défaut (attention sécurité)
  • Communique avec containerd pour l’exécution

Accès root via Docker

Être membre du groupe docker équivaut à être root sur la marchine. 3 4 Et pour cause, l’accès au socket Docker permet de monter le filesystem de l’hôte dans un conteneur, et subséquemment d’obtenir un shell root sur l’hôte.

docker run -v /:/hostroot -it alpine chroot /hostroot

Client Docker

Le client envoit des requêtes au daemon — lequel pouvant être distant — via l’API REST. Ce dernier est configuré dans ~/.docker/config.json. Il existe de nombreux front-ends 4 :

Registries

Un registry est un dépôt d’images Docker. On compte notamment :

  • Docker Hub
  • GitHub Container Registry

Configuration

Le démon Docker est configuré via le fichier /etc/docker/daemon.json, conformément à la documentation dockerd.

Storage driver

Le driver de stockage contrôle comment les images et les conteneurs sont stockées et gérées sur l’hôte.

  • containerd : Docker Engine >= 29.0 utilise le stockage par défaut containerd, lequel usant de snapshooters pour le stockage des images.
  • overlay2 : Classic storage driver. Most widely compatible across all currently supported Linux distributions, and requires no extra configuration.
  • btrfs et zfs : Allow for advanced options, such as creating snapshots, but require more maintenance and setup. Each relies on the backing filesystem being configured correctly.

Daemon socket

À lire : https://docs.docker.com/engine/deprecated/#unauthenticated-tcp-connections

HTTP Proxies

Footnotes

  1. https://github.com/moby/moby/issues/38373#issuecomment-447393517

  2. https://wiki.archlinux.org/title/Docker

  3. https://github.com/moby/moby/issues/9976

  4. https://docs.docker.com/engine/security/security/ 2