DevOps

Docker : packager une application pour qu'elle tourne partout

Le problème 'ça marche sur ma machine' a une solution depuis 2013. Docker encapsule une application et toutes ses dépendances dans un conteneur portable.

Docker : packager une application pour qu'elle tourne partout

Avant Docker, déployer une application impliquait de s'assurer que le serveur avait la bonne version de Node.js, les bonnes dépendances système, les bons variables d'environnement. Une différence entre dev et prod suffisait à tout casser. "Ça marche sur ma machine" était une blague qui n'amusait personne en production.

Docker résout ce problème en encapsulant l'application et tout son environnement dans un conteneur. Le conteneur tourne identiquement sur le laptop du développeur, dans la CI, et en production.

Comment ça fonctionne

Un conteneur Docker n'est pas une VM. Il partage le kernel Linux de l'hôte mais isole les processus, le système de fichiers et le réseau via des fonctionnalités kernel (namespaces et cgroups). Résultat : démarrage en secondes, consommation mémoire réduite, densité d'applications bien supérieure aux VMs traditionnelles.

Le point de départ est le Dockerfile : la recette de construction du conteneur.

Dockerfile
FROM node:18-alpine
 
WORKDIR /app
 
COPY package*.json ./
RUN npm ci --only=production
 
COPY . .
 
EXPOSE 3000
CMD ["npm", "start"]

docker build -t monapp . construit l'image. docker run -p 3000:3000 monapp démarre le conteneur.

Multi-stage builds : réduire drastiquement la taille de l'image

Le Dockerfile précédent copie tout le code source dans l'image finale. Pour une app compilée, c'est du gaspillage — les outils de build n'ont pas leur place en production.

Dockerfile
# Stage 1 : build
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
 
# Stage 2 : production (image finale)
FROM node:18-alpine AS runner
 
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
 
WORKDIR /app
COPY --from=builder --chown=nextjs:nodejs /app/dist ./dist
COPY --from=builder --chown=nextjs:nodejs /app/package*.json ./
 
RUN npm ci --only=production
 
USER nextjs
EXPOSE 3000
CMD ["node", "dist/server.js"]

L'image builder peut faire 1.2 GB avec tous les outils de build. L'image finale descend à 50-100 MB parce qu'elle ne contient que ce qui est nécessaire au runtime. Le gain est immédiat sur le temps de déploiement et le stockage.

Docker Compose : orchestrer plusieurs services localement

Une application réelle ne tourne pas seule — elle a besoin d'une base de données, d'un cache, parfois d'un worker. Docker Compose décrit l'ensemble dans un fichier YAML.

docker-compose.yml
version: '3.8'
 
services:
  web:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/myapp
      - REDIS_URL=redis://redis:6379
    depends_on:
      - db
      - redis
 
  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: myapp
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
    volumes:
      - pgdata:/var/lib/postgresql/data
 
  redis:
    image: redis:7-alpine
    volumes:
      - redisdata:/data
 
volumes:
  pgdata:
  redisdata:
docker compose up -d      # Démarrer en background
docker compose logs -f    # Suivre les logs
docker compose down       # Arrêter et supprimer les conteneurs

docker compose up démarre toute la stack en une commande. Un développeur qui rejoint le projet n'a pas besoin d'installer PostgreSQL et Redis manuellement — Docker s'en charge.

Sécurité : les bonnes pratiques de base

Par défaut, les processus dans un conteneur tournent en root. C'est un risque si le conteneur est compromis — root dans le conteneur peut devenir root sur l'hôte dans certaines configurations.

La correction est simple : créer un utilisateur non-privilégié et l'utiliser.

RUN addgroup -g 1001 -S appgroup \
    && adduser -S appuser -u 1001 -G appgroup
 
USER appuser

Autres bonnes pratiques :

# Limiter les ressources
docker run \
  --memory 512m \
  --cpus 0.5 \
  --read-only \
  myapp:latest
# Scanner les vulnérabilités d'une image
docker scout cves monapp:latest
 
# Alternative avec Trivy
trivy image --severity HIGH,CRITICAL monapp:latest

Les commandes du quotidien

# Construire une image
docker build -t monapp:v1 .
 
# Lister les images locales
docker images
 
# Démarrer un conteneur
docker run -d -p 3000:3000 --name monapp monapp:v1
 
# Voir les conteneurs en cours
docker ps
 
# Voir les logs
docker logs -f monapp
 
# Entrer dans un conteneur en cours
docker exec -it monapp sh
 
# Arrêter et supprimer
docker stop monapp && docker rm monapp
 
# Nettoyer les images et conteneurs inutilisés
docker system prune

Docker vs alternatives

Podman est la principale alternative — compatible Docker, sans daemon centralisé, et rootless par défaut (les conteneurs tournent avec les permissions de l'utilisateur). Sur RHEL/Fedora, c'est le choix par défaut. Les commandes sont quasi identiques : remplacez docker par podman.

Pour l'orchestration à grande échelle, Kubernetes (K8s) prend le relais de Docker Compose. Pour la plupart des projets, GitHub Actions suffit à automatiser le cycle build → test → deploy. K8s gère le scaling automatique, la distribution sur plusieurs machines, les rollouts progressifs et la récupération sur erreur. Mais c'est une couche de complexité significative — Docker Compose couvre la plupart des besoins pour un projet standard.

Le déploiement classique sur VPS passe par SSH avec des clés d'authentification — Docker rend l'artefact portable, SSH le livre.

Docker a changé la façon dont les équipes déploient. Pas parce que c'est une technologie compliquée, mais parce qu'elle résout un problème réel de façon élégante : votre application devient un artifact portable et reproductible.

Écrit par William Loree