Aller au contenu principal

Exercices et Projets Docker Swarm


Objectifs du chapitre

  • Consolider les connaissances acquises
  • Pratiquer avec des scénarios réels
  • Réaliser des projets complets
  • Se préparer à la production

1 - Exercices de base

Exercice 1 : Initialisation du cluster

Objectif : Créer un cluster Swarm basique

# 1. Initialiser le Swarm
docker swarm init --advertise-addr <votre-ip>

# 2. Vérifier l'état
docker node ls
docker info | grep -i swarm

# 3. Créer votre premier service
docker service create --name hello --replicas 3 -p 80:80 nginx:alpine

# 4. Vérifier le service
docker service ls
docker service ps hello

# 5. Tester l'accès
curl http://localhost

# 6. Nettoyer
docker service rm hello

Questions :

  • Que contient le token de join ?
  • Pourquoi utiliser --advertise-addr ?

Exercice 2 : Scaling et rolling updates

Objectif : Maîtriser le scaling et les mises à jour

# 1. Créer un service
docker service create --name web \
--replicas 3 \
--update-parallelism 1 \
--update-delay 10s \
nginx:1.24-alpine

# 2. Observer les tâches
watch docker service ps web

# 3. Scaler à 10 réplicas
docker service scale web=10

# 4. Mettre à jour l'image
docker service update --image nginx:1.25-alpine web

# 5. Observer le rolling update
docker service ps web

# 6. Faire un rollback
docker service rollback web

Exercice 3 : Réseaux overlay

Objectif : Configurer la communication inter-services

# 1. Créer un réseau overlay
docker network create --driver overlay app-network

# 2. Créer un service backend
docker service create --name api \
--network app-network \
--replicas 2 \
nginx:alpine

# 3. Créer un service frontend
docker service create --name web \
--network app-network \
--replicas 2 \
-p 80:80 \
nginx:alpine

# 4. Tester la connectivité
docker exec -it $(docker ps -q -f name=web) ping api

# 5. Vérifier le DNS interne
docker exec -it $(docker ps -q -f name=web) nslookup api

2 - Exercices intermédiaires

Exercice 4 : Secrets et configurations

Objectif : Gérer les données sensibles

# 1. Créer un secret
echo "supersecret123" | docker secret create db_password -

# 2. Créer une configuration
cat << EOF > nginx.conf
server {
listen 80;
location / {
return 200 'Hello from Swarm!';
}
}
EOF
docker config create nginx_conf nginx.conf

# 3. Créer un service utilisant le secret et la config
docker service create --name secure-app \
--secret db_password \
--config source=nginx_conf,target=/etc/nginx/conf.d/default.conf \
nginx:alpine

# 4. Vérifier l'accès au secret (dans le conteneur)
docker exec -it $(docker ps -q -f name=secure-app) cat /run/secrets/db_password

Exercice 5 : Contraintes de placement

Objectif : Contrôler le placement des services

# 1. Ajouter des labels aux nœuds
docker node update --label-add env=production $(docker node ls -q | head -1)
docker node update --label-add type=web $(docker node ls -q | head -1)

# 2. Créer un service avec contraintes
docker service create --name prod-only \
--constraint 'node.labels.env==production' \
--replicas 3 \
nginx:alpine

# 3. Vérifier le placement
docker service ps prod-only

# 4. Tester une contrainte impossible
docker service create --name impossible \
--constraint 'node.labels.env==staging' \
--replicas 2 \
nginx:alpine
# Observer que le service ne peut pas démarrer

# 5. Utiliser des préférences de spread
docker service create --name spread-test \
--replicas 6 \
--placement-pref 'spread=node.labels.type' \
nginx:alpine

Exercice 6 : Stack complète

Objectif : Déployer une application multi-services

Créez le fichier stack.yml :

version: "3.9"

services:
web:
image: nginx:alpine
ports:
- "80:80"
networks:
- frontend
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s

api:
image: httpd:alpine
networks:
- frontend
- backend
deploy:
replicas: 2

db:
image: postgres:15-alpine
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/db_pass
secrets:
- db_pass
volumes:
- db-data:/var/lib/postgresql/data
networks:
- backend
deploy:
replicas: 1
placement:
constraints:
- node.role == manager

networks:
frontend:
driver: overlay
backend:
driver: overlay
internal: true

volumes:
db-data:

secrets:
db_pass:
file: ./db_password.txt
# Créer le fichier de secret
echo "mydbpassword" > db_password.txt

# Déployer la stack
docker stack deploy -c stack.yml myapp

# Vérifier
docker stack services myapp
docker stack ps myapp

# Supprimer
docker stack rm myapp

3 - Projets complets

Projet 1 : Application web haute disponibilité

Architecture :

                   Internet


┌───────────────┐
│ Traefik │
│ (Reverse │
│ Proxy) │
└───────┬───────┘

┌────────────┼────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Web 1 │ │ Web 2 │ │ Web 3 │
└────┬────┘ └────┬────┘ └────┬────┘
│ │ │
└────────────┼────────────┘


┌───────────────┐
│ PostgreSQL │
│ (Primary) │
└───────────────┘

docker-compose.yml :

version: "3.9"

services:
traefik:
image: traefik:v2.10
command:
- "--api.dashboard=true"
- "--providers.docker=true"
- "--providers.docker.swarmMode=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- traefik-public
deploy:
mode: global
placement:
constraints:
- node.role == manager
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`traefik.localhost`)"
- "traefik.http.routers.dashboard.service=api@internal"

web:
image: nginx:alpine
networks:
- traefik-public
- backend
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 30s
failure_action: rollback
labels:
- "traefik.enable=true"
- "traefik.http.routers.web.rule=Host(`app.localhost`)"
- "traefik.http.services.web.loadbalancer.server.port=80"
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost"]
interval: 30s
timeout: 10s
retries: 3

api:
image: httpd:alpine
networks:
- traefik-public
- backend
deploy:
replicas: 2
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`api.localhost`)"
- "traefik.http.services.api.loadbalancer.server.port=80"

db:
image: postgres:15-alpine
environment:
POSTGRES_DB: app
POSTGRES_USER: app
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
secrets:
- db_password
volumes:
- db-data:/var/lib/postgresql/data
networks:
- backend
deploy:
replicas: 1
placement:
constraints:
- node.labels.db == true
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
interval: 30s
timeout: 10s
retries: 5

redis:
image: redis:7-alpine
command: redis-server --appendonly yes
volumes:
- redis-data:/data
networks:
- backend
deploy:
replicas: 1

networks:
traefik-public:
driver: overlay
backend:
driver: overlay
internal: true

volumes:
db-data:
redis-data:

secrets:
db_password:
external: true

Déploiement :

# Préparer
docker node update --label-add db=true $(docker node ls -q | head -1)
echo "secretpassword" | docker secret create db_password -

# Déployer
docker stack deploy -c docker-compose.yml webapp

# Tester
curl -H "Host: app.localhost" http://localhost
curl -H "Host: api.localhost" http://localhost

# Monitoring
watch docker stack services webapp

Projet 2 : Stack de monitoring

docker-compose.monitoring.yml :

version: "3.9"

services:
prometheus:
image: prom/prometheus:v2.47.0
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.enable-lifecycle'
volumes:
- prometheus-data:/prometheus
configs:
- source: prometheus_config
target: /etc/prometheus/prometheus.yml
networks:
- monitoring
deploy:
replicas: 1
placement:
constraints:
- node.role == manager

grafana:
image: grafana/grafana:10.1.0
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- grafana-data:/var/lib/grafana
networks:
- monitoring
- traefik-public
deploy:
replicas: 1
labels:
- "traefik.enable=true"
- "traefik.http.routers.grafana.rule=Host(`grafana.localhost`)"
- "traefik.http.services.grafana.loadbalancer.server.port=3000"

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
networks:
- monitoring
deploy:
mode: global

node-exporter:
image: prom/node-exporter:v1.6.1
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
networks:
- monitoring
deploy:
mode: global

alertmanager:
image: prom/alertmanager:v0.26.0
volumes:
- alertmanager-data:/alertmanager
configs:
- source: alertmanager_config
target: /etc/alertmanager/alertmanager.yml
networks:
- monitoring
deploy:
replicas: 1

networks:
monitoring:
driver: overlay
traefik-public:
external: true

volumes:
prometheus-data:
grafana-data:
alertmanager-data:

configs:
prometheus_config:
file: ./prometheus.yml
alertmanager_config:
file: ./alertmanager.yml

4 - Quiz de révision

Questions

  1. Quel est le nombre minimum de managers pour une tolérance à 1 panne ?

  2. Quelle commande permet de mettre un nœud en maintenance ?

  3. Comment créer un réseau overlay chiffré ?

  4. Quelle est la différence entre mode VIP et DNSRR ?

  5. Comment accéder aux secrets dans un conteneur ?

  6. Quelle option garantit un rolling update sans downtime ?

  7. Comment forcer le redéploiement des tâches d'un service ?

  8. Quel est le quorum pour un cluster de 5 managers ?

Réponses

Voir les réponses
  1. 3 managers (quorum = 2, tolère 1 panne)

  2. docker node update --availability drain <node>

  3. docker network create --driver overlay --opt encrypted <name>

  4. VIP : une seule IP virtuelle avec LB transparent. DNSRR : DNS retourne toutes les IPs, LB côté client

  5. Les secrets sont disponibles dans /run/secrets/<name>

  6. --update-order start-first (démarre le nouveau avant d'arrêter l'ancien)

  7. docker service update --force <service>

  8. Quorum = 3 (5/2 + 1 = 3)


5 - Défis avancés

Défi 1 : Blue-Green Deployment

Implémentez un déploiement blue-green :

  • Service "blue" actif sur le port 80
  • Déployez "green" sur le port 8080
  • Testez green
  • Basculez le trafic
  • Supprimez blue

Défi 2 : Auto-scaling

Créez un script qui :

  • Surveille l'utilisation CPU des services
  • Scale automatiquement entre 2 et 10 réplicas
  • Log les actions de scaling

Défi 3 : Disaster Recovery

Mettez en place :

  • Backup automatique quotidien
  • Script de restauration testé
  • Procédure documentée de recovery

Résumé du cours

Vous avez appris :

  • L'architecture et les concepts Docker Swarm
  • La création et gestion de clusters
  • Les services, tasks et stacks
  • Le networking overlay et routing mesh
  • La gestion des secrets et configurations
  • Le scaling et load balancing
  • La haute disponibilité
  • Les bonnes pratiques de production
Prochaines étapes
  • Pratiquez avec des environnements réels
  • Explorez Kubernetes pour des besoins avancés
  • Intégrez Swarm dans vos pipelines CI/CD
  • Certifiez vos compétences Docker

← Bonnes pratiques | Table des matières