Aller au contenu principal

Meilleures Pratiques de Déploiement

Guide pour déployer votre intégration MONCRENEAU en production de manière sécurisée et fiable.

Vue d'Ensemble

Le déploiement en production nécessite une attention particulière à la sécurité, la fiabilité et la surveillance. Ce guide vous accompagne à travers les étapes essentielles pour un déploiement réussi.

Avant le Déploiement

✅ Checklist Préalable

  • Tests complets réussis
  • Webhooks testés et vérifiés (signature HMAC)
  • Gestion d'erreurs implémentée (retry, circuit breaker)
  • Monitoring configuré (logs, métriques, alertes)
  • Documentation interne à jour
  • Équipe formée et prête
  • Plan de rollback défini
  • Sauvegardes configurées

Vérifications Techniques

# 1. Vérifier que les tests passent
npm test

# 2. Vérifier les variables d'environnement
env | grep MONCRENEAU

# 3. Tester la connexion à l'API
curl -H "Authorization: Bearer $MONCRENEAU_API_KEY" \
{{API_BASE_URL}}/appointments

# 4. Vérifier que les webhooks fonctionnent
# (créer un rendez-vous et vérifier la réception)

Étape 1: Sécuriser les Clés API

Obtenir et Stocker les Clés

  1. Connectez-vous à votre compte MONCRENEAU
  2. Allez dans Paramètres > API > Clés
  3. Générez votre clé API de production
  4. Sauvegardez immédiatement dans votre gestionnaire de secrets

Bonnes Pratiques de Stockage

# ❌ JAMAIS dans le code
const apiKey = "mk_abc123...";

# ❌ JAMAIS dans Git
MONCRENEAU_API_KEY=mk_abc123... # Dans .env commité

# ✅ Variables d'environnement du serveur
# Heroku
heroku config:set MONCRENEAU_API_KEY=mk_abc123...

# AWS Systems Manager
aws ssm put-parameter \
--name /prod/moncreneau/api_key \
--value "mk_abc123..." \
--type SecureString

# Kubernetes Secret
kubectl create secret generic moncreneau-api \
--from-literal=api-key=mk_abc123...

# Docker Compose with secrets
docker secret create moncreneau_api_key -

Étape 2: Configuration des Environnements

Structure Recommandée

.env.development   # Variables de développement local
.env.production # Variables de production

Exemple de Configuration

[object Object]

Vérification au Démarrage

// app.js
const config = require('./config/api');

function validateConfig() {
const required = ['apiUrl', 'apiKey', 'webhookSecret'];

for (const key of required) {
if (!config[key]) {
console.error(`❌ Missing config: ${key}`);
process.exit(1);
}
}

console.log('✓ Configuration validated');
console.log(`Environment: ${process.env.NODE_ENV}`);
console.log(`API URL: ${config.apiUrl}`);
console.log(`API Key: ${config.apiKey.substring(0, 10)}...`);
}

validateConfig();

Étape 3: Configuration des Webhooks

Configurer l'URL Webhook

  1. Dashboard MONCRENEAU > Webhooks > Configuration
  2. Entrez votre URL de production: https://votre-app.com/webhooks/moncreneau
  3. Copiez le secret webhook (commence par whsec_...)
  4. Sauvegardez-le dans vos variables d'environnement

Vérifier l'URL Webhook

# L'URL doit être:
# ✅ HTTPS (certificat SSL valide)
# ✅ Accessible publiquement (pas localhost)
# ✅ Répond en < 5 secondes

# Test manuel
curl -X POST https://votre-app.com/webhooks/moncreneau \
-H "Content-Type: application/json" \
-H "X-Moncreneau-Signature: sha256=test" \
-d '{"id":"evt_test","type":"appointment.created"}'

# Devrait retourner 401 (signature invalide) ou 200

Étape 4: Déploiement Progressif

Stratégie Blue-Green

# 1. Déployer la nouvelle version (green)
# Mais sans router le trafic

# 2. Tester la version green
curl https://green.votre-app.com/health

# 3. Créer un rendez-vous de test
curl -X POST https://green.votre-app.com/api/appointments \
-H "Authorization: Bearer $MONCRENEAU_API_KEY" \
-d '{ ... }'

# 4. Vérifier que le webhook arrive
# Vérifier dans les logs: "Webhook received: appointment.created"

# 5. Si OK, router le trafic vers green
# 6. Surveiller les métriques pendant 1h
# 7. Si OK, supprimer blue

Déploiement par Feature Flags

const FeatureFlags = {
ALLOW_APPOINTMENTS: process.env.FEATURE_ALLOW_APPOINTMENTS === 'true',
WEBHOOK_PROCESSING: process.env.FEATURE_WEBHOOK_PROCESSING === 'true'
};

function canCreateAppointment() {
if (!FeatureFlags.ALLOW_APPOINTMENTS) {
throw new Error('Appointment creation is temporarily disabled');
}
return true;
}

// Activer progressivement
// 1. Déployer avec flags désactivés
// 2. Activer pour 10% du trafic
// 3. Augmenter progressivement à 100%

Déploiement Canary

// Router une partie du trafic vers la nouvelle version
const shouldUseNewVersion = () => {
const percentage = parseInt(process.env.CANARY_PERCENTAGE || '0');
return Math.random() * 100 < percentage;
};

async function createAppointment(data) {
if (shouldUseNewVersion()) {
return createAppointmentV2(data);
}
return createAppointmentV1(data);
}

Étape 5: Vérifications Post-Déploiement

Tests en Production

[object Object]

Monitoring Renforcé Initial

// Alertes plus strictes les premiers jours
const alertThresholds = {
production_first_week: {
errorRate: 0.01, // 1% (vs 5% normal)
responseTime: 200, // 200ms (vs 500ms normal)
webhookDelay: 5 // 5s (vs 60s normal)
}
};

// Logs détaillés temporaires
if (isFirstWeekInProduction()) {
logger.level = 'debug';
logger.info('Running in first week mode - enhanced logging');
}

Étape 6: Gestion des Crédits

Vérifier le Solde

async function checkCreditsBeforeLaunch() {
const response = await api.get('/organizations/me');
const credits = response.data.availableCredits;

console.log(`Available credits: ${credits}`);

if (credits < 100) {
console.warn('⚠️ Low credits! Consider purchasing more.');
}

return credits;
}

Alertes de Crédits

// Surveiller le solde de crédits
setInterval(async () => {
const org = await api.get('/organizations/me');
const credits = org.data.availableCredits;

if (credits < 50) {
await sendAlert('ALERT: Only ' + credits + ' credits left!');
} else if (credits < 100) {
await sendAlert('WARNING: Less than 100 credits remaining');
}
}, 3600000); // Toutes les heures

Rollback

Plan de Rollback Rapide

# 1. Si problème critique détecté
# 2. Revenir immédiatement à la version précédente

# Heroku
heroku releases:rollback

# Kubernetes
kubectl rollout undo deployment/votre-app

# Docker
docker-compose down
docker-compose -f docker-compose.old.yml up -d

# 3. Vérifier que l'app fonctionne
curl https://votre-app.com/health

Critères de Rollback

Déclencher un rollback si:

  • ❌ Taux d'erreur > 10% pendant 5 minutes
  • ❌ Temps de réponse > 2 secondes
  • ❌ Webhooks non reçus depuis 10 minutes
  • ❌ Crédits consommés anormalement vite
  • ❌ Plus de 5 rapports d'erreur utilisateur

Checklist de Déploiement

Avant le Déploiement

  • Tous les tests passent
  • Clés API obtenues et sécurisées
  • Variables d'environnement configurées
  • URL webhook configurée et testée
  • Monitoring et alertes activés
  • Équipe prévenue (date et heure)
  • Plan de rollback documenté
  • Backup des données actuelles fait

Pendant le Déploiement

  • Déploiement en heures creuses
  • Monitoring en temps réel actif
  • Équipe disponible (dev + ops)
  • Communication aux utilisateurs envoyée
  • Premier rendez-vous de test créé
  • Webhook de test reçu et traité
  • Métriques normales (erreurs, latence)

Après le Déploiement

  • Surveillance renforcée pendant 24h
  • Aucune erreur critique détectée
  • Webhooks reçus correctement
  • Crédits consommés normalement
  • Utilisateurs satisfaits (pas de plaintes)
  • Documentation mise à jour
  • Post-mortem si incidents

Communication

Annonce aux Utilisateurs

📢 Maintenance Planifiée

Date: [Date de votre maintenance]
Heure: 02:00 - 04:00 GMT
Durée: Environ 30 minutes

Pendant cette période, notre système de rendez-vous sera
temporairement indisponible. Les rendez-vous existants ne
sont pas affectés.

Merci de votre compréhension !

Notification de Succès

✅ Déploiement Terminé

Notre système de prise de rendez-vous a été mis à jour avec succès.
Vous pouvez continuer à utiliser le service normalement.

Si vous rencontrez le moindre problème, contactez-nous
immédiatement.

Troubleshooting

Erreur: "Invalid API Key"

// Vérifier l'environnement
console.log('NODE_ENV:', process.env.NODE_ENV);
console.log('API Key:', process.env.MONCRENEAU_API_KEY?.substring(0, 10));

// Vérifier que la clé est bien définie
if (!process.env.MONCRENEAU_API_KEY) {
console.error('❌ API Key not set!');
}

Webhooks Non Reçus

# 1. Vérifier l'URL configurée
# Dashboard > Webhooks > Configuration > URL

# 2. Tester manuellement
curl -X POST https://votre-app.com/webhooks/moncreneau \
-d '{"id":"evt_test","type":"appointment.created"}'

# 3. Vérifier les logs du serveur
tail -f /var/log/app.log | grep webhook

# 4. Vérifier le pare-feu
# L'IP de MONCRENEAU doit être autorisée

Crédits Consommés Trop Vite

// Audit des créations de rendez-vous
const recentAppointments = await db.appointments.find({
createdAt: { $gte: new Date(Date.now() - 3600000) } // 1h
});

console.log(`${recentAppointments.length} appointments in last hour`);

// Si anormal, suspendre temporairement
if (recentAppointments.length > 100) {
console.error('⚠️ Abnormal appointment rate!');
// Déclencher une alerte
// Potentiellement désactiver la création temporairement
}

Monitoring et Observabilité

Métriques Essentielles

// Exemple avec Prometheus
const apiRequestsTotal = new Counter({
name: 'moncreneau_api_requests_total',
help: 'Total API requests',
labelNames: ['method', 'status']
});

const apiDuration = new Histogram({
name: 'moncreneau_api_duration_seconds',
help: 'API request duration'
});

const creditsGauge = new Gauge({
name: 'moncreneau_credits_available',
help: 'Available credits'
});

Logs Structurés

logger.info('Appointment created', {
requestId: req.id,
appointmentId: appointment.id,
organizationId: org.id,
duration: Date.now() - start,
creditsRemaining: org.availableCredits
});

Sécurité en Production

HTTPS Obligatoire

// Forcer HTTPS en production
app.use((req, res, next) => {
if (process.env.NODE_ENV === 'production' && !req.secure) {
return res.redirect('https://' + req.headers.host + req.url);
}
next();
});

Rate Limiting

const rateLimit = require('express-rate-limit');

const apiLimiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 100, // Max 100 requêtes par minute
message: 'Too many requests, please try again later.'
});

app.use('/api/', apiLimiter);

Headers de Sécurité

const helmet = require('helmet');
app.use(helmet());

// Headers personnalisés
app.use((req, res, next) => {
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('X-XSS-Protection', '1; mode=block');
next();
});

Ressources