Docker a transformé la façon dont on développe et déploie des applications. Mais entre l'usage sur le poste du développeur et la prod, les enjeux sont différents. Tour complet des bonnes pratiques pour les deux contextes.
Avant Docker, le classique "ça marche sur ma machine" était une blague de développeur et un cauchemar d'ops. L'environnement de développement divergeait inévitablement de la production — versions de langage différentes, librairies systèmes absentes, variables d'environnement oubliées. Docker n'a pas inventé les conteneurs, mais il a rendu leur usage accessible à tous. En 2021, c'est un outil que tout développeur backend doit maîtriser.
Ce que Docker résout fondamentalement
Un conteneur Docker empaquète une application avec tout ce dont elle a besoin pour tourner : le code, le runtime, les librairies système, les variables d'environnement. Là où une machine virtuelle virtualise tout le matériel (lourd, lent), un conteneur partage le noyau du système hôte et n'isole que le processus (léger, rapide).
Le résultat : le même artefact tourne de la même façon sur votre laptop, en CI, en staging et en production. Pas de "mais sur mon poste ça marche", pas de drift entre environnements.
Docker pour le développeur
Démarrer des services tiers sans rien installer
C'est le premier bénéfice immédiat. Besoin de PostgreSQL pour ce projet ? Plus besoin d'installer Postgres localement, de gérer plusieurs versions, d'avoir des conflits entre projets.
docker run -d \
--name postgres-dev \
-e POSTGRES_PASSWORD=secret \
-e POSTGRES_DB=myapp \
-p 5432:5432 \
postgres:14
Une commande, un service qui tourne. Même chose pour Redis, Elasticsearch, RabbitMQ, ou n'importe quelle dépendance.
Docker Compose — l'orchestrateur du développeur
Un fichier docker-compose.yml décrit l'ensemble des services de l'application et leurs dépendances. Un docker compose up lance tout. C'est le standard pour les environnements de développement.
version: '3.9'
services:
app:
build: .
ports:
- "3000:3000"
volumes:
- .:/app
- /app/node_modules
environment:
DATABASE_URL: postgres://user:secret@db:5432/myapp
depends_on:
db:
condition: service_healthy
db:
image: postgres:14
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: secret
POSTGRES_DB: myapp
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d myapp"]
interval: 5s
timeout: 5s
retries: 5
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Le healthcheck sur la base de données est important : sans lui, l'application peut démarrer avant que Postgres soit prêt à accepter des connexions.
Hot reload et volumes
Le volume .:/app monte le code source local dans le conteneur. Toute modification de fichier est immédiatement visible dans le conteneur — le hot reload fonctionne sans reconstruire l'image.
Écrire un Dockerfile de qualité
Le Dockerfile est le blueprint de votre image. Un mauvais Dockerfile produit des images lourdes, lentes à construire, et potentiellement peu sûres.
Multi-stage build — la bonne pratique fondamentale
# Étape 1 : build
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# Étape 2 : image finale légère
FROM node:18-alpine AS runner
WORKDIR /app
# Ne pas tourner en root
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/main.js"]
Ce pattern multi-stage garantit que les outils de build (compilateurs, devDependencies) ne se retrouvent pas dans l'image finale. Une image Node.js bien construite pèse 150–200 Mo au lieu de 1 Go+.
L'ordre des instructions compte
Docker met en cache chaque layer. Placez les instructions qui changent peu en haut, celles qui changent souvent en bas :
# Bon ordre : deps d'abord, code ensuite
COPY package*.json ./
RUN npm ci
COPY . . # invalidé à chaque changement de code, mais pas le layer npm ci
.dockerignore
Indispensable. Sans lui, COPY . . embarque node_modules, .git, les fichiers de config locaux, parfois des secrets.
node_modules
.git
.env
.env.local
*.log
dist
coverage
Docker en production
En production, les enjeux changent : disponibilité, sécurité, observabilité, scalabilité.
Ne jamais tourner en root
Par défaut, les processus dans un conteneur tournent en root. Si le conteneur est compromis, l'attaquant a les droits root dans le conteneur — et potentiellement sur l'hôte via des mauvaises configurations. Toujours créer un utilisateur non-privilégié.
Images minimales
Préférez alpine ou distroless aux images complètes. Moins de packages installés = moins de surface d'attaque = moins de CVE à corriger.
| Image | Taille |
|---|---|
node:18 |
~950 Mo |
node:18-slim |
~240 Mo |
node:18-alpine |
~170 Mo |
Gestion des secrets
Ne jamais mettre de secrets dans le Dockerfile ou dans les variables d'environnement en clair dans docker-compose.yml en production. Utilisez Docker Secrets, Vault, ou les mécanismes du cloud provider (AWS Secrets Manager, GCP Secret Manager).
# En prod : pas ça
environment:
DATABASE_PASSWORD: supersecret # visible dans docker inspect
# Préférer les secrets Docker Swarm ou un gestionnaire externe
secrets:
db_password:
external: true
Healthchecks
Un healthcheck dans le Dockerfile permet à l'orchestrateur (Docker Swarm, Kubernetes) de savoir si le conteneur est réellement prêt à recevoir du trafic.
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget --quiet --tries=1 --spider http://localhost:3000/health || exit 1
Limites de ressources
Sans limites, un conteneur peut consommer toute la mémoire ou le CPU du nœud hôte.
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
memory: 256M
Kubernetes : l'étape suivante
Pour les applications à fort trafic ou nécessitant haute disponibilité, Docker seul ne suffit pas. Kubernetes orchestre des clusters de conteneurs : scheduling, auto-scaling, self-healing, rolling deployments.
Mais Kubernetes a une courbe d'apprentissage abrupte. Pour beaucoup d'équipes, Docker Swarm (intégré à Docker) est un juste milieu — plus simple, suffisant pour orchestrer quelques dizaines de services.
En résumé
Docker n'est pas une technologie de déploiement réservée aux ops. C'est un outil de développement autant que de production. Maîtriser les bonnes pratiques — multi-stage builds, images légères, non-root, healthchecks, gestion des secrets — est ce qui distingue un usage naïf d'un usage professionnel.
La promesse initiale reste tenue : une image construite une fois, déployable partout, de la même façon.