Initiation à Docker — Conteneurisation d'un serveur web

Fiche mission

Technicien : Dufosse
Module : BTS SIO SISR — VIRT-05 « Initiation à Docker » et « Persistance et réseau des conteneurs »
Environnement : VM Ubuntu 26.04, administrée en SSH

Contexte et objectifs

Docker est un moteur de conteneurisation : il isole une application avec ses librairies et dépendances dans un environnement autonome. Contrairement à la virtualisation classique, où c'est le matériel qui est partagé, les conteneurs partagent le noyau de l'OS hôte, ce qui les rend bien plus légers et rapides à lancer.

Dans ce TP, j'ai installé Docker sur une VM Ubuntu, construit ma propre image de serveur web nginx à partir d'un Dockerfile, publié le service sur le réseau, personnalisé son contenu (directives COPY et ADD), ouvert un shell interactif dans un conteneur et rendu ses journaux persistants grâce à un volume.

Docker Dockerfile nginx Ubuntu 26.04 Conteneurisation Volumes

Étape A — Installation et construction de l'image

Preuve 1 — Installation de Docker

J'ai installé le moteur Docker depuis les dépôts Ubuntu (paquet docker.io, version 29.1.3), dans un répertoire de travail dédié au TP.

mkdir ~/tp_docker && cd ~/tp_docker
apt update
apt install -y docker.io
Installation du paquet docker.io
APT Docker Engine
Preuve 2 — Dockerfile et build

J'ai écrit un Dockerfile qui part d'une image Ubuntu, installe nginx, expose le port 80 et lance nginx au premier plan (daemon off;) pour que le conteneur reste actif. L'image est construite sous le nom mon_serveur_nginx.

FROM ubuntu:26.04
RUN apt update \
    && apt install -yf \
    nginx
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
docker build -t mon_serveur_nginx .
Lancement du build et téléchargement de l'image ubuntu:26.04

Au lancement, Docker télécharge l'image de base ubuntu:26.04 depuis Docker Hub, couche par couche. Les avertissements debconf affichés pendant le build ne sont pas des erreurs : aucun terminal n'est disponible pendant la construction, l'installation passe donc en mode non interactif. Les services ne démarrent pas non plus à ce stade, c'est l'instruction CMD qui lance nginx au démarrage du conteneur.

Build de l'image mon_serveur_nginx
Dockerfile docker build Couches d'image
Preuve 3 — Images et lancement du conteneur

docker image ls montre deux images : mon_serveur_nginx (236 Mo) et ubuntu:26.04 (160 Mo). L'image Ubuntu est présente car elle sert de base (FROM) : Docker la télécharge depuis Docker Hub puis empile les couches du Dockerfile par-dessus. La différence de taille correspond à la couche nginx.

docker run --name instance_nginx --detach --publish 8000:80 mon_serveur_nginx

Le conteneur tourne en arrière-plan et le port 8000 de la VM est redirigé vers le port 80 du conteneur. On voit apparaître le pont docker0 et l'interface virtuelle veth du conteneur. Le serveur est ensuite géré avec docker ps (liste des conteneurs actifs) et docker stop.

docker image ls, docker run et interfaces réseau docker ps puis docker stop

docker ps confirme le conteneur instance_nginx actif avec le port 8000 publié, puis docker stop l'arrête.

docker run Publication de ports Réseau bridge

Étape B — Personnalisation du contenu

Preuve 4 — Page personnalisée avec COPY

J'ai ajouté un fichier index.html à l'image grâce à la directive COPY, placée juste avant EXPOSE, puis reconstruit l'image et relancé le conteneur (après suppression de l'ancien, dont le nom était encore réservé).

COPY ./index.html /var/www/html/index.html

Le navigateur affiche désormais ma page à la place de la page par défaut de nginx.

Page index.html servie par le conteneur
COPY Rebuild docker rm
Preuve 5 — Contenu distant avec ADD

J'ai remplacé COPY par ADD, qui peut récupérer un fichier depuis une URL au moment du build. Le conteneur sert alors la page « World Wide Web » du CERN, le tout premier site web de l'histoire.

ADD --chown=www-data:www-data http://info.cern.ch/hypertext/WWW/TheProject.html /var/www/html/index.html

L'option --chown donne le fichier à l'utilisateur www-data, sous lequel tourne nginx. Sans elle, le fichier téléchargé appartiendrait à root avec des droits restreints et nginx renverrait une erreur 403.

Page du CERN récupérée par ADD
DirectiveFichiers locauxURL distanteExtraction tar auto
COPYOuiNonNon
ADDOuiOuiOui

Bonne pratique : utiliser COPY par défaut, plus prévisible, et réserver ADD aux cas qui en ont besoin.

ADD Droits Linux chown

Étape C — Shell interactif et persistance

Preuve 6 — Shell dans un conteneur

J'ai ouvert un shell directement dans un conteneur. L'option -i garde l'entrée standard ouverte et -t alloue un pseudo-terminal : ensemble, elles donnent un shell interactif. Le /bin/bash remplace le CMD, nginx n'est donc pas lancé dans ce cas.

docker run -i -t mon_serveur_nginx /bin/bash

La capture illustre l'isolation : depuis l'intérieur du conteneur, la commande docker n'existe pas. Le conteneur a son propre système de fichiers et ne voit pas les outils installés sur l'hôte.

docker introuvable depuis le shell du conteneur
Mode interactif Isolation
Preuve 7 — Volume pour les journaux

J'ai créé un volume nommé nginx_log et l'ai monté sur /var/log/ du conteneur pour rendre les journaux persistants.

docker volume create nginx_log
docker run -d -p 8000:80 -v nginx_log:/var/log/ mon_serveur_nginx
Création du volume nginx_log

Sur l'hôte, le volume se trouve dans /var/lib/docker/volumes/nginx_log/_data/. Comme il était vide, Docker l'a rempli au premier montage avec le contenu existant de /var/log du conteneur.

FichierRôle
nginx/access.logChaque requête HTTP : IP client, date, méthode et page, code de réponse, navigateur
nginx/error.logErreurs de nginx (vide : aucune erreur)
apt/Historique des installations apt faites pendant le build
dpkg.logDétail des paquets installés
alternatives.logLiens système créés pendant l'installation
Contenu du volume et access.log

Les requêtes de mon navigateur apparaissent dans access.log avec un code 304 Not Modified (page déjà en cache). Les logs restent sur l'hôte même si le conteneur est supprimé, et se consultent sans entrer dans le conteneur.

docker volume Persistance Logs nginx

Étape D — Persistance avec un bind mount

Preuve 8 — Dossier de l'hôte monté dans le conteneur

Un conteneur est stateless : s'il est supprimé, ses données disparaissent avec lui. Après avoir arrêté les instances précédentes, j'ai monté un dossier de la VM directement dans le conteneur (bind mount), à l'emplacement servi par nginx.

docker rm -f $(docker ps -q)
mkdir ~/tp_docker/data
docker run -d -p 8000:80 -v ~/tp_docker/data/:/var/www/html/fichiers mon_serveur_nginx
Suppression des conteneurs en cours

J'ai créé document.html sur la VM uniquement. Il apparaît aussitôt dans le conteneur, puis nginx le sert sans reconstruire l'image.

document.html visible dans le conteneur document.html servi par nginx
TypeEmplacement sur l'hôteUsage typique
Volume nomméGéré par Docker (/var/lib/docker/volumes)Données applicatives, logs
Bind mountDossier choisi (~/tp_docker/data)Contenu modifié depuis l'hôte, fichiers de config
Bind mount Persistance docker exec

Étape E — Réseau entre conteneurs

Preuve 9 — Réseau dédié tp_ct_net

J'ai créé un réseau Docker dédié et y ai connecté le serveur nginx sous le nom serveur. Le réseau utilise le driver bridge avec le sous-réseau 172.18.0.0/16, distinct du réseau par défaut en 172.17.0.0/16.

docker network create tp_ct_net
docker run -d -p 8000:80 -v ~/tp_docker/data/:/var/www/html/fichiers \
  --network tp_ct_net --name serveur mon_serveur_nginx
docker network inspect tp_ct_net
docker network inspect tp_ct_net
docker network Driver bridge Adressage IP
Preuve 10 — Communication par nom de conteneur

J'ai lancé un second conteneur Alpine sur le même réseau, puis pingé le serveur par son nom.

docker run --network tp_ct_net -it alpine:latest /bin/sh
ping -c 4 serveur
Conteneur Alpine sur tp_ct_net Ping du conteneur serveur

Le nom serveur est résolu en 172.18.0.2 grâce au DNS interne que Docker fournit sur les réseaux créés par l'utilisateur (absent du réseau par défaut). Résultat : 4 paquets reçus sur 4, 0 % de perte, environ 0,1 ms de latence.

DNS Docker Alpine ICMP Isolation réseau

Bilan

Compétences mobilisées

Ce TP m'a permis de comprendre le cycle de vie complet d'un conteneur : écriture d'un Dockerfile, construction d'une image en couches, exécution, publication de ports, mise à jour du contenu, accès interactif, persistance des données (volumes et bind mounts) et mise en réseau de plusieurs conteneurs. Ce sont les mêmes briques que j'utilise sur mon serveur personnel, où plusieurs services tournent en conteneurs.

Virtualisation Docker Linux Services réseau