TP9 : High Throughput on Woodytoys - PasRP-Theo/Admin-II GitHub Wiki
La directive build
permet de construire une image à partir de fichiers sources locaux. Cette approche fonctionne parfaitement en environnement de développement, mais présente des limitations majeures avec Docker Swarm :
- Contexte de build distribué : En Swarm, les nœuds du cluster ne partagent pas le contexte de build, donc ils ne peuvent pas construire d'images localement
- Images pré-construites requises : Swarm nécessite des images déjà construites et disponibles sur un registre (Docker Hub, registre privé, etc.)
-
Solution : Il faut remplacer
build
parimage
et publier l'image avant le déploiement
┌─────────────┐ HTTP ┌──────────────┐ API ┌─────────────┐
│ Client │────────────▶│ Frontend │────────────▶│ API │
│ Web │ │ (Nginx) │ │ Backend │
└─────────────┘ └──────────────┘ └─────────────┘
│
▼
┌──────────────┐ ┌─────────────┐
│ Cache │◀────────────│ Base de │
│ (Redis) │ SQL │ données │
└──────────────┘ │ (Postgres) │
└─────────────┘
Description des flux :
- Client Web → Frontend : Le client envoie des requêtes HTTP au frontend Nginx. Le frontend affiche l'interface utilisateur et communique avec l'API
- Frontend → API Backend : Le frontend envoie des requêtes à l'API pour récupérer/modifier des données. L'API traite les requêtes et renvoie des réponses (JSON)
- API Backend → Base de données : L'API interroge la DB Postgres pour récupérer/stocker des données (requêtes SQL)
- API Backend → Cache Redis : Pour optimiser les performances, l'API vérifie si les données demandées sont présentes dans le cache Redis avant d'interroger la DB
Modifications du docker-compose.yml :
- Remplacement des directives
build
parimage
avec les références aux images publiées sur Docker Hub - Ajout de la directive
deploy
avec la configuration des replicas pour chaque service - Configuration des réseaux pour la communication inter-services
#!/bin/bash
set -e
default_version="16"
version=${1:-"$default_version"}
Build et tag des images
docker build -t pasrptheoo/woody_api_misc:"$version" api_misc
docker tag pasrptheoo/woody_api_misc:"$version" pasrptheoo/woody_api_misc:latest
docker build -t pasrptheoo/woody_api_orders:"$version" api_orders
docker tag pasrptheoo/woody_api_orders:"$version" pasrptheoo/woody_api_orders:latest
docker build -t pasrptheoo/woody_api_products:"$version" api_products
docker tag pasrptheoo/woody_api_products:"$version" pasrptheoo/woody_api_products:latest
docker build -t pasrptheoo/woody_rp:"$version" reverse-proxy
docker tag pasrptheoo/woody_rp:"$version" pasrptheoo/woody_rp:latest
docker build -t pasrptheoo/woody_database:"$version" database
docker tag pasrptheoo/woody_database:"$version" pasrptheoo/woody_database:latest
docker build -t pasrptheoo/woody_front:"$version" front
docker tag pasrptheoo/woody_front:"$version" pasrptheoo/woody_front:latest
Push des images
docker push pasrptheoo/woody_api_products:"$version"
docker push pasrptheoo/woody_api_products:latest
docker push pasrptheoo/woody_api_orders:"$version"
docker push pasrptheoo/woody_api_orders:latest
docker push pasrptheoo/woody_api_misc:"$version"
docker push pasrptheoo/woody_api_misc:latest
docker push pasrptheoo/woody_rp:"$version"
docker push pasrptheoo/woody_rp:latest
docker push pasrptheoo/woody_front:"$version"
docker push pasrptheoo/woody_front:latest
docker push pasrptheoo/woody_database:"$version"
docker push pasrptheoo/woody_database:latest
docker stack deploy -c docker-compose.yml woodytoys
Problème identifié : Chargement lent dû à une image volumineuse (5.0 MB) observé via les DevTools du navigateur.
Méthode de mesure : Utilisation de la commande time wget -r
pour mesurer les performances de téléchargement.
Résultats initiaux :
- Temps total : 16s
- Fichiers téléchargés : 2 fichiers, 4.8M
- Débit : 307 KB/s
Méthodologie :
- Utilisation de
time wget -r
pour des mesures reproductibles - Tests avant et après modifications du nombre de répliques
- Configurations testées : 1, 2, et 3 répliques pour les services API et Frontend
- Enregistrement du temps total, nombre de fichiers et taille téléchargée
Configuration | Temps réel | Temps utilisateur | Temps système | Débit |
---|---|---|---|---|
1 replica | 19.141s | 0.032s | 0.068s | 307 KB/s |
2 replicas | 19.115s | 0.026s | 0.066s | 307 KB/s |
3 replicas | 19.127s | 0.029s | 0.067s | 307 KB/s |
L'augmentation du nombre de répliques ne résout pas les problèmes de performance. Les différences observées sont négligeables et restent dans la marge d'erreur des mesures.
Limitations des tests :
- Le goulot d'étranglement principal est la taille de l'image (4.8M)
- Les répliques n'améliorent pas le débit de téléchargement d'un fichier statique
- Les conditions de test ne représentent pas une charge réelle avec plusieurs utilisateurs simultanés
Au-delà des répliques, plusieurs optimisations sont possibles :
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/api/ping"]
interval: 30s
timeout: 10s
retries: 3
- Vérification automatique du bon fonctionnement de l'API
- Redémarrage automatique en cas de dysfonctionnement
- Amélioration de la disponibilité du service
deploy:
placement:
constraints:
- node.labels.storage == ssd
- node.role == manager
- Assure que la DB est déployée sur un nœud avec des ressources adéquates
- Améliore la stabilité et les performances de stockage
redis:
image: redis:latest
networks:
woody_net:
aliases:
- redis
deploy:
replicas: 3
from flask import Flask, request
from flask_cors import CORS
import redis
import woody
app = Flask('my_api')
cors = CORS(app)
r = redis.Redis(host='redis', port=6379, db=0)
@app.get('/api/ping')
def ping():
return 'ping'
Sans cache (première requête) :
real 0m18.151s
user 0m0.001s
sys 0m0.010s
Avec cache (requête suivante) :
real 0m3.090s
user 0m0.011s
sys 0m0.000s
Amélioration spectaculaire : Le temps de réponse a diminué de 83% (de 18.151s à 3.090s) pour les requêtes répétitives.
Explication : Les lectures répétées sur les mêmes données n'ont plus besoin d'interroger la base PostgreSQL, mais sont servies directement depuis la mémoire avec Redis, ce qui est beaucoup plus rapide.
Note : La mise en place d'un CDN n'a pas été réalisée dans le cadre de ce projet pour des raisons budgétaires (refus d'utiliser une carte bancaire pour ce cours).
- Réduction du temps de chargement : Distribution géographique des ressources
- Décharge du serveur frontend : Les ressources statiques sont servies par le CDN
- Meilleure réactivité de l'application : Latence réduite grâce à la proximité géographique
- Scalabilité améliorée : Gestion automatique des pics de trafic
L'utilisation d'un CDN est une solution efficace pour améliorer les performances des ressources statiques, complétant les autres optimisations mises en place (répliques et cache Redis).
Configuration d'une base de données principale (Master) qui gère toutes les opérations d'écriture, et une ou plusieurs bases de données secondaires (Slaves) qui sont des copies synchronisées du Master et qui gèrent les opérations de lecture.
- Répartition de charge : Séparation des opérations de lecture et d'écriture
- Amélioration des performances : Les requêtes de lecture sont distribuées sur plusieurs serveurs
- Haute disponibilité : Redondance en cas de panne du serveur principal
- Scalabilité horizontale : Possibilité d'ajouter des slaves selon les besoins
- Complexité de configuration : Mise en place et maintenance plus complexes
- Latence de synchronisation : Délai possible entre l'écriture et la disponibilité en lecture sur les slaves
- Coût infrastructure : Nécessite des ressources supplémentaires
- Cohérence éventuelle : Les données peuvent être temporairement incohérentes entre Master et Slaves
- Gestion des pannes : Nécessite une stratégie de basculement (failover) en cas de panne du Master