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
- Connectez-vous à votre compte MONCRENEAU
- Allez dans Paramètres > API > Clés
- Générez votre clé API de production
- 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
- Dashboard MONCRENEAU > Webhooks > Configuration
- Entrez votre URL de production:
https://votre-app.com/webhooks/moncreneau - Copiez le secret webhook (commence par
whsec_...) - 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();
});