TP - Déployer MonsterStack dans Docker Swarm
Objectifs du TP
Section titled “Objectifs du TP”Dans ce TP, vous allez déployer l’application MonsterStack dans un cluster Docker Swarm en exploitant les fonctionnalités avancées d’orchestration :
- Configuration de services répliqués avec stratégie de déploiement
- Limites et réservations de ressources (CPU/RAM)
- Healthchecks et auto-réparation
- Rolling updates zero-downtime
- Gestion des secrets avec Docker Secrets
- Réseaux overlay pour la communication inter-services
- Placement constraints pour contrôler où tournent les conteneurs
- Mise à jour progressive avec rollback automatique
Prérequis
Section titled “Prérequis”- Un cluster Docker Swarm fonctionnel (voir TP 310_swarm_install)
- L’application MonsterStack (disponible dans ce dossier)
- Accès au manager Swarm
Architecture de l’application
Section titled “Architecture de l’application”MonsterStack est une application web composée de trois services :
- Frontend : Application Flask (Python) qui affiche une interface web et génère des avatars de monstres
- ImageBackend : Service qui génère les images de monstres (dnmonster)
- Redis : Cache pour stocker les images générées
┌─────────────────┐│ Frontend │ :5000 (Python Flask)│ (3 replicas) │└────────┬────────┘ │ ┌────┴────┐ │ │ ▼ ▼┌────────┐ ┌────────┐│ Redis │ │ImageBck│ :8080 (dnmonster)│(1 rep) │ │(2 rep) │└────────┘ └────────┘Étape 1 : Explorer le code de base
Section titled “Étape 1 : Explorer le code de base”Le dossier contient :
app/: Code source de l’application FlaskDockerfile: Pour builder l’image du frontenddocker-compose.yml: Version simple pour développement localdocker-compose.swarm.yml: Version optimisée pour Swarm (à créer)
Tester en local avec Docker Compose
Section titled “Tester en local avec Docker Compose”Avant de déployer dans Swarm, testons l’application localement :
cd 312_swarm_monsterstackdocker compose up -dVisitez http://localhost:5000 et testez l’application. Tapez votre nom pour générer un avatar de monstre !
docker compose downÉtape 2 : Créer le fichier docker-compose.swarm.yml
Section titled “Étape 2 : Créer le fichier docker-compose.swarm.yml”Nous allons créer un fichier Docker Compose optimisé pour Swarm avec toutes les fonctionnalités avancées.
Créez le fichier docker-compose.swarm.yml :
version: '3.8'
services: frontend: image: ${DOCKER_REGISTRY:-docker.io}/${DOCKER_USER:-myuser}/monsterstack-frontend:${TAG:-latest} ports: - "5000:5000" environment: - CONTEXT=PROD - REDIS_DOMAIN=redis - IMAGEBACKEND_DOMAIN=imagebackend networks: - monster_network deploy: replicas: 3 update_config: parallelism: 1 # Met à jour 1 conteneur à la fois delay: 10s # Attend 10s entre chaque mise à jour order: start-first # Démarre les nouveaux avant d'arrêter les anciens (zero-downtime) failure_action: rollback # Rollback automatique en cas d'échec monitor: 30s # Surveille pendant 30s après chaque mise à jour rollback_config: parallelism: 2 delay: 5s order: stop-first monitor: 20s restart_policy: condition: on-failure delay: 5s max_attempts: 3 window: 120s resources: limits: cpus: '0.5' # Maximum 50% d'un CPU memory: 256M reservations: cpus: '0.25' # Réserve au minimum 25% d'un CPU memory: 128M placement: constraints: - node.role == worker # Déploie uniquement sur les workers preferences: - spread: node.labels.zone # Répartit entre les zones si définies labels: - "traefik.enable=true" - "traefik.http.routers.monsterstack.rule=Host(`monsterstack.local`)" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:5000/healthz"] interval: 30s timeout: 10s retries: 3 start_period: 40s
imagebackend: image: amouat/dnmonster:1.0 networks: - monster_network deploy: replicas: 2 update_config: parallelism: 1 delay: 10s order: start-first failure_action: rollback restart_policy: condition: on-failure delay: 5s max_attempts: 3 resources: limits: cpus: '0.3' memory: 128M reservations: cpus: '0.1' memory: 64M placement: constraints: - node.role == worker healthcheck: test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:8080/"] interval: 30s timeout: 5s retries: 3 start_period: 20s
redis: image: redis:7-alpine networks: - monster_network deploy: replicas: 1 restart_policy: condition: on-failure delay: 5s max_attempts: 3 resources: limits: cpus: '0.3' memory: 256M reservations: cpus: '0.1' memory: 128M placement: constraints: - node.role == manager # Redis sur le manager pour la persistance healthcheck: test: ["CMD", "redis-cli", "ping"] interval: 30s timeout: 3s retries: 3 start_period: 10s volumes: - redis_data:/data command: redis-server --appendonly yes # Persistence activée
networks: monster_network: driver: overlay attachable: true driver_opts: encrypted: "true" # Chiffrement du trafic entre nœuds
volumes: redis_data: driver: localPoints importants de cette configuration Swarm
Section titled “Points importants de cette configuration Swarm”1. Deploy section (spécifique à Swarm) :
replicas: Nombre de conteneurs à lancerupdate_config: Stratégie de mise à jour progressiverollback_config: Stratégie de rollback automatiquerestart_policy: Politique de redémarrage des conteneursresources: Limites et réservations CPU/RAMplacement: Contraintes de placement sur les nœuds
2. Resources :
limits: Valeurs maximales (hard limit)reservations: Ressources garanties réservées par le scheduler
3. Update strategies :
order: start-first: Zero-downtime (démarre avant d’arrêter)order: stop-first: Libère les ressources avant (économie)failure_action: rollback: Retour automatique à la version précédente
4. Healthchecks :
- Vérifie la santé du conteneur
- Si échec après X retries → redémarrage automatique
- Intégré au rolling update pour valider avant de continuer
5. Placement constraints :
node.role == worker: Uniquement sur les workersnode.role == manager: Uniquement sur les managers (ex: Redis pour persistance)node.labels.zone == eu-west-1: Selon des labels personnalisés
Étape 3 : Builder et pousser l’image du frontend
Section titled “Étape 3 : Builder et pousser l’image du frontend”Le frontend nécessite de builder une image personnalisée.
3.1 Builder l’image
Section titled “3.1 Builder l’image”docker build -t monsterstack-frontend:1.0 .3.2 Pousser sur un registry
Section titled “3.2 Pousser sur un registry”Pour que tous les nœuds Swarm puissent accéder à l’image :
Option A : Docker Hub (public/privé)
# Se connecterdocker login docker.io
# Tagger l'imagedocker tag monsterstack-frontend:1.0 docker.io/<votre_user>/monsterstack-frontend:1.0
# Pousserdocker push docker.io/<votre_user>/monsterstack-frontend:1.0Option B : Registry local (pour tests)
# Lancer un registry local sur le managerdocker service create --name registry --publish 5000:5000 registry:2
# Tagger et pousserdocker tag monsterstack-frontend:1.0 localhost:5000/monsterstack-frontend:1.0docker push localhost:5000/monsterstack-frontend:1.0
# Modifier docker-compose.swarm.yml pour utiliser localhost:5000Option C : GitLab Registry (recommandé en CI/CD)
Voir TP 324_mise_en_oeuvre_ci_gitlab pour l’intégration CI/CD complète.
Étape 4 : Déployer le stack dans Swarm
Section titled “Étape 4 : Déployer le stack dans Swarm”4.1 Déployer avec docker stack deploy
Section titled “4.1 Déployer avec docker stack deploy”# Exporter les variables d'environnement si nécessaireexport DOCKER_USER=votre_userexport TAG=1.0
# Déployer le stackdocker stack deploy -c docker-compose.swarm.yml monsterstackLa commande docker stack deploy :
- Crée ou met à jour tous les services définis
- Crée automatiquement le réseau overlay
- Applique les configurations de réplication et placement
4.2 Vérifier le déploiement
Section titled “4.2 Vérifier le déploiement”# Lister les stacksdocker stack ls
# Lister les services du stackdocker stack services monsterstack
# Détails d'un servicedocker service ps monsterstack_frontend
# Logs d'un servicedocker service logs monsterstack_frontend
# Voir où tournent les conteneursdocker service ps monsterstack_frontend --format "table {{.Name}}\t{{.Node}}\t{{.CurrentState}}"Attendez que tous les services soient à X/X replicas (peut prendre 1-2 minutes).
4.3 Tester l’application
Section titled “4.3 Tester l’application”Trouvez l’IP d’un nœud Swarm et visitez http://<IP_NODE>:5000
Grâce au routing mesh, le port 5000 est accessible depuis n’importe quel nœud du cluster, même si le conteneur tourne ailleurs !
# Test en ligne de commandecurl http://<IP_NODE>:5000
# Générer un monstrecurl -X POST -d "name=Docker" http://<IP_NODE>:5000/Étape 5 : Tester le scaling dynamique
Section titled “Étape 5 : Tester le scaling dynamique”Swarm permet de scaler les services à chaud sans interruption.
# Scaler le frontend à 6 replicasdocker service scale monsterstack_frontend=6
# Observer le scaling en temps réelwatch docker service ps monsterstack_frontend
# Vérifier la charge sur les nœudsdocker node lsdocker node ps <NODE_ID>
# Retour à 3 replicasdocker service scale monsterstack_frontend=3Questions :
- Combien de temps prend le scaling ?
- L’application reste-t-elle disponible pendant le scaling ?
- Comment les conteneurs sont-ils répartis sur les nœuds ?
Étape 6 : Tester le rolling update
Section titled “Étape 6 : Tester le rolling update”Simulons une mise à jour de l’application avec zero-downtime.
6.1 Préparer une nouvelle version
Section titled “6.1 Préparer une nouvelle version”Modifiez légèrement l’application (par exemple dans app/monster_icon.py, changez le titre de la page) et rebuildez :
# Modifier le codesed -i 's/<title>MonsterStack<\/title>/<title>MonsterStack v2<\/title>/' app/templates/index.html
# Rebuilderdocker build -t <votre_registry>/monsterstack-frontend:2.0 .docker push <votre_registry>/monsterstack-frontend:2.06.2 Mettre à jour le service
Section titled “6.2 Mettre à jour le service”# Mise à jour progressivedocker service update --image <votre_registry>/monsterstack-frontend:2.0 monsterstack_frontend
# Observer le rolling updatewatch docker service ps monsterstack_frontendObservez :
- Les conteneurs sont mis à jour 1 par 1 (parallelism: 1)
- Swarm attend 10 secondes entre chaque (delay: 10s)
- Les nouveaux conteneurs démarrent avant l’arrêt des anciens (order: start-first)
- Le healthcheck valide chaque conteneur avant de continuer
Pendant la mise à jour :
# L'application reste accessiblewhile true; do curl -s http://<IP_NODE>:5000 | grep title; sleep 1; doneVous devriez voir une transition progressive de v1 à v2 sans interruption !
6.3 Simuler un échec et rollback automatique
Section titled “6.3 Simuler un échec et rollback automatique”Mettons à jour avec une image cassée :
# Image qui n'existe pas ou qui crash au démarragedocker service update --image <votre_registry>/monsterstack-frontend:broken monsterstack_frontendObservez :
- Swarm détecte l’échec via le healthcheck
- Après plusieurs échecs, il déclenche le rollback automatique
- Le service revient à la version précédente (v2.0)
# Vérifier l'historique des mises à jourdocker service inspect monsterstack_frontend --prettyÉtape 7 : Utiliser Docker Secrets (bonus)
Section titled “Étape 7 : Utiliser Docker Secrets (bonus)”Les secrets permettent de gérer de façon sécurisée les mots de passe et clés.
7.1 Créer un secret
Section titled “7.1 Créer un secret”# Créer un secret pour Redis (par exemple un mot de passe)echo "SuperSecretPassword123" | docker secret create redis_password -
# Lister les secretsdocker secret ls
# Inspecter (le contenu reste chiffré)docker secret inspect redis_password7.2 Utiliser le secret dans docker-compose.swarm.yml
Section titled “7.2 Utiliser le secret dans docker-compose.swarm.yml”Modifiez le fichier pour utiliser le secret :
services: redis: image: redis:7-alpine secrets: - redis_password command: > sh -c "redis-server --requirepass $$(cat /run/secrets/redis_password) --appendonly yes" # ... reste de la config
frontend: # ... secrets: - redis_password environment: - REDIS_PASSWORD_FILE=/run/secrets/redis_password # ...
secrets: redis_password: external: true # Le secret existe déjàLes secrets sont :
- Montés dans
/run/secrets/<secret_name> - Chiffrés en transit et au repos
- Accessibles uniquement aux services autorisés
- Jamais exposés dans les logs ou
docker inspect
7.3 Redéployer avec les secrets
Section titled “7.3 Redéployer avec les secrets”docker stack deploy -c docker-compose.swarm.yml monsterstackLe code applicatif doit être modifié pour lire le mot de passe depuis /run/secrets/redis_password au lieu d’une variable d’environnement classique.
Étape 8 : Monitoring et observabilité
Section titled “Étape 8 : Monitoring et observabilité”8.1 Surveiller les ressources
Section titled “8.1 Surveiller les ressources”# Stats en temps réel d'un servicedocker stats $(docker ps -q -f name=monsterstack_frontend)
# Voir les événements du servicedocker service logs --tail 50 -f monsterstack_frontend
# Voir les détails d'un conteneur spécifiquedocker inspect <CONTAINER_ID>8.2 Visualiser avec Visualizer (optionnel)
Section titled “8.2 Visualiser avec Visualizer (optionnel)”Déployer un outil de visualisation du cluster :
docker service create \ --name=viz \ --publish=8080:8080/tcp \ --constraint=node.role==manager \ --mount=type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \ dockersamples/visualizerVisitez http://<IP_MANAGER>:8080 pour voir une carte du cluster en temps réel.
8.3 Utiliser Portainer (recommandé)
Section titled “8.3 Utiliser Portainer (recommandé)”Portainer offre une interface web complète pour gérer Swarm.
curl -L https://downloads.portainer.io/ce2-19/portainer-agent-stack.yml -o portainer-agent-stack.yml
docker stack deploy -c portainer-agent-stack.yml portainerVisitez http://<IP_MANAGER>:9000 et créez un compte admin.
Étape 9 : Nettoyage
Section titled “Étape 9 : Nettoyage”# Supprimer le stack completdocker stack rm monsterstack
# Vérifier que tout est supprimédocker stack lsdocker service ls
# Supprimer le réseau (optionnel, fait automatiquement)docker network rm monsterstack_monster_network
# Supprimer les secretsdocker secret rm redis_passwordQuestions de réflexion
Section titled “Questions de réflexion”1. Limites de ressources
Section titled “1. Limites de ressources”- Que se passe-t-il si un conteneur dépasse sa limite de mémoire ?
- Réponse : Il est tué par l’OOM Killer et redémarré selon la restart_policy
- Pourquoi séparer
limitsetreservations?- Réponse : Les réservations garantissent les ressources minimales, les limites empêchent la surconsommation
2. Placement
Section titled “2. Placement”- Pourquoi placer Redis sur le manager ?
- Réponse : Pour garantir la persistance (le volume reste sur le même nœud)
- Comment répartir équitablement la charge ?
- Réponse : Utiliser
preferences: spreadsur des labels de nœuds
- Réponse : Utiliser
3. Stratégie de mise à jour
Section titled “3. Stratégie de mise à jour”- Quand utiliser
order: start-firstvsorder: stop-first?- start-first : Applications stateless, zero-downtime (besoin de plus de ressources temporairement)
- stop-first : Ressources limitées, applications stateful (évite les conflits)
- Comment tester une mise à jour sur un seul conteneur d’abord ?
- Réponse : Mettre
parallelism: 1etdelayélevé, tester, puis continuer ou rollback
- Réponse : Mettre
4. Haute disponibilité
Section titled “4. Haute disponibilité”- Que se passe-t-il si un worker tombe ?
- Réponse : Les conteneurs sont automatiquement relancés sur d’autres nœuds (self-healing)
- Comment gérer la perte du manager ?
- Réponse : Utiliser plusieurs managers (quorum 3 ou 5) avec Raft consensus
5. Comparaison avec Kubernetes
Section titled “5. Comparaison avec Kubernetes”- Différences principales ?
- Swarm : Simple, intégré à Docker, suffisant pour PME
- Kubernetes : Plus complexe, écosystème riche, standard pour grandes infras
- Quand choisir Swarm vs Kubernetes ?
- Swarm : Équipe petite, infrastructure simple, besoin de rapidité
- Kubernetes : Équipe expérimentée, multi-cloud, écosystème mature
Améliorations possibles
Section titled “Améliorations possibles”1. Ajouter un reverse proxy (Traefik)
Section titled “1. Ajouter un reverse proxy (Traefik)”Traefik s’intègre nativement avec Swarm pour le routing HTTP.
docker network create --driver=overlay traefik-public
docker service create \ --name traefik \ --constraint=node.role==manager \ --publish 80:80 \ --publish 8080:8080 \ --mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \ --network traefik-public \ traefik:v2.10 \ --api.insecure=true \ --providers.docker=true \ --providers.docker.swarmMode=true \ --providers.docker.exposedbydefault=false \ --entrypoints.web.address=:80Ajoutez ensuite des labels au service frontend (déjà présents dans le docker-compose.swarm.yml).
2. Ajouter de la persistance avec volumes nommés
Section titled “2. Ajouter de la persistance avec volumes nommés”Pour les données critiques, utiliser des volumes avec drivers spécifiques (NFS, GlusterFS, etc.).
3. Monitoring avec Prometheus + Grafana
Section titled “3. Monitoring avec Prometheus + Grafana”Stack complète pour monitorer les métriques Swarm.
4. CI/CD avec GitLab
Section titled “4. CI/CD avec GitLab”Voir TP 325_deploiement_swarm_gitlab pour automatiser le déploiement.