321 lines
9.4 KiB
Markdown
321 lines
9.4 KiB
Markdown
# Runbook — Migration Client Pilote vers Production
|
||
|
||
**Applicable à :** Clients A (TechVision ESN) et B (RH Conseil)
|
||
**Durée estimée :** 2–4 heures par client (fenêtre de maintenance recommandée)
|
||
**Prérequis :** Cluster production opérationnel (EKS eu-west-3), Keycloak prod configuré
|
||
|
||
---
|
||
|
||
## Vue d'ensemble
|
||
|
||
```
|
||
Staging (api-staging.veylant.ai) Production (api.veylant.ai)
|
||
│ │
|
||
├── PostgreSQL staging DB →→→→→→→→ ├── PostgreSQL production DB
|
||
├── Keycloak staging realm →→→→→→→→ ├── Keycloak production realm
|
||
├── Redis staging ├── Redis production
|
||
└── Utilisateurs staging └── Utilisateurs production
|
||
```
|
||
|
||
---
|
||
|
||
## Phase 1 — Pré-migration (J-1)
|
||
|
||
### 1.1 Backup complet du staging
|
||
|
||
```bash
|
||
# Backup PostgreSQL staging
|
||
kubectl exec -n veylant deploy/postgres -- \
|
||
pg_dump -U veylant veylant_db | gzip > backup_staging_$(date +%Y%m%d).sql.gz
|
||
|
||
# Vérifier le backup
|
||
gunzip -t backup_staging_$(date +%Y%m%d).sql.gz && echo "Backup OK"
|
||
|
||
# Uploader vers S3 (conservation pendant la migration)
|
||
aws s3 cp backup_staging_$(date +%Y%m%d).sql.gz \
|
||
s3://veylant-backups-production/migration/
|
||
```
|
||
|
||
### 1.2 Inventaire des utilisateurs à migrer
|
||
|
||
```bash
|
||
# Exporter la liste des utilisateurs Keycloak staging
|
||
kubectl exec -n keycloak deploy/keycloak -- \
|
||
/opt/keycloak/bin/kcadm.sh get users \
|
||
-r veylant-staging \
|
||
--server http://localhost:8080 \
|
||
--realm master \
|
||
--user admin --password admin \
|
||
> users_staging.json
|
||
|
||
# Compter les utilisateurs actifs (30 derniers jours)
|
||
psql "$STAGING_DB_URL" -c \
|
||
"SELECT COUNT(*) FROM users WHERE last_login > NOW() - INTERVAL '30 days';"
|
||
```
|
||
|
||
### 1.3 Validation de l'environnement production
|
||
|
||
```bash
|
||
# Vérifier que le cluster production est opérationnel
|
||
kubectl get nodes -n veylant --context=production
|
||
kubectl get pods -n veylant --context=production
|
||
|
||
# Vérifier la connectivité API production
|
||
curl -sf https://api.veylant.ai/healthz | jq .
|
||
|
||
# Vérifier Keycloak production
|
||
curl -sf https://auth.veylant.ai/realms/veylant/.well-known/openid-configuration | jq .issuer
|
||
|
||
# Confirmer le backup automatique actif
|
||
kubectl get cronjob veylant-postgres-backup -n veylant --context=production
|
||
```
|
||
|
||
### 1.4 Communication client
|
||
|
||
- [ ] Envoyer email de notification J-1 (fenêtre de maintenance, impact estimé)
|
||
- [ ] Confirmer contact technique côté client disponible pendant la migration
|
||
- [ ] Partager le runbook rollback avec le client
|
||
|
||
---
|
||
|
||
## Phase 2 — Migration des données PostgreSQL
|
||
|
||
### 2.1 Export depuis staging
|
||
|
||
```bash
|
||
# Export complet avec données clients seulement (pas les configs système)
|
||
pg_dump \
|
||
--host="$STAGING_DB_HOST" \
|
||
--username="$STAGING_DB_USER" \
|
||
--dbname="$STAGING_DB_NAME" \
|
||
--table=users \
|
||
--table=api_keys \
|
||
--table=routing_rules \
|
||
--table=gdpr_processing_registry \
|
||
--table=ai_act_classifications \
|
||
--format=custom \
|
||
--no-privileges \
|
||
--no-owner \
|
||
-f migration_data.dump
|
||
|
||
echo "Export size: $(du -sh migration_data.dump)"
|
||
```
|
||
|
||
### 2.2 Import vers production
|
||
|
||
```bash
|
||
# Appliquer les migrations DDL d'abord (production doit être à jour)
|
||
kubectl exec -n veylant deploy/veylant-proxy --context=production -- \
|
||
/app/proxy migrate up
|
||
|
||
# Import des données
|
||
pg_restore \
|
||
--host="$PROD_DB_HOST" \
|
||
--username="$PROD_DB_USER" \
|
||
--dbname="$PROD_DB_NAME" \
|
||
--no-privileges \
|
||
--no-owner \
|
||
--clean \
|
||
--if-exists \
|
||
-v \
|
||
migration_data.dump
|
||
|
||
# Vérifier l'intégrité
|
||
psql "$PROD_DB_URL" -c "SELECT COUNT(*) FROM users;"
|
||
psql "$PROD_DB_URL" -c "SELECT COUNT(*) FROM routing_rules;"
|
||
```
|
||
|
||
### 2.3 Vérification post-import
|
||
|
||
```bash
|
||
# Comparer les compteurs staging vs production
|
||
STAGING_USERS=$(psql "$STAGING_DB_URL" -t -c "SELECT COUNT(*) FROM users;")
|
||
PROD_USERS=$(psql "$PROD_DB_URL" -t -c "SELECT COUNT(*) FROM users;")
|
||
|
||
echo "Staging users: $STAGING_USERS | Production users: $PROD_USERS"
|
||
|
||
if [ "$STAGING_USERS" != "$PROD_USERS" ]; then
|
||
echo "ERROR: User count mismatch — abort migration"
|
||
exit 1
|
||
fi
|
||
```
|
||
|
||
---
|
||
|
||
## Phase 3 — Reconfiguration Keycloak Production
|
||
|
||
### 3.1 Création du realm production
|
||
|
||
```bash
|
||
# Se connecter à Keycloak production
|
||
KEYCLOAK_URL="https://auth.veylant.ai"
|
||
KEYCLOAK_ADMIN_TOKEN=$(curl -s \
|
||
-d "client_id=admin-cli" \
|
||
-d "username=admin" \
|
||
-d "password=$KEYCLOAK_ADMIN_PASSWORD" \
|
||
-d "grant_type=password" \
|
||
"$KEYCLOAK_URL/realms/master/protocol/openid-connect/token" | jq -r .access_token)
|
||
|
||
# Importer la configuration du realm depuis staging
|
||
# (exportée au format JSON lors de la phase 1.2)
|
||
curl -sf -X POST \
|
||
-H "Authorization: Bearer $KEYCLOAK_ADMIN_TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d @realm-export.json \
|
||
"$KEYCLOAK_URL/admin/realms"
|
||
```
|
||
|
||
### 3.2 Import des utilisateurs
|
||
|
||
```bash
|
||
# Importer les utilisateurs avec leurs rôles
|
||
# Note: les mots de passe ne peuvent pas être migrés — les utilisateurs recevront un email de reset
|
||
for user in $(jq -r '.[].id' users_staging.json); do
|
||
USER_DATA=$(jq --arg id "$user" '.[] | select(.id == $id)' users_staging.json)
|
||
curl -sf -X POST \
|
||
-H "Authorization: Bearer $KEYCLOAK_ADMIN_TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d "$USER_DATA" \
|
||
"$KEYCLOAK_URL/admin/realms/veylant/users"
|
||
done
|
||
|
||
echo "Imported $(jq length users_staging.json) users"
|
||
```
|
||
|
||
### 3.3 Réinitialisation des mots de passe
|
||
|
||
```bash
|
||
# Envoyer un email de reset de mot de passe à tous les utilisateurs migrés
|
||
USER_IDS=$(curl -sf \
|
||
-H "Authorization: Bearer $KEYCLOAK_ADMIN_TOKEN" \
|
||
"$KEYCLOAK_URL/admin/realms/veylant/users?max=1000" | jq -r '.[].id')
|
||
|
||
for USER_ID in $USER_IDS; do
|
||
curl -sf -X PUT \
|
||
-H "Authorization: Bearer $KEYCLOAK_ADMIN_TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d '["UPDATE_PASSWORD"]' \
|
||
"$KEYCLOAK_URL/admin/realms/veylant/users/$USER_ID/execute-actions-email"
|
||
sleep 0.1 # Rate limit emails
|
||
done
|
||
```
|
||
|
||
---
|
||
|
||
## Phase 4 — Validation
|
||
|
||
### 4.1 Smoke tests API
|
||
|
||
```bash
|
||
# Obtenir un token de test (compte admin pré-créé)
|
||
TOKEN=$(curl -sf \
|
||
-d "client_id=veylant-api" \
|
||
-d "username=admin-test@veylant.ai" \
|
||
-d "password=$TEST_ADMIN_PASSWORD" \
|
||
-d "grant_type=password" \
|
||
"https://auth.veylant.ai/realms/veylant/protocol/openid-connect/token" | jq -r .access_token)
|
||
|
||
# Test endpoints principaux
|
||
curl -sf -H "Authorization: Bearer $TOKEN" https://api.veylant.ai/v1/admin/users | jq length
|
||
curl -sf -H "Authorization: Bearer $TOKEN" https://api.veylant.ai/v1/admin/routing-rules | jq length
|
||
|
||
# Test proxy (avec model user-role)
|
||
curl -sf -X POST \
|
||
-H "Authorization: Bearer $TOKEN" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"Hello"}]}' \
|
||
https://api.veylant.ai/v1/chat/completions | jq .choices[0].message.content
|
||
|
||
echo "Smoke tests passed"
|
||
```
|
||
|
||
### 4.2 Validation des audit logs
|
||
|
||
```bash
|
||
# Vérifier que les logs sont bien envoyés à ClickHouse
|
||
curl -sf -H "Authorization: Bearer $TOKEN" \
|
||
"https://api.veylant.ai/v1/admin/logs?limit=5" | jq '.[].request_id'
|
||
```
|
||
|
||
### 4.3 Validation du dashboard
|
||
|
||
```bash
|
||
# Ouvrir le dashboard client et vérifier les métriques
|
||
open "https://dashboard.veylant.ai"
|
||
# Vérifier manuellement : graphiques RPS, latence, erreurs, PII
|
||
```
|
||
|
||
---
|
||
|
||
## Phase 5 — Cutover SSO (Go-Live)
|
||
|
||
### 5.1 Mise à jour des URLs côté client
|
||
|
||
Informer le contact technique du client de mettre à jour :
|
||
|
||
| Paramètre | Staging | Production |
|
||
|-----------|---------|------------|
|
||
| `base_url` OpenAI SDK | `https://api-staging.veylant.ai/v1` | `https://api.veylant.ai/v1` |
|
||
| OIDC Issuer (si SAML) | `https://auth-staging.veylant.ai/realms/veylant` | `https://auth.veylant.ai/realms/veylant` |
|
||
| Dashboard | `https://dashboard-staging.veylant.ai` | `https://dashboard.veylant.ai` |
|
||
|
||
### 5.2 Mise à jour CORS production
|
||
|
||
```bash
|
||
# Ajouter le domaine dashboard client dans config.yaml production
|
||
# Exemple Client B (RH Conseil) : dashboard sur dashboard.rh-conseil.fr
|
||
kubectl edit configmap veylant-proxy-config -n veylant --context=production
|
||
# Ajouter sous server.allowed_origins:
|
||
# - "https://dashboard.rh-conseil.fr"
|
||
|
||
# Redémarrer le proxy pour prendre en compte la nouvelle config
|
||
kubectl rollout restart deployment/veylant-proxy-blue -n veylant --context=production
|
||
kubectl rollout status deployment/veylant-proxy-blue -n veylant --context=production
|
||
```
|
||
|
||
### 5.3 Confirmation Go-Live
|
||
|
||
- [ ] Envoyer email de confirmation au client : migration réussie
|
||
- [ ] Planifier NPS de suivi J+7
|
||
- [ ] Archiver le dump staging utilisé pour la migration
|
||
|
||
---
|
||
|
||
## Rollback
|
||
|
||
### Rollback Phase 2 (avant cutover)
|
||
|
||
```bash
|
||
# Restaurer la base production depuis le backup staging
|
||
pg_restore \
|
||
--host="$PROD_DB_HOST" \
|
||
--username="$PROD_DB_USER" \
|
||
--dbname="$PROD_DB_NAME" \
|
||
--clean \
|
||
migration_data.dump
|
||
|
||
echo "Rollback Phase 2 terminé — base production restaurée"
|
||
```
|
||
|
||
### Rollback Phase 5 (après cutover)
|
||
|
||
```bash
|
||
# Rediriger le trafic vers staging (intervention DNS)
|
||
# Contact ops@veylant.ai immédiatement
|
||
|
||
# Informer le client : retour en staging, investigation en cours
|
||
# ETA rollback DNS : < 5 minutes (TTL court configuré en préparation)
|
||
```
|
||
|
||
---
|
||
|
||
## Checklist finale
|
||
|
||
- [ ] Backup staging conservé 30 jours
|
||
- [ ] Tous les utilisateurs ont reçu l'email de reset mot de passe
|
||
- [ ] Smoke tests API passés
|
||
- [ ] Dashboard client accessible
|
||
- [ ] CORS mis à jour avec domaine client
|
||
- [ ] NPS suivi planifié J+7
|
||
- [ ] Staging désactivé après 2 semaines (coûts)
|