Skip to content

TP9 cmd

PasRP-Theo edited this page Jun 5, 2025 · 2 revisions

TP WoodyToys - High Throughput Guide Complet

1. Préparation et Configuration Docker Hub

Connexion à Docker Hub

# Se connecter avec votre token
docker login -u pasrptheoo
# À l'invite du mot de passe, entrez : dckr_pat__qNuhqj21A4HFR2FlHMiKTYa3DY

2. Découverte et Adaptation du Service Web

Pourquoi la directive "build" ne convient pas pour Docker Swarm ?

La directive build dans docker-compose.yml ne fonctionne pas avec Docker Swarm car :

  • Swarm est distribué : Les nœuds du swarm n'ont pas accès au contexte de build local
  • Images pré-construites requises : Swarm nécessite des images déjà construites et disponibles dans un registry
  • Sécurité : Évite l'exécution de builds sur les nœuds de production

Docker Compose Initial (pour développement local)

# docker-compose-dev.yml
version: '3.8'
services:
  frontend:
    build: ./frontend
    ports:
      - "3000:3000"
    depends_on:
      - backend
    
  backend:
    build: ./backend
    ports:
      - "5000:5000"
    depends_on:
      - database
    environment:
      - DATABASE_URL=postgresql://user:password@database:5432/woodytoys
    
  database:
    image: postgres:13
    environment:
      POSTGRES_DB: woodytoys
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    volumes:
      - db_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"

volumes:
  db_data:

Script de Build et Push

#!/bin/bash
# build_push.sh

# Configuration
DOCKER_USER="pasrptheoo"
PROJECT_NAME="woodytoys"
TAG="latest"

# Build et push frontend
echo "Building frontend..."
docker build -t ${DOCKER_USER}/${PROJECT_NAME}-frontend:${TAG} ./frontend
docker push ${DOCKER_USER}/${PROJECT_NAME}-frontend:${TAG}

# Build et push backend
echo "Building backend..."
docker build -t ${DOCKER_USER}/${PROJECT_NAME}-backend:${TAG} ./backend
docker push ${DOCKER_USER}/${PROJECT_NAME}-backend:${TAG}

echo "All images pushed successfully!"

Docker Compose pour Swarm

# docker-compose-stack.yml
version: '3.8'
services:
  frontend:
    image: pasrptheoo/woodytoys-frontend:latest
    ports:
      - "3000:3000"
    deploy:
      replicas: 2
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
      placement:
        constraints:
          - node.role == worker
    depends_on:
      - backend
    networks:
      - woodytoys-network
    
  backend:
    image: pasrptheoo/woodytoys-backend:latest
    ports:
      - "5000:5000"
    deploy:
      replicas: 3
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
      placement:
        constraints:
          - node.role == worker
    depends_on:
      - database
    environment:
      - DATABASE_URL=postgresql://user:password@database:5432/woodytoys
    networks:
      - woodytoys-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
      interval: 30s
      timeout: 10s
      retries: 3
    
  database:
    image: postgres:13
    environment:
      POSTGRES_DB: woodytoys
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
    volumes:
      - db_data:/var/lib/postgresql/data
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure
      placement:
        constraints:
          - node.role == manager
    networks:
      - woodytoys-network

volumes:
  db_data:

networks:
  woodytoys-network:
    driver: overlay

3. Déploiement et Mesures de Performance

Déploiement sur Swarm

# Déployer la stack
docker stack deploy -c docker-compose-stack.yml woodytoys-l2-2

# Vérifier le déploiement
docker stack services woodytoys-l2-2
docker stack ps woodytoys-l2-2

Campagne de Mesures

# Test de performance basique
time wget -r http://votre-url-swarm

# Test avec curl (plus détaillé)
curl -w "@curl-format.txt" -o /dev/null -s http://votre-url-swarm

# Monitoring en temps réel
watch docker service ls

Créez le fichier curl-format.txt :

     time_namelookup:  %{time_namelookup}\n
        time_connect:  %{time_connect}\n
     time_appconnect:  %{time_appconnect}\n
    time_pretransfer:  %{time_pretransfer}\n
       time_redirect:  %{time_redirect}\n
  time_starttransfer:  %{time_starttransfer}\n
                     ----------\n
          time_total:  %{time_total}\n

Mise à l'échelle dynamique

# Augmenter les replicas sans arrêter le service
docker service scale woodytoys-l2-2_backend=5
docker service scale woodytoys-l2-2_frontend=4

# Vérifier la mise à l'échelle
docker service ps woodytoys-l2-2_backend

4. Implémentation du Cache Redis

Ajout de Redis au docker-compose

# Ajouter ce service à docker-compose-stack.yml
  redis:
    image: redis:7-alpine
    deploy:
      replicas: 1
      restart_policy:
        condition: on-failure
      placement:
        constraints:
          - node.role == manager
    networks:
      - woodytoys-network
    command: redis-server --appendonly yes
    volumes:
      - redis_data:/data

# Ajouter aux volumes
volumes:
  db_data:
  redis_data:

Code Python pour le Cache (Backend Flask)

# backend/cache.py
import redis
import json
import functools
from flask import current_app

def get_redis_client():
    return redis.Redis(host='redis', port=6379, db=0, decode_responses=True)

def cache_aside(expiration=300):
    """
    Décorateur pour implémenter le pattern Cache Aside
    """
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            # Générer une clé unique basée sur la fonction et ses arguments
            cache_key = f"{func.__name__}:{hash(str(args) + str(kwargs))}"
            
            r = get_redis_client()
            
            # Essayer de récupérer depuis le cache
            cached_result = r.get(cache_key)
            if cached_result:
                current_app.logger.info(f"Cache HIT for {cache_key}")
                return json.loads(cached_result)
            
            # Cache MISS - exécuter la fonction
            current_app.logger.info(f"Cache MISS for {cache_key}")
            result = func(*args, **kwargs)
            
            # Mettre en cache le résultat
            r.setex(cache_key, expiration, json.dumps(result, default=str))
            
            return result
        return wrapper
    return decorator

# Exemple d'utilisation dans votre API
@app.route('/api/products')
@cache_aside(expiration=60)  # Cache pendant 1 minute
def get_products():
    # Simulation d'une requête coûteuse
    products = db.execute("SELECT * FROM products").fetchall()
    return [dict(product) for product in products]

Mise à jour du Backend avec Redis

# backend/app.py (exemple d'intégration)
from flask import Flask, jsonify
import redis
import time
import psycopg2
from cache import cache_aside, get_redis_client

app = Flask(__name__)

@app.route('/health')
def health_check():
    return jsonify({"status": "healthy"}), 200

@app.route('/api/products')
@cache_aside(expiration=60)
def get_products():
    # Simulation du throttling artificiel
    time.sleep(0.5)  # Ne pas supprimer - limitation volontaire
    
    # Requête à la base de données
    # ... votre logique métier ici
    return {"products": ["toy1", "toy2", "toy3"]}

@app.route('/api/cache/stats')
def cache_stats():
    """Endpoint pour monitorer les statistiques du cache"""
    r = get_redis_client()
    info = r.info()
    return jsonify({
        "keyspace_hits": info.get("keyspace_hits", 0),
        "keyspace_misses": info.get("keyspace_misses", 0),
        "used_memory": info.get("used_memory_human", "0B"),
        "connected_clients": info.get("connected_clients", 0)
    })

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

5. Configuration CDN avec GCore

Étapes de Configuration GCore

  1. Inscription sur GCore : Créez un compte sur https://gcore.com

  2. Création d'un CDN Resource :

    • Origin : http://votre-domaine-swarm
    • CNAME : cdn-woodytoys.votre-domaine.com
  3. Configuration DNS :

# Ajoutez ce CNAME à votre zone DNS
cdn-woodytoys.l2-2.votre-domaine.com CNAME cl-xyz.gcdn.co

Adaptation du Code HTML

<!-- frontend/public/index.html -->
<!DOCTYPE html>
<html>
<head>
    <title>WoodyToys</title>
    <!-- Ressources statiques via CDN -->
    <link rel="stylesheet" href="https://cdn-woodytoys.l2-2.votre-domaine.com/css/style.css">
    <script src="https://cdn-woodytoys.l2-2.votre-domaine.com/js/app.js"></script>
</head>
<body>
    <!-- Contenu de votre application -->
    <div id="app">
        <img src="https://cdn-woodytoys.l2-2.votre-domaine.com/images/logo.png" alt="WoodyToys">
    </div>
</body>
</html>

6. Tests de Performance et Optimisations

Script de Test Complet

#!/bin/bash
# performance_test.sh

URL="http://votre-swarm-url"
RESULTS_DIR="./performance_results"
mkdir -p $RESULTS_DIR

echo "=== Test de Performance WoodyToys ==="
date > $RESULTS_DIR/test_$(date +%Y%m%d_%H%M%S).log

# Test sans cache
echo "Test sans cache..." | tee -a $RESULTS_DIR/test_$(date +%Y%m%d_%H%M%S).log
for i in {1..10}; do
    curl -w "%{time_total}\n" -o /dev/null -s $URL/api/products
done | awk '{sum+=$1; n++} END {print "Temps moyen:", sum/n, "secondes"}' | tee -a $RESULTS_DIR/test_$(date +%Y%m%d_%H%M%S).log

# Test avec cache (après warmup)
echo "Test avec cache..." | tee -a $RESULTS_DIR/test_$(date +%Y%m%d_%H%M%S).log
curl -s $URL/api/products > /dev/null  # Warmup
for i in {1..10}; do
    curl -w "%{time_total}\n" -o /dev/null -s $URL/api/products
done | awk '{sum+=$1; n++} END {print "Temps moyen:", sum/n, "secondes"}' | tee -a $RESULTS_DIR/test_$(date +%Y%m%d_%H%M%S).log

# Test de charge avec ab (Apache Bench)
echo "Test de charge concurrent..." | tee -a $RESULTS_DIR/test_$(date +%Y%m%d_%H%M%S).log
ab -n 100 -c 10 $URL/ | tee -a $RESULTS_DIR/test_$(date +%Y%m%d_%H%M%S).log

Monitoring Avancé

# Monitoring des services
docker service logs woodytoys-l2-2_backend
docker service logs woodytoys-l2-2_redis

# Stats détaillées
docker stats $(docker ps -q --filter "label=com.docker.swarm.service.name=woodytoys-l2-2_backend")

7. Optimisations Avancées pour Docker Swarm

Health Checks Optimisés

# Dans le service backend
healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
  interval: 15s
  timeout: 5s
  retries: 3
  start_period: 30s

Contraintes de Placement

# Placement stratégique des services
backend:
  deploy:
    placement:
      constraints:
        - node.role == worker
        - node.labels.performance == high
      preferences:
        - spread: node.labels.zone

database:
  deploy:
    placement:
      constraints:
        - node.role == manager  # Base de données sur manager pour stabilité
        - node.labels.storage == ssd

Configuration de Load Balancing

# Ajout d'un load balancer dédié
nginx:
  image: nginx:alpine
  ports:
    - "80:80"
    - "443:443"
  configs:
    - source: nginx_config
      target: /etc/nginx/nginx.conf
  deploy:
    replicas: 2
    placement:
      constraints:
        - node.role == manager

configs:
  nginx_config:
    external: true

8. Solutions pour l'Optimisation de Base de Données

1. Réplication Maître-Esclave (Read Replicas)

Description : Configuration d'une base de données principale pour les écritures et de plusieurs répliques en lecture seule.

Avantages :

  • Répartition de la charge de lecture
  • Amélioration des performances pour les requêtes SELECT
  • Haute disponibilité

Inconvénients :

  • Complexité de configuration
  • Latence de réplication
  • Cohérence éventuelle des données

Configuration Docker :

db-master:
  image: postgres:13
  environment:
    POSTGRES_REPLICATION_USER: replicator
    POSTGRES_REPLICATION_PASSWORD: secret
  command: |
    postgres 
    -c wal_level=replica 
    -c max_wal_senders=3 
    -c max_replication_slots=3

db-slave:
  image: postgres:13
  environment:
    PGUSER: postgres
    POSTGRES_MASTER_SERVICE: db-master
  command: |
    bash -c "
    pg_basebackup -h db-master -D /var/lib/postgresql/data -U replicator -v -P -W
    postgres -c hot_standby=on"

2. Sharding Horizontal

Description : Division des données en plusieurs bases selon une clé de partitionnement.

Avantages :

  • Scalabilité horizontale infinie
  • Isolation des données
  • Performance améliorée pour les requêtes ciblées

Inconvénients :

  • Complexité applicative
  • Requêtes cross-shard difficiles
  • Gestion des clés de partitionnement

3. Connection Pooling

Configuration avec PgBouncer :

pgbouncer:
  image: pgbouncer/pgbouncer:latest
  environment:
    DATABASES_HOST: database
    DATABASES_PORT: 5432
    DATABASES_USER: user
    DATABASES_PASSWORD: password
    DATABASES_DBNAME: woodytoys
    POOL_MODE: transaction
    MAX_CLIENT_CONN: 100
    DEFAULT_POOL_SIZE: 20

9. Résultats Attendus et Documentation

Template de Documentation Wiki

# Résultats des Tests de Performance WoodyToys

## Configuration Initiale
- **Replicas Backend** : 1
- **Replicas Frontend** : 1  
- **Cache** : Non activé
- **CDN** : Non configuré

### Métriques Initiales
- Temps de réponse moyen : XXX ms
- Throughput : XXX req/s
- Taux d'erreur : XXX%

## Après Scaling (Replicas)
- **Replicas Backend** : 3
- **Replicas Frontend** : 2

### Métriques après Scaling
- Temps de réponse moyen : XXX ms (-XX%)
- Throughput : XXX req/s (+XX%)
- Taux d'erreur : XXX%

## Après Implémentation Cache Redis
### Métriques avec Cache
- Temps de réponse moyen : XXX ms (-XX%)
- Cache Hit Ratio : XX%
- Réduction charge DB : XX%

## Après CDN
### Métriques avec CDN
- Temps de chargement ressources statiques : XXX ms (-XX%)
- Bande passante économisée : XX%

## Conclusions
[Vos analyses et recommandations]

10. Commandes de Maintenance

# Mise à jour de la stack
docker stack deploy -c docker-compose-stack.yml woodytoys-l2-2

# Scaling manuel
docker service scale woodytoys-l2-2_backend=5

# Nettoyage
docker stack rm woodytoys-l2-2
docker system prune -f

# Logs et debugging
docker service logs -f woodytoys-l2-2_backend
docker service ps woodytoys-l2-2_backend --no-trunc

Ce guide complet vous permettra de réaliser l'ensemble du TP avec succès. N'oubliez pas de documenter chaque étape et vos observations dans votre wiki !

Clone this wiki locally