Scalability - Scénarios de Scaling¶
Date: 2026-01-04 Status: Production-Ready
Introduction¶
Ce document détaille les scénarios de montée en charge de la plateforme Urbafix, de la configuration initiale single-instance jusqu'au déploiement multi-régions avec auto-scaling.
Principe : Activation progressive, opt-in, sans refonte complète.
Scénario 1: Single Instance (Actuel)¶
Architecture¶
┌─────────────────────┐
│ Reverse Proxy │ (au choix)
│ (SSL termination) │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ Docker Host │
│ │
│ ┌──────────────┐ │
│ │ Web Container│ │ PHP 8.2 + Nginx
│ │ - Sessions │ │
│ │ file │ │
│ │ - Cache null │ │
│ │ - Queue sync │ │
│ │ - Uploads │ │
│ │ local │ │
│ └──────┬───────┘ │
│ │ │
│ ┌──────▼───────┐ │
│ │ DB Container │ │ MariaDB 10.11
│ │ │ │
│ └──────────────┘ │
└─────────────────────┘
Caractéristiques¶
- Instances web : 1
- Sessions : Fichiers PHP locaux
- Cache : Aucun (queries DB directes)
- Uploads : Filesystem local
- Queue : Exécution synchrone
Capacité¶
- Utilisateurs concurrents : ~50
- Requêtes/seconde : ~10 req/s
- Incidents/jour : ~500
- Stockage photos : Limité par disque serveur
Configuration¶
Coût¶
- Serveur : 1 VPS (2 vCPU, 4 GB RAM) : ~20€/mois
- Backup : Snapshots : ~5€/mois
- Total : ~25€/mois
Limites¶
- ❌ Pas de haute disponibilité (SPOF)
- ❌ Sessions perdues si restart
- ❌ Scaling vertical uniquement
- ❌ Backup manual
Scénario 2: Single Instance + Redis¶
Architecture¶
┌─────────────────────┐
│ Reverse Proxy │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ Docker Host │
│ │
│ ┌──────────────┐ │
│ │ Web Container│───┼──┐
│ │ - Sessions │ │ │
│ │ redis │ │ │
│ │ - Cache │ │ │
│ │ redis │ │ │
│ │ - Queue sync │ │ │
│ └──────────────┘ │ │
│ │ │ │
│ ┌──────▼───────┐ │ │
│ │ DB Container │ │ │
│ └──────────────┘ │ │
│ │ │ │
│ ┌──────▼───────┐ │ │
│ │Redis Container◄──┘ │
│ └──────────────┘ │
└─────────────────────┘
Amélioration¶
- ✅ Sessions persistantes (survive restart)
- ✅ Cache DB queries (-50% load DB)
- ✅ Préparation multi-instances
Configuration¶
# .env
SESSION_DRIVER=redis
CACHE_DRIVER=redis
STORAGE_DRIVER=local
QUEUE_DRIVER=sync
REDIS_PASSWORD=secure_password_32chars
Commandes¶
# Lancer Redis
docker-compose --profile redis up -d
# Vérifier
docker-compose exec redis redis-cli -a ${REDIS_PASSWORD} PING
Capacité¶
- Utilisateurs concurrents : ~100 (+100%)
- Requêtes/seconde : ~20 req/s (+100%)
- Cache hit ratio : ~60-70%
Coût¶
- Serveur : 1 VPS (2 vCPU, 4 GB RAM) : ~20€/mois
- Redis : Inclus (même serveur)
- Total : ~20€/mois (identique)
Scénario 3: Horizontal Scaling (2-3 instances)¶
Architecture¶
┌─────────────────────┐
│ Load Balancer │ (au choix)
│ (Round Robin) │
└──────────┬──────────┘
│
┌─────┴─────┬─────────┐
│ │ │
┌────▼────┐ ┌───▼────┐ ┌──▼─────┐
│ Web 1 │ │ Web 2 │ │ Web 3 │ Stateless
│ :8080 │ │ :8081 │ │ :8082 │
└────┬────┘ └───┬────┘ └──┬─────┘
│ │ │
└─────┬────┴────┬─────┘
│ │
┌─────▼─────┐ │
│ Redis │ │ Sessions + Cache
│ :6379 │ │
└───────────┘ │
│
┌──────▼──────┐
│ MariaDB │
│ :3306 │
└─────────────┘
Migration¶
Étape 1 : Activer Redis (voir Scénario 2)
Étape 2 : Lancer instances
# Instance 1
WEB_PORT=8080 docker-compose --profile redis up -d
# Instance 2
WEB_PORT=8081 docker-compose --profile redis up -d
# Instance 3
WEB_PORT=8082 docker-compose --profile redis up -d
Étape 3 : Configurer le load balancer
Round-robin sur les 3 instances (127.0.0.1:8080, :8081, :8082), avec health check GET /health.php (attendu : HTTP 200) — la configuration exacte dépend du reverse proxy/load balancer choisi.
Étape 4 : Redémarrer le load balancer
Capacité¶
- Utilisateurs concurrents : ~300 (x3)
- Requêtes/seconde : ~60 req/s (x3)
- Haute disponibilité : Oui (⅔ instances peuvent tomber)
Configuration¶
# .env (identique pour toutes instances)
SESSION_DRIVER=redis
CACHE_DRIVER=redis
STORAGE_DRIVER=local # ⚠️ Volume partagé NFS requis
QUEUE_DRIVER=sync
Coût¶
- Serveurs : 3 VPS (2 vCPU, 4 GB RAM) : ~60€/mois
- Load Balancer : 1 VPS (1 vCPU, 2 GB RAM) : ~10€/mois
- Total : ~70€/mois
Limites¶
- ⚠️ Uploads locaux : NFS requis (complexité)
- ⚠️ DB single point of failure
- ⚠️ Queue sync : latence uploads
Scénario 4: Cloud-Native (S3 + Async Queue)¶
Architecture¶
┌─────────────────────┐
│ Load Balancer │
│ (AWS ALB/ELB) │
└──────────┬──────────┘
│
┌─────┴─────┬─────────┐
│ │ │
┌────▼────┐ ┌───▼────┐ ┌──▼─────┐
│ Web 1 │ │ Web 2 │ │ Web 3 │ Stateless
└────┬────┘ └───┬────┘ └──┬─────┘
│ │ │
└─────┬────┴────┬─────┘
│ │
┌─────▼─────┐ │
│ Redis │ │ Sessions + Cache + Queue
│ ElastiCache│ │
└─────┬─────┘ │
│ │
┌─────▼─────┐ │
│ Workers │ │ 3 workers Supervisord
│ (pHash, │ │
│ Blur, │ │
│ Email) │ │
└─────┬─────┘ │
│ │
┌─────▼─────┐ │
│ AWS S3 │ │ Uploads
└───────────┘ │
│
┌──────▼──────┐
│ RDS │ MariaDB managed
│ (Multi-AZ) │
└─────────────┘
Migration¶
Étape 1 : Migrer uploads vers S3
# Créer bucket
aws s3 mb s3://urbafix-prod-uploads --region eu-west-3
# Copier uploads existants
aws s3 sync ./public/backend/uploads/ s3://urbafix-prod-uploads/
# Modifier .env
STORAGE_DRIVER=s3
STORAGE_S3_BUCKET=urbafix-prod-uploads
STORAGE_S3_REGION=eu-west-3
STORAGE_S3_KEY=AKIA...
STORAGE_S3_SECRET=...
# Redémarrer
docker-compose restart web
Étape 2 : Activer queue async
# .env
QUEUE_DRIVER=redis
# Créer bin/worker.php (voir DOCKER_STATELESS.md)
# Configurer Supervisord
# /etc/supervisor/conf.d/urbafix-workers.conf
# Lancer workers
supervisorctl reread
supervisorctl update
supervisorctl start urbafix-worker:*
Étape 3 : Migrer vers RDS
# Créer RDS instance (AWS Console)
# - Engine: MariaDB 10.11
# - Multi-AZ: Enabled
# - Storage: 100 GB gp3
# Backup DB actuelle
mysqldump -h localhost -u root -p urbafix > backup.sql
# Restore vers RDS
mysql -h urbafix-prod.xxxxx.eu-west-3.rds.amazonaws.com \
-u admin -p urbafix < backup.sql
# Modifier .env
DB_HOST=urbafix-prod.xxxxx.eu-west-3.rds.amazonaws.com
DB_USER=admin
DB_PASSWORD=rds_password
# Redémarrer
docker-compose restart web
Étape 4 : Migrer vers ElastiCache Redis
# Créer ElastiCache cluster (AWS Console)
# - Engine: Redis 7.x
# - Node type: cache.t3.micro
# - Multi-AZ: Enabled
# Modifier .env
SESSION_REDIS_HOST=urbafix-prod.xxxxx.cache.amazonaws.com
CACHE_REDIS_HOST=urbafix-prod.xxxxx.cache.amazonaws.com
QUEUE_REDIS_HOST=urbafix-prod.xxxxx.cache.amazonaws.com
# Redémarrer
docker-compose restart web
Capacité¶
- Utilisateurs concurrents : ~1000
- Requêtes/seconde : ~200 req/s
- Incidents/jour : ~10000
- Haute disponibilité : 99.95% (Multi-AZ)
Configuration¶
# .env
SESSION_DRIVER=redis
CACHE_DRIVER=redis
STORAGE_DRIVER=s3
QUEUE_DRIVER=redis
# AWS managed services
DB_HOST=urbafix-prod.xxxxx.rds.amazonaws.com
SESSION_REDIS_HOST=urbafix-prod.xxxxx.cache.amazonaws.com
STORAGE_S3_BUCKET=urbafix-prod-uploads
Coût¶
- EC2 : 3 instances t3.medium : ~90€/mois
- RDS : db.t3.medium Multi-AZ : ~120€/mois
- ElastiCache : cache.t3.micro : ~20€/mois
- S3 : 100 GB + requêtes : ~5€/mois
- ALB : ~20€/mois
- CloudWatch : ~10€/mois
- Total : ~265€/mois
Scénario 5: Auto-Scaling (ECS/Kubernetes)¶
Architecture (AWS ECS)¶
┌─────────────────────────────────┐
│ CloudFront CDN │ Edge caching
└──────────┬──────────────────────┘
│
┌──────────▼──────────────────────┐
│ Application Load Balancer │
│ (HTTPS termination) │
└──────────┬──────────────────────┘
│
┌──────────▼──────────────────────┐
│ ECS Cluster │
│ │
│ ┌──────────────────────────┐ │
│ │ ECS Service (Web) │ │
│ │ - Desired: 3 │ │
│ │ - Min: 2 │ │
│ │ - Max: 10 │ │
│ │ - Auto-scaling: │ │
│ │ CPU > 70% → +1 task │ │
│ │ CPU < 30% → -1 task │ │
│ └──────┬───────────────────┘ │
│ │ │
│ ┌──────▼───────────────────┐ │
│ │ ECS Service (Workers) │ │
│ │ - Desired: 3 │ │
│ │ - Queue-based scaling │ │
│ └──────────────────────────┘ │
└─────────────────────────────────┘
│
┌─────┴─────┬─────────┐
│ │ │
┌────▼────┐ ┌───▼────┐ ┌──▼─────┐
│ RDS │ │ElastiCache│ │ S3 │
│Multi-AZ │ │ Redis │ │Uploads│
└─────────┘ └─────────┘ └────────┘
Déploiement ECS¶
task-definition.json :
{
"family": "urbafix-web",
"containerDefinitions": [
{
"name": "web",
"image": "urbafix/web:latest",
"memory": 1024,
"cpu": 512,
"essential": true,
"portMappings": [
{
"containerPort": 80,
"protocol": "tcp"
}
],
"environment": [
{"name": "SESSION_DRIVER", "value": "redis"},
{"name": "CACHE_DRIVER", "value": "redis"},
{"name": "STORAGE_DRIVER", "value": "s3"},
{"name": "QUEUE_DRIVER", "value": "redis"}
],
"secrets": [
{
"name": "DB_PASSWORD",
"valueFrom": "arn:aws:secretsmanager:eu-west-3:123456789:secret:urbafix/db_password"
},
{
"name": "REDIS_PASSWORD",
"valueFrom": "arn:aws:secretsmanager:eu-west-3:123456789:secret:urbafix/redis_password"
}
],
"healthCheck": {
"command": ["CMD-SHELL", "curl -f http://localhost/health.php || exit 1"],
"interval": 30,
"timeout": 5,
"retries": 3
}
}
]
}
Auto-Scaling Policy :
{
"TargetValue": 70.0,
"PredefinedMetricSpecification": {
"PredefinedMetricType": "ECSServiceAverageCPUUtilization"
},
"ScaleOutCooldown": 60,
"ScaleInCooldown": 300
}
Capacité¶
- Utilisateurs concurrents : Illimité (auto-scale)
- Requêtes/seconde : 500+ req/s
- Elasticité : Oui (min 2, max 10 tasks)
Coût¶
- ECS Fargate : 3-10 tasks (dynamique) : ~150-500€/mois
- RDS : db.r6g.large Multi-AZ : ~400€/mois
- ElastiCache : cache.r6g.large Cluster : ~300€/mois
- S3 : 1 TB + requêtes : ~30€/mois
- ALB : ~25€/mois
- CloudFront : 100 GB egress : ~10€/mois
- Total : ~915-1265€/mois (variable selon charge)
Scénario 6: Multi-Régions (Disaster Recovery)¶
Architecture¶
┌─────────────────┐
│ Route 53 │ Géo-routing
│ Health Checks │
└────┬───────┬────┘
│ │
┌──────────┘ └──────────┐
│ │
┌─────────▼─────────┐ ┌─────────▼─────────┐
│ Region EU-WEST-3 │ │ Region EU-WEST-1 │
│ (Paris - Primary)│ │(Ireland - Failover)│
│ │ │ │
│ - ECS Cluster │ │ - ECS Cluster │
│ - RDS Primary │◄───────►│ - RDS Read Replica│
│ - ElastiCache │ │ - ElastiCache │
│ - S3 (CRR) │◄───────►│ - S3 (Replica) │
└───────────────────┘ └───────────────────┘
Configuration¶
Route 53 Health Check :
{
"Type": "HTTPS",
"ResourcePath": "/health.php",
"FullyQualifiedDomainName": "urbafix.fr",
"Port": 443,
"RequestInterval": 30,
"FailureThreshold": 3
}
RDS Replication :
# Créer read replica en eu-west-1
aws rds create-db-instance-read-replica \
--db-instance-identifier urbafix-replica-ireland \
--source-db-instance-identifier urbafix-prod-paris \
--db-instance-class db.r6g.large \
--availability-zone eu-west-1a
# Promouvoir en cas de disaster
aws rds promote-read-replica \
--db-instance-identifier urbafix-replica-ireland
S3 Cross-Region Replication :
{
"Role": "arn:aws:iam::123456789:role/s3-replication",
"Rules": [
{
"Status": "Enabled",
"Priority": 1,
"Filter": {},
"Destination": {
"Bucket": "arn:aws:s3:::urbafix-uploads-ireland",
"ReplicationTime": {
"Status": "Enabled",
"Time": {"Minutes": 15}
}
}
}
]
}
Failover Automatique¶
Procédure : 1. Route 53 health check échoue (3 tentatives) 2. Bascule DNS vers région Ireland (TTL 60s) 3. ECS tasks démarrent en Ireland 4. Read replica promue en primary 5. Application opérationnelle en <5 minutes
Coût¶
- Région primaire : ~1000€/mois
- Région failover :
- RDS replica : ~400€/mois
- ElastiCache standby : ~300€/mois
- S3 CRR : ~50€/mois
- Route 53 : ~10€/mois
- Total : ~1760€/mois
RTO / RPO¶
- RTO (Recovery Time Objective) : <5 minutes
- RPO (Recovery Point Objective) : <1 minute (async replication)
Métriques & Monitoring¶
Indicateurs Clés¶
| Métrique | Seuil OK | Seuil Warning | Seuil Critical | Action |
|---|---|---|---|---|
| CPU Web | <60% | 60-80% | >80% | Scale out |
| Memory Web | <70% | 70-85% | >85% | Scale out |
| DB Connections | <50 | 50-80 | >80 | Optimize queries |
| Cache Hit Ratio | >60% | 40-60% | <40% | Increase TTL |
| Queue Size | <100 | 100-500 | >500 | Add workers |
| Response Time | <200ms | 200-500ms | >500ms | Investigate |
| Error Rate | <0.1% | 0.1-1% | >1% | Alert on-call |
Dashboards Grafana¶
Dashboard Web :
# Requêtes par seconde
rate(http_requests_total[5m])
# Latence P95
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
# Taux d'erreur
rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m])
Dashboard Queue :
# Jobs en attente
queue_size{queue="default"}
# Jobs traités par seconde
rate(queue_jobs_processed_total[5m])
# Durée moyenne traitement
rate(queue_job_duration_seconds_sum[5m]) / rate(queue_job_duration_seconds_count[5m])
Recommandations par Taille¶
Petite Municipalité (<10k habitants)¶
Configuration : Scénario 1 ou 2 - Single instance + Redis optionnel - Coût : 20-25€/mois - Capacité : 500 incidents/jour
Municipalité Moyenne (10k-50k habitants)¶
Configuration : Scénario 3 - 2-3 instances + Redis + load balancer - Coût : 70€/mois - Capacité : 2000 incidents/jour
Grande Ville (>50k habitants)¶
Configuration : Scénario 4 - 3+ instances + S3 + RDS + ElastiCache - Coût : 265€/mois - Capacité : 10000 incidents/jour
Métropole / Multi-Villes¶
Configuration : Scénario 5 - ECS Auto-Scaling + AWS managed services - Coût : 915-1265€/mois - Capacité : Illimitée
National / Haute Disponibilité¶
Configuration : Scénario 6 - Multi-régions + Disaster Recovery - Coût : 1760€/mois - RTO <5min, RPO <1min
Checklist Migration¶
Scénario 1 → 2 (Ajouter Redis)¶
- Lancer container Redis (
--profile redis) - Configurer
SESSION_DRIVER=redis - Configurer
CACHE_DRIVER=redis - Tester health endpoint
- Monitoring Redis (connexions, mémoire)
Scénario 2 → 3 (Horizontal Scaling)¶
- Configurer NFS pour uploads (ou migrer S3)
- Lancer 2-3 instances web
- Configurer le load balancer
- Tester health checks
- Vérifier répartition charge
Scénario 3 → 4 (Cloud-Native)¶
- Créer bucket S3 + copier uploads
- Configurer
STORAGE_DRIVER=s3 - Activer
QUEUE_DRIVER=redis - Lancer workers Supervisord
- Migrer vers RDS
- Migrer vers ElastiCache
- Configurer ALB
- Tests charge
Scénario 4 → 5 (Auto-Scaling)¶
- Containeriser avec Docker
- Créer task definitions ECS
- Configurer auto-scaling policies
- Déployer sur ECS
- Tests scaling (stress test)
- Monitoring CloudWatch
Scénario 5 → 6 (Multi-Régions)¶
- Créer read replica RDS
- Configurer S3 CRR
- Dupliquer ECS cluster région 2
- Configurer Route 53 health checks
- Tests failover
- Documentation runbook disaster recovery
Ressources¶
- DOCKER_STATELESS.md - Configuration Docker
- DEPLOYMENT.md - Procédures déploiement
- ARCHITECTURE.md - Architecture technique
- CONFIGURATION.md - Variables environnement