Introduction
En 2026, Docker reste l'outil incontournable pour la conteneurisation, mais en production, une simple image de base ne suffit plus. Les défis incluent la réduction de la taille des images (jusqu'à 90% avec multi-stage), la gestion des secrets sans exposition, les healthchecks pour l'orchestration fiable, et la sécurité contre les vulnérabilités CVE. Ce tutoriel expert vous guide pas à pas pour déployer une API Node.js multi-conteneurs optimisée : scaling horizontal, monitoring intégré et scans automatisés. Vous apprendrez à passer d'un setup dev à une stack prod robuste, scalable et sécurisée, évitant les pièges qui coulent 70% des déploiements Docker en entreprise. À la fin, vous bookmarquerez ce guide pour vos CI/CD pipelines.
Prérequis
- Docker 27+ et Docker Compose 2.29+ installés
- Node.js 22+ pour l'app exemple
- Connaissances avancées en Linux, réseaux et YAML
- Accès à un registry privé (Docker Hub ou Harbor)
- Outils : Trivy pour scans sécurité
Créer l'app Node.js de base
mkdir docker-prod-app && cd docker-prod-app
npm init -y
npm install express cors helmet morgan
cat > package.json << 'EOF'
{
"name": "docker-prod-app",
"version": "1.0.0",
"main": "server.js",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.19.2",
"cors": "^2.8.5",
"helmet": "^7.1.0",
"morgan": "^1.10.0"
}
}
EOF
cat > server.js << 'EOF'
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const app = express();
app.use(helmet());
app.use(cors());
app.use(morgan('combined'));
app.get('/health', (req, res) => res.status(200).json({ status: 'OK' }));
app.get('/api/users', (req, res) => res.json([{ id: 1, name: 'User1' }]));
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Server on port ${port}`));
EOFCe script bash initialise une API Express sécurisée avec Helmet (sécurité headers), CORS, logging Morgan et un endpoint health. Il est production-ready dès le départ, évitant les configs manuelles. Exécutez-le pour générer l'app de test ; notez l'endpoint /health pour les checks Docker.
Dockerfile multi-stage optimisé
Un Dockerfile multi-stage réduit la taille finale de 80-90% en séparant build et runtime. Imaginez un ouvrier (stage build) qui assemble les outils puis les passe à un livreur léger (stage runtime) : seul le strict nécessaire voyage.
Dockerfile multi-stage
FROM node:22-alpine AS builder
WORKDIR /app
COPY package*.json .
RUN npm ci --only=production && npm cache clean --force
COPY . .
FROM node:22-alpine AS runtime
WORKDIR /app
RUN addgroup --gid 1001 --system appgroup && \
adduser --uid 1001 --system appuser --ingroup appgroup
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appgroup /app/server.js .
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
ENTRYPOINT ["node", "server.js"]Ce Dockerfile utilise deux stages : builder pour npm ci (prod only), runtime pour un conteneur minimal avec user non-root (sécurité). Le HEALTHCHECK automatise les redémarrages. Taille finale <100MB vs 1GB natif ; piège : oublier --only=production gonfle l'image.
.dockerignore pour builds rapides
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
*.md
coverage
.nyc_outputCe .dockerignore exclut les fichiers inutiles, accélérant les builds de 50% et réduisant les vulnérabilités. Comme un bagage cabine : seul l'essentiel monte à bord. Oublier node_modules expose des binaires locaux.
Orchestration avec Docker Compose
Docker Compose gère multi-services : API + Redis + Nginx reverse proxy. En prod, on sépare dev/prod via overrides et secrets pour éviter les leaks.
docker-compose.yml de base
services:
api:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- PORT=3000
volumes:
- ./logs:/app/logs
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
depends_on:
redis:
condition: service_healthy
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
api:
condition: service_healthyCe compose orchestre API, Redis et Nginx avec healthchecks conditionnels (depends_on). Volumes pour logs persistants. Piège : sans condition: service_healthy, les services démarrent asynchrones et crashent.
docker-compose.prod.yml avec secrets
services:
api:
build:
context: .
dockerfile: Dockerfile
ports:
- "3000"
environment:
- NODE_ENV=production
secrets:
- db_password
deploy:
replicas: 3
resources:
limits:
cpus: '0.5'
memory: 512M
restart_policy:
condition: on-failure
redis:
deploy:
replicas: 1
secrets:
db_password:
external: trueOverride prod pour scaling (replicas), ressources limitées et secrets externes (docker secret create). Utilisez avec docker compose -f docker-compose.yml -f docker-compose.prod.yml up. Évite l'expo en .env ; piège : secrets non-external leakent en clair.
Scripts de build et scan sécurité
Automatisez build, push et scans avec bash pour CI/CD. Trivy détecte les CVE avant push.
Script build et push
#!/bin/bash
set -e
TAG=prod-app:$(date +%Y%m%d)
REGISTRY=yourregistry.com
# Build multi-arch
DOCKER_BUILDKIT=1 docker buildx create --use
DOCKER_BUILDKIT=1 docker buildx build --platform linux/amd64,linux/arm64 \
-t $REGISTRY/api:$TAG -t $REGISTRY/api:latest --push .
# Scan sécurité
trivy image --exit-code 1 --no-progress --severity HIGH,CRITICAL $REGISTRY/api:$TAG
echo "Image $TAG pushed and scanned"Ce script buildx multi-arch (amd64/arm64), pushe et scanne avec Trivy (exit 1 si HIGH/CRIT). Installez trivy via brew/apt. Piège : sans --platform, les déploiements cloud (Lambda, ECS) échouent sur arch diff.
Init Docker Swarm pour scaling
#!/bin/bash
docker swarm init --advertise-addr $(hostname -i)
# Stack deploy
docker stack deploy -c docker-compose.prod.yml prod-stack \
--with-registry-auth
# Scale API à 5 replicas
docker service scale prod-stack_api=5
# Inspect
docker service ls
docker service ps prod-stack_apiInitialise Swarm mode pour orchestration native (sans K8s). Déploye stack avec auth registry. Scale dynamique ; piège : sans --advertise-addr, nodes rejoignent pas le cluster.
Bonnes pratiques
- Toujours non-root : USER dans Dockerfile réduit blast radius (99% attaques).
- Multi-stage + .dockerignore : <100MB images, builds <1min.
- Healthchecks + conditions : Zéro downtime restarts.
- Secrets externes : Jamais en env vars ou volumes.
- Scan CI obligatoire : Trivy/Snyk avant push.
Erreurs courantes à éviter
- Oublier cache npm : Builds x10 lents ; utilisez npm ci --cache.
- Ports exposés inutilement : EXPOSE n'est pas bind ; utilisez -p sélectif.
- Pas de ressources limits : OOM kills en prod ; toujours cpus/memory.
- Swarm sans labels : Traçabilité perdue ; ajoutez deploy/labels.
Pour aller plus loin
Passez à Kubernetes avec Helm (tuto Learni), intégrez Prometheus/Grafana pour monitoring, ou explorez Podman rootless. Découvrez nos formations DevOps Learni pour certifications Docker EE et CI/CD avancés.