Aller au contenu principal

Stacks et déploiement


Objectifs du chapitre

  • Comprendre les stacks Docker
  • Écrire des fichiers de stack (docker-compose)
  • Déployer et gérer des stacks
  • Utiliser les options de déploiement

1 - Qu'est-ce qu'une Stack ?

Définition

Une stack est un groupe de services liés, définis dans un fichier YAML et déployés ensemble.

┌─────────────────────────────────────────────────────────────┐
│ Stack "myapp" │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Service: web (3 replicas) │ │
│ │ Service: api (2 replicas) │ │
│ │ Service: db (1 replica) │ │
│ │ Service: redis (1 replica) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Network: frontend │ │
│ │ Network: backend │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Volume: db-data │ │
│ │ Config: nginx-config │ │
│ │ Secret: db-password │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘

Stack vs Docker Compose

AspectDocker ComposeDocker Stack
ModeStandaloneSwarm
Commandedocker compose updocker stack deploy
BuildOuiNon (images pré-construites)
depends_onOuiNon
deployIgnoréUtilisé

2 - Fichier de Stack

Structure de base

version: "3.9"

services:
web:
image: nginx:alpine
ports:
- "80:80"
deploy:
replicas: 3

api:
image: myapi:latest
deploy:
replicas: 2

networks:
default:
driver: overlay

volumes:
data:

Section deploy

services:
web:
image: nginx
deploy:
# Nombre de réplicas
replicas: 3

# Mode de déploiement
mode: replicated # ou global

# Placement
placement:
constraints:
- node.role == worker
- node.labels.zone == eu-west
preferences:
- spread: node.labels.zone

# Ressources
resources:
limits:
cpus: '0.5'
memory: 256M
reservations:
cpus: '0.25'
memory: 128M

# Stratégie de mise à jour
update_config:
parallelism: 2
delay: 10s
failure_action: rollback
order: start-first

# Stratégie de rollback
rollback_config:
parallelism: 1
delay: 5s

# Politique de redémarrage
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s

# Labels
labels:
com.example.description: "Web frontend"

3 - Déployer une Stack

Commande de base

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

# Avec plusieurs fichiers
docker stack deploy \
-c docker-compose.yml \
-c docker-compose.prod.yml \
myapp

Vérifier le déploiement

# Lister les stacks
docker stack ls

# Services d'une stack
docker stack services myapp

# Tâches d'une stack
docker stack ps myapp

# Tâches avec filtres
docker stack ps myapp --filter "desired-state=running"

Mettre à jour une stack

# Re-déployer avec les changements
docker stack deploy -c docker-compose.yml myapp

# Swarm compare l'état désiré et applique les différences

Supprimer une stack

# Supprimer
docker stack rm myapp

# Attention : supprime tous les services, réseaux (pas les volumes)

4 - Exemple complet : Application web

docker-compose.yml

version: "3.9"

services:
# Reverse proxy / Load balancer
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
configs:
- source: nginx_config
target: /etc/nginx/conf.d/default.conf
networks:
- frontend
deploy:
replicas: 2
placement:
constraints:
- node.role == manager
update_config:
parallelism: 1
delay: 10s

# Application API
api:
image: myregistry/api:${VERSION:-latest}
environment:
- NODE_ENV=production
- DATABASE_URL=postgres://postgres:${DB_PASSWORD}@db:5432/app
- REDIS_URL=redis://redis:6379
networks:
- frontend
- backend
secrets:
- db_password
- jwt_secret
deploy:
replicas: 3
resources:
limits:
cpus: '1'
memory: 512M
update_config:
parallelism: 1
delay: 30s
failure_action: rollback
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3

# Base de données
db:
image: postgres:15-alpine
environment:
- POSTGRES_DB=app
- POSTGRES_PASSWORD_FILE=/run/secrets/db_password
volumes:
- db-data:/var/lib/postgresql/data
networks:
- backend
secrets:
- db_password
deploy:
replicas: 1
placement:
constraints:
- node.labels.db == true
resources:
limits:
cpus: '2'
memory: 2G

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

# Worker pour les jobs asynchrones
worker:
image: myregistry/worker:${VERSION:-latest}
environment:
- DATABASE_URL=postgres://postgres:${DB_PASSWORD}@db:5432/app
- REDIS_URL=redis://redis:6379
networks:
- backend
secrets:
- db_password
deploy:
replicas: 2
resources:
limits:
cpus: '0.5'
memory: 256M

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

volumes:
db-data:
redis-data:

configs:
nginx_config:
file: ./nginx-default.conf

secrets:
db_password:
external: true
jwt_secret:
external: true

Déploiement

# Créer les secrets d'abord
echo "supersecretpassword" | docker secret create db_password -
echo "jwtsecretkey" | docker secret create jwt_secret -

# Déployer
VERSION=1.0.0 docker stack deploy -c docker-compose.yml myapp

# Vérifier
docker stack services myapp

5 - Variables et fichiers d'environnement

Utiliser des variables

services:
api:
image: myapi:${VERSION:-latest}
environment:
- LOG_LEVEL=${LOG_LEVEL:-info}
# Avec variables d'environnement
VERSION=2.0.0 LOG_LEVEL=debug docker stack deploy -c stack.yml myapp

# Avec fichier .env
export $(cat .env | xargs) && docker stack deploy -c stack.yml myapp

Fichier .env

# .env
VERSION=1.2.3
LOG_LEVEL=info
DB_PASSWORD=secret
DOMAIN=example.com

6 - Fichiers multiples

Fichier de base + overrides

# docker-compose.yml (base)
version: "3.9"
services:
api:
image: myapi:latest
networks:
- backend

# docker-compose.prod.yml (override production)
version: "3.9"
services:
api:
deploy:
replicas: 5
resources:
limits:
memory: 1G
# Combiner les fichiers
docker stack deploy \
-c docker-compose.yml \
-c docker-compose.prod.yml \
myapp

Organisation recommandée

project/
├── docker-compose.yml # Base
├── docker-compose.prod.yml # Production overrides
├── docker-compose.dev.yml # Development overrides
├── configs/
│ └── nginx.conf
├── secrets/
│ └── .gitkeep
└── scripts/
├── deploy-prod.sh
└── deploy-staging.sh

7 - Healthchecks et dépendances

Healthcheck dans la stack

services:
api:
image: myapi
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
deploy:
replicas: 3

Gérer les dépendances (sans depends_on)

services:
api:
image: myapi
deploy:
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 10
# L'API redémarre jusqu'à ce que db soit prête

db:
image: postgres:15
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5

8 - Bonnes pratiques

Images

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

# ❌ Éviter latest en production
# image: myregistry/api:latest

Ressources

services:
api:
deploy:
resources:
# ✅ Toujours définir des limites
limits:
cpus: '1'
memory: 512M
# ✅ Définir des réservations pour le scheduling
reservations:
cpus: '0.25'
memory: 256M

Réseaux

networks:
frontend:
driver: overlay

backend:
driver: overlay
internal: true # ✅ Isoler le backend

# ❌ Éviter un seul réseau pour tout

Mises à jour

services:
api:
deploy:
update_config:
parallelism: 1 # Un à la fois
delay: 30s # Attendre entre chaque
failure_action: rollback # Rollback si échec
order: start-first # Nouveau avant de tuer l'ancien

Résumé des commandes

CommandeDescription
docker stack deploy -c file.yml nameDéployer une stack
docker stack lsLister les stacks
docker stack services nameServices d'une stack
docker stack ps nameTâches d'une stack
docker stack rm nameSupprimer une stack
Points clés
  • Les stacks utilisent la section deploy du docker-compose
  • depends_on n'est pas supporté, utilisez healthchecks + restart
  • Utilisez des tags versionnés, pas latest
  • Définissez toujours des limites de ressources

Exercices pratiques

  1. Créez une stack avec nginx + api + redis
  2. Configurez le rolling update pour zéro downtime
  3. Ajoutez des healthchecks à tous les services
  4. Testez la mise à jour d'un service dans la stack

← Réseaux Swarm | Secrets et Configs →