Skip to main content

Docker Swarm best practices


Chapter objectives

  • Apply production best practices
  • Secure your Swarm cluster
  • Optimize performance
  • Standardize deployments

1 - Cluster architecture

Sizing

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 │
│ │
└─────────────────────────────────────────────────────────────┘

Recommendations

ComponentMinimumRecommended
Managers35
Workers2Depending on load
Manager RAM4 GB8 GB
Worker RAM2 GB4-16 GB
StorageSSD 50 GBSSD 100+ GB

Role separation

# 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 - Security

Encryption

# 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

Certificate rotation

# 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

Secure networks

# 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 and configs

# ✅ Utiliser des secrets pour les données sensibles
secrets:
db_password:
external: true

# ❌ Ne jamais faire
environment:
- DB_PASSWORD=plaintext

Principle of least privilege

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 and registries

Image versions

# ✅ Toujours utiliser des tags versionnés
services:
web:
image: myregistry/web:1.2.3

# ❌ Éviter
services:
web:
image: myregistry/web:latest

Private registry

# 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

Sign images

# Activer le content trust
export DOCKER_CONTENT_TRUST=1

# Signer et pousser
docker push myregistry/app:1.0

4 - Deployments

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

Mandatory healthchecks

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 - Resource management

Mandatory limits

services:
app:
deploy:
resources:
limits:
cpus: '1'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M

Avoid over-provisioning

# 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

Network isolation

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 and service discovery

# Utiliser des noms de service, pas des IPs
services:
api:
environment:
- DATABASE_HOST=db
- REDIS_HOST=redis
- CACHE_HOST=memcached

Avoid host mode

# ✅ Mode ingress (load balancing)
ports:
- "80:80"

# ⚠️ Mode host (cas spécifiques uniquement)
ports:
- target: 80
published: 80
mode: host

7 - Logging and monitoring

Centralize 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}}"

Monitoring stack

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 - Organization and conventions

Naming

# 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 - Production checklist

Before deployment

## 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 - Common mistakes to avoid

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

Security

# ❌ 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

Deployment

# ❌ Images :latest
# ❌ Pas de healthcheck
# ❌ Pas de limites de ressources

# ✅ Tags versionnés
# ✅ Healthchecks sur tous les services
# ✅ Limits et reservations définis

Summary

CategoryBest practice
Managers3 or 5, distributed, in drain mode
SecurityAutolock, secrets, encrypted networks
ImagesVersioned tags, private registry
DeploymentRolling update, healthchecks
ResourcesMandatory limits and reservations
NetworkingFrontend/backend isolation
MonitoringPrometheus + Grafana + alerts
Key points
  • Separate managers from workers
  • Use secrets, never plaintext
  • Healthchecks on all services
  • Monitoring and alerting are mandatory

Practical exercises

  1. Configure a cluster with best practices
  2. Create a stack with full network isolation
  3. Set up monitoring with Prometheus
  4. Test a rolling update with zero-downtime

← High availability | Exercises and projects →