Docker : Du développement à la production
Guide complet Docker pour conteneuriser vos applications. Dockerfile, Docker Compose, multi-stage builds et déploiement production expliqués avec exemples pratiques.

Docker révolutionne la façon de développer, tester et déployer des applications. En encapsulant une application et ses dépendances dans un conteneur portable, Docker élimine le fameux "ça marche sur ma machine" et garantit une cohérence entre tous les environnements. Ce guide couvre le parcours complet, du premier Dockerfile au déploiement en production.
Docker Desktop 5.x apporte des améliorations majeures de performance, notamment le support natif de containerd, une gestion optimisée des ressources et une intégration transparente avec Kubernetes. Les images multi-architecture (ARM/x86) sont désormais standard.
Les fondamentaux de la conteneurisation
Un conteneur est une unité logicielle légère qui package le code, le runtime, les bibliothèques système et les paramètres. Contrairement aux machines virtuelles qui virtualisent le matériel, les conteneurs partagent le noyau du système hôte, ce qui les rend plus rapides à démarrer et moins gourmands en ressources.
# terminal
# Installation de Docker sur Ubuntu
sudo apt update
sudo apt install -y docker.io
# Ajout de l'utilisateur au groupe docker (évite sudo)
sudo usermod -aG docker $USER
# Vérification de l'installation
docker --version
# Docker version 26.1.0, build 1234567
# Premier conteneur : télécharge l'image et exécute
docker run hello-worldCette commande télécharge l'image hello-world depuis Docker Hub et lance un conteneur qui affiche un message de confirmation.
# terminal
# Lister les conteneurs en cours d'exécution
docker ps
# Lister tous les conteneurs (y compris arrêtés)
docker ps -a
# Lister les images téléchargées
docker images
# Supprimer un conteneur
docker rm <container_id>
# Supprimer une image
docker rmi <image_name>Ces commandes de base permettent de gérer le cycle de vie des conteneurs et des images.
Création du premier Dockerfile
Un Dockerfile contient les instructions pour construire une image Docker. Chaque instruction crée une couche (layer) dans l'image finale, ce qui permet la mise en cache et la réutilisation.
# Dockerfile
# Image de base : Node.js 22 sur Alpine Linux (légère)
FROM node:22-alpine
# Définition du répertoire de travail dans le conteneur
WORKDIR /app
# Copie des fichiers de dépendances en premier (optimisation cache)
COPY package*.json ./
# Installation des dépendances
RUN npm ci --only=production
# Copie du code source
COPY . .
# Exposition du port (documentation)
EXPOSE 3000
# Commande de démarrage
CMD ["node", "server.js"]L'ordre des instructions est crucial pour l'optimisation du cache. Les fichiers qui changent rarement (package.json) sont copiés avant le code source.
# terminal
# Construction de l'image avec un tag
docker build -t my-app:1.0 .
# Exécution du conteneur
docker run -d -p 3000:3000 --name my-app-container my-app:1.0
# Vérification des logs
docker logs my-app-container
# Accès au shell du conteneur
docker exec -it my-app-container shLe flag -d lance le conteneur en arrière-plan, -p mappe le port 3000 du conteneur vers le port 3000 de l'hôte.
Les images Alpine sont significativement plus petites (environ 5 Mo vs 120 Mo pour Debian). Cependant, elles utilisent musl libc au lieu de glibc, ce qui peut causer des incompatibilités avec certaines dépendances natives. En cas de problème, préférer les images basées sur Debian (node:22-slim).
Multi-stage builds pour la production
Les multi-stage builds permettent de créer des images de production optimisées en séparant l'environnement de build de l'environnement d'exécution. Seuls les artefacts nécessaires sont inclus dans l'image finale.
# Dockerfile.production
# ============================================
# Stage 1 : Build
# ============================================
FROM node:22-alpine AS builder
WORKDIR /app
# Copie et installation des dépendances (incluant devDependencies)
COPY package*.json ./
RUN npm ci
# Copie du code source
COPY . .
# Build de l'application (TypeScript, bundling, etc.)
RUN npm run build
# ============================================
# Stage 2 : Production
# ============================================
FROM node:22-alpine AS production
# Utilisateur non-root pour la sécurité
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
WORKDIR /app
# Copie uniquement les fichiers nécessaires depuis le stage builder
COPY /app/dist ./dist
COPY /app/node_modules ./node_modules
COPY /app/package.json ./
# Changement vers l'utilisateur non-root
USER nodejs
# Variables d'environnement
ENV NODE_ENV=production
ENV PORT=3000
EXPOSE 3000
# Commande de démarrage
CMD ["node", "dist/server.js"]Cette approche réduit considérablement la taille de l'image finale en excluant les outils de build, les devDependencies et les fichiers source.
# terminal
# Construction avec le fichier spécifique
docker build -f Dockerfile.production -t my-app:production .
# Comparaison des tailles d'images
docker images | grep my-app
# my-app production abc123 150MB
# my-app 1.0 def456 450MBLa réduction de taille peut atteindre 60-70% selon les projets, améliorant les temps de déploiement et réduisant la surface d'attaque.
Docker Compose pour l'orchestration locale
Docker Compose simplifie la gestion d'applications multi-conteneurs. Un fichier YAML déclare tous les services, leurs configurations et leurs dépendances.
# docker-compose.yml
version: "3.9"
services:
# Application principale
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- DATABASE_URL=postgresql://postgres:secret@db:5432/myapp
- REDIS_URL=redis://cache:6379
volumes:
# Montage du code source pour le hot-reload
- ./src:/app/src
- ./package.json:/app/package.json
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
networks:
- app-network
# Base de données PostgreSQL
db:
image: postgres:16-alpine
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: secret
POSTGRES_DB: myapp
volumes:
# Persistance des données
- postgres_data:/var/lib/postgresql/data
# Script d'initialisation
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
networks:
- app-network
# Cache Redis
cache:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: redis-server --appendonly yes
networks:
- app-network
# Volumes nommés pour la persistance
volumes:
postgres_data:
redis_data:
# Réseau dédié pour l'isolation
networks:
app-network:
driver: bridgeLes services communiquent via leurs noms (db, cache) grâce au réseau Docker interne. Les healthchecks garantissent que les dépendances sont prêtes avant le démarrage de l'application.
# terminal
# Démarrage de tous les services
docker compose up -d
# Voir les logs de tous les services
docker compose logs -f
# Logs d'un service spécifique
docker compose logs -f app
# Arrêt et suppression des conteneurs
docker compose down
# Suppression incluant les volumes (attention : perte de données)
docker compose down -v
# Reconstruction après modification du Dockerfile
docker compose up -d --buildPrêt à réussir tes entretiens DevOps ?
Entraîne-toi avec nos simulateurs interactifs, fiches express et tests techniques.
Gestion des secrets et variables d'environnement
La gestion sécurisée des secrets est cruciale en production. Docker propose plusieurs approches selon le contexte.
# docker-compose.override.yml (développement uniquement)
version: "3.9"
services:
app:
env_file:
- .env.development
environment:
- DEBUG=truePour la production, les secrets Docker offrent une meilleure sécurité.
# docker-compose.production.yml
version: "3.9"
services:
app:
build:
context: .
dockerfile: Dockerfile.production
secrets:
- db_password
- api_key
environment:
- NODE_ENV=production
- DATABASE_PASSWORD_FILE=/run/secrets/db_password
- API_KEY_FILE=/run/secrets/api_key
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
file: ./secrets/api_key.txtLe code de l'application lit les secrets depuis les fichiers montés.
const fs = require('fs');
const path = require('path');
// Fonction utilitaire pour lire les secrets Docker
function readSecret(secretName) {
const secretPath = `/run/secrets/${secretName}`;
// Vérification de l'existence du fichier secret
if (fs.existsSync(secretPath)) {
return fs.readFileSync(secretPath, 'utf8').trim();
}
// Fallback vers les variables d'environnement classiques
const envVar = secretName.toUpperCase();
return process.env[envVar];
}
module.exports = {
databasePassword: readSecret('db_password'),
apiKey: readSecret('api_key'),
};Cette approche évite d'exposer les secrets dans les variables d'environnement ou les images Docker.
Optimisation des images Docker
Plusieurs techniques permettent de réduire la taille des images et d'améliorer les performances.
# Dockerfile.optimized
FROM node:22-alpine AS base
# Installation des outils nécessaires en une seule couche
RUN apk add --no-cache \
dumb-init \
&& rm -rf /var/cache/apk/*
# ============================================
# Stage : Dependencies
# ============================================
FROM base AS deps
WORKDIR /app
# Copie uniquement les fichiers de lock pour le cache
COPY package.json package-lock.json ./
# Installation avec cache npm monté (BuildKit)
RUN \
npm ci --only=production
# ============================================
# Stage : Builder
# ============================================
FROM base AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN \
npm ci
COPY . .
RUN npm run build
# ============================================
# Stage : Production
# ============================================
FROM base AS production
# Métadonnées de l'image
LABEL maintainer="team@example.com"
LABEL version="1.0"
LABEL description="Production-ready Node.js application"
# Utilisateur non-root
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
WORKDIR /app
# Copie des dépendances de production
COPY /app/node_modules ./node_modules
# Copie du build
COPY /app/dist ./dist
COPY /app/package.json ./
USER nodejs
ENV NODE_ENV=production
# dumb-init comme PID 1 pour la gestion des signaux
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "dist/server.js"]L'utilisation de dumb-init assure une gestion correcte des signaux Unix, permettant un arrêt gracieux du conteneur.
# terminal
# Activation de BuildKit pour les fonctionnalités avancées
export DOCKER_BUILDKIT=1
# Construction avec cache et sortie détaillée
docker build --progress=plain -t my-app:optimized .
# Analyse des couches de l'image
docker history my-app:optimized
# Inspection détaillée de l'image
docker inspect my-app:optimizedScanner régulièrement les images pour détecter les vulnérabilités avec des outils comme Trivy ou Snyk. Les images de base doivent être mises à jour régulièrement pour inclure les correctifs de sécurité.
Networking avancé Docker
Docker offre plusieurs drivers réseau pour différents cas d'usage.
# docker-compose.networking.yml
version: "3.9"
services:
# Frontend accessible publiquement
frontend:
build: ./frontend
ports:
- "80:80"
networks:
- frontend-network
- backend-network
# API accessible uniquement depuis le frontend
api:
build: ./api
networks:
- backend-network
- database-network
# Pas de ports exposés à l'extérieur
# Base de données isolée
database:
image: postgres:16-alpine
networks:
- database-network
# Accessible uniquement par l'API
networks:
frontend-network:
driver: bridge
backend-network:
driver: bridge
internal: true # Pas d'accès internet
database-network:
driver: bridge
internal: trueCette configuration isole les services selon le principe du moindre privilège. La base de données n'est accessible que par l'API.
# terminal
# Inspection des réseaux Docker
docker network ls
# Détails d'un réseau spécifique
docker network inspect app-network
# Création d'un réseau personnalisé
docker network create --driver bridge --subnet 172.28.0.0/16 custom-network
# Connexion d'un conteneur à un réseau existant
docker network connect custom-network my-containerVolumes et persistance des données
Les volumes Docker préservent les données au-delà du cycle de vie des conteneurs.
# docker-compose.volumes.yml
version: "3.9"
services:
app:
image: my-app:latest
volumes:
# Volume nommé pour les données persistantes
- app_data:/app/data
# Montage bind pour le développement
- ./uploads:/app/uploads:rw
# Montage en lecture seule pour la configuration
- ./config:/app/config:ro
backup:
image: alpine
volumes:
# Accès au même volume pour les sauvegardes
- app_data:/data:ro
- ./backups:/backups
command: |
sh -c "tar czf /backups/backup-$$(date +%Y%m%d).tar.gz /data"
volumes:
app_data:
driver: local
driver_opts:
type: none
device: /path/to/host/data
o: bindLa distinction entre volumes nommés et montages bind est importante : les volumes sont gérés par Docker tandis que les montages bind utilisent directement le système de fichiers hôte.
# terminal
# Liste des volumes
docker volume ls
# Inspection d'un volume
docker volume inspect app_data
# Suppression des volumes orphelins
docker volume prune
# Sauvegarde d'un volume
docker run --rm -v app_data:/data -v $(pwd):/backup alpine \
tar czf /backup/volume-backup.tar.gz /dataDéploiement en production
Un workflow de déploiement robuste inclut la construction, les tests et le push vers un registry.
# deploy.sh
#!/bin/bash
set -e
# Variables
REGISTRY="registry.example.com"
IMAGE_NAME="my-app"
VERSION=$(git describe --tags --always)
echo "Building version: $VERSION"
# Construction de l'image de production
docker build \
-f Dockerfile.production \
-t $REGISTRY/$IMAGE_NAME:$VERSION \
-t $REGISTRY/$IMAGE_NAME:latest \
--build-arg BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \
--build-arg VERSION=$VERSION \
.
# Scan de sécurité
echo "Running security scan..."
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image $REGISTRY/$IMAGE_NAME:$VERSION
# Push vers le registry
echo "Pushing to registry..."
docker push $REGISTRY/$IMAGE_NAME:$VERSION
docker push $REGISTRY/$IMAGE_NAME:latest
echo "Deployment complete: $REGISTRY/$IMAGE_NAME:$VERSION"Pour les déploiements sur un serveur, un fichier compose de production distinct permet d'adapter la configuration.
# docker-compose.prod.yml
version: "3.9"
services:
app:
image: registry.example.com/my-app:latest
restart: always
deploy:
replicas: 3
resources:
limits:
cpus: "0.5"
memory: 512M
reservations:
cpus: "0.25"
memory: 256M
update_config:
parallelism: 1
delay: 10s
failure_action: rollback
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"Cette configuration définit les ressources allouées, la stratégie de mise à jour et les healthchecks pour un déploiement fiable.
# terminal
# Déploiement avec Docker Compose en production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
# Mise à jour sans interruption (rolling update)
docker compose -f docker-compose.yml -f docker-compose.prod.yml pull
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --no-deps app
# Rollback en cas de problème
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --no-deps \
--scale app=0 && \
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --no-deps appMonitoring et debugging des conteneurs
La surveillance des conteneurs est essentielle en production.
# terminal
# Statistiques en temps réel de tous les conteneurs
docker stats
# Statistiques d'un conteneur spécifique avec format personnalisé
docker stats my-app --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
# Inspection des processus dans un conteneur
docker top my-app
# Événements Docker en temps réel
docker events --filter container=my-app
# Copie de fichiers depuis/vers un conteneur
docker cp my-app:/app/logs/error.log ./error.logPour un debugging approfondi, plusieurs techniques sont disponibles.
# terminal
# Shell interactif dans un conteneur en cours d'exécution
docker exec -it my-app sh
# Exécution d'une commande unique
docker exec my-app cat /app/config/settings.json
# Démarrage d'un conteneur en mode debug
docker run -it --rm --entrypoint sh my-app:latest
# Inspection des variables d'environnement
docker exec my-app printenv
# Analyse des logs avec filtres
docker logs my-app --since 1h --tail 100 | grep ERROR# docker-compose.monitoring.yml
version: "3.9"
services:
app:
# ... configuration existante
labels:
- "prometheus.scrape=true"
- "prometheus.port=3000"
- "prometheus.path=/metrics"
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=15d'
grafana:
image: grafana/grafana:latest
ports:
- "3001:3000"
volumes:
- grafana_data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_PASSWORD=secret
volumes:
prometheus_data:
grafana_data:Cette stack de monitoring permet de collecter et visualiser les métriques des conteneurs.
Conclusion
Docker transforme le cycle de développement en garantissant la cohérence entre tous les environnements. La conteneurisation apporte portabilité, isolation et reproductibilité, des qualités essentielles pour les applications modernes.
Checklist pour Docker en production
- ✅ Multi-stage builds pour des images optimisées
- ✅ Utilisateur non-root dans les conteneurs
- ✅ Healthchecks configurés pour tous les services
- ✅ Secrets gérés via Docker secrets ou variables d'environnement sécurisées
- ✅ Limites de ressources (CPU, mémoire) définies
- ✅ Volumes pour la persistance des données critiques
- ✅ Logging centralisé avec rotation des fichiers
- ✅ Scan de sécurité des images avant déploiement
- ✅ Stratégie de mise à jour sans interruption
- ✅ Réseau isolé entre les services
Passe à la pratique !
Teste tes connaissances avec nos simulateurs d'entretien et tests techniques.
La maîtrise de Docker constitue une compétence fondamentale pour tout développeur moderne. De l'environnement local au déploiement en production, Docker standardise les workflows et simplifie les opérations. Les concepts présentés ici forment une base solide pour explorer ensuite Kubernetes et l'orchestration de conteneurs à grande échelle.
Tags
Partager
Articles similaires

Kubernetes : Déployer votre première application
Guide pratique pour déployer une application sur Kubernetes. De l'installation de minikube aux Deployments, Services et ConfigMaps, avec des exemples concrets.

Questions d'entretien DevOps essentielles : Guide complet 2026
Préparez vos entretiens DevOps avec les questions incontournables sur CI/CD, Kubernetes, Docker, Terraform et les pratiques SRE. Réponses détaillées incluses.

Questions d'entretien Terraform : guide complet Infrastructure as Code 2026
Preparez les entretiens Terraform avec les questions essentielles sur la gestion du state, les modules, les workspaces, les providers et les bonnes pratiques IaC. Mis a jour pour Terraform 1.14 et HCP Terraform en 2026.