Ce que vous saurez faire à la fin
- Installer Docker Engine et Docker Compose sur Linux et Mac, puis vérifier que tout fonctionne avec un conteneur de test.
- Écrire un Dockerfile multi-stage pour une application web PHP ou Node.js, en réduisant l’image finale de 1,2 Go à 80 Mo.
- Orchestrer plusieurs services (app, base de données, cache Redis, reverse proxy Nginx) avec docker-compose et des volumes persistants.
- Pousser et tirer vos images depuis Docker Hub avec authentification, et automatiser le build pour vos équipes à Dakar ou Abidjan.
- Diagnostiquer les problèmes courants : conteneur qui crashe, port déjà utilisé, données perdues au redémarrage, image trop lourde.
Durée : 4h. Pré-requis : Linux/Mac (Ubuntu 22.04 ou macOS 13+), Git installé, compte Docker Hub gratuit, 4 Go de RAM libre, 10 Go d’espace disque, budget 0 FCFA pour la pratique locale.
Étape 1 — Comprendre pourquoi Docker change tout pour une PME
Avant Docker, déployer une application PHP sur un nouveau serveur prenait souvent 3 à 6 heures : installer la bonne version de PHP, les extensions, configurer Nginx, créer la base MySQL. Avec Docker, le même déploiement prend 8 minutes. Pour une PME sénégalaise qui gère 4 sites e-commerce et 2 applications internes, le gain dépasse 60 heures par mois. Docker emballe l’application avec toutes ses dépendances dans un conteneur isolé, qui tourne identiquement sur votre Mac à Dakar, sur le serveur de test à Abidjan et sur la production AWS à Paris.
Le coût d’un VPS pour héberger 5 conteneurs (Hetzner CX22, 4 Go RAM) descend à 4 000 FCFA par mois, contre 25 000 FCFA pour un hébergement mutualisé classique avec moins de flexibilité.
Étape 2 — Installer Docker sur Ubuntu 22.04
sudo apt update
sudo apt install -y ca-certificates curl gnupg lsb-release
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo usermod -aG docker $USER
newgrp docker
docker run hello-world
La dernière commande télécharge une mini image et affiche un message de bienvenue. Si vous voyez « Hello from Docker! », l’installation est réussie. Sur Mac, téléchargez Docker Desktop depuis docker.com, installez le .dmg, ouvrez l’application et attendez la baleine verte dans la barre des tâches.
Étape 3 — Lancer un premier conteneur Nginx
docker run -d --name web-test -p 8080:80 nginx:alpine
docker ps
curl http://localhost:8080
docker logs web-test
docker stop web-test
docker rm web-test
Vous venez de lancer un serveur web Nginx accessible sur le port 8080 de votre machine, sans rien installer d’autre. L’image alpine pèse 23 Mo seulement. Pour un test rapide d’une démo client, c’est instantané.
Étape 4 — Écrire un Dockerfile pour une application Node.js
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
FROM node:20-alpine AS runtime
WORKDIR /app
RUN addgroup -g 1001 -S nodejs && adduser -S nodeapp -u 1001
COPY --from=builder --chown=nodeapp:nodejs /app/dist ./dist
COPY --from=builder --chown=nodeapp:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodeapp:nodejs /app/package.json ./
USER nodeapp
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=3s CMD wget -q -O- http://localhost:3000/health || exit 1
CMD ["node", "dist/server.js"]
Ce Dockerfile multi-stage compile l’application dans un premier conteneur (avec toutes les dépendances de build), puis copie uniquement les artefacts nécessaires dans une image finale légère. Résultat : 80 Mo au lieu de 1,2 Go. L’utilisateur non-root améliore la sécurité, indispensable si vous hébergez des données clients sénégalais soumises à la loi sur la protection des données personnelles.
Étape 5 — Construire et tester l’image
docker build -t mon-app:1.0 .
docker images | grep mon-app
docker run -d --name app-prod -p 3000:3000 mon-app:1.0
docker exec -it app-prod sh
docker stats app-prod --no-stream
La commande docker stats affiche la consommation CPU et RAM en temps réel. Une app Node.js bien optimisée tourne autour de 50 Mo de RAM au repos, contre 200 Mo pour une stack non containerisée équivalente.
Étape 6 — Dockerfile pour une application PHP avec Nginx
FROM composer:2.7 AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader --no-scripts
FROM php:8.3-fpm-alpine AS app
RUN apk add --no-cache nginx supervisor && \
docker-php-ext-install pdo_mysql opcache
COPY docker/nginx.conf /etc/nginx/nginx.conf
COPY docker/supervisord.conf /etc/supervisord.conf
COPY docker/php.ini /usr/local/etc/php/conf.d/custom.ini
WORKDIR /var/www/html
COPY --from=vendor /app/vendor ./vendor
COPY . .
RUN chown -R www-data:www-data /var/www/html
EXPOSE 80
CMD ["supervisord", "-c", "/etc/supervisord.conf"]
Cette image combine PHP-FPM et Nginx dans un seul conteneur via supervisord. Pour une boutique e-commerce WooCommerce ou un site Laravel, c’est la configuration standard. La taille finale tourne autour de 180 Mo, parfaite pour un déploiement rapide depuis un VPS à Dakar avec une connexion fibre limitée.
Étape 7 — Orchestrer plusieurs services avec docker-compose
version: "3.9"
services:
web:
build: .
container_name: pme_web
ports:
- "8080:80"
depends_on:
db:
condition: service_healthy
environment:
DB_HOST: db
DB_USER: pme_user
DB_PASSWORD: ChangeMe2026!
DB_NAME: pme_prod
volumes:
- ./uploads:/var/www/html/uploads
networks:
- backend
restart: unless-stopped
db:
image: mariadb:11
container_name: pme_db
environment:
MARIADB_ROOT_PASSWORD: RootStrong2026!
MARIADB_DATABASE: pme_prod
MARIADB_USER: pme_user
MARIADB_PASSWORD: ChangeMe2026!
volumes:
- db_data:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect"]
interval: 10s
retries: 5
networks:
- backend
restart: unless-stopped
cache:
image: redis:7-alpine
container_name: pme_cache
networks:
- backend
restart: unless-stopped
volumes:
db_data:
networks:
backend:
driver: bridge
Lancez tout avec docker compose up -d. Trois conteneurs démarrent en parallèle, communiquent sur un réseau privé bridge nommé « backend », et la base MariaDB persiste ses données dans le volume db_data même si le conteneur est supprimé.
Étape 8 — Comprendre volumes et réseaux
Les volumes Docker stockent les données hors du cycle de vie du conteneur. Sans volume, supprimer le conteneur MariaDB efface toutes les commandes de votre site e-commerce. Avec un volume nommé (db_data), vous pouvez détruire et recréer le conteneur sans perdre une ligne. Pour des sauvegardes, montez un volume bind qui pointe vers un dossier de votre disque hôte.
Les réseaux Docker isolent les conteneurs. Sur le réseau « backend », web peut joindre db simplement avec le nom « db » comme hôte, sans exposer le port MySQL au monde extérieur. Seul le port 8080 du service web est public. C’est la règle d’or : exposez le minimum.
Étape 9 — Tableau récapitulatif des commandes essentielles
| Commande | Usage | Fréquence |
|---|---|---|
| docker ps -a | Lister tous les conteneurs (actifs et stoppés) | Quotidien |
| docker logs -f <nom> | Suivre les logs en temps réel | Quotidien |
| docker exec -it <nom> sh | Ouvrir un shell dans un conteneur | Hebdomadaire |
| docker compose up -d | Lancer la stack en arrière-plan | À chaque déploiement |
| docker compose down | Arrêter et supprimer les conteneurs | Maintenance |
| docker system prune -a | Nettoyer images et conteneurs inutilisés | Mensuel |
| docker volume ls | Lister les volumes | Avant sauvegarde |
| docker network inspect <net> | Détailler un réseau | Debug |
Étape 10 — Pousser une image sur Docker Hub
docker login -u votre_utilisateur
docker tag mon-app:1.0 votre_utilisateur/mon-app:1.0
docker tag mon-app:1.0 votre_utilisateur/mon-app:latest
docker push votre_utilisateur/mon-app:1.0
docker push votre_utilisateur/mon-app:latest
docker pull votre_utilisateur/mon-app:1.0
Docker Hub est gratuit pour les dépôts publics. Pour un dépôt privé (recommandé pour vos clients), comptez 5 USD par mois soit 3 000 FCFA. Alternative : Gitea avec registre intégré sur votre VPS, gratuit après installation.
Étape 11 — Sauvegarder un volume pour un client
docker run --rm \
-v pme_db_data:/data \
-v $(pwd)/backups:/backup \
alpine tar czf /backup/db_$(date +%Y%m%d).tar.gz -C /data .
ls -lh backups/
docker run --rm \
-v pme_db_data:/data \
-v $(pwd)/backups:/backup \
alpine tar xzf /backup/db_20260423.tar.gz -C /data
Cette technique fonctionne pour tout volume : MySQL, MongoDB, fichiers uploadés, configurations Nginx. Automatisez via cron pour une sauvegarde nocturne envoyée vers Backblaze B2 (1 200 FCFA par mois pour 200 Go).
Étape 12 — Limiter les ressources d’un conteneur
services:
web:
image: mon-app:1.0
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
reservations:
cpus: "0.25"
memory: 128M
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
Sans limites, un conteneur qui fuit peut consommer toute la RAM du serveur et planter les autres services. Sur un VPS partagé entre plusieurs clients PME, ces garde-fous sont obligatoires.
Étape 13 — Scanner une image pour les vulnérabilités
docker scout cves mon-app:1.0
docker scout recommendations mon-app:1.0
docker scout compare mon-app:1.0 --to mon-app:0.9
Docker Scout (intégré depuis 2023) audite votre image et signale les CVE critiques. Pour une boutique en ligne traitant des paiements Wave ou Orange Money, c’est un contrôle obligatoire avant chaque mise en production.
Erreurs classiques à éviter
- Utiliser :latest en production : vous ne savez pas quelle version tourne, le rollback devient impossible. Toujours taguer avec un numéro précis (1.2.3).
- Oublier les volumes : redémarrer la stack efface les données client. Catastrophe garantie le jour où votre site e-commerce perd 3 mois de commandes.
- Exposer la base MySQL sur 0.0.0.0 : attaque par force brute en moins de 24 heures. Limitez aux réseaux internes Docker uniquement.
- Mettre les mots de passe dans le Dockerfile : ils restent dans toutes les couches de l’image, lisibles par n’importe qui ayant accès au registre. Utilisez des variables d’environnement ou Docker secrets.
- Construire l’image en root : si un attaquant exploite une faille, il prend le contrôle total. Toujours créer un utilisateur non-root.
- Ne jamais nettoyer : en 6 mois, docker system df affiche 80 Go d’images obsolètes. Lancez docker system prune mensuellement.
Checklist Docker pour mise en production
✓ Image construite en multi-stage avec base alpine ou slim
✓ Utilisateur non-root configuré dans le Dockerfile
✓ Tag versionné (1.2.3) au lieu de :latest
✓ Healthcheck défini pour chaque service
✓ Volumes nommés pour toutes les données persistantes
✓ Réseau bridge dédié, pas de mode host
✓ Limites CPU et mémoire fixées dans docker-compose
✓ Logs rotatés (max-size 10m, max-file 3)
✓ Variables sensibles passées via .env, jamais en dur
✓ Image scannée avec docker scout cves
✓ Sauvegarde automatique des volumes critiques
✓ restart: unless-stopped sur les services critiques
✓ Documentation README avec docker compose up commands
✓ Test de restauration d'une sauvegarde validé
✓ Monitoring CPU/RAM activé (Portainer ou cAdvisor)