Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Conteneurs et images

APERTO-NOTA

Prérequis


Installation et architecture

01. Installation de Docker

Solution to Exercise #

Résultat attendu de docker run hello-world :

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image...
 4. The Docker daemon streamed that output to the Docker client, which sent it to your terminal.

Comprendre la sortie de docker info :

docker info interroge le daemon et retourne l’état complet de l’installation. Sections clés à repérer :

Client:
 Version:    26.1.4               <- version du client CLI
 Context:    default

Server:
 Containers: 1                    <- nombre total de conteneurs
  Running:   0                    <- conteneurs actifs
  Paused:    0
  Stopped:   1                    <- conteneurs arrêtés (non supprimés)
 Images:     1                    <- images présentes localement
 Server Version: 26.1.4           <- version du daemon
 Storage Driver: overlay2         <- driver de système de fichiers en couches
  Backing Filesystem: extfs       <- système de fichiers de l'hôte
 Logging Driver: json-file        <- driver de logs par défaut
 Cgroup Driver: systemd           <- gestionnaire de Cgroups
 Cgroup Version: 2               <- version des Cgroups (v2 recommandé)
 Plugins:
  Volume:    local
  Network:   bridge host ipvlan macvlan null overlay
 Runtimes:   runc                 <- runtime OCI utilisé
 containerd version: ...
 runc version: ...

Operating System: Ubuntu 22.04.4 LTS
Architecture: x86_64
CPUs: 4                          <- processeurs disponibles
Total Memory: 7.6GiB             <- mémoire totale visible par Docker
Docker Root Dir: /var/lib/docker <- répertoire de stockage des données
Registry: https://index.docker.io/v1/  <- registry par défaut (Docker Hub)

Points de vigilance :

  • Storage Driver: overlay2 est le driver recommandé sur Linux — vfs indique un problème de configuration

  • Cgroup Version: 2 est la version moderne ; 1 est progressivement abandonnée

  • Docker Root Dir : surveiller l’espace disque disponible sur cette partition

  • Warnings: en bas de la sortie signale des problèmes de configuration à corriger


02. Prise en main de Docker Desktop

Solution to Exercise #

Correspondance Desktop / CLI :

Écran Docker DesktopCommande CLI équivalente
Containers → listedocker ps -a
Containers → Logsdocker logs NOM
Containers → Statsdocker stats NOM --no-stream
Containers → Terminaldocker exec -it NOM sh
Containers → Inspectdocker inspect NOM
Images → listedocker images
Images → Pulldocker pull IMAGE:TAG
Volumes → listedocker volume ls
Settings → Resourcesdocker info (CPUs, Total Memory)
Settings → Docker Engine/etc/docker/daemon.json (Linux)

Section Resources : les limites affichées (ex. 4 CPU, 8 Go RAM) correspondent aux ressources allouées à la VM Linux interne que Docker Desktop utilise sur Windows et macOS. Sur Linux natif, Docker accède directement au matériel — il n’y a pas de limite de ce type.

Section Docker Engine : le JSON affiché est le contenu de daemon.json. On peut y ajouter des paramètres comme un registry miroir ou un log driver spécifique.


03. Syntaxe et aide en ligne

Solution to Exercise #

docker --help affiche deux catégories de commandes :

Management Commands:   <- commandes orientées objet (moderne)
  container   Manage containers
  image       Manage images
  network     Manage networks
  volume      Manage volumes
  ...

Commands:              <- commandes historiques (toujours valides)
  run         Create and run a new container
  ps          List containers
  images      List images
  rm          Remove containers
  rmi         Remove images
  ...
Ancienne syntaxeSyntaxe moderne
docker psdocker container ls
docker ps -adocker container ls -a
docker imagesdocker image ls
docker rm NOMdocker container rm NOM
docker rmi IMAGEdocker image rm IMAGE
docker inspect NOMdocker container inspect NOM

Les deux syntaxes coexistent. La syntaxe moderne est préférée pour la lisibilité et la cohérence dans les scripts.

Options clés de docker run :

-d, --detach          Détacher le conteneur (arrière-plan)
-it                   Mode interactif + TTY
--name string         Nommer le conteneur
-p, --publish list    Publier un port : HÔTE:CONTENEUR
-e, --env list        Variable d'environnement
-v, --volume list     Monter un volume
--network string      Réseau à utiliser
--rm                  Supprimer le conteneur à l'arrêt
--restart string      Politique de redémarrage
--memory string       Limite mémoire (ex. 128m)
--cpus decimal        Limite CPU (ex. 0.5)

04. Limiter et surveiller les ressources

Solution to Exercise #

Étape 1 — docker inspect sans limite :

"Memory": 0,
"NanoCpus": 0,

0 = pas de limite. Le conteneur peut utiliser toutes les ressources de l’hôte.

Étape 2 — docker inspect avec limites :

"Memory": 134217728,
"NanoCpus": 500000000,
  • 134217728 octets = 128 Mo

  • 500000000 nanoCPUs = 0,5 CPU

Étape 3 — Dépassement mémoire :

Killed

Le processus est terminé par le kernel OOM Killer. docker inspect affichera "OOMKilled": true dans l’état du conteneur.

Étape 4 — docker update :

Les limites sont modifiables à chaud sans redémarrer le conteneur — utile pour absorber un pic de charge imprévu sans interruption de service.

Quand utiliser les limites de ressources :

  • Toujours en production sur un hôte multi-conteneurs

  • Quand plusieurs services partagent le même serveur (base de données, web, cache)

  • Pour protéger l’hôte contre un conteneur défaillant ou malveillant

  • En CI/CD pour éviter qu’un build runaway bloque les autres jobs


05. Isolation des processus et diagnostic

Solution to Exercise (Note) #

Étape 1 — PID depuis l’hôte :

root  3842  nginx: master process nginx -g daemon off;
nginx 3871  nginx: worker process

PID depuis docker top :

UID    PID    PPID   C  STIME  TTY  TIME      CMD
root   3842   3821   0  10:00  ?    00:00:00  nginx: master process nginx -g daemon off;
nginx  3871   3842   0  10:00  ?    00:00:00  nginx: worker process

PID via /proc/1/status depuis l’intérieur :

Name:   nginx
Pid:    1

Depuis le conteneur, nginx est le PID 1. Depuis l’hôte (ps ou docker top), le même processus a un PID élevé (ex. 3842). C’est l’effet du Namespace PID.

Étape 2 — hostname :

# Hôte : mon-serveur
# Conteneur : a1b2c3d4e5f6  (ID court du conteneur)

Personnalisable avec --hostname MON_NOM au lancement.

Étape 3 — interfaces réseau :

# Hôte : eth0, docker0 172.17.0.1, ...
# Conteneur : lo, eth0@if19 172.17.0.2/16

Chaque conteneur a sa propre pile réseau via le Namespace net.

Étape 4 — nsenter :

# ip addr depuis le namespace réseau du conteneur
1: lo: <LOOPBACK,UP>
18: eth0@if19: <BROADCAST,UP> inet 172.17.0.2/16

Quand utiliser nsenter :

  • Conteneur basé sur une image distroless ou scratch (pas de shell)

  • Conteneur bloqué qui ne répond plus au daemon Docker

  • Inspection réseau sans installer d’outils dans l’image de production

  • Audit de sécurité depuis l’hôte sans modifier le conteneur


Comparer VM et conteneurs

06. Empreinte mémoire et temps de démarrage

Solution to Exercise #

Étape 1 — docker stats --no-stream :

CONTAINER ID   NAME         CPU %   MEM USAGE / LIMIT   MEM %
a1b2c3d4e5f6   nginx-demo   0.00%   3.2MiB / 7.6GiB     0.04%

Étape 2 — time docker run :

Conteneur démarré
real    0m0.412s

Démarrage en moins d’une seconde. Une VM met 20 à 60 s.

Étape 3 — docker inspect (mémoire/CPU) :

"Memory": 0,
"NanoCpus": 0,
"CpuShares": 0,

0 signifie aucune limite imposée — le conteneur peut utiliser toutes les ressources disponibles. Pour limiter : docker run --memory 128m --cpus 0.5 ...

Étape 4 — docker image inspect (taille) :

"Size": 43122160,
"VirtualSize": 43122160,

Soit ~43 Mo. Une image VM Ubuntu de base occupe 2 à 4 Go.

Étape 5 — docker ps --size :

CONTAINER ID   IMAGE          SIZE
a1b2c3d4e5f6   nginx:alpine   2B (virtual 43.1MB)

La couche d’écriture du conteneur actif (2 B ici) s’ajoute à la taille virtuelle de l’image de base, qui reste partagée en lecture seule entre tous les conteneurs issus de nginx:alpine.

Étape 6 — docker system df :

TYPE            TOTAL   ACTIVE   SIZE      RECLAIMABLE
Images          3       1        187MB     144MB (77%)
Containers      1       1        2B        0B
Local Volumes   0       0        0B        0B
Build Cache     0       0        0B        0B

Étape 7 — VmRSS depuis /proc :

VmRSS:      3284 kB

Mesure directe du noyau — cohérente avec ce que docker stats rapporte. Confirme que Docker ne fait pas de magie : c’est un processus Linux ordinaire, visible depuis l’hôte.

Étape 8 — nsenter :

# ps aux depuis le namespace PID du conteneur
PID   USER   COMMAND
1     root   nginx: master process
29    nginx  nginx: worker process

# ip addr depuis le namespace réseau du conteneur
1: lo: <LOOPBACK,UP>
18: eth0@if19: <BROADCAST,UP>  inet 172.17.0.2/16

nsenter entre directement dans les namespaces du conteneur sans passer par le daemon Docker. Utile pour diagnostiquer un conteneur dont l’image ne contient pas de shell.

Étape 9 — namespaces du processus :

lrwxrwxrwx 1 root root 0 cgroup -> cgroup:[4026531835]
lrwxrwxrwx 1 root root 0 ipc    -> ipc:[4026532345]
lrwxrwxrwx 1 root root 0 mnt    -> mnt:[4026532343]
lrwxrwxrwx 1 root root 0 net    -> net:[4026532348]
lrwxrwxrwx 1 root root 0 pid    -> pid:[4026532346]
lrwxrwxrwx 1 root root 0 user   -> user:[4026531837]
lrwxrwxrwx 1 root root 0 uts    -> uts:[4026532344]

Chaque namespace correspond à une dimension d’isolation : pid (processus), net (réseau), mnt (montages), ipc (IPC), uts (hostname), user (utilisateurs). Les numéros d’inode diffèrent de ceux de l’hôte — preuve de l’isolation effective.

Machine virtuelleConteneur Docker
Empreinte mémoire256 Mo – 2 Go3 – 50 Mo
Taille image disque2 – 20 Go5 – 200 Mo
Temps de démarrage20 – 60 s< 1 s
IsolationHyperviseur + OS invitéNamespaces + Cgroups
Visibilité depuis l’hôteOpaque (hyperviseur)Processus visible dans /proc
Densité sur un hôte10 – 20 VM100+ conteneurs

07. Coexistence de versions

Solution to Exercise #

Résultat attendu :

v18.20.4
v20.15.1
v22.3.0
Node.js absent de l'hôte

Sans Docker, faire coexister trois versions de Node.js nécessiterait nvm ou une gestion manuelle des chemins. Docker isole chaque version dans son propre conteneur — aucune interférence possible, aucune installation résiduelle sur l’hôte.


08. Déploiement rapide

Solution to Exercise #

Sur des VM classiques : provisioning + installation OS + Apache + PHP + MySQL + WordPress = 30 min minimum, sans compter la configuration réseau entre les machines.

Avec Docker : docker run tire les images, démarre les conteneurs et les connecte en réseau en moins de 2 minutes, à l’identique sur n’importe quel hôte disposant de Docker.

Gestion des conteneurs et images

09. Cycle de vie d’un conteneur

Solution to Exercise #

Résultat attendu de docker images ubuntu-custom :

REPOSITORY      TAG   IMAGE ID       CREATED          SIZE
ubuntu-custom   1.0   a1b2c3d4e5f6   30 seconds ago   132MB

Résultat attendu dans le conteneur modèle (étape 4) :

modification
/usr/bin/curl

L’état du conteneur d’origine est intégralement présent dans le nouveau conteneur.


10. Supervision des conteneurs

Solution to Exercise #

Résultat attendu de docker logs après un curl :

172.17.0.1 - - [15/Jun/2026:10:00:00 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/7.88.1" "-"

Résultat attendu de docker top :

UID    PID    PPID   C  STIME  TTY  TIME      CMD
root   1234   1210   0  10:00  ?    00:00:00  nginx: master process nginx -g daemon off;
nginx  1265   1234   0  10:00  ?    00:00:00  nginx: worker process

Résultat attendu de docker stats --no-stream :

CONTAINER ID   NAME            CPU %   MEM USAGE / LIMIT   MEM %
a1b2c3d4e5f6   nginx-monitor   0.00%   6.5MiB / 7.6GiB     0.08%

11. Variables d’environnement

Résultat attendu de SHOW DATABASES; :

+--------------------+
| Database           |
+--------------------+
| mylab_docker       |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
VariableRôle
MYSQL_ROOT_PASSWORDMot de passe root (obligatoire)
MYSQL_DATABASECrée une base à l’initialisation
MYSQL_USER / MYSQL_PASSWORDCrée un utilisateur dédié

Création d’images

12. Construire une image avec Dockerfile

Résultat attendu de docker history mon-site:1.0 :

IMAGE          CREATED        CREATED BY                                      SIZE
a1b2c3d4e5f6   1 minute ago   CMD ["nginx" "-g" "daemon off;"]               0B
<missing>      1 minute ago   COPY index.html /usr/share/nginx/html/index.…  312B
<missing>      2 weeks ago    EXPOSE map[80/tcp:{}]                           0B
...

Chaque instruction Dockerfile génère une couche. Les couches héritées de nginx:alpine sont partagées avec toutes les images qui en dérivent — aucune duplication sur le disque.


13. Optimiser un Dockerfile

Comparaison de tailles typique :

REPOSITORY    TAG   SIZE
flask-avant   1.0   512MB
flask-apres   1.0   71MB
Problème initialCorrection
8 commandes RUN distinctes1 seule commande chaînée avec &&
Image Ubuntu (77 Mo)Image python:alpine (24 Mo)
vim et curl inutiles en prodSupprimés
Code copié avant dépendancesrequirements.txt en premier (cache Docker)

Exercice en autonomie - Choisir et configurer les images pour un site WordPress

Solution to Exercise #

Images recommandées pour la pile WordPress / load balancing

ComposantImageTag recommandéVarianteRaison
Base de donnéesmariadb10.11DebianVersion LTS, compatibilité garantie avec WordPress
Applicationwordpress6.5-php8.3-apacheApachePlus simple à démarrer ; intègre Apache
Reverse proxy / LBnginx1.27-alpineAlpineImage minimale ~5 Mo, surface d’attaque réduite

Variables d’environnement de l’image mariadb :

VariableObligatoireRôle
MYSQL_ROOT_PASSWORDOuiMot de passe administrateur
MYSQL_DATABASENonCrée une base au démarrage
MYSQL_USERNonCrée un utilisateur applicatif
MYSQL_PASSWORDNon (requis si MYSQL_USER)Mot de passe de l’utilisateur

Variables d’environnement de l’image wordpress :

VariableRôle
WORDPRESS_DB_HOSTNom ou IP du serveur MariaDB
WORDPRESS_DB_NAMENom de la base de données
WORDPRESS_DB_USERUtilisateur applicatif
WORDPRESS_DB_PASSWORDMot de passe
WORDPRESS_TABLE_PREFIXPréfixe des tables (optionnel, sécurité)

Exemple de fichier .env :

.env
# Base de données
MYSQL_ROOT_PASSWORD=R00t_S3cur3!2026   
MYSQL_DATABASE=wordpress_prod
MYSQL_USER=wp_app
MYSQL_PASSWORD=Wp_Appl1c@tion!

# WordPress
WORDPRESS_TABLE_PREFIX=wpx_

Exemple de fichier .env

Fichier .gitignore :

.env
*.env
*.env.*

Règles de sécurité pour les fichiers .env :

  • Ne jamais committer un .env contenant des secrets dans Git, même dans un dépôt privé

  • Un secret poussé par erreur reste dans l’historique même après suppression — il faut le révoquer

  • Utiliser des mots de passe d’au moins 16 caractères avec majuscules, chiffres et caractères spéciaux

  • Ne pas utiliser le même mot de passe pour MYSQL_ROOT_PASSWORD et MYSQL_PASSWORD

  • En production, préférer un gestionnaire de secrets (Vault, Docker Secrets) plutôt qu’un fichier .env

  • Vérifier les permissions : chmod 600 .env (lecture propriétaire uniquement)

Pourquoi Alpine pour Nginx et Debian pour MariaDB / WordPress ?

Nginx en Alpine (~5 Mo) : surface d’attaque minimale, temps de pull rapide en CI/CD, parfaitement suffisant pour un reverse proxy sans extensions système.

MariaDB et WordPress en Debian : les extensions PHP et les drivers MySQL dépendent de bibliothèques système absentes d’Alpine ; la compatibilité est plus large et les images officielles sont mieux testées sur cette base.