# 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. - Published: 2026-01-21 - Updated: 2026-03-28 - Author: SharpSkill - Tags: docker, containerization, devops, docker compose, deployment - Reading time: 18 min --- 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 en 2026** > > 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. ```bash # 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-world ``` Cette commande télécharge l'image `hello-world` depuis Docker Hub et lance un conteneur qui affiche un message de confirmation. ```bash # 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 # Supprimer une image docker rmi ``` 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 # 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. ```bash # 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 sh ``` Le flag `-d` lance le conteneur en arrière-plan, `-p` mappe le port 3000 du conteneur vers le port 3000 de l'hôte. > **Alpine vs Debian** > > 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 # 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 --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules COPY --from=builder /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. ```bash # 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 450MB ``` La 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. ```yaml # 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: bridge ``` Les 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. ```bash # 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 --build ``` ## 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. ```yaml # docker-compose.override.yml (développement uniquement) version: "3.9" services: app: env_file: - .env.development environment: - DEBUG=true ``` Pour la production, les secrets Docker offrent une meilleure sécurité. ```yaml # 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.txt ``` Le code de l'application lit les secrets depuis les fichiers montés. ```javascript // config/secrets.js 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 # 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 --mount=type=cache,target=/root/.npm \ npm ci --only=production # ============================================ # Stage : Builder # ============================================ FROM base AS builder WORKDIR /app COPY package.json package-lock.json ./ RUN --mount=type=cache,target=/root/.npm \ 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 --from=deps --chown=nodejs:nodejs /app/node_modules ./node_modules # Copie du build COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist COPY --from=builder --chown=nodejs:nodejs /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. ```bash # 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:optimized ``` > **Sécurité des images** > > Scanner 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. ```yaml # 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: true ``` Cette configuration isole les services selon le principe du moindre privilège. La base de données n'est accessible que par l'API. ```bash # 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-container ``` ## Volumes et persistance des données Les volumes Docker préservent les données au-delà du cycle de vie des conteneurs. ```yaml # 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: bind ``` La 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. ```bash # 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 /data ``` ## Déploiement en production Un workflow de déploiement robuste inclut la construction, les tests et le push vers un registry. ```bash # 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. ```yaml # 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. ```bash # 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 app ``` ## Monitoring et debugging des conteneurs La surveillance des conteneurs est essentielle en production. ```bash # 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.log ``` Pour un debugging approfondi, plusieurs techniques sont disponibles. ```bash # 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 ``` ```yaml # 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 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. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/fr/blog/devops/docker-du-developpement-a-la-production