feat: Infrastructure multi-tenant avec isolation par sous-domaine
Une application SaaS éducative nécessite une séparation stricte des données entre établissements scolaires. L'architecture multi-tenant par sous-domaine (ecole-alpha.classeo.local) permet cette isolation tout en utilisant une base de code unique. Le choix d'une résolution basée sur les sous-domaines plutôt que sur des headers ou tokens facilite le routage au niveau infrastructure (reverse proxy) et offre une UX plus naturelle où chaque école accède à "son" URL dédiée.
This commit is contained in:
182
docs/DEPLOYMENT.md
Normal file
182
docs/DEPLOYMENT.md
Normal file
@@ -0,0 +1,182 @@
|
||||
# Déploiement en Production
|
||||
|
||||
## Architecture Multi-tenant
|
||||
|
||||
Classeo utilise une architecture multi-tenant où chaque école a son propre sous-domaine :
|
||||
- `ecole-alpha.classeo.fr`
|
||||
- `ecole-beta.classeo.fr`
|
||||
|
||||
## Différences Dev vs Prod
|
||||
|
||||
| Aspect | Dev | Prod |
|
||||
|--------|-----|------|
|
||||
| Domaine | `classeo.local` | `classeo.fr` |
|
||||
| Frontend | `:5174` | même domaine |
|
||||
| API | `:18000/api` | `/api` (même domaine) |
|
||||
| HTTPS | Non | Oui (obligatoire) |
|
||||
| Reverse proxy | Non | Oui |
|
||||
| Base de données | Une seule (SQLite/PostgreSQL) | Une par tenant |
|
||||
|
||||
## Configuration Reverse Proxy
|
||||
|
||||
En production, un reverse proxy route les requêtes sur le même domaine :
|
||||
- `ecole-alpha.classeo.fr/` → Frontend (SvelteKit)
|
||||
- `ecole-alpha.classeo.fr/api` → Backend (FrankenPHP)
|
||||
|
||||
### Option recommandée : Caddy (intégré à FrankenPHP)
|
||||
|
||||
```caddyfile
|
||||
# Caddyfile pour production
|
||||
*.classeo.fr {
|
||||
# Certificats SSL automatiques via Let's Encrypt
|
||||
tls {
|
||||
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
|
||||
}
|
||||
|
||||
# API routes
|
||||
handle /api/* {
|
||||
reverse_proxy php:8000
|
||||
}
|
||||
|
||||
# Frontend
|
||||
handle {
|
||||
reverse_proxy frontend:3000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Alternative : nginx
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name ~^(?<subdomain>.+)\.classeo\.fr$;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/classeo.fr/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/classeo.fr/privkey.pem;
|
||||
|
||||
location /api {
|
||||
proxy_pass http://php:8000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://frontend:3000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Variables d'environnement Production
|
||||
|
||||
### Backend (.env.prod)
|
||||
|
||||
```env
|
||||
APP_ENV=prod
|
||||
APP_DEBUG=0
|
||||
APP_SECRET=<générer-une-clé-sécurisée>
|
||||
|
||||
TRUSTED_HOSTS=^(.+\.)?classeo\.fr$
|
||||
TRUSTED_PROXIES=REMOTE_ADDR
|
||||
|
||||
TENANT_BASE_DOMAIN=classeo.fr
|
||||
|
||||
DATABASE_URL=postgresql://user:password@db-host:5432/classeo_master
|
||||
|
||||
REDIS_URL=redis://redis-host:6379
|
||||
MESSENGER_TRANSPORT_DSN=amqp://user:password@rabbitmq-host:5672/%2f/messages
|
||||
|
||||
# JWT
|
||||
JWT_PASSPHRASE=<générer-une-passphrase-sécurisée>
|
||||
```
|
||||
|
||||
### Frontend
|
||||
|
||||
```env
|
||||
PUBLIC_API_URL=https://classeo.fr/api
|
||||
PUBLIC_BASE_DOMAIN=classeo.fr
|
||||
PUBLIC_MERCURE_URL=https://classeo.fr/.well-known/mercure
|
||||
```
|
||||
|
||||
## Certificats SSL
|
||||
|
||||
### Wildcard avec Let's Encrypt + Cloudflare DNS
|
||||
|
||||
Pour les sous-domaines dynamiques, un certificat wildcard est nécessaire :
|
||||
|
||||
```bash
|
||||
# Avec certbot et Cloudflare DNS
|
||||
certbot certonly \
|
||||
--dns-cloudflare \
|
||||
--dns-cloudflare-credentials /etc/cloudflare.ini \
|
||||
-d classeo.fr \
|
||||
-d "*.classeo.fr"
|
||||
```
|
||||
|
||||
### Avec Caddy (automatique)
|
||||
|
||||
Caddy gère automatiquement les certificats wildcard si vous configurez un provider DNS.
|
||||
|
||||
## Base de données par tenant
|
||||
|
||||
Chaque tenant a sa propre base de données PostgreSQL :
|
||||
- `classeo_tenant_alpha`
|
||||
- `classeo_tenant_beta`
|
||||
|
||||
### Création d'un nouveau tenant
|
||||
|
||||
```bash
|
||||
# 1. Créer la base de données
|
||||
php bin/console tenant:database:create classeo_tenant_<nom>
|
||||
|
||||
# 2. Exécuter les migrations
|
||||
php bin/console tenant:migrate <subdomain>
|
||||
|
||||
# 3. Ajouter le tenant au registry (ou en base master)
|
||||
```
|
||||
|
||||
## Options de déploiement
|
||||
|
||||
### 1. VPS simple (petit volume)
|
||||
|
||||
- Un serveur avec Docker Compose
|
||||
- Convient pour < 50 écoles
|
||||
- Coût : ~20-50€/mois
|
||||
|
||||
### 2. Docker Swarm (moyen volume)
|
||||
|
||||
- Plusieurs serveurs avec orchestration
|
||||
- Scaling horizontal
|
||||
- Convient pour 50-500 écoles
|
||||
|
||||
### 3. Kubernetes (grand volume)
|
||||
|
||||
- Orchestration avancée
|
||||
- Auto-scaling
|
||||
- Convient pour 500+ écoles
|
||||
- Coût plus élevé, complexité accrue
|
||||
|
||||
## Checklist de mise en production
|
||||
|
||||
- [ ] Configurer le domaine DNS (wildcard `*.classeo.fr`)
|
||||
- [ ] Obtenir certificat SSL wildcard
|
||||
- [ ] Configurer le reverse proxy (Caddy ou nginx)
|
||||
- [ ] Configurer les variables d'environnement prod
|
||||
- [ ] Générer les clés JWT de production
|
||||
- [ ] Configurer la base de données master
|
||||
- [ ] Créer les bases de données tenant
|
||||
- [ ] Configurer les backups automatiques
|
||||
- [ ] Configurer le monitoring (logs, métriques)
|
||||
- [ ] Tester le déploiement sur un environnement staging
|
||||
- [ ] Configurer CI/CD pour les déploiements automatiques
|
||||
|
||||
## TODO
|
||||
|
||||
- [ ] Créer un `compose.prod.yaml` pour la production
|
||||
- [ ] Script de création automatique de tenant
|
||||
- [ ] Interface admin pour gérer les tenants
|
||||
- [ ] Monitoring et alerting
|
||||
Reference in New Issue
Block a user