ITSkillsCenter
Développement Web

Docker : conteneuriser ses applications

6 min de lecture
Docker : conteneuriser ses applications

Ce que vous saurez faire à la fin

  1. Installer Docker et lancer votre premier conteneur
  2. Écrire un Dockerfile multi-stage optimisé
  3. Orchestrer plusieurs services avec Docker Compose
  4. Gérer volumes, réseaux, secrets et health checks
  5. Déployer en production avec registry et stratégie de mise à jour

Durée : 3 heures. Pré-requis : Linux/Mac/Windows avec WSL2, 10 Go d’espace disque, compte Docker Hub.

Étape 1 — Installer Docker

# Linux (un-liner officiel)
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
# Déconnectez-vous puis reconnectez-vous

# macOS
brew install --cask docker
# Ou téléchargez Docker Desktop depuis docker.com

# Windows: Docker Desktop avec WSL2 activé

# Vérification
docker --version
docker compose version
docker run hello-world

Étape 2 — Premier conteneur

# Lancer un conteneur Nginx
docker run -d -p 8080:80 --name web nginx:alpine

# Vérifier
curl http://localhost:8080
docker ps
docker logs web

# Entrer dans le conteneur
docker exec -it web sh

# Arrêter et supprimer
docker stop web
docker rm web

Étape 3 — Premier Dockerfile

  1. Créez un dossier mon-app/ avec un simple server.js :
// server.js
const http = require("http");
http.createServer((req, res) => {
  res.writeHead(200);
  res.end("Hello from Docker!");
}).listen(3000);
console.log("Server running on 3000");
  1. Créez Dockerfile :
FROM node:20-alpine
WORKDIR /app
COPY server.js .
EXPOSE 3000
USER node
CMD ["node", "server.js"]
  1. Build et run :
docker build -t mon-app:1.0 .
docker run -d -p 3000:3000 --name app mon-app:1.0

curl http://localhost:3000
docker logs -f app

Étape 4 — Dockerfile multi-stage optimisé

# syntax=docker/dockerfile:1.7
FROM node:20-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN --mount=type=cache,target=/root/.npm npm ci --only=production

FROM node:20-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM node:20-alpine AS runtime
ENV NODE_ENV=production
WORKDIR /app

# Utilisateur non-root
RUN addgroup -S app && adduser -S app -G app
USER app

COPY --from=build --chown=app:app /app/dist ./dist
COPY --from=deps --chown=app:app /app/node_modules ./node_modules
COPY --chown=app:app package.json ./

EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
  CMD wget --spider -q http://localhost:3000/health || exit 1

CMD ["node", "dist/server.js"]
  1. Avantages : image finale < 200 MB, sans outils de build, sans racines, avec healthcheck.

Étape 5 — .dockerignore

Évite d’envoyer des fichiers inutiles au daemon Docker.

node_modules
dist
.git
.env
.env.*
*.log
coverage
.vscode
.idea
README.md
Dockerfile*
docker-compose*
.github

Étape 6 — Volumes pour la persistance

# Volume nommé (recommandé en prod)
docker volume create pgdata

docker run -d \
  --name pg \
  -v pgdata:/var/lib/postgresql/data \
  -e POSTGRES_PASSWORD=secret \
  -p 5432:5432 \
  postgres:16

# Bind mount (pour dev)
docker run -v "$(pwd):/app" -w /app node:20 npm test

# Lister, inspecter, supprimer
docker volume ls
docker volume inspect pgdata
docker volume rm pgdata  # attention: perd les données

Étape 7 — Docker Compose : stack complet

  1. Créez compose.yaml :
services:
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: itsc
      POSTGRES_USER: itsc
      POSTGRES_PASSWORD: ${DB_PASS:?DB_PASS manquant}
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U itsc"]
      interval: 5s
      retries: 10

  redis:
    image: redis:7-alpine
    command: ["redis-server", "--save", "60", "1"]
    volumes:
      - redisdata:/data

  api:
    build: .
    environment:
      DATABASE_URL: postgres://itsc:${DB_PASS}@db:5432/itsc
      REDIS_URL: redis://redis:6379
    ports:
      - "3000:3000"
    depends_on:
      db: { condition: service_healthy }
      redis: { condition: service_started }
    restart: unless-stopped
    deploy:
      resources:
        limits: { cpus: "1.0", memory: "512M" }

volumes:
  pgdata:
  redisdata:
  1. Créez .env :
DB_PASS=un_mot_de_passe_fort
  1. Lancez :
docker compose up -d
docker compose ps
docker compose logs -f api
docker compose exec db psql -U itsc
docker compose down            # stop et retire
docker compose down -v         # + retire volumes

Étape 8 — Réseau et DNS interne

  1. Docker Compose crée automatiquement un réseau itsc_default.
  2. Dans le conteneur api, les noms db et redis sont résolus par DNS interne.
  3. Pas besoin d’exposer les ports DB à l’extérieur. Seul api est exposé via le port 3000.
docker network ls
docker network inspect itsc_default

Étape 9 — Secrets et variables d’environnement

services:
  api:
    secrets:
      - db_password
      - api_key
    environment:
      DB_PASSWORD_FILE: /run/secrets/db_password

secrets:
  db_password:
    file: ./secrets/db_password.txt
  api_key:
    external: true      # géré par Docker secret

Dans le code, lire depuis /run/secrets/db_password au lieu de variables d’env. Les secrets ne sont pas dans les logs ni dans l’image.

Étape 10 — Image Go ultra-légère

FROM golang:1.22-alpine AS build
WORKDIR /src
COPY go.* ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app -ldflags="-s -w" ./cmd/server

FROM scratch
COPY --from=build /app /app
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
USER 65532:65532
ENTRYPOINT ["/app"]

Image finale : 15 MB au lieu de 900 MB. Aucun OS, aucun shell : surface d’attaque minimale.

Étape 11 — Scan de sécurité

# Trivy (gratuit, très complet)
brew install trivy
trivy image mon-app:1.0

# Docker Scout (intégré Docker Desktop)
docker scout cves mon-app:1.0
docker scout recommendations mon-app:1.0

# Snyk
npm install -g snyk
snyk container test mon-app:1.0

Étape 12 — Push vers un registry

# Docker Hub
docker login
docker tag mon-app:1.0 monuser/mon-app:1.0
docker push monuser/mon-app:1.0

# GitHub Container Registry (recommandé)
echo $GH_TOKEN | docker login ghcr.io -u monuser --password-stdin
docker tag mon-app:1.0 ghcr.io/monorg/mon-app:1.0
docker push ghcr.io/monorg/mon-app:1.0

# Registry privé interne
docker login registry.itsc.sn
docker tag mon-app:1.0 registry.itsc.sn/mon-app:1.0
docker push registry.itsc.sn/mon-app:1.0

Étape 13 — Déploiement en production

# Sur le serveur
docker pull ghcr.io/monorg/mon-app:1.2.3
docker compose pull api
docker compose up -d --no-deps api

# Rolling update avec un script simple
#!/usr/bin/env bash
set -e
docker pull ghcr.io/monorg/mon-app:$1
docker compose up -d --no-deps --scale api=2 api
sleep 15   # laisse temps au health check
docker compose up -d --no-deps --scale api=1 api

Étape 14 — Monitoring et logs

docker stats                      # CPU/RAM/IO temps réel
docker logs --tail=100 -f api
docker inspect --format='{{json .State}}' api | jq

# Logs centralisés (driver gelf/loki)
docker run -d --log-driver=loki \
  --log-opt loki-url="https://loki.itsc.sn/loki/api/v1/push" \
  mon-app:1.0

Étape 15 — Nettoyage

# Voir ce qui prend de la place
docker system df

# Nettoyer proprement
docker container prune         # conteneurs arrêtés
docker image prune -a          # images non utilisées
docker volume prune            # volumes orphelins
docker network prune           # réseaux orphelins

# Tout d'un coup (attention)
docker system prune -a --volumes

Checklist production

✓ Dockerfile multi-stage, image finale < 300 MB
✓ USER non-root dans le conteneur
✓ HEALTHCHECK défini
✓ .dockerignore présent
✓ Secrets via Docker secrets, jamais en env brut
✓ Compose avec depends_on + healthchecks
✓ Limits CPU/RAM définies
✓ Scan Trivy: 0 vuln High/Critical
✓ Tags avec version sémantique (pas latest en prod)
✓ Registry privé pour images internes
Besoin d'un site web ?

Confiez-nous la Création de Votre Site Web

Site vitrine, e-commerce ou application web — nous transformons votre vision en réalité digitale. Accompagnement personnalisé de A à Z.

À partir de 250.000 FCFA
Parlons de Votre Projet
Publicité