11:48 + 300 s (tolérance unreachable:NoExecute) → 4 pods supprimés, dont hashicorp-vault-0
Vault repart scellé, comme le prévoit Shamir 1/1 sans auto-unseal. Personne ne le redescelle. Pendant onze jours le VSO répond 503 Vault is sealed et plus aucun Secret n'est réconcilié — découvert par hasard.
Ce qui a marché : l'alerte infra. Ce qui a manqué : la conséquence. Le nœud est revenu, les pods ont été recréés, l'alerte s'est éteinte — et personne n'a su que Vault resterait scellé.
Ce n'était ni un crash ni un OOMKill : lastState: {} et restartCount: 0 sur un pod de 11 jours. Et le StatefulSet n'a aucune livenessProbe — seulement une readiness exec: vault status, dont l'échec ne redémarre rien. D'où 196 906 events Unhealthy pour zéro redémarrage.
Pourquoi kube-state-metrics et pas vault_core_unsealed : Vault n'expose ses métriques que via /v1/sys/metrics, qui exige un token ou l'ouverture d'un endpoint non authentifié. Or la readinessProbe du chart est littéralement vault status — un Vault scellé est NotReady. kube-state-metrics est déjà scrapé, ça ne coûte rien, et ça ne touche pas à la config de Vault.
⚠ Le sélecteur [0-9]+ est une correction, pas un détail. Ma première version utilisait hashicorp-vault-.*, qui attrape aussi les pods du Vault Secrets Operator — dont un exemplaire terminé traîne en permanence. La règle tirait donc en continu. Vérifié contre le Prometheus du cluster : avec [0-9]+ elle ne rend que hashicorp-vault-0, et ne tire pas.
2. Vault n'est plus le pod le plus faible du cluster
Mesuré avant : priorityClassName="", priority=0, QoS BestEffort. Dernier servi par le scheduler, premier évincé par le kubelet — alors que tout le cluster dépend de lui et qu'il exige une intervention humaine pour revenir. Sur 114 pods, 78 sont BestEffort : Vault était noyé dedans.
PriorityClass vault-critical à 900000000. Sous longhorn-critical (1000000000) parce que Vault stocke sur un PVC — le stockage doit survivre à Vault. Au-dessus de tout le reste.
requests: {memory: 512Mi, cpu: 100m}. Vault consomme 175 Mio : très en dessous de sa requête, donc tout en bas de la liste d'éviction.
Aucune limite, délibérément. Une limite trop basse déclenche un OOMKill, et un OOMKill sur ce pod coûte un descellement manuel. Or je n'ai pas pu établir de pic mémoire fiable : les séries cAdvisor de ce cluster rendent huit valeurs contradictoires pour ce pod, jusqu'à 2,3 Gio — invraisemblable pour un Vault en storage "file". On ne pose pas une limite sur un chiffre auquel on ne croit pas.
3. Le PDB existant : gardé, mais son piège est maintenant écrit
maxUnavailable: 0 sur un StatefulSet à un réplica donne ALLOWED DISRUPTIONS: 0en permanence. Un kubectl drain du nœud qui héberge Vault ne se terminera donc jamais. C'est le comportement voulu — il force à traiter Vault consciemment — mais la sortie de secours n'était écrite nulle part. Elle l'est.
Ce que ça ne résout pas
Ni la priorité ni les requests n'empêchent la suppression par le node-lifecycle controller quand un nœud devient injoignable — c'est exactement ce qui est arrivé le 30/08, et aucun réglage de pod ne l'évite. Ce cas-là est traité par l'alerte, pas par la protection.
L'auto-unseal reste écarté : décision assumée, documentée dans factory → vibe/guidebooks/lab-ecosystem/secrets-and-vault.md. Le descellement restera manuel ; c'est le délai de détection qui passe de onze jours à dix minutes.
⚠ Application
Le StatefulSet est en updateStrategy: OnDelete. Les réglages du point 2 ne prendront effet qu'à la prochaine suppression du pod — laquelle rescellera Vault. À faire au moment choisi, clé sous la main. L'alerte et le PDB, eux, s'appliquent au sync.
Vérifications
helm template rend la PriorityClass, le PDB, et un StatefulSet portant priorityClassName: vault-critical + les requests
helm lint passe
Requête de l'alerte testée contre le Prometheus du cluster — série présente, règle silencieuse Vault descellé
Faux positif du premier sélecteur identifié et corrigé par mesure
Vérifier que l'alerte remonte bien sur Telegram après sync
## L'incident, reconstitué depuis Prometheus
| Heure UTC, 30/08 | Mesuré |
|---|---|
| 11:30 | mémoire disponible sur **pi3** : 3,6 Gio |
| 11:35 | → 1,7 Gio |
| 11:45 | node-exporter ne répond plus |
| 11:46 | `NoeudInjoignable` **tire** |
| 11:48 | nœud `Ready = false` |
| **11:53** | 11:48 **+ 300 s** (tolérance `unreachable:NoExecute`) → 4 pods supprimés, dont `hashicorp-vault-0` |
Vault repart **scellé**, comme le prévoit Shamir 1/1 sans auto-unseal. Personne ne le redescelle. Pendant **onze jours** le VSO répond `503 Vault is sealed` et plus aucun Secret n'est réconcilié — découvert par hasard.
**Ce qui a marché :** l'alerte infra. **Ce qui a manqué :** la conséquence. Le nœud est revenu, les pods ont été recréés, l'alerte s'est éteinte — et personne n'a su que Vault resterait scellé.
Ce n'était ni un crash ni un OOMKill : `lastState: {}` et `restartCount: 0` sur un pod de 11 jours. Et le StatefulSet n'a **aucune livenessProbe** — seulement une readiness `exec: vault status`, dont l'échec ne redémarre rien. D'où 196 906 events `Unhealthy` pour zéro redémarrage.
## 1. L'alerte qui manquait — `VaultIndisponible`
```promql
kube_pod_status_ready{namespace="tools", pod=~"hashicorp-vault-[0-9]+", condition="true"} == 0
or absent(...)
for: 10m
```
**Pourquoi kube-state-metrics et pas `vault_core_unsealed`** : Vault n'expose ses métriques que via `/v1/sys/metrics`, qui exige un token ou l'ouverture d'un endpoint non authentifié. Or la readinessProbe du chart est littéralement `vault status` — un Vault scellé est NotReady. kube-state-metrics est déjà scrapé, ça ne coûte rien, et ça ne touche pas à la config de Vault.
⚠ **Le sélecteur `[0-9]+` est une correction, pas un détail.** Ma première version utilisait `hashicorp-vault-.*`, qui attrape aussi les pods du Vault Secrets Operator — dont un exemplaire terminé traîne en permanence. **La règle tirait donc en continu.** Vérifié contre le Prometheus du cluster : avec `[0-9]+` elle ne rend que `hashicorp-vault-0`, et ne tire pas.
## 2. Vault n'est plus le pod le plus faible du cluster
Mesuré **avant** : `priorityClassName=""`, `priority=0`, QoS **`BestEffort`**. Dernier servi par le scheduler, premier évincé par le kubelet — alors que tout le cluster dépend de lui et qu'il exige une intervention humaine pour revenir. Sur 114 pods, **78 sont BestEffort** : Vault était noyé dedans.
- **PriorityClass `vault-critical`** à `900000000`. Sous `longhorn-critical` (1000000000) parce que Vault stocke sur un PVC — le stockage doit survivre à Vault. Au-dessus de tout le reste.
- **`requests: {memory: 512Mi, cpu: 100m}`**. Vault consomme 175 Mio : très en dessous de sa requête, donc tout en bas de la liste d'éviction.
**Aucune limite, délibérément.** Une limite trop basse déclenche un OOMKill, et un OOMKill sur ce pod coûte un descellement manuel. Or je n'ai pas pu établir de pic mémoire fiable : les séries cAdvisor de ce cluster rendent **huit valeurs contradictoires** pour ce pod, jusqu'à 2,3 Gio — invraisemblable pour un Vault en `storage "file"`. On ne pose pas une limite sur un chiffre auquel on ne croit pas.
## 3. Le PDB existant : gardé, mais son piège est maintenant écrit
`maxUnavailable: 0` sur un StatefulSet à un réplica donne `ALLOWED DISRUPTIONS: 0` **en permanence**. Un `kubectl drain` du nœud qui héberge Vault ne se terminera donc **jamais**. C'est le comportement voulu — il force à traiter Vault consciemment — mais la sortie de secours n'était écrite nulle part. Elle l'est.
## Ce que ça ne résout pas
Ni la priorité ni les requests n'empêchent la suppression par le node-lifecycle controller quand un nœud devient injoignable — **c'est exactement ce qui est arrivé le 30/08**, et aucun réglage de pod ne l'évite. Ce cas-là est traité par l'alerte, pas par la protection.
L'**auto-unseal reste écarté** : décision assumée, documentée dans `factory` → `vibe/guidebooks/lab-ecosystem/secrets-and-vault.md`. Le descellement restera manuel ; c'est le **délai de détection** qui passe de onze jours à dix minutes.
## ⚠ Application
Le StatefulSet est en `updateStrategy: OnDelete`. Les réglages du point 2 **ne prendront effet qu'à la prochaine suppression du pod** — laquelle rescellera Vault. À faire au moment choisi, clé sous la main. L'alerte et le PDB, eux, s'appliquent au sync.
## Vérifications
- [x] `helm template` rend la PriorityClass, le PDB, et un StatefulSet portant `priorityClassName: vault-critical` + les requests
- [x] `helm lint` passe
- [x] Requête de l'alerte **testée contre le Prometheus du cluster** — série présente, règle silencieuse Vault descellé
- [x] Faux positif du premier sélecteur identifié et corrigé par mesure
- [ ] Vérifier que l'alerte remonte bien sur Telegram après sync
🤖 Generated with [Claude Code](https://claude.com/claude-code)
INCIDENT
Le 30/08 à 11:35, pi3 passe de 3,6 Gio de mémoire disponible à 1,7 en cinq
minutes. À 11:45 node-exporter ne répond plus, à 11:48 le nœud est NotReady, et
300 s plus tard — la tolérance `node.kubernetes.io/unreachable:NoExecute` — le
node-lifecycle controller supprime quatre pods, dont hashicorp-vault-0.
Vault repart SCELLÉ, comme le prévoit Shamir 1/1 sans auto-unseal. Personne ne
le redescelle. Pendant onze jours le VSO répond 503 « Vault is sealed » et plus
aucun Secret n'est réconcilié — découvert par hasard, en enquêtant sur autre
chose.
Ce qui a fonctionné ce jour-là : `NoeudInjoignable` a tiré à 11:46. L'infra a
parlé. Ce qui a manqué : la CONSÉQUENCE. Le nœud est revenu, les pods ont été
recréés, l'alerte s'est éteinte, et personne n'a su que Vault, lui, resterait
scellé.
Ce n'était ni un crash ni un OOMKill : `lastState: {}` et `restartCount: 0` sur
un pod vieux de 11 jours. Et le StatefulSet n'a aucune livenessProbe — seulement
une readiness `exec: vault status`, dont l'échec ne redémarre rien. D'où 196 906
events Unhealthy pour zéro redémarrage.
1. L'ALERTE QUI MANQUAIT — VaultIndisponible
`kube_pod_status_ready{namespace="tools", pod=~"hashicorp-vault-[0-9]+"} == 0`
pendant 10 min, avec `or absent(...)` pour couvrir la disparition du pod.
Pourquoi kube-state-metrics et pas `vault_core_unsealed` : Vault n'expose ses
métriques que via /v1/sys/metrics, qui exige un token ou l'ouverture d'un
endpoint non authentifié. Or la readinessProbe du chart est littéralement
`vault status` : un Vault scellé est NotReady. kube-state-metrics est déjà
scrapé, ça ne coûte rien et ça ne touche pas à la configuration de Vault.
⚠ Le sélecteur est ancré sur `[0-9]+`, et c'est une correction, pas un détail :
ma première version utilisait `hashicorp-vault-.*`, qui attrape aussi les pods
du Vault Secrets Operator — dont un exemplaire terminé traîne en permanence. La
règle tirait donc en continu. Vérifié contre le Prometheus du cluster : avec
`[0-9]+` elle ne rend que hashicorp-vault-0 et ne tire pas.
2. VAULT N'EST PLUS LE POD LE PLUS FAIBLE DU CLUSTER
Mesuré avant : `priorityClassName=""`, `priority=0`, QoS `BestEffort`. Dernier
servi par le scheduler, premier évincé par le kubelet. Sur 114 pods, 78 sont
BestEffort — Vault était noyé dedans, alors que tout le cluster dépend de lui
et qu'il exige une intervention humaine pour revenir.
- PriorityClass `vault-critical` à 900000000. Sous `longhorn-critical`
(1000000000) parce que Vault stocke sur un PVC : le stockage doit survivre à
Vault. Au-dessus de tout le reste.
- `requests: {memory: 512Mi, cpu: 100m}`. Vault consomme 175 Mio : très en
dessous de sa requête, donc tout en bas de la liste d'éviction.
AUCUNE LIMITE, DÉLIBÉRÉMENT. Une limite trop basse déclenche un OOMKill, et un
OOMKill sur ce pod coûte un descellement manuel. Or je n'ai pas pu établir de
pic mémoire fiable : les séries cAdvisor de ce cluster rendent huit valeurs
contradictoires pour ce pod, jusqu'à 2,3 Gio, ce qui est invraisemblable pour un
Vault en storage "file". On ne pose pas une limite sur un chiffre auquel on ne
croit pas.
3. LE PDB EXISTANT : GARDÉ, MAIS SON PIÈGE EST MAINTENANT ÉCRIT
`maxUnavailable: 0` sur un StatefulSet à un réplica donne ALLOWED DISRUPTIONS: 0
en permanence. Un `kubectl drain` du nœud qui héberge Vault ne se terminera donc
jamais. C'est le comportement voulu — il force à traiter Vault consciemment —
mais la sortie de secours (`--disable-eviction`, ou suppression manuelle du pod)
n'était écrite nulle part. Elle l'est.
CE QUE ÇA NE RÉSOUT PAS, ET IL FAUT LE DIRE
Ni la priorité ni les requests n'empêchent la suppression par le node-lifecycle
controller quand un nœud devient injoignable — c'est exactement ce qui est
arrivé le 30/08, et aucun réglage de pod ne l'évite. Ce cas-là est traité par
l'alerte, pas par la protection.
L'auto-unseal reste écarté : décision assumée, documentée dans factory
vibe/guidebooks/lab-ecosystem/secrets-and-vault.md. Le descellement restera
manuel ; c'est le délai de détection qui passe de onze jours à dix minutes.
⚠ APPLICATION : le StatefulSet est en `updateStrategy: OnDelete`. Les réglages
du point 2 ne prendront effet qu'à la prochaine suppression du pod — laquelle
rescellera Vault. À faire au moment choisi, clé sous la main.
Vérifié : `helm template` rend la PriorityClass, le PDB et un StatefulSet
portant `priorityClassName: vault-critical` et les requests ; `helm lint` passe ;
la requête de l'alerte testée contre le Prometheus du cluster.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
L'incident, reconstitué depuis Prometheus
NoeudInjoignabletireReady = falseunreachable:NoExecute) → 4 pods supprimés, donthashicorp-vault-0Vault repart scellé, comme le prévoit Shamir 1/1 sans auto-unseal. Personne ne le redescelle. Pendant onze jours le VSO répond
503 Vault is sealedet plus aucun Secret n'est réconcilié — découvert par hasard.Ce qui a marché : l'alerte infra. Ce qui a manqué : la conséquence. Le nœud est revenu, les pods ont été recréés, l'alerte s'est éteinte — et personne n'a su que Vault resterait scellé.
Ce n'était ni un crash ni un OOMKill :
lastState: {}etrestartCount: 0sur un pod de 11 jours. Et le StatefulSet n'a aucune livenessProbe — seulement une readinessexec: vault status, dont l'échec ne redémarre rien. D'où 196 906 eventsUnhealthypour zéro redémarrage.1. L'alerte qui manquait —
VaultIndisponiblePourquoi kube-state-metrics et pas
vault_core_unsealed: Vault n'expose ses métriques que via/v1/sys/metrics, qui exige un token ou l'ouverture d'un endpoint non authentifié. Or la readinessProbe du chart est littéralementvault status— un Vault scellé est NotReady. kube-state-metrics est déjà scrapé, ça ne coûte rien, et ça ne touche pas à la config de Vault.⚠ Le sélecteur
[0-9]+est une correction, pas un détail. Ma première version utilisaithashicorp-vault-.*, qui attrape aussi les pods du Vault Secrets Operator — dont un exemplaire terminé traîne en permanence. La règle tirait donc en continu. Vérifié contre le Prometheus du cluster : avec[0-9]+elle ne rend quehashicorp-vault-0, et ne tire pas.2. Vault n'est plus le pod le plus faible du cluster
Mesuré avant :
priorityClassName="",priority=0, QoSBestEffort. Dernier servi par le scheduler, premier évincé par le kubelet — alors que tout le cluster dépend de lui et qu'il exige une intervention humaine pour revenir. Sur 114 pods, 78 sont BestEffort : Vault était noyé dedans.vault-criticalà900000000. Souslonghorn-critical(1000000000) parce que Vault stocke sur un PVC — le stockage doit survivre à Vault. Au-dessus de tout le reste.requests: {memory: 512Mi, cpu: 100m}. Vault consomme 175 Mio : très en dessous de sa requête, donc tout en bas de la liste d'éviction.Aucune limite, délibérément. Une limite trop basse déclenche un OOMKill, et un OOMKill sur ce pod coûte un descellement manuel. Or je n'ai pas pu établir de pic mémoire fiable : les séries cAdvisor de ce cluster rendent huit valeurs contradictoires pour ce pod, jusqu'à 2,3 Gio — invraisemblable pour un Vault en
storage "file". On ne pose pas une limite sur un chiffre auquel on ne croit pas.3. Le PDB existant : gardé, mais son piège est maintenant écrit
maxUnavailable: 0sur un StatefulSet à un réplica donneALLOWED DISRUPTIONS: 0en permanence. Unkubectl draindu nœud qui héberge Vault ne se terminera donc jamais. C'est le comportement voulu — il force à traiter Vault consciemment — mais la sortie de secours n'était écrite nulle part. Elle l'est.Ce que ça ne résout pas
Ni la priorité ni les requests n'empêchent la suppression par le node-lifecycle controller quand un nœud devient injoignable — c'est exactement ce qui est arrivé le 30/08, et aucun réglage de pod ne l'évite. Ce cas-là est traité par l'alerte, pas par la protection.
L'auto-unseal reste écarté : décision assumée, documentée dans
factory→vibe/guidebooks/lab-ecosystem/secrets-and-vault.md. Le descellement restera manuel ; c'est le délai de détection qui passe de onze jours à dix minutes.⚠ Application
Le StatefulSet est en
updateStrategy: OnDelete. Les réglages du point 2 ne prendront effet qu'à la prochaine suppression du pod — laquelle rescellera Vault. À faire au moment choisi, clé sous la main. L'alerte et le PDB, eux, s'appliquent au sync.Vérifications
helm templaterend la PriorityClass, le PDB, et un StatefulSet portantpriorityClassName: vault-critical+ les requestshelm lintpasse🤖 Generated with Claude Code
INCIDENT Le 30/08 à 11:35, pi3 passe de 3,6 Gio de mémoire disponible à 1,7 en cinq minutes. À 11:45 node-exporter ne répond plus, à 11:48 le nœud est NotReady, et 300 s plus tard — la tolérance `node.kubernetes.io/unreachable:NoExecute` — le node-lifecycle controller supprime quatre pods, dont hashicorp-vault-0. Vault repart SCELLÉ, comme le prévoit Shamir 1/1 sans auto-unseal. Personne ne le redescelle. Pendant onze jours le VSO répond 503 « Vault is sealed » et plus aucun Secret n'est réconcilié — découvert par hasard, en enquêtant sur autre chose. Ce qui a fonctionné ce jour-là : `NoeudInjoignable` a tiré à 11:46. L'infra a parlé. Ce qui a manqué : la CONSÉQUENCE. Le nœud est revenu, les pods ont été recréés, l'alerte s'est éteinte, et personne n'a su que Vault, lui, resterait scellé. Ce n'était ni un crash ni un OOMKill : `lastState: {}` et `restartCount: 0` sur un pod vieux de 11 jours. Et le StatefulSet n'a aucune livenessProbe — seulement une readiness `exec: vault status`, dont l'échec ne redémarre rien. D'où 196 906 events Unhealthy pour zéro redémarrage. 1. L'ALERTE QUI MANQUAIT — VaultIndisponible `kube_pod_status_ready{namespace="tools", pod=~"hashicorp-vault-[0-9]+"} == 0` pendant 10 min, avec `or absent(...)` pour couvrir la disparition du pod. Pourquoi kube-state-metrics et pas `vault_core_unsealed` : Vault n'expose ses métriques que via /v1/sys/metrics, qui exige un token ou l'ouverture d'un endpoint non authentifié. Or la readinessProbe du chart est littéralement `vault status` : un Vault scellé est NotReady. kube-state-metrics est déjà scrapé, ça ne coûte rien et ça ne touche pas à la configuration de Vault. ⚠ Le sélecteur est ancré sur `[0-9]+`, et c'est une correction, pas un détail : ma première version utilisait `hashicorp-vault-.*`, qui attrape aussi les pods du Vault Secrets Operator — dont un exemplaire terminé traîne en permanence. La règle tirait donc en continu. Vérifié contre le Prometheus du cluster : avec `[0-9]+` elle ne rend que hashicorp-vault-0 et ne tire pas. 2. VAULT N'EST PLUS LE POD LE PLUS FAIBLE DU CLUSTER Mesuré avant : `priorityClassName=""`, `priority=0`, QoS `BestEffort`. Dernier servi par le scheduler, premier évincé par le kubelet. Sur 114 pods, 78 sont BestEffort — Vault était noyé dedans, alors que tout le cluster dépend de lui et qu'il exige une intervention humaine pour revenir. - PriorityClass `vault-critical` à 900000000. Sous `longhorn-critical` (1000000000) parce que Vault stocke sur un PVC : le stockage doit survivre à Vault. Au-dessus de tout le reste. - `requests: {memory: 512Mi, cpu: 100m}`. Vault consomme 175 Mio : très en dessous de sa requête, donc tout en bas de la liste d'éviction. AUCUNE LIMITE, DÉLIBÉRÉMENT. Une limite trop basse déclenche un OOMKill, et un OOMKill sur ce pod coûte un descellement manuel. Or je n'ai pas pu établir de pic mémoire fiable : les séries cAdvisor de ce cluster rendent huit valeurs contradictoires pour ce pod, jusqu'à 2,3 Gio, ce qui est invraisemblable pour un Vault en storage "file". On ne pose pas une limite sur un chiffre auquel on ne croit pas. 3. LE PDB EXISTANT : GARDÉ, MAIS SON PIÈGE EST MAINTENANT ÉCRIT `maxUnavailable: 0` sur un StatefulSet à un réplica donne ALLOWED DISRUPTIONS: 0 en permanence. Un `kubectl drain` du nœud qui héberge Vault ne se terminera donc jamais. C'est le comportement voulu — il force à traiter Vault consciemment — mais la sortie de secours (`--disable-eviction`, ou suppression manuelle du pod) n'était écrite nulle part. Elle l'est. CE QUE ÇA NE RÉSOUT PAS, ET IL FAUT LE DIRE Ni la priorité ni les requests n'empêchent la suppression par le node-lifecycle controller quand un nœud devient injoignable — c'est exactement ce qui est arrivé le 30/08, et aucun réglage de pod ne l'évite. Ce cas-là est traité par l'alerte, pas par la protection. L'auto-unseal reste écarté : décision assumée, documentée dans factory vibe/guidebooks/lab-ecosystem/secrets-and-vault.md. Le descellement restera manuel ; c'est le délai de détection qui passe de onze jours à dix minutes. ⚠ APPLICATION : le StatefulSet est en `updateStrategy: OnDelete`. Les réglages du point 2 ne prendront effet qu'à la prochaine suppression du pod — laquelle rescellera Vault. À faire au moment choisi, clé sous la main. Vérifié : `helm template` rend la PriorityClass, le PDB et un StatefulSet portant `priorityClassName: vault-critical` et les requests ; `helm lint` passe ; la requête de l'alerte testée contre le Prometheus du cluster. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>