Docker Compose — Application multi-conteneurs
Technicien : Dufosse
Module : BTS SIO SISR — VIRT-05 « Introduction à Docker Compose »
Environnement : VM Ubuntu 26.04, Docker Compose v2
Docker Compose permet de décrire et de lancer une application composée de
plusieurs conteneurs à partir d'un seul fichier YAML. Chaque conteneur devient un
service, et le fichier définit aussi les réseaux et les volumes. C'est une
forme simple d'Infrastructure as Code : toute l'architecture se déploie avec
une seule commande, docker compose up -d.
J'ai construit progressivement une stack de trois services : un serveur web nginx, un serveur de base de données MariaDB et un client MariaDB construit à partir de mon propre Dockerfile. J'ai ensuite rendu la base persistante avec un volume, puis isolé la base sur un réseau interne en supprimant son exposition sur l'hôte.
Étape A — Premiers services
Premier service du Compose file : un serveur nginx basé sur l'image nginx:alpine,
publié sur le port 8080 de la VM et relancé automatiquement en cas d'arrêt
(restart: unless-stopped).
services:
web:
image: nginx:alpine
container_name: my-web-server
ports:
- "8080:80"
environment:
- NGINX_HOST=localhost
- NGINX_PORT=80
restart: unless-stopped
J'ai ajouté un service db_server basé sur l'image officielle MariaDB, avec le mot
de passe root défini par variable d'environnement et la base exposée sur le port 33060 de
l'hôte.
db_server:
image: mariadb:latest
container_name: db_server
ports:
- "33060:3306"
environment:
- MARIADB_ROOT_PASSWORD=BTS_Ynov26
restart: unless-stopped
Le mot de passe est volontairement laissé en clair : c'est celui d'un environnement de TP, supprimé à la fin de la séance. En production, il serait sorti du fichier Compose (fichier .env exclu de Git ou secret Docker).
Les logs montrent deux fois « ready for connections » : au premier lancement, le script d'initialisation démarre un serveur temporaire pour créer la base et appliquer le mot de passe, puis lance le serveur définitif.
Étape B — Conteneur construit par Compose
J'ai écrit un Dockerfile basé sur Alpine Linux qui installe uniquement le client MariaDB et Bash, puis garde le conteneur actif pendant une heure pour pouvoir s'y connecter.
FROM alpine:latest
RUN apk add --no-cache mariadb-client bash
CMD ["bash", "-c", "sleep 3600 & wait"]
Avant de l'intégrer au Compose file, je l'ai testé seul. Le conteneur contient bien
bash et mariadb-client ; mariadb-common n'est qu'une
dépendance du client, aucun serveur n'est installé.
Le service db_client utilise build: . : Compose construit lui-même
l'image à partir du Dockerfile du dossier. depends_on garantit que la base
démarre avant le client.
db_client:
build: .
container_name: db_client
depends_on:
- db_server
Depuis le client, je me connecte à la base en utilisant simplement le nom du service
db_server, résolu par le DNS interne de Docker.
Étape C — Persistance de la base
Pour prouver la persistance, j'ai créé une base test_persistance contenant une
table et une ligne, puis supprimé et recréé les conteneurs avec docker compose down
puis docker compose up -d.
Sans volume, la base a disparu : seules les bases système subsistent.
J'ai alors ajouté un volume nommé db_data, monté sur /var/lib/mysql
où MariaDB stocke ses fichiers, et refait exactement le même test.
db_server:
volumes:
- db_data:/var/lib/mysql
volumes:
db_data:
Avec le volume, les données sont toujours présentes après recréation.
docker compose down supprime les conteneurs mais conserve les volumes nommés (sauf avec l'option -v).
Étape D — Isolation réseau
Exposer une base de données sur l'hôte n'est pas une bonne pratique de sécurité. J'ai donc
créé un réseau backend réservé à db_server et db_client,
et supprimé la publication du port 33060. Le service web reste seul sur le réseau par défaut.
name: myapp
services:
web:
image: nginx:alpine
container_name: my-web-server
ports:
- "8080:80"
environment:
- NGINX_HOST=localhost
- NGINX_PORT=80
restart: unless-stopped
db_server:
image: mariadb:latest
container_name: db_server
environment:
- MARIADB_ROOT_PASSWORD=BTS_Ynov26
volumes:
- db_data:/var/lib/mysql
networks:
- backend
restart: unless-stopped
db_client:
build: .
container_name: db_client
depends_on:
- db_server
networks:
- backend
volumes:
db_data:
networks:
backend:
| Vérification | Résultat attendu | Résultat |
|---|---|---|
docker compose ps | db_server n'a plus de port publié | OK |
docker network ls | Réseaux myapp_backend et myapp_default | OK |
ss -tlnp | grep 33060 | Aucun processus en écoute | OK |
Requête depuis db_client | Données toujours accessibles | OK |
La base n'est plus joignable depuis la VM ni depuis le réseau local, mais le client y accède
toujours via le réseau interne. Ce dernier test valide en même temps l'isolation réseau et la
persistance, puisque les données ont survécu à la recréation de db_server.
Bilan
Ce TP m'a permis de passer de conteneurs lancés un par un à une architecture complète décrite dans un seul fichier versionnable : services, image construite sur mesure, volume persistant et segmentation réseau. C'est la même approche que j'utilise sur mon serveur personnel, où l'ensemble des services est déployé avec Docker Compose.