Aller au contenu
English
Compte : non connecté, synchronisation possible Mon apprentissage
Selectionner le thème
English

Créer une collection de postmortems SRE avec Astro Starlight

Par mis à jour le

Améliorer cette page

Vous gérez une infrastructure en production. Un jour, inévitablement, un incident survient : une base de données tombe, une API devient lente, un déploiement échoue. L'équipe résout le problème dans l'urgence. Et puis... rien. Pas de trace, pas d'analyse, pas de prévention.

Résultat : le même incident se reproduit trois mois après. Personne ne se souvient de la solution. On recommence à zéro.

Un postmortem SRE est un document d'analyse écrit après un incident de production. Le SRE (Site Reliability Engineering) est la discipline, née chez Google, qui applique les méthodes du logiciel à la fiabilité des services. Il documente ce qui s'est passé, pourquoi, comment l'incident a été résolu, et surtout quelles actions concrètes éviteront que ça se reproduise. Cette pratique transforme les échecs en opportunités d'apprentissage collectif.

Ce guide vous fait créer une collection Astro dédiée aux postmortems, avec une validation automatique des champs du front-matter, un template structuré selon les pratiques SRE (Google, Netflix), et un script interactif pour générer de nouveaux documents sans copier-coller.

Ce guide s'adresse à une équipe qui publie déjà sa documentation avec Astro Starlight et veut y ranger ses postmortems avec la même rigueur que son code, grâce à un schéma Zod.

  • Situer le postmortem parmi les familles de documentation et en citer les bénéfices.
  • Étendre le schéma de la collection docs de Starlight avec des champs propres aux postmortems.
  • Valider ces champs au build avec Zod et reconnaître les erreurs les plus courantes.
  • Générer un postmortem complet avec un script Node.js interactif.
  • Appliquer la méthode des 5 Pourquoi dans un esprit blameless.
  • Afficher les postmortems dans la barre latérale avec autogenerate.

Avant de commencer, situez les postmortems dans l'écosystème de la documentation : le guide Documentation opérationnelle : structurer pour retrouver présente les 5 familles de documentation, dont l'Historique qui contient les postmortems.

Connaissances techniques requises :

  • Notion de collections Astro (fichiers Markdown groupés avec validation)
  • Bases de Zod (bibliothèque de validation TypeScript)
  • JavaScript/Node.js pour comprendre le script de génération
  • Familiarité avec le front-matter YAML (métadonnées en haut des fichiers Markdown)

Installation nécessaire :

Selon les 5 familles de documentation opérationnelle, le postmortem appartient à la famille Historique. Cette famille conserve la mémoire des décisions et des événements pour éviter de répéter les erreurs.

Différence avec les autres familles :

Type de docQuestionExempleQuand l'écrire
Runbook (Procédures)Comment agir ?"Redémarrer PostgreSQL en production"Avant l'incident (procédure préventive)
Postmortem (Historique)Que s'est-il passé ? Pourquoi ?"Panne PostgreSQL du 15 mai 2025"Après l'incident (analyse)
ADR (Historique)Pourquoi ce choix ?"Pourquoi PostgreSQL et pas MySQL"Lors d'une décision d'architecture

Le cycle vertueux : Le postmortem identifie souvent la nécessité de créer ou mettre à jour un runbook. Par exemple, après un incident "Saturation disque Kubernetes", vous créerez un runbook "Nettoyer les images Docker inutilisées".

Les postmortems SRE remplissent plusieurs fonctions critiques :

  • Apprentissage collectif : Partager les leçons apprises avec toute l'équipe (pas juste ceux qui ont géré l'incident)
  • Culture blameless : Analyser les systèmes et processus défaillants, jamais blâmer les personnes
  • Prévention : Identifier les actions correctives concrètes pour éviter la récurrence (avec responsables et dates)
  • Transparence : Communiquer sur les incidents avec les parties prenantes (clients, management)
  • Base de connaissances : Constituer un historique permettant de détecter les patterns ("3 incidents disque en 2 mois → problème systémique")
  • Amélioration des runbooks : Mettre à jour les procédures d'intervention quand elles s'avèrent incomplètes

Avant de commencer, clarifions ce concept clé.

Une collection Astro est un groupe de fichiers de contenu (Markdown, MDX) déclaré dans src/content.config.ts, avec des règles de validation communes. Starlight en déclare déjà une, docs, qui couvre tout src/content/docs/. Concrètement :

  • La collection docs regroupe tous les fichiers de src/content/docs/, y compris le sous-dossier postmortems/
  • Son schéma décrit les champs du front-matter : type attendu, valeurs permises, champ obligatoire ou facultatif
  • Si un fichier viole le schéma (valeur hors liste, mauvais type, champ obligatoire absent) → le build échoue avec une erreur claire

Pourquoi c'est utile pour les postmortems :

  • Uniformité : tous les postmortems emploient les mêmes noms de champs (pas de severite d'un côté et de priority de l'autre)
  • Erreurs détectées tôt : si vous écrivez severity: critique, le build vous le dit immédiatement
  • Exploitation des données : Vous pourrez filtrer par sévérité, calculer le MTTR moyen (temps moyen de rétablissement), etc.
  • Documentation vivante : Le schéma sert de documentation sur les champs attendus

Le schéma ci-dessous montre où se joue cette validation : le script produit un fichier, le build le confronte au schéma Zod, et seule une page dont le front-matter est valide devient une page HTML ; sinon le build s'arrête avec un message qui nomme le champ fautif.

Le script scripts/create-postmortem.js pose huit questions et génère le fichier postmortems/AAAA-MM-JJ-slug.md avec son front-matter et ses sections SRE. Au npm run build, ce fichier est confronté au schéma Zod étendu de content.config.ts : un front-matter valide donne une page HTML dans dist/postmortems/, un front-matter invalide (valeur hors liste, mauvais type) fait échouer le build.front-matter validefront-matter invalidescripts/create-postmortem.js8 questions, YAML générépostmortems/AAAA-MM-JJ-slug.mdfront-matter + sections SREcontent.config.tsschéma Zod étendunpm run buildPage HTMLdist/postmortems/…Build en échecvaleur hors liste, mauvais type
Source · SVG
Échap pour fermer, + et − pour zoomer, défilement pour se déplacer. 100 %

Vous allez mettre en place :

  1. Un schéma Zod pour valider les métadonnées des postmortems (date d'incident au format texte, sévérité limitée à 4 valeurs, etc.)
  2. La collection docs existante, dont le schéma est étendu avec les champs postmortem
  3. Un template structuré incluant toutes les sections SRE (chronologie, 5 Pourquoi, actions correctives, leçons apprises)
  4. Un script de génération interactif pour créer rapidement de nouveaux postmortems sans copier-coller
  • Répertoiresrc/
    • content.config.ts Configuration des collections avec schéma postmortem
    • Répertoirecontent/
      • Répertoiredocs/
        • Répertoirepostmortems/ Dossier dédié aux postmortems
          • 2025-05-15-panne-postgresql.md
          • 2025-06-03-degradation-api.md
        • Répertoiresre/ Pages de référence classiques
          • metriques-slo.md
          • observabilite.md
          • runbooks.md
  • Répertoirescripts/
    • create-postmortem.js Script de génération interactive

Zod est une bibliothèque TypeScript qui permet de définir la structure attendue des données et de valider automatiquement que chaque fichier respecte cette structure.

Concrètement : Si vous définissez severity: z.enum(['critical', 'high', 'medium', 'low']), alors :

  • severity: critical → accepté
  • severity: high → accepté
  • severity: critique → ERREUR : « critique » n'est pas dans la liste autorisée
  • absence de severity → ERREUR si le champ est obligatoire, accepté s'il est déclaré .optional()

Pourquoi c'est important : Sans schéma, chacun écrirait les postmortems "à sa façon" (un avec severite, l'autre avec priority, un autre sans rien). Avec le schéma, tout le monde suit la même structure.

  1. Ouvrir le fichier de configuration des collections

    Éditez src/content.config.ts pour ajouter le schéma postmortem.

    Ce fichier centralise la définition de toutes les collections de contenu (docs, blog, etc.). Vous allez y ajouter les champs spécifiques aux postmortems.

    src/content.config.ts
    import { defineCollection } from 'astro:content';
    import { docsLoader } from '@astrojs/starlight/loaders';
    import { docsSchema } from '@astrojs/starlight/schema';
    import { z } from 'astro:content';
    // Schema pour les postmortems SRE
    const postmortemSchema = z.object({
    title: z.string(),
    description: z.string(),
    incident_date: z.string(),
    severity: z.enum(['critical', 'high', 'medium', 'low']),
    services_affected: z.array(z.string()),
    duration_minutes: z.number(),
    incident_lead: z.string(),
    participants: z.array(z.string()),
    status: z.enum(['draft', 'published', 'archived']).default('draft'),
    });
    export const collections = {
    docs: defineCollection({
    loader: docsLoader(),
    schema: docsSchema({
    // Ajouter les champs postmortem de manière optionnelle
    extend: z.object({
    incident_date: z.string().optional(),
    severity: z.enum(['critical', 'high', 'medium', 'low']).optional(),
    services_affected: z.array(z.string()).optional(),
    duration_minutes: z.number().optional(),
    incident_lead: z.string().optional(),
    participants: z.array(z.string()).optional(),
    status: z.enum(['draft', 'published', 'archived']).optional(),
    }),
    }),
    }),
    };

    Explication ligne par ligne :

    • Lignes 7-17 : Définition du schéma postmortem (pour référence, mais pas directement utilisé)
    • Lignes 24-32 : Extension du schéma docs existant avec les champs postmortem en optionnel (.optional()), grâce à l'option extend de docsSchema()
    • docsLoader() et docsSchema() sont fournis par Starlight lui-même : aucun plugin supplémentaire n'est nécessaire, seul le bloc extend est ajouté à la déclaration de la collection docs
    • Pourquoi optionnel ? : Les fichiers dans docs/ ne sont pas tous des postmortems. Un guide normal n'a pas besoin de incident_date. Seuls les fichiers dans docs/postmortems/ rempliront ces champs.
    • Conséquence : avec des champs optionnels, le build refuse une valeur hors liste ou un mauvais type, mais pas un champ absent. C'est le script de génération, plus bas, qui garantit que chaque postmortem porte tous ses champs.

    Détail des champs :

    ChampTypePourquoiExemple
    incident_datestring (format ISO)Date de l'incident pour tri chronologique"2025-05-15"
    severityenum (4 valeurs)Criticité strictement définie (évite les variations)critical, high, medium, low
    services_affectedarray de stringsListe des services impactés (permet de filtrer)["api-client", "frontend"]
    duration_minutesnumberDurée pour calculer le MTTR (Mean Time To Recovery)125 (= 2h05)
    incident_leadstringResponsable de la gestion de l'incident"Marie Dubois"
    participantsarray de stringsPersonnes impliquées dans la résolution["Marie", "Thomas", "Sophie"]
    statusenumWorkflow de publication (draft → published → archived)draft, published, archived
  2. Créer le dossier des postmortems

    Pourquoi un dossier dédié ? : regrouper les postmortems dans src/content/docs/postmortems/ leur donne une URL commune (/postmortems/...) et permet à la barre latérale de les lister automatiquement avec autogenerate. C'est le principe de la « convention over configuration » : la structure du répertoire organise le contenu.

    Fenêtre de terminal
    mkdir -p src/content/docs/postmortems
  3. Vérifier que le schéma fonctionne

    Créez un fichier de test pour valider :

    Pourquoi tester ? : Avant de créer le vrai template, vérifiez que le schéma fonctionne. Si vous avez fait une erreur de syntaxe dans content.config.ts, le build vous le dira tout de suite, avant que vous n'écriviez de vrais postmortems.

    Fenêtre de terminal
    cat > src/content/docs/postmortems/test.md << 'EOF'
    ---
    title: "Test postmortem"
    description: "Validation du schéma"
    incident_date: "2025-01-13"
    severity: high
    services_affected:
    - test-service
    duration_minutes: 30
    incident_lead: Test User
    participants:
    - Test User
    status: draft
    ---
    ## Test
    Ceci est un test de validation du schéma.
    EOF

    Lancez le build pour vérifier :

    Fenêtre de terminal
    npm run build

    Résultat attendu :

    17:07:53 [content] Synced content
    17:07:54 [build] ✓ Completed in 500ms.

    Si vous voyez cette sortie sans erreur, le schéma fonctionne correctement.

    Si vous voyez une erreur, c'est généralement :

    • Une valeur hors liste dans severity (ex : critique au lieu de critical)
    • Un mauvais type (ex : duration_minutes: "30" entre guillemets, donc une chaîne au lieu d'un nombre)
    • Une erreur de syntaxe dans content.config.ts (parenthèses, virgules)

    Vous pouvez supprimer le fichier de test une fois validé :

    Fenêtre de terminal
    rm src/content/docs/postmortems/test.md

Imaginez créer un nouveau postmortem à chaque incident :

  1. Sans script : Copier un ancien postmortem, remplacer tous les champs un par un, risquer d'oublier quelque chose, formater le YAML manuellement → 5-10 minutes
  2. Avec script : Répondre à 8 questions, le fichier est généré automatiquement avec tous les champs correctement formatés → 1 minute

Le script garantit aussi que :

  • Le nom de fichier suit le format YYYY-MM-DD-slug.md (tri chronologique)
  • Le YAML est toujours valide (pas d'erreur d'indentation)
  • Tous les champs du schéma et toutes les sections du template sont présents
  1. Créer le dossier scripts

    Fenêtre de terminal
    mkdir -p scripts
  2. Créer le script de génération

    Voir le code complet du script (157 lignes)
    scripts/create-postmortem.js
    #!/usr/bin/env node
    import { writeFileSync, existsSync } from 'fs';
    import { createInterface } from 'readline';
    const rl = createInterface({
    input: process.stdin,
    output: process.stdout
    });
    function question(query) {
    return new Promise(resolve => rl.question(query, resolve));
    }
    function slugify(text) {
    return text.toLowerCase()
    .normalize('NFD').replace(/[\u0300-\u036f]/g, '')
    .replace(/[^a-z0-9]+/g, '-')
    .replace(/^-+|-+$/g, '');
    }
    async function main() {
    console.log('\nCréation d\'un nouveau postmortem SRE\n');
    const title = await question('Titre de l\'incident : ');
    const description = await question('Description courte : ');
    const incidentDate = await question('Date de l\'incident (YYYY-MM-DD) : ');
    const severity = await question('Sévérité (critical/high/medium/low) : ');
    const services = await question('Services affectés (séparés par des virgules) : ');
    const duration = await question('Durée en minutes : ');
    const lead = await question('Responsable incident : ');
    const participants = await question('Participants (séparés par des virgules) : ');
    const servicesArray = services.split(',').map(s => s.trim());
    const participantsArray = participants.split(',').map(s => s.trim());
    const slug = slugify(title);
    const filename = `src/content/docs/postmortems/${incidentDate}-${slug}.md`;
    if (existsSync(filename)) {
    console.error(`\nErreur : le fichier ${filename} existe déjà !`);
    rl.close();
    process.exit(1);
    }
    const template = `---
    title: "${title}"
    description: "${description}"
    incident_date: "${incidentDate}"
    severity: ${severity}
    services_affected:
    ${servicesArray.map(s => ` - ${s}`).join('\n')}
    duration_minutes: ${duration}
    incident_lead: ${lead}
    participants:
    ${participantsArray.map(p => ` - ${p}`).join('\n')}
    status: draft
    ---
    ## Résumé
    [Décrivez brièvement l'incident en 2-3 phrases : quoi, quand, impact]
    ## Chronologie
    ### Détection
    - **Date/Heure** : ${incidentDate}
    - **Comment** : [Comment l'incident a-t-il été détecté ?]
    - **Par qui** : ${lead}
    ### Investigation
    - **HH:MM** - [Action entreprise]
    - **HH:MM** - [Découverte importante]
    - **HH:MM** - [Autre événement]
    ### Résolution
    - **HH:MM** - [Action de résolution]
    - **HH:MM** - [Vérification du retour à la normale]
    ## Impact
    ### Services touchés
    ${servicesArray.map(s => `- **${s}** : [Décrire l'impact]`).join('\n')}
    ### Métriques
    - **Durée** : ${duration} minutes
    - **Utilisateurs impactés** : [Nombre ou pourcentage]
    - **Requêtes en échec** : [Si applicable]
    - **Perte financière estimée** : [Si applicable]
    ## Cause racine
    ### Cause immédiate
    [Qu'est-ce qui a directement causé l'incident ?]
    ### Causes contributives
    1. [Facteur qui a facilité ou aggravé l'incident]
    2. [Autre facteur]
    ### Pourquoi cela s'est-il produit ?
    Utilisez la méthode des "5 Pourquoi" :
    1. **Pourquoi** l'incident s'est-il produit ? → [Réponse]
    2. **Pourquoi** [réponse précédente] ? → [Réponse]
    3. **Pourquoi** [réponse précédente] ? → [Réponse]
    4. **Pourquoi** [réponse précédente] ? → [Réponse]
    5. **Pourquoi** [réponse précédente] ? → [Cause racine]
    ## Ce qui a bien fonctionné
    - [Point positif #1]
    - [Point positif #2]
    ## Ce qui peut être amélioré
    - [Point d'amélioration #1]
    - [Point d'amélioration #2]
    ## Actions correctives
    | Action | Responsable | Date limite | Statut | Priorité |
    |--------|-------------|-------------|--------|----------|
    | [Action #1] | [@personne] | YYYY-MM-DD | En attente | Haute |
    | [Action #2] | [@personne] | YYYY-MM-DD | En attente | Moyenne |
    | [Action #3] | [@personne] | YYYY-MM-DD | En attente | Basse |
    ### Statuts possibles
    - En attente
    - En cours
    - Terminé
    - Annulé
    ## Leçons apprises
    ### Ce que nous avons appris
    1. [Leçon #1]
    2. [Leçon #2]
    ### Ce que nous ferons différemment
    1. [Changement #1]
    2. [Changement #2]
    ## Références
    - [Lien vers le ticket d'incident]
    - [Lien vers les métriques]
    - [Lien vers les logs]
    `;
    writeFileSync(filename, template);
    console.log(`\nPostmortem créé : ${filename}`);
    console.log('\nÀ faire ensuite :');
    console.log(' 1. Remplissez les sections du postmortem');
    console.log(' 2. Changez le statut de "draft" à "published" quand terminé');
    console.log(' 3. Lancez "npm run build" pour générer le site\n');
    rl.close();
    }
    main().catch(error => {
    console.error('Erreur:', error);
    rl.close();
    process.exit(1);
    });
  3. Rendre le script exécutable

    Fenêtre de terminal
    chmod +x scripts/create-postmortem.js
  4. Tester le script

    Lancez le script :

    Fenêtre de terminal
    node scripts/create-postmortem.js

    Sortie interactive :

    Création d'un nouveau postmortem SRE
    Titre de l'incident : Panne base de données PostgreSQL production
    Description courte : Indisponibilité complète du service client pendant 2h suite à un problème de réplication
    Date de l'incident (YYYY-MM-DD) : 2025-05-15
    Sévérité (critical/high/medium/low) : critical
    Services affectés (séparés par des virgules) : api-client,frontend,dashboard
    Durée en minutes : 125
    Responsable incident : Marie Dubois
    Participants (séparés par des virgules) : Marie Dubois,Thomas Martin,Sophie Laurent
    Postmortem créé : src/content/docs/postmortems/2025-05-15-panne-base-de-donnees-postgresql-production.md
    À faire ensuite :
    1. Remplissez les sections du postmortem
    2. Changez le statut de "draft" à "published" quand terminé
    3. Lancez "npm run build" pour générer le site

La section « Cause racine » applique la méthode des 5 Pourquoi : on repose la question « pourquoi ? » sur chaque réponse, jusqu'à atteindre une cause système. Dans l'exemple ci-dessous, la chaîne part du serveur PostgreSQL qui ne répond plus et aboutit à l'absence d'architecture séparant les charges OLTP (les transactions courantes) et OLAP (les analyses lourdes). S'arrêter à la requête analytics, la cause immédiate, aurait laissé le défaut d'architecture en place.

Voici un exemple réaliste de postmortem SRE structuré :

Voir le postmortem complet : Panne PostgreSQL production
src/content/docs/postmortems/2025-05-15-panne-postgresql.md
---
title: "Panne base de données PostgreSQL production"
description: "Indisponibilité complète du service client pendant 2h suite à un problème de réplication"
incident_date: "2025-05-15"
severity: critical
services_affected:
- api-client
- frontend
- dashboard
duration_minutes: 125
incident_lead: Marie Dubois
participants:
- Marie Dubois
- Thomas Martin
- Sophie Laurent
status: published
---
## Résumé
Le 15 mai 2025 à 14h23, une défaillance de la réplication PostgreSQL a provoqué une indisponibilité complète de l'API client et des interfaces frontend. L'incident a duré 2h05 minutes et a impacté 100% des utilisateurs. La résolution a nécessité une bascule manuelle sur le serveur secondaire après identification d'un blocage de réplication causé par une transaction longue.
## Chronologie
### Détection
- **Date/Heure** : 2025-05-15 à 14:23
- **Comment** : Alertes Prometheus - Erreurs 500 sur l'API client (taux > 50%)
- **Par qui** : Marie Dubois (ingénieure SRE de permanence)
### Investigation
- **14:23** - Alerte Prometheus déclenchée : API client retourne 500
- **14:25** - Vérification des logs applicatifs : timeout de connexion PostgreSQL
- **14:28** - Connexion au serveur PostgreSQL primaire : réplication en retard de 15 minutes
- **14:32** - Identification d'une transaction bloquée depuis 14:15 (requête analytics longue)
- **14:35** - Tentative de kill de la transaction bloquée : échec
- **14:38** - Décision de bascule sur le serveur secondaire
- **14:42** - Promotion du secondaire en primaire
- **14:45** - Mise à jour de la configuration HAProxy
- **14:48** - Redémarrage des pods API client
### Résolution
- **14:52** - API client opérationnelle sur nouveau primaire
- **15:05** - Vérification complète : toutes les métriques normales
- **15:28** - Reconstruction du serveur défaillant comme nouveau secondaire
## Impact
### Services touchés
- **api-client** : Indisponibilité totale (100% des requêtes en erreur)
- **frontend** : Pages utilisateur inaccessibles (erreurs de chargement)
- **dashboard** : Console d'administration en erreur
### Métriques
- **Durée** : 125 minutes (2h05)
- **Utilisateurs impactés** : 12 453 utilisateurs actifs (100%)
- **Requêtes en échec** : 47 892 requêtes HTTP 500
- **Perte financière estimée** : ~15 000 € (estimation basée sur la perte de transactions)
- **SLA respecté** : Non (SLA mensuel : 99.9% → 99.85% ce mois-ci)
## Cause racine
### Cause immédiate
Une requête analytics non optimisée exécutée sur le serveur primaire à 14:15 a verrouillé des tables critiques pendant plus de 20 minutes. Ce verrouillage a bloqué le processus de réplication WAL (Write-Ahead Logging), créant un retard de réplication croissant jusqu'à rendre le secondaire inutilisable comme failover.
### Causes contributives
1. **Absence de timeout sur les requêtes analytics** : La requête problématique ne disposait d'aucune limite de temps d'exécution
2. **Monitoring insuffisant du lag de réplication** : Le seuil d'alerte était fixé à 30 minutes (trop élevé)
3. **Requête analytics exécutée directement sur le primaire** : Pas de séparation entre charge opérationnelle et analytique
### Pourquoi cela s'est-il produit ?
Utilisez la méthode des "5 Pourquoi" :
1. **Pourquoi** l'incident s'est-il produit ? → Le serveur PostgreSQL primaire ne répondait plus
2. **Pourquoi** le serveur primaire ne répondait plus ? → La réplication était bloquée par une transaction longue
3. **Pourquoi** la transaction bloquait la réplication ? → Elle verrouillait des tables critiques sans timeout
4. **Pourquoi** n'y avait-il pas de timeout ? → Les requêtes analytics n'étaient pas isolées et non limitées
5. **Pourquoi** les requêtes analytics n'étaient pas isolées ? → **Cause racine : Absence d'architecture séparant les charges OLTP et OLAP**
## Ce qui a bien fonctionné
- **Détection rapide** : Les alertes Prometheus ont fonctionné correctement (< 2 minutes)
- **Communication** : Le statut a été communiqué sur status.example.com en moins de 10 minutes
- **Procédure de failover** : Bien que manuelle, la procédure était documentée et a été suivie efficacement
- **Collaboration d'équipe** : Les 3 ingénieurs SRE ont coordonné l'incident sans confusion
## Ce qui peut être amélioré
- **Automatisation du failover** : La bascule manuelle a pris 20 minutes
- **Monitoring de la réplication** : Alertes trop tardives (seuil à 30 min)
- **Séparation des charges** : Aucun read-replica dédié pour l'analytics
- **Documentation** : La procédure de rollback n'était pas documentée
## Actions correctives
| Action | Responsable | Date limite | Statut | Priorité |
|--------|-------------|-------------|--------|----------|
| Implémenter Patroni pour failover automatique | @thomas-martin | 2025-06-01 | En cours | Haute |
| Configurer un read-replica dédié analytics | @sophie-laurent | 2025-05-25 | Terminé | Haute |
| Abaisser le seuil d'alerte réplication à 5 minutes | @marie-dubois | 2025-05-16 | Terminé | Haute |
| Ajouter statement_timeout sur toutes les connexions | @thomas-martin | 2025-05-20 | Terminé | Haute |
| Documenter la procédure de rollback | @marie-dubois | 2025-05-18 | Terminé | Moyenne |
| Créer des game days pour tester le failover | @sophie-laurent | 2025-06-15 | En attente | Moyenne |
| Auditer toutes les requêtes analytics | @thomas-martin | 2025-05-30 | En cours | Basse |
### Statuts possibles
- En attente
- En cours
- Terminé
- Annulé
## Leçons apprises
### Ce que nous avons appris
1. **Importance de l'isolation des charges** : Mélanger OLTP et OLAP sur le même serveur est dangereux
2. **Failover automatique essentiel** : 20 minutes de bascule manuelle représentent 16% du temps total d'incident
3. **Monitoring proactif** : Des alertes à 30 minutes de lag arrivent trop tard pour prévenir un incident
4. **Timeouts obligatoires** : Toute requête doit avoir une limite de temps, sans exception
### Ce que nous ferons différemment
1. **Architecture** : Toutes les nouvelles bases de données auront un read-replica dédié pour l'analytics
2. **Automation** : Patroni sera le standard pour toute base PostgreSQL critique
3. **Testing** : Un game day trimestriel simulera des pannes de base de données
4. **Governance** : Toute connexion applicative aura un statement_timeout configuré
## Références
- [Ticket incident INC-2025-0515](https://jira.example.com/INC-2025-0515)
- [Dashboard Grafana - PostgreSQL](https://grafana.example.com/d/postgresql-overview)
- [Logs Loki - Période incident](https://loki.example.com/logs?from=1715780000&to=1715787600)
- [Status page communication](https://status.example.com/incidents/2025-05-15-database)

Après avoir créé vos postmortems, lancez le build :

Fenêtre de terminal
npm run build

Résultat du build :

17:07:53 [content] Synced content
17:07:54 [build] ✓ Completed in 500ms.
17:07:55 [vite] ✓ built in 1.15s
generating static routes
17:07:55 ✓ Completed in 162ms.
Indexed 6 pages

Vérifiez que les pages sont générées :

Fenêtre de terminal
ls -la dist/postmortems/

Sortie :

drwxrwxr-x 3 bob bob 4096 janv. 13 17:07 .
drwxrwxr-x 8 bob bob 4096 janv. 13 17:07 ..
drwxrwxr-x 2 bob bob 4096 janv. 13 17:07 2025-05-15-panne-postgresql

Chaque postmortem génère un répertoire avec une page HTML statique :

Fenêtre de terminal
ls -lh dist/postmortems/2025-05-15-panne-postgresql/

Sortie :

-rw-rw-r-- 1 bob bob 63K janv. 13 17:07 index.html

Pour rendre les postmortems accessibles dans la navigation, ajoutez-les à la sidebar d'Astro.

  1. Ouvrir la configuration Astro

    Éditez astro.config.mjs :

    astro.config.mjs
    export default defineConfig({
    site: 'https://example.com',
    integrations: [
    starlight({
    title: 'My Docs',
    sidebar: [
    {
    label: 'Guides',
    items: [
    { label: 'Example Guide', slug: 'guides/example' },
    ],
    },
    {
    label: 'Reference',
    autogenerate: { directory: 'reference' },
    },
    // Ajouter la section SRE
    {
    label: 'SRE',
    collapsed: false,
    items: [
    { label: 'Postmortems', autogenerate: { directory: 'postmortems' } },
    { label: 'Métriques & SLO', slug: 'sre/metriques-slo' },
    { label: 'Observabilité', slug: 'sre/observabilite' },
    { label: 'Runbooks', slug: 'sre/runbooks' },
    ],
    },
    ],
    }),
    ],
    });

    Explication :

    • autogenerate: { directory: 'postmortems' } : Génère automatiquement un lien pour chaque fichier dans src/content/docs/postmortems
    • collapsed: false : La section SRE est ouverte par défaut
    • Les autres liens (metriques-slo, etc.) pointent vers des pages fixes, désignées une à une par leur slug
  2. Vérifier dans le navigateur

    Lancez le serveur de développement :

    Fenêtre de terminal
    npm run dev

    Ouvrez http://localhost:4321/ et vérifiez que la sidebar affiche la section SRE, comme sur la capture ci-dessous.

Sidebar SRE avec Postmortems

Après avoir configuré la sidebar, le build génère toutes les pages :

17:14:52 [build] 18 page(s) built in 2.68s
Indexed 9 pages

Pages générées :

Fenêtre de terminal
find dist -name "index.html" | grep -E "(sre|postmortem)"

Sortie :

dist/sre/runbooks/index.html
dist/sre/observabilite/index.html
dist/sre/metriques-slo/index.html
dist/postmortems/2025-05-15-panne-postgresql/index.html

Un postmortem ne sert que s'il est rédigé à chaque incident, structuré de la même façon et exploitable ensuite : le script de génération lève la friction de rédaction, le schéma Zod impose la structure, et le front-matter typé rend les données exploitables.

  • Les postmortems SRE transforment les incidents en opportunités d'apprentissage
  • Un schéma Zod valide les métadonnées et structure la documentation
  • Des champs .optional() refusent une valeur hors liste, mais pas un champ absent : c'est le script qui garantit leur présence
  • La méthode des 5 Pourquoi remonte à la cause racine système
  • Un script de génération accélère la création de nouveaux postmortems
  • La culture blameless analyse les systèmes, pas les personnes
  • Les actions correctives avec responsables et dates limitent la récurrence

Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

Ce projet vous est utile ?

Les guides, formations, quiz et labs restent gratuits. Si vous les utilisez régulièrement, vous pouvez contribuer à leur maintenance et à leur développement.

Devenir membre de soutien (nouvel onglet) Soutenir une fois (nouvel onglet) En savoir plus

Suivez aussi le projet sur LinkedIn