TP9 : High Throughput on Woodytoys - GuillaumeDelferiere/Admin-R-seaux_2_M1-4 GitHub Wiki
1. Configuration de l'environnement de travail
1.1 Installation et récupération du projet
# Création du répertoire de travail
mkdir -p ~/high-throughput && cd ~/high-throughput
# Clonage du repository GitHub
git clone https://github.com/xavier-dubruille/woodytoys .
1.2 Validation du fonctionnement local
# Navigation vers le dossier services
cd services
# Lancement de l'application via Docker Compose
docker compose up -d
# Validation de l'accès aux endpoints
curl http://localhost
curl http://localhost/api/misc/time
# Arrêt de l'environnement local
docker compose down
2. Préparation pour l'orchestration Docker Swarm
2.1 Script de construction et publication des images
cd ~/high-throughput
# Création du script de build automatisé
cat > publish_images.sh << 'EOF'
#!/bin/bash
set -e
registry_user="osirus705"
tag_version="1.0"
docker build -t $registry_user/woody_api:$tag_version services/api
docker build -t $registry_user/woody_rp:$tag_version services/reverse-proxy
docker build -t $registry_user/woody_database:$tag_version services/database
docker build -t $registry_user/woody_front:$tag_version services/front
docker tag $registry_user/woody_api:$tag_version $registry_user/woody_api:latest
docker tag $registry_user/woody_rp:$tag_version $registry_user/woody_rp:latest
docker tag $registry_user/woody_database:$tag_version $registry_user/woody_database:latest
docker tag $registry_user/woody_front:$tag_version $registry_user/woody_front:latest
docker login
docker push $registry_user/woody_api:$tag_version
docker push $registry_user/woody_api:latest
docker push $registry_user/woody_rp:$tag_version
docker push $registry_user/woody_rp:latest
docker push $registry_user/woody_database:$tag_version
docker push $registry_user/woody_database:latest
docker push $registry_user/woody_front:$tag_version
docker push $registry_user/woody_front:latest
EOF
chmod +x build_push.sh
# Exécution du script de publication
./build_push.sh
2.2 Fichier de configuration Docker Stack
cat > stack.yml << 'EOF'
version: "3.9"
services:
database:
image: osirus705/woody_database:latest
environment:
- MYSQL_DATABASE=woody
- MYSQL_ROOT_PASSWORD=pass
volumes:
- mysql-data:/var/lib/mysql
deploy:
replicas: 1
placement:
constraints:
- node.role == manager
restart_policy:
condition: on-failure
backend-api:
image: osirus705/woody_api:latest
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/api/ping"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
placement:
constraints:
- node.role == worker
frontend:
image: osirus705/woody_front:latest
deploy:
replicas: 2
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
proxy:
image: osirus705/woody_rp:latest
ports:
- "80:8080"
deploy:
replicas: 2
restart_policy:
condition: on-failure
depends_on:
- frontend
- backend-api
networks:
default:
driver: overlay
volumes:
mysql-data:
EOF
3. Déploiement et mesures de performance initiales
3.1 Lancement de la stack sur Swarm
# Déploiement sur le nœud manager
cd ~/high-throughput
docker stack deploy -c stack.yml woodytoys-app
# Vérification du statut des services
docker stack services woodytoys-app
docker stack ps woodytoys-app
# Attente du démarrage complet
sleep 60
3.2 Tests de performance de base
# Mesure des temps de réponse standard
time wget -O - http://swarm.m1-4.ephec-ti.be > /dev/null
time curl http://swarm.m1-4.ephec-ti.be/api/misc/heavy?name=benchmark
time curl http://swarm.m1-4.ephec-ti.be/api/products/last
# Test de téléchargement récursif
time wget -r -np -nH --cut-dirs=1 http://swarm.m1-4.ephec-ti.be
4. Optimisation par scaling horizontal
4.1 Augmentation des instances d'API
# Scaling du service API
docker service scale woodytoys-app_backend-api=5
# Contrôle du scaling
docker service ls
4.2 Évaluation post-scaling
# Nouvelles mesures de performance
time wget -O - http://swarm.m1-4.ephec-ti.be > /dev/null
time curl http://swarm.m1-4.ephec-ti.be/api/misc/heavy?name=benchmark
time curl http://swarm.m1-4.ephec-ti.be/api/products/last
# Test de charge parallèle
for i in {1..5}; do
curl -s http://swarm.m1-4.ephec-ti.be/api/misc/heavy?name=test$i &
done
wait
5. Implémentation du cache avec Redis
5.1 Développement du module de cache
# Création du dossier pour l'API avec cache
mkdir -p ~/high-throughput/api-with-cache && cd ~/high-throughput/api-with-cache
# Copie des sources originales
cp -r ~/high-throughput/services/api/* .
# Module de gestion du cache Redis
cat > cache_manager.py << 'EOF'
import redis
import json
from functools import wraps
redis_connection = redis.Redis(host='redis', port=6379, db=0)
def cached_response(expiry_time=60):
"""
Décorateur pour la mise en cache des réponses
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# Construction de la clé de cache unique
cache_key = f"{func.__name__}:{str(args)}:{str(kwargs)}"
# Tentative de récupération depuis le cache
cached_data = redis_connection.get(cache_key)
if cached_data:
print(f"Cache hit pour {cache_key}")
return cached_data.decode('utf-8')
# Exécution de la fonction et mise en cache
print(f"Cache miss pour {cache_key}")
result = func(*args, **kwargs)
redis_connection.setex(cache_key, expiry_time, result)
return result
return wrapper
return decorator
EOF
# API modifiée avec cache Redis
cat > main.py << 'EOF'
import uuid
from datetime import datetime
from flask import Flask, request
from flask_cors import CORS
import woody
from cache_manager import cached_response
import redis
app = Flask('woody_api')
cors = CORS(app)
redis_connection = redis.Redis(host='redis', port=6379, db=0)
@app.get('/api/ping')
def health_check():
return 'healthy'
@app.route('/api/misc/time', methods=['GET'])
def current_time():
return f'timestamp: {datetime.now()}'
@app.route('/api/misc/heavy', methods=['GET'])
@cached_response(expiry_time=30)
def heavy_computation():
name = request.args.get('name')
result = woody.make_some_heavy_computation(name)
return f'{datetime.now()}: {result}'
@app.route('/api/products', methods=['GET'])
def create_product():
product_name = request.args.get('product')
woody.add_product(str(product_name))
redis_connection.delete('get_latest_product:():{}')
return str(product_name) or "none"
@app.route('/api/products/<int:product_id>', methods=['GET'])
def fetch_product(product_id):
return "fonctionnalité en développement"
@app.route('/api/products/last', methods=['GET'])
@cached_response(expiry_time=15)
def get_latest_product():
latest_product = woody.get_last_product()
return f'database: {datetime.now()} - {latest_product}'
@app.route('/api/orders/do', methods=['GET'])
def process_order():
order_product = request.args.get('order')
order_uuid = str(uuid.uuid4())
handle_order(order_uuid, order_product)
return f"Commande {order_uuid} créée pour le produit : {order_product}"
@app.route('/api/orders/', methods=['GET'])
def check_order():
order_uuid = request.args.get('order_id')
order_status = woody.get_order(order_uuid)
return f'commande "{order_uuid}": {order_status}'
def handle_order(order_id, product):
validation_status = woody.make_heavy_validation(product)
woody.save_order(order_id, validation_status, product)
if __name__ == "__main__":
woody.launch_server(app, host='0.0.0.0', port=5000)
EOF
5.2 Construction et déploiement avec Redis
# Build de l'image avec cache
docker build -t osirus705/woody_api:with-redis .
docker push osirus705/woody_api:with-redis
# Configuration stack avec Redis
cd ~/high-throughput
cat > docker-stack-redis.yml << 'EOF'
version: "3.9"
services:
database:
image: osirus705/woody_database:latest
environment:
- MYSQL_DATABASE=woody
- MYSQL_ROOT_PASSWORD=pass
volumes:
- mysql-data:/var/lib/mysql
deploy:
replicas: 1
placement:
constraints:
- node.role == manager
restart_policy:
condition: on-failure
cache-server:
image: redis:alpine
deploy:
replicas: 1
restart_policy:
condition: on-failure
backend-api:
image: osirus705/woody_api:with-redis
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/api/ping"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
deploy:
replicas: 5
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
placement:
constraints:
- node.role == worker
frontend:
image: osirus705/woody_front:latest
deploy:
replicas: 2
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
proxy:
image: osirus705/woody_rp:latest
ports:
- "80:8080"
deploy:
replicas: 2
restart_policy:
condition: on-failure
depends_on:
- frontend
- backend-api
networks:
default:
driver: overlay
volumes:
mysql-data:
EOF
# Récupération de l'image Redis
docker pull redis:alpine
# Déploiement avec cache Redis
docker stack deploy -c docker-stack-redis.yml woodytoys-app
# Attente du démarrage
sleep 60
5.3 Tests de performance avec cache
# Test initial (cache miss)
time curl http://swarm.m1-4.ephec-ti.be/api/misc/heavy?name=benchmark
# Test suivant (cache hit)
time curl http://swarm.m1-4.ephec-ti.be/api/misc/heavy?name=benchmark
# Test API produit (cache miss)
time curl http://swarm.m1-4.ephec-ti.be/api/products/last
# Test API produit (cache hit)
time curl http://swarm.m1-4.ephec-ti.be/api/products/last
6. Intégration CDN
6.1 Configuration du service CDN Gcore
- Inscription sur https://gcore.com/
- Navigation vers "CDN" > "Resources" dans le tableau de bord
- Configuration d'une nouvelle ressource :
- URL d'origine: http://swarm.m1-4.ephec-ti.be
- Nom de la ressource: woodytoys-cdn-resource
- CNAME personnalisé: cdn.m1-4.ephec-ti.be
6.2 Configuration DNS pour le CDN
# Configuration sur le serveur DNS
cd ~/dns-public
# Ajout du CNAME dans la zone DNS
echo "cdn IN CNAME XXXXX.cdn.gcore.com." >> zone/m1-4.zone
# Mise à jour du serial dans le SOA
# Redémarrage du service DNS
docker restart dns
# Test de résolution DNS
dig @localhost cdn.m1-4.ephec-ti.be
6.3 Adaptation du frontend pour le CDN
mkdir -p ~/high-throughput/frontend-cdn && cd ~/high-throughput/frontend-cdn
# Copie des fichiers frontend
cp -r ~/high-throughput/services/front/* .
# Modification de index.html pour utiliser le CDN
# Remplacement de /5mo.jpg par https://cdn.m1-4.ephec-ti.be/5mo.jpg dans index.html
# Construction et publication de l'image
docker build -t osirus705/woody_front:cdn-enabled .
docker push osirus705/woody_front:cdn-enabled
6.4 Déploiement de la version optimisée CDN
cd ~/tp-high-throughput
cat > docker-stack-complete.yml << 'EOF'
version: "3.9"
services:
database:
image: osirus705/woody_database:latest
environment:
- MYSQL_DATABASE=woody
- MYSQL_ROOT_PASSWORD=pass
volumes:
- mysql-data:/var/lib/mysql
deploy:
replicas: 1
placement:
constraints:
- node.role == manager
restart_policy:
condition: on-failure
cache-server:
image: redis:alpine
deploy:
replicas: 1
restart_policy:
condition: on-failure
backend-api:
image: osirus705/woody_api:with-redis
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/api/ping"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
deploy:
replicas: 5
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
placement:
constraints:
- node.role == worker
frontend:
image: osirus705/woody_front:cdn-enabled
deploy:
replicas: 2
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
proxy:
image: osirus705/woody_rp:latest
ports:
- "80:8080"
deploy:
replicas: 2
restart_policy:
condition: on-failure
depends_on:
- frontend
- backend-api
networks:
default:
driver: overlay
volumes:
mysql-data:
EOF
# Déploiement de la configuration complète
docker stack deploy -c docker-stack-complete.yml woodytoys-app
# Pause pour le démarrage
sleep 60
6.5 Tests de performance avec CDN
# Mesure du temps de chargement de page
time wget -O - http://swarm.m1-4.ephec-ti.be > /dev/null
# Test de téléchargement récursif optimisé
time wget -r -np -nH --cut-dirs=1 http://swarm.m1-4.ephec-ti.be
7. Analyse des résultats
7.1 Comparaison des performances
| Version | Chargement récursif | API calcul intensif | API base de données |
|---|---|---|---|
| Baseline | 16.065 s | 5.029 s | 15.198 s |
| Avec replicas | 16.093 s | 5.040 s | 15.169 s |
| Avec Redis | 16.102 s | 5.058 s (miss) / 0.021 s (hit) | 15.214 s (miss) / 0.032 s (hit) |
| Avec CDN | 0.030 s | identique | identique |
7.2 Analyse des améliorations
-
Scaling horizontal :
- Impact sur l'API : Amélioration limitée due à la nature single-thread de Flask
- Impact sur les requêtes DB : Pas d'amélioration notable car la DB reste un goulot d'étranglement
-
Mise en cache Redis :
- Impact majeur sur les appels répétés
- Gain de 99.6% sur l'API calcul intensif et 99.8% sur l'API DB lors d'un hit cache
-
CDN :
- Amélioration drastique pour les ressources statiques
- Réduction de 99.8% du temps de chargement global
8. Stratégies d'amélioration pour la base de données
Pour optimiser davantage les performances de la base de données, plusieurs approches sont envisageables :
8.1 Architecture Master-Slave
Cette approche sépare les opérations de lecture et d'écriture :
- Un serveur maître gère les opérations d'écriture
- Des serveurs esclaves répliqués gèrent les opérations de lecture
Bénéfices :
- Amélioration de la scalabilité en lecture
- Distribution efficace de la charge
- Amélioration de la disponibilité
8.2 Pattern CQRS (Command Query Responsibility Segregation)
Le CQRS sépare :
- Le modèle de commandes (écritures) : optimisé pour les transactions ACID
- Le modèle de requêtes (lectures) : optimisé pour les performances, potentiellement dénormalisé
Bénéfices :
- Optimisation spécialisée pour chaque type d'opération
- Amélioration des performances de lecture
- Évolutivité indépendante des composants
8.3 Partitionnement horizontal (Sharding)
Le sharding divise les données sur plusieurs serveurs selon une stratégie de partitionnement.
Bénéfices :
- Scalabilité horizontale quasi-infinie
- Distribution équilibrée de la charge
- Performance améliorée pour les requêtes ciblées