Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BAUeCFpDkRD6tU5wGsc1C
9.4 KiB
11 — Observabilité
Durée : environ 2 h.
Principe : ce qui n'est pas surveillé n'existe pas. Une panne détectée par un client est une panne détectée trop tard.
1. Les composants, et pourquoi chacun est là
| Composant | Rôle | Pourquoi lui |
|---|---|---|
| Loki | Journaux | Indexe des étiquettes, pas le texte intégral : ~10× moins gourmand qu'Elasticsearch, largement suffisant ici. |
| Promtail | Collecte | Lit /var/log/pods (containerd, pas Docker : k3s n'utilise pas Docker). Découpe le JSON pino en champs. |
| Prometheus | Métriques | 15 jours de rétention. Sans métriques, on ne voit pas une dégradation venir. |
| node-exporter | Métriques système | Présent pour une alerte avant tout : disque plein, la panne la plus fréquente d'un serveur laissé seul. |
| Alertmanager | Routage | Envoie sur Discord. Règles d'inhibition pour éviter le déluge. |
| Grafana | Consultation | Une interface pour les deux sources. |
| healthchecks.io ou BetterStack | Externe | Indispensable : voir §5. |
Coût : 0 €. Empreinte : environ 1,5 Go de RAM sur les 16 du serveur.
2. Déployer
export KUBECONFIG=~/.kube/xpeditis-prod.yaml
cd infra/prod
make deploy-monitoring
Le script vérifie d'abord que les secrets grafana-admin et
alertmanager-secrets existent, puis déploie les six composants et attend leur
démarrage.
kubectl -n monitoring get pods,pvc
3. Grafana
https://grafana.xpeditis.com — identifiants dans le Secret
monitoring/grafana-admin.
Si vous obtenez un 403
C'est le middleware Traefik admin-ip-allowlist. Renseignez votre IP :
$EDITOR k8s/base/08-traefik-middlewares.yaml # section admin-ip-allowlist
kubectl apply -f k8s/base/08-traefik-middlewares.yaml
Grafana donne accès à l'intégralité de vos journaux applicatifs, jetons tronqués et identifiants clients compris. Trois couches le protègent : le firewall Hetzner (IP Cloudflare uniquement), le filtrage par IP Traefik, et l'authentification Grafana. Si votre IP est dynamique, remplacez la deuxième par Cloudflare Access (Zero Trust, gratuit) plutôt que d'élargir la liste.
Vérifier les sources de données
Connections → Data sources : Loki et Prometheus doivent afficher
« Data source is working ».
Premières requêtes utiles
# Toutes les erreurs du backend sur 1 h
{namespace="xpeditis-prod", service="xpeditis-backend"} | json | level="error"
# Échecs d'authentification
{namespace="xpeditis-prod"} |= "Unauthorized" or "Invalid credentials"
# Requêtes PostgreSQL lentes (> 500 ms)
{namespace="xpeditis-prod"} |= "duration:" | json | duration > 500
# Suivre une requête de bout en bout par son identifiant
{namespace="xpeditis-prod"} | json | reqId="<uuid>"
# Taux d'erreur 5xx
sum(rate(traefik_service_requests_total{code=~"5.."}[5m]))
/ sum(rate(traefik_service_requests_total[5m]))
# Latence P95 par service
histogram_quantile(0.95, sum(rate(traefik_service_request_duration_seconds_bucket[5m])) by (le, service))
# Connexions PostgreSQL utilisées
sum(pg_stat_database_numbackends) / on() pg_settings_max_connections
# Espace disque restant
node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"}
4. Les alertes
Définies dans k8s/monitoring/03-prometheus.yaml.
| Alerte | Seuil | Gravité |
|---|---|---|
BackendIndisponible |
Aucune instance saine, 2 min | critique |
FrontendIndisponible |
Aucune instance saine, 2 min | critique |
TauxErreur5xxEleve |
> 5 % pendant 5 min | critique |
LatenceElevee |
P95 > 2 s pendant 10 min | avertissement |
DisqueBientotPlein |
< 15 % libre, 10 min | critique |
MemoireNoeudSaturee |
> 90 %, 10 min | avertissement |
ConteneurRedemarreEnBoucle |
> 3 démarrages en 15 min | critique |
PostgresInjoignable |
pg_up == 0, 2 min |
critique |
ConnexionsPostgresSaturees |
> 80 %, 5 min | avertissement |
CertificatBientotExpire |
< 15 jours | critique |
Les critiques sont répétées toutes les heures tant qu'elles ne sont pas résolues ; les avertissements toutes les 12 h.
Tester le routage — avant d'en avoir besoin
kubectl -n monitoring port-forward svc/alertmanager 9093:9093 &
curl -XPOST http://localhost:9093/api/v2/alerts \
-H 'Content-Type: application/json' \
-d '[{"labels":{"alertname":"TestDeRoutage","severity":"critique"},
"annotations":{"summary":"Test de la chaine d alerte"}}]'
Le message doit arriver sur Discord en moins d'une minute. S'il n'arrive pas, toutes les alertes ci-dessus sont décoratives. Vérifiez alors :
kubectl -n monitoring logs deploy/alertmanager --tail=50
kubectl -n monitoring get secret alertmanager-secrets -o jsonpath='{.data.discord-webhook}' | base64 -d
Vérifier que Prometheus collecte bien
kubectl -n monitoring port-forward svc/prometheus 9090:9090 &
open http://localhost:9090/targets
Toutes les cibles doivent être UP. La plus fragile est postgres
(10.10.1.20:9187) : elle passe par le réseau privé.
# Si postgres est DOWN
ssh deploy@<db_ip> 'cd /opt/xpeditis/data-node && sudo docker compose ps postgres-exporter'
kubectl -n monitoring exec deploy/prometheus -- wget -qO- http://10.10.1.20:9187/metrics | head -3
5. Supervision externe — la seule qui compte quand tout brûle
Prometheus, Alertmanager et Grafana tournent sur le serveur qu'ils surveillent. Si app-01 s'éteint, ils s'éteignent avec lui : personne ne vous prévient. C'est le point aveugle structurel de toute supervision interne.
Il faut donc un observateur extérieur.
5.1 Disponibilité
Sur healthchecks.io (gratuit) ou BetterStack :
| Contrôle | URL | Fréquence | Alerte après |
|---|---|---|---|
| API | https://api.xpeditis.com/api/v1/health |
1 min | 2 échecs |
| Application | https://app.xpeditis.com/ |
1 min | 2 échecs |
| Vitrine | https://xpeditis.com/ |
5 min | 2 échecs |
Notification par e-mail et SMS (ou notification téléphonique) : un e-mail n'est pas lu à 3 h du matin.
5.2 Battement de cœur des sauvegardes
C'est le silence qui doit alerter. Une alerte « la sauvegarde n'a pas tourné » évaluée par un composant hébergé sur la même machine ne se déclenche pas quand la machine est éteinte — précisément quand elle serait utile.
- Créez un check de type heartbeat, période 1 jour, marge 6 h.
- Copiez son URL de ping dans
.env.datade db-01 :
ssh deploy@<db_ip> 'sudo $EDITOR /opt/xpeditis/data-node/.env.data'
# BACKUP_HEARTBEAT_URL=https://hc-ping.com/<uuid>
- Déclenchez une sauvegarde pour vérifier :
ssh deploy@<db_ip> 'sudo systemctl start xpeditis-backup.service'
Le check doit passer au vert sur healthchecks.io.
5.3 Expiration du certificat
Ajoutez un contrôle TLS externe sur app.xpeditis.com (alerte à 14 jours).
Il double l'alerte Prometheus, mais fonctionne même quand le cluster est mort.
6. Sentry
Sentry apporte ce que les journaux ne donnent pas : la pile d'appels, les variables locales et le regroupement des erreurs identiques.
Le backend embarque @sentry/node. Ajoutez SENTRY_DSN au Secret puis
redémarrez :
sops k8s/base/03-secrets.sops.yaml # SENTRY_DSN: "https://...@sentry.io/..."
make secrets-apply
kubectl -n xpeditis-prod rollout restart deploy/xpeditis-backend
Vérifiez que le DSN est bien lu au démarrage. Le code appelle
configService.get('SENTRY_DSN'): si l'initialisation n'est pas câblée dansmain.ts, la variable est ignorée en silence. Provoquez une erreur de test et vérifiez qu'elle apparaît dans Sentry — sinon la variable ne sert à rien.
Configurez la purge des données personnelles côté Sentry (Settings → Security & Privacy → Data Scrubbing) : sans elle, des e-mails et jetons clients partent chez un sous-traitant américain — ce qui a des conséquences RGPD (16).
7. Ce qu'il faut regarder chaque semaine
Quinze minutes, le lundi :
make -C infra/prod status
- Erreurs — Grafana,
{namespace="xpeditis-prod"} | json | level="error"sur 7 jours. Une erreur récurrente est un défaut, pas du bruit. - Latence — le P95 dérive-t-il ? Une dégradation lente précède la panne.
- Disque —
node_filesystem_avail_bytes. Projetez : dans combien de semaines l'alerte se déclenchera-t-elle ? - Sauvegardes — le check heartbeat est-il vert ? La vérification
hebdomadaire de restauration est-elle passée ?
ssh deploy@<db_ip> 'sudo journalctl -u xpeditis-backup-verify -n 30 --no-pager' - Firewall CI —
hcloud firewall describe xpeditis-prod-fw-cicd: vide ? - Certificats —
kubectl get certificate -A:Ready: Truepartout ?
8. Contrôle
[ ] 6 composants de supervision Running
[ ] Grafana accessible, sources Loki et Prometheus fonctionnelles
[ ] Toutes les cibles Prometheus UP, y compris postgres
[ ] Alerte de test reçue sur Discord
[ ] Contrôles de disponibilité externes configurés (API, app, vitrine)
[ ] Battement de cœur des sauvegardes configuré et vert
[ ] Contrôle externe d'expiration du certificat
[ ] Sentry connecté et erreur de test visible
[ ] Purge des données personnelles activée dans Sentry
[ ] Routine hebdomadaire notée dans l'agenda
→ Suite : 12 — Sauvegardes et restauration