Ce que vous allez apprendre :
- Créer des images personnalisées avec Dockerfile
- Optimiser la taille des images
- Utiliser Docker Compose pour applications multi-conteneurs
- Gérer les volumes et la persistance
- Bonnes pratiques de sécurité
- Publier vos images sur Docker Hub
Format :
- 20 min : Théorie Dockerfile et Docker Compose
- 40 min : Travaux pratiques intensifs
Qu'est-ce que Docker Compose ?
Docker Compose est un outil pour définir et exécuter des applications Docker multi-conteneurs. Avec Compose, vous utilisez un fichier YAML pour configurer les services de votre application. Ensuite, avec une seule commande, vous créez et démarrez tous les services depuis votre configuration.
Cas d'usage typique : WordPress + MySQL
Avantages :
- Configuration déclarative (fichier YAML)
- Gestion simplifiée de plusieurs conteneurs
- Réseaux et volumes automatiques
- Reproductibilité garantie
- Idéal pour développement et tests
Fichier docker-compose.yaml :
services:
wordpress:
image: wordpress
restart: always
ports:
- 8080:80
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: exampleuser
WORDPRESS_DB_PASSWORD: examplepass
WORDPRESS_DB_NAME: exampledb
volumes:
- wordpress:/var/www/html
networks:
- db
db:
image: mysql:8.0
restart: always
environment:
MYSQL_DATABASE: exampledb
MYSQL_USER: exampleuser
MYSQL_PASSWORD: examplepass
MYSQL_RANDOM_ROOT_PASSWORD: '1'
volumes:
- db:/var/lib/mysql
networks:
- db
volumes:
wordpress:
db:
networks:
db:
# Specity driver options
driver: bridge
driver_opts:
com.docker.network.bridge.host_binding_ipv4: "127.0.0.1"Source : https://hub.docker.com/_/wordpress
1. Créer le fichier docker-compose.yaml
# Créer un répertoire pour le projet
mkdir wordpress-app
cd wordpress-app
# Créer le fichier (copier le contenu du slide précédent)
notepad docker-compose.yaml # Windows
nano docker-compose.yaml # Linux/Mac
code docker-compose.yaml # Les deux ...2. Démarrer la topologie (WordPress + MySQL)
⚠️ ATTENTION : Bien arrêter le container utilisé en Exercice 1 :docker stop some-wordpress
docker compose upOptions utiles :
# Mode détaché (background)
docker compose up -d
# Reconstruire les images avant de démarrer
docker compose up --build
# Voir les logs en temps réel
docker compose up --no-start && docker compose logs -f3. Accéder au site
- Ouvrir http://localhost:8080
- Suivre l'assistant d'installation WordPress
Voir les logs de la topologie :
# Tous les services
docker compose logs
# Suivre les logs en temps réel
docker compose logs -f
# Logs d'un service spécifique
docker compose logs wordpress
docker compose logs db
# Dernières 50 lignes
docker compose logs --tail=50Lister les conteneurs :
docker compose ps
# Sortie :
# NAME IMAGE STATUS PORTS
# wordpress-app-db-1 mysql:8.0 Up 2 minutes 3306/tcp
# wordpress-app-wordpress-1 wordpress:latest Up 2 minutes 0.0.0.0:8080->80/tcpAutres commandes utiles :
# Arrêter les services
docker compose stop
# Démarrer les services arrêtés
docker compose start
# Redémarrer les services
docker compose restart
# Arrêter et supprimer les conteneurs
docker compose down
# Arrêter et supprimer conteneurs + volumes
docker compose down -vÉtape 1 : Identifier le service WordPress
docker compose ps
# Noter le nom du service : wordpressÉtape 2 : Entrer dans le conteneur
docker compose exec -ti wordpress bashÉtape 3 : Chercher le fichier
# Dans le conteneur
find / -name "wp-config.php" 2>/dev/null
# Résultat attendu :
# /var/www/html/wp-config.phpÉtape 4 : Sortir du conteneur
exitUtiliser docker compose cp :
# Voir l'aide
docker compose cp --help
# Copier du conteneur vers l'hôte
docker compose cp wordpress:/var/www/html/wp-config.php ./wp-config.php
# Vérifier que le fichier a été copié
ls -l wp-config.php # Linux/Mac
dir wp-config.php # WindowsCopier dans l'autre sens (hôte → conteneur) :
docker compose cp ./mon-fichier.php wordpress:/var/www/html/Alternative avec docker cp :
# Obtenir le nom complet du conteneur
docker compose ps
# Copier avec docker cp
docker cp wordpress-app-wordpress-1:/var/www/html/wp-config.php ./Voir les statistiques des conteneurs :
# Avec Docker
docker stats
# Sortie :
# CONTAINER ID NAME CPU % MEM USAGE / LIMIT
# abc123def456 wordpress-app-wordpress-1 0.5% 128MiB / 7.7GiB
# def456ghi789 wordpress-app-db-1 1.2% 256MiB / 7.7GiBCommandes utiles :
# Stats sans streaming (une seule fois)
docker stats --no-stream
# Stats de conteneurs spécifiques
docker stats wordpress-app-wordpress-1 wordpress-app-db-1
# Format personnalisé
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"Analyser :
- Utilisation CPU
- Consommation mémoire
- I/O réseau et disque
- Identifier les conteneurs gourmands
Objectif : Rendre WordPress accessible sur http://localhost:9080
Étape 1 : Arrêter les services
docker compose downÉtape 2 : Modifier docker-compose.yaml
services:
wordpress:
# ... autres configurations ...
ports:
- "9080:80" # Changé de 8080 à 9080Étape 3 : Redémarrer
docker compose up -dÉtape 4 : Vérifier
- Ouvrir http://localhost:9080
- WordPress devrait être accessible
Note : Le port interne (80) reste inchangé, seul le port externe change.
Référence : https://www.docker.com/blog/how-to-use-the-official-nginx-docker-image/
⚠️ ATTENTION : Ne pas suivre la section "Setting up a reverse proxy server" du tutoriel Docker. Concentrez-vous uniquement sur les sections de base pour cet exercice.
Lancer NGINX :
docker run -it --rm -d -p 8080:80 --name web nginxExplication des options :
-it: Mode interactif avec terminal--rm: Supprimer automatiquement après arrêt-d: Mode détaché (background)-p 8080:80: Mapper le port 80 du conteneur sur le port 8080 de l'hôte--name web: Nommer le conteneur "web"
Tester :
- Ouvrir http://localhost:8080
- Vous devriez voir la page d'accueil NGINX
Page par défaut :
Welcome to nginx!
If you see this page, the nginx web server is successfully installed and working.
Arrêter le conteneur :
docker stop webCréer un répertoire et un fichier HTML :
# Créer le répertoire
mkdir site-content
cd site-content
# Créer index.html
echo '<!doctype html>
<html>
<head>
<title>Mon Site</title>
</head>
<body>
<h1>Hello du container Nginx de votre-nom !</h1>
<p>Ceci est mon site web personnalisé !</p>
</body>
</html>' > index.htmlLancer NGINX avec un volume monté :
docker run -it --rm -d -p 8080:80 --name web \
-v ./site-content:/usr/share/nginx/html \
nginxExplication :
-v ./site-content:/usr/share/nginx/html: Monte le répertoire local dans le conteneur- Le contenu de
site-contentremplace le contenu par défaut de NGINX
Tester :
- Ouvrir http://localhost:8080
- Vous devriez voir votre HTML personnalisé
⚠️ ATTENTION : Utiliser des fichiers locaux plutôt que votre repértoire réseau pour cet exercice, quitte à copier/coller le contenu à la fin du TP.
Pourquoi créer un Dockerfile ?
- Les volumes sont parfaits pour le développement local
- Pour déployer, il faut inclure les fichiers dans l'image
- Le Dockerfile permet de créer une image portable
Créer un Dockerfile :
cd site-content
gedit DockerfileContenu du Dockerfile :
FROM nginx:latest
COPY ./index.html /usr/share/nginx/html/index.htmlExplication ligne par ligne :
-
FROM nginx:latest- Image de base : dernière version de NGINX
- Télécharge l'image si elle n'existe pas localement
-
COPY ./index.html /usr/share/nginx/html/index.html- Copie le fichier local
index.html - Vers le répertoire
/usr/share/nginx/html/dans l'image - Écrase le fichier par défaut de NGINX
- Copie le fichier local
Commande de build :
docker build -t webserver .Explication :
docker build: Commande pour construire une image-t webserver: Tag (nom) de l'image.: Contexte de build (répertoire courant)
Sortie attendue :
[+] Building 2.3s (7/7) FINISHED
=> [internal] load build definition from Dockerfile
=> => transferring dockerfile: 123B
=> [internal] load .dockerignore
=> [internal] load metadata for docker.io/library/nginx:latest
=> [1/2] FROM docker.io/library/nginx:latest
=> [internal] load build context
=> => transferring context: 234B
=> [2/2] COPY ./index.html /usr/share/nginx/html/index.html
=> exporting to image
=> => exporting layers
=> => writing image sha256:abc123...
=> => naming to docker.io/library/webserver
Vérifier l'image créée :
docker images webserver
# REPOSITORY TAG IMAGE ID CREATED SIZE
# webserver latest abc123def456 10 seconds ago 187MBArrêter l'ancien conteneur :
docker stop webLancer le nouveau conteneur :
docker run -it --rm -d -p 8080:80 --name web webserverDifférence importante :
- Pas besoin de
-v(volume) cette fois - Le HTML est inclus dans l'image
- L'image est portable et peut être partagée
Tester :
- Ouvrir http://localhost:8080
- Votre page HTML personnalisée s'affiche
Avantages :
- Image autonome et portable
- Pas de dépendance externe
- Peut être déployée n'importe où
- Prête pour la production
Étape 1 : Se connecter à Docker Hub
docker login
# Entrer votre nom d'utilisateur et mot de passe Docker HubÉtape 2 : Tagger l'image
docker tag webserver <votre-utilisateur-Docker>/mywebserver
# Exemple :
# docker tag webserver jdupont/mywebserverÉtape 3 : Pousser l'image
docker push <votre-utilisateur-Docker>/mywebserver
# Exemple :
# docker push jdupont/mywebserverSortie attendue :
The push refers to repository [docker.io/jdupont/mywebserver]
abc123def456: Pushed
def456ghi789: Mounted from library/nginx
...
latest: digest: sha256:xyz789... size: 1234
Étape 4 : Vérifier sur Docker Hub
- Aller sur https://hub.docker.com/r/votre-utilisateur/mywebserver
- Votre image est maintenant publique !
Supprimer votre image locale :
docker rmi votre-utilisateur/mywebserver
docker rmi webserverRécupérer l'image d'un collègue :
# Demander le nom d'utilisateur d'un collègue
docker pull utilisateur-collegue/mywebserver
# Lancer le conteneur
docker run -d -p 8081:80 utilisateur-collegue/mywebserverTester :
- Ouvrir http://localhost:8081
- Vous voyez le site de votre collègue !
Constats :
- Partage facile d'applications
- Reproductibilité garantie
- Collaboration simplifiée
- Base du DevOps moderne
| Instruction | Description | Exemple |
|---|---|---|
FROM |
Image de base | FROM node:18-alpine |
WORKDIR |
Répertoire de travail | WORKDIR /app |
COPY |
Copier fichiers | COPY . /app |
ADD |
Copier + extraire archives | ADD app.tar.gz /app |
RUN |
Exécuter commande (build) | RUN npm install |
CMD |
Commande par défaut | CMD ["node", "app.js"] |
ENTRYPOINT |
Point d'entrée | ENTRYPOINT ["python"] |
ENV |
Variable d'environnement | ENV NODE_ENV=production |
EXPOSE |
Documenter port | EXPOSE 3000 |
VOLUME |
Point de montage | VOLUME /data |
USER |
Utilisateur | USER node |
ARG |
Argument de build | ARG VERSION=1.0 |
Exemple complet :
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
USER node
CMD ["node", "server.js"]1. Utiliser des images de base légères
# ❌ Lourd (1.2 GB)
FROM ubuntu:latest
# ✅ Léger (5 MB)
FROM alpine:latest
# ✅ Optimisé pour Node.js (50 MB)
FROM node:18-alpine2. Multi-stage builds
# Stage 1: Build
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# Stage 2: Production
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY package*.json ./
RUN npm install --production
CMD ["node", "dist/server.js"]3. Minimiser les layers
# ❌ Plusieurs layers
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y git
# ✅ Un seul layer
RUN apt-get update && \
apt-get install -y curl git && \
rm -rf /var/lib/apt/lists/*4. Utiliser .dockerignore
node_modules
npm-debug.log
.git
.env
*.md
1. Ne pas exécuter en tant que root
FROM node:18-alpine
# Créer un utilisateur non-root
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
# Changer de propriétaire
COPY --chown=nodejs:nodejs . /app
# Utiliser l'utilisateur non-root
USER nodejs
CMD ["node", "server.js"]2. Scanner les vulnérabilités
# Avec Docker Scout (intégré)
docker scout cves nginx:latest
# Avec Trivy
trivy image nginx:latest3. Utiliser des versions spécifiques
# ❌ Version flottante
FROM node:latest
# ✅ Version fixe
FROM node:18.19.0-alpine3.194. Minimiser les privilèges
# Copier seulement ce qui est nécessaire
COPY --chown=nodejs:nodejs package*.json ./
COPY --chown=nodejs:nodejs src/ ./src/
# Pas de COPY . . qui copie tout5. Ne jamais inclure de secrets
# ❌ JAMAIS faire ça
ENV API_KEY=secret123
ENV DB_PASSWORD=password
# ✅ Utiliser des secrets Docker ou variables d'environnement au runtimeVariables d'environnement avec fichier .env :
# Fichier .env
MYSQL_ROOT_PASSWORD=secret123
WORDPRESS_VERSION=6.4# docker-compose.yaml
services:
wordpress:
image: wordpress:${WORDPRESS_VERSION}
environment:
WORDPRESS_DB_PASSWORD: ${MYSQL_ROOT_PASSWORD}Dépendances et healthchecks :
services:
db:
image: mysql:8.0
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
wordpress:
depends_on:
db:
condition: service_healthyRéseaux personnalisés :
services:
frontend:
networks:
- frontend-net
backend:
networks:
- frontend-net
- backend-net
networks:
frontend-net:
backend-net:✅ Docker Compose
- Configuration multi-conteneurs avec YAML
- Commandes : up, down, logs, ps, exec
- Gestion des volumes et réseaux
✅ Dockerfile
- Création d'images personnalisées
- Instructions principales (FROM, COPY, RUN, CMD)
- Build et tag d'images
✅ Optimisation
- Images légères (Alpine)
- Multi-stage builds
- Réduction des layers
✅ Sécurité
- Utilisateurs non-root
- Scan de vulnérabilités
- Versions spécifiques
✅ Partage
- Publication sur Docker Hub
- Collaboration entre équipes
Points à clarifier ?
- Dockerfile pas clair ?
- Problèmes avec Docker Compose ?
- Questions sur l'optimisation ?
Prochaine étape : Module 4 - Introduction à Kubernetes
Avant de continuer :
- Assurez-vous d'avoir réussi les exercices
- Votre image est-elle sur Docker Hub ?
- Testez les commandes Docker Compose
Prochaine étape : Kubernetes - L'orchestration à grande échelle !
Rendez-vous dans 15 minutes ! 🚀
- Slides 1-9 : Docker Compose (25 min)
- Slides 10-16 : Dockerfile et build (25 min)
- Slides 17-21 : Optimisation et sécurité (10 min)
- Vérifier que docker-compose.yaml est correct
- Aider au dépannage des problèmes de build
- S'assurer que tout le monde peut publier sur Docker Hub
- Encourager l'expérimentation
- Erreurs de syntaxe YAML (indentation)
- Port déjà utilisé
- Problèmes de permissions sur les volumes
- Échec de connexion à Docker Hub
- Créer un Dockerfile pour une application Python
- Ajouter un service Redis à docker-compose.yaml
- Optimiser une image existante
