# 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)