Bonnes pratiques Docker Swarm
Objectifs du chapitre
- Appliquer les bonnes pratiques de production
- Sécuriser votre cluster Swarm
- Optimiser les performances
- Standardiser les déploiements
1 - Architecture du cluster
Dimensionnement
Production Standard:
┌─────────────────────────────────────────────────────────────┐
│ │
│ Managers (3 ou 5) Workers (N) │
│ ┌───────────────┐ ┌───────────────┐ │
│ │ RAM: 4-8 Go │ │ RAM: 2-16 Go │ │
│ │ CPU: 2-4 cores│ │ CPU: 2-8 cores│ │
│ │ SSD: 50 Go │ │ SSD: 50+ Go │ │
│ └───────────────┘ └───────────────┘ │
│ │
│ Managers: gestion uniquement │
│ Workers: exécution des charges de travail │
│ │
└─────────────────────────────────────────────────────────────┘
Recommandations
| Composant | Minimum | Recommandé |
|---|---|---|
| Managers | 3 | 5 |
| Workers | 2 | Selon charge |
| RAM Manager | 4 Go | 8 Go |
| RAM Worker | 2 Go | 4-16 Go |
| Stockage | SSD 50 Go | SSD 100+ Go |
Séparation des rôles
# Ne pas exécuter de charges de travail sur les managers
docker node update --availability drain manager1
docker node update --availability drain manager2
docker node update --availability drain manager3
# Ou utiliser des contraintes
docker service create --name app \
--constraint 'node.role==worker' \
myapp
2 - Sécurité
Chiffrement
# Activer l'autolock (chiffrement au repos)
docker swarm update --autolock=true
# Sauvegarder la clé de déverrouillage !
docker swarm unlock-key
# Rotation régulière de la clé
docker swarm unlock-key --rotate
Rotation des certificats
# Réduire la durée des certificats (par défaut 90 jours)
docker swarm update --cert-expiry 48h
# Forcer la rotation
docker swarm ca --rotate
Réseaux sécurisés
# Chiffrer le trafic overlay
docker network create --driver overlay --opt encrypted secure-net
# Réseau interne (pas d'accès externe)
docker network create --driver overlay --internal backend-net
Secrets et configs
# ✅ Utiliser des secrets pour les données sensibles
secrets:
db_password:
external: true
# ❌ Ne jamais faire
environment:
- DB_PASSWORD=plaintext
Principe du moindre privilège
services:
app:
# Utilisateur non-root
user: "1000:1000"
# Lecture seule si possible
read_only: true
# Limiter les capabilities
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
# Pas de privilege escalation
security_opt:
- no-new-privileges:true
3 - Images et registres
Versions d'images
# ✅ Toujours utiliser des tags versionnés
services:
web:
image: myregistry/web:1.2.3
# ❌ Éviter
services:
web:
image: myregistry/web:latest
Registre privé
# Déployer un registre dans le cluster
docker service create --name registry \
--publish 5000:5000 \
--mount type=volume,source=registry-data,target=/var/lib/registry \
--constraint 'node.role==manager' \
registry:2
# Utiliser les credentials
docker service create --name app \
--with-registry-auth \
myregistry/app:1.0
Signer les images
# Activer le content trust
export DOCKER_CONTENT_TRUST=1
# Signer et pousser
docker push myregistry/app:1.0
4 - Déploiements
Rolling updates
services:
app:
deploy:
replicas: 5
update_config:
parallelism: 1 # Un à la fois
delay: 30s # Délai entre chaque
failure_action: rollback
monitor: 60s # Surveillance après update
order: start-first # Nouveau avant l'ancien
rollback_config:
parallelism: 1
delay: 10s
Healthchecks obligatoires
services:
api:
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
Zero-downtime deployment
services:
web:
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 30s
order: start-first # Démarre le nouveau AVANT d'arrêter l'ancien
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 30s
5 - Gestion des ressources
Limites obligatoires
services:
app:
deploy:
resources:
limits:
cpus: '1'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
Éviter le sur-provisionnement
# Calculer la capacité du cluster
Total Workers: 3 nœuds × 8 Go RAM = 24 Go
# Réserver pour le système
Disponible: 24 Go - (3 × 1 Go système) = 21 Go
# Planifier les services
Service A: 5 replicas × 512 Mo = 2.5 Go
Service B: 3 replicas × 1 Go = 3 Go
...
6 - Networking
Isolation réseau
services:
nginx:
networks:
- frontend
api:
networks:
- frontend
- backend
db:
networks:
- backend # Pas d'accès depuis frontend
networks:
frontend:
driver: overlay
backend:
driver: overlay
internal: true
DNS et service discovery
# Utiliser des noms de service, pas des IPs
services:
api:
environment:
- DATABASE_HOST=db
- REDIS_HOST=redis
- CACHE_HOST=memcached
Éviter le mode host
# ✅ Mode ingress (load balancing)
ports:
- "80:80"
# ⚠️ Mode host (cas spécifiques uniquement)
ports:
- target: 80
published: 80
mode: host
7 - Logging et monitoring
Centraliser les logs
services:
app:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# Ou avec un driver centralisé
app-centralized:
logging:
driver: "fluentd"
options:
fluentd-address: "localhost:24224"
tag: "docker.{{.Name}}"
Stack de monitoring
version: "3.9"
services:
prometheus:
image: prom/prometheus:v2.47.0
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus-data:/prometheus
deploy:
placement:
constraints:
- node.role == manager
grafana:
image: grafana/grafana:10.1.0
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana
deploy:
replicas: 1
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker:/var/lib/docker:ro
deploy:
mode: global
volumes:
prometheus-data:
grafana-data:
8 - Organisation et conventions
Nommage
# Services
{app}-{composant}
exemple: myapp-api, myapp-web, myapp-worker
# Réseaux
{app}-{tier}
exemple: myapp-frontend, myapp-backend
# Volumes
{app}-{service}-data
exemple: myapp-db-data, myapp-redis-data
# Secrets
{app}-{service}-{type}
exemple: myapp-db-password, myapp-api-key
Labels
services:
api:
deploy:
labels:
com.example.app: "myapp"
com.example.env: "production"
com.example.team: "backend"
com.example.version: "1.2.3"
Documentation
# Documenter dans le fichier compose
# =====================================================
# MyApp Production Stack
# =====================================================
# Version: 1.2.3
# Maintainer: [email protected]
#
# Prerequisites:
# - Secrets: db_password, jwt_secret
# - Networks: traefik-public (external)
#
# Deployment:
# docker stack deploy -c docker-compose.yml myapp
# =====================================================
version: "3.9"
services:
# ...
9 - Checklist production
Avant déploiement
## Checklist Pré-Déploiement
### Infrastructure
- [ ] 3+ managers configurés
- [ ] Workers labellisés par zone
- [ ] Firewall configuré (ports 2377, 7946, 4789)
- [ ] Autolock activé
### Images
- [ ] Tags versionnés (pas :latest)
- [ ] Scannées pour vulnérabilités
- [ ] Stockées dans un registre privé
### Services
- [ ] Healthchecks définis
- [ ] Limites de ressources définies
- [ ] Rolling update configuré
- [ ] Restart policy définie
- [ ] Réplicas >= 2 pour services critiques
### Réseau
- [ ] Réseaux overlay créés
- [ ] Backend isolé (internal)
- [ ] Chiffrement activé si nécessaire
### Secrets
- [ ] Pas de secrets en clair
- [ ] Secrets créés et documentés
- [ ] Plan de rotation défini
### Monitoring
- [ ] Prometheus/Grafana déployés
- [ ] Alertes configurées
- [ ] Logs centralisés
### Backup
- [ ] Script de backup en place
- [ ] Test de restauration effectué
10 - Erreurs courantes à éviter
Architecture
# ❌ Tous les managers dans la même zone
# ❌ Nombre pair de managers
# ❌ Managers exécutant des workloads
# ✅ Managers répartis géographiquement
# ✅ 3 ou 5 managers
# ✅ Managers en mode drain
Sécurité
# ❌ Secrets dans les variables d'environnement
# ❌ Conteneurs root
# ❌ Réseaux non isolés
# ✅ Docker secrets
# ✅ Utilisateurs non-root
# ✅ Réseaux séparés frontend/backend
Déploiement
# ❌ Images :latest
# ❌ Pas de healthcheck
# ❌ Pas de limites de ressources
# ✅ Tags versionnés
# ✅ Healthchecks sur tous les services
# ✅ Limits et reservations définis
Résumé
| Catégorie | Bonne pratique |
|---|---|
| Managers | 3 ou 5, répartis, en mode drain |
| Sécurité | Autolock, secrets, réseaux chiffrés |
| Images | Tags versionnés, registre privé |
| Déploiement | Rolling update, healthchecks |
| Ressources | Limits et reservations obligatoires |
| Networking | Isolation frontend/backend |
| Monitoring | Prometheus + Grafana + alertes |
Points clés
- Séparez les managers des workers
- Utilisez des secrets, jamais de plaintext
- Healthchecks sur tous les services
- Monitoring et alerting obligatoires
Exercices pratiques
- Configurez un cluster avec les bonnes pratiques
- Créez une stack avec isolation réseau complète
- Mettez en place le monitoring avec Prometheus
- Testez un rolling update avec zero-downtime