Aller au contenu
Documentation medium

Créer une collection de postmortems SRE avec Astro Starlight

36 min de lecture

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 3 mois plus tard. 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. 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.

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

Avant de commencer, vous devez comprendre où se situent les postmortems dans l’écosystème de documentation opérationnelle :

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 :

  • Projet Astro Starlight fonctionnel (voir les guides précédents)
  • Node.js 20+ pour exécuter le script de génération

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 MySQLLors 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 Markdown organisés dans un dossier, avec des règles de validation communes. Par exemple :

  • Tous les fichiers dans src/content/docs/postmortems/ = collection “postmortems”
  • Règle commune : chaque postmortem DOIT avoir un champ incident_date, severity, services_affected, etc.
  • Si vous oubliez un champ obligatoire → le build échoue avec une erreur claire

Analogie : Imaginez un formulaire web. Les champs requis (nom, email, etc.) sont validés avant soumission. Ici, c’est pareil : Astro valide chaque fichier avant de générer le site.

Pourquoi c’est utile pour les postmortems :

  • Uniformité : Tous les postmortems ont la même structure (pas de champ manquant)
  • Erreurs détectées tôt : Si vous oubliez severity, le build vous le dit immédiatement
  • Exploitation des données : Vous pourrez filtrer par sévérité, calculer le MTTR moyen, etc.
  • Documentation vivante : Le schéma sert de documentation sur les champs attendus

Vous allez mettre en place :

  1. Un schéma Zod pour valider les métadonnées des postmortems (date d’incident obligatoire, sévérité limitée à 4 valeurs, etc.)
  2. Une collection Astro dédiée, étendue avec le schéma postmortem (réutilisant la collection “docs” existante)
  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/
    • Répertoirecontent/
      • config.ts Configuration des collections avec schéma postmortem
      • Répertoiredocs/
        • Répertoirepostmortems/ Collection dédiée aux postmortems
          • 2025-05-15-panne-postgresql.md
          • 2025-06-03-degradation-api.md
        • Répertoiresre/ Pages de référence (pas de collection)
          • 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: critiqueERREUR : “critique” n’est pas dans la liste autorisée
  • ❌ Absence de severityERREUR : champ obligatoire manquant

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 { docsSchema } from '@astrojs/starlight/schema';
    import { blogSchema } from 'starlight-blog/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({
    schema: docsSchema({
    extend: (context) => {
    const base = blogSchema(context);
    // Ajouter les champs postmortem de manière optionnelle
    return base.extend({
    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 25-32 : Extension du schéma docs existant avec les champs postmortem en optionnel (.optional())
    • 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.

    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é ? : Tous les fichiers dans src/content/docs/postmortems/ seront automatiquement reconnus comme des postmortems par Astro. C’est le principe de la “convention over configuration” : la structure du répertoire définit le type de 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 config.ts, le build vous le dira maintenant au lieu de plus tard.

    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 faute de frappe dans severity (ex: critique au lieu de critical)
    • Un champ manquant (ex: oubli de duration_minutes)
    • Une erreur dans 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)
  • Toutes les sections obligatoires sont présentes
  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('\n📝 Cré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(`\n❌ 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(`\n✅ Postmortem créé : ${filename}`);
    console.log('\n💡 Prochaines étapes :');
    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
    💡 Prochaines étapes :
    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

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 normales (sans collection)
  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 :

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
  • Les postmortems SRE transforment les incidents en opportunités d’apprentissage
  • Un schéma Zod valide les métadonnées et structure la documentation
  • 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