Après Loki : trois restes mesurés — l'app loki non adoptée par ArgoCD, une sonde à 1 s, et pi2 saturé #44

Open
opened 2026-09-07 18:58:54 +02:00 by arcodange · 2 comments
Owner

La collecte de journaux (#38, PR #40) est déployée et fonctionne — prouvé deux fois avec des instruments propres : un pod écrit 6 lignes, Loki en rend 6 ; un autre pod est supprimé, kubectl logs répond NotFound, et Loki garde sa ligne. Grafana porte la source de données et l'interroge avec succès.

Trois choses restent, mesurées en la déployant.

1. L'application loki n'est pas adoptée par ArgoCD

État : OutOfSync / Healthy. alloy et grafana sont, eux, Synced / Healthy.

La cause est connue : la pile a d'abord été installée à la main (helm install) pendant la mise au point, avant que la PR ne soit fusionnée. Les secrets sh.helm.release.v1.loki.* et …alloy.* existent toujours dans tools, et le StatefulSet porte les deux marques :

managed-by=Helm   release=loki   app.kubernetes.io/instance=loki

⚠ Pendant un moment, les deux applications ont aussi été en Unknown avec :

ComparisonError: Failed to load target state: failed to generate manifest
for source 1 of 1: rpc error: code = DeadlineExceeded

Le repo-server d'ArgoCD n'arrivait pas à rendre les manifestes dans son délai. alloy s'en est sorti seul, loki pas encore. ArgoCD n'est pas géré depuis ce dépôt, donc son délai ne se règle pas par une PR ici — c'est à arbitrer.

Le remède propre est probablement de retirer les releases Helm faites à la main pour que git soit la seule source. ⚠ Je ne l'ai pas fait sans avis : au moment où je l'ai envisagé, ArgoCD ne pouvait pas rendre alloy, donc désinstaller aurait détruit une collecte qui marche sans garantie de la voir revenir.

2. La sonde de disponibilité de Loki exige une réponse en 1 seconde

readinessProbe: { timeoutSeconds: 1, periodSeconds: 10, failureThreshold: 3 }

Mesuré : 30 échecs en 25 minutes, context deadline exceeded, pendant que Loki tournait parfaitement — il finissait de rejouer son WAL, avec un checkpoint done time=4m23s. Conséquence : aucun endpoint derrière le service, donc la source de données Grafana était inutilisable tant que ça durait.

Une seconde est un défaut amont raisonnable sur une machine rapide. Sur un Raspberry Pi qui rejoue un WAL, c'est un piège. ⚠ Le remède n'est pas d'assouplir sans réfléchir : c'est de séparer « Loki démarre » de « Loki répond mal », par exemple avec un startupProbe généreux et une readinessProbe qui reste stricte ensuite.

3. pi2 est saturé, et Loki y a atterri

kubectl top nodes ce matin, avant Loki : pi1 77 %, pi2 106 %, pi3 64 %. Après : l'agent a mesuré +3,2 points sur pi2, ~645 Mio ajoutés au cluster.

Le symptôme est arrivé tout de suite : le nouveau pod Grafana a été planifié sur pi2 et y est resté bloqué 20 minutes, Running mais jamais prêt — 1 seconde de CPU consommée. Il a fallu fermer pi2 à la planification pour qu'il démarre sur pi3, en 165 s.

⚠ La cause profonde est nommée par l'agent : le planificateur ne voit que les requests, et elles ne sont presque pas déclarées sur ce cluster (17 % demandés contre 108 % utilisés). Le planificateur croit donc pi2 libre. Tant que ce sera vrai, il continuera d'y poser des charges.

⚠ Et metrics-server a cessé de répondre par moments pendant ces manipulations — à surveiller, c'est peut-être le même symptôme.

Ce que je n'ai pas vérifié

  • Le compacteur n'a jamais été vu supprimer quoi que ce soit : « le volume ne grossit pas indéfiniment » reste non démontré, avec 30 jours de rétention déclarés.
  • Aucune gate ne démarre Loki sur la configuration rendue — une config cassée passerait la CI. Les deux pannes de mise au point de ce lot seraient repassées.
  • Les premières lignes d'un pod très bref sont perdues : ~5 s séparent l'écriture de l'attachement du tailer. Un pod qui vit 2 s n'est pas collecté — je m'y suis fait prendre en le vérifiant.

🤖 Generated with Claude Code

La collecte de journaux (#38, PR #40) est **déployée et fonctionne** — prouvé deux fois avec des instruments propres : un pod écrit 6 lignes, Loki en rend 6 ; un autre pod est supprimé, `kubectl logs` répond `NotFound`, et Loki garde sa ligne. Grafana porte la source de données et l'interroge avec succès. Trois choses restent, mesurées en la déployant. ## 1. L'application `loki` n'est pas adoptée par ArgoCD État : `OutOfSync` / `Healthy`. `alloy` et `grafana` sont, eux, `Synced` / `Healthy`. La cause est connue : la pile a d'abord été installée **à la main** (`helm install`) pendant la mise au point, avant que la PR ne soit fusionnée. Les secrets `sh.helm.release.v1.loki.*` et `…alloy.*` existent toujours dans `tools`, et le StatefulSet porte les deux marques : ``` managed-by=Helm release=loki app.kubernetes.io/instance=loki ``` ⚠ Pendant un moment, les deux applications ont aussi été en `Unknown` avec : ``` ComparisonError: Failed to load target state: failed to generate manifest for source 1 of 1: rpc error: code = DeadlineExceeded ``` Le repo-server d'ArgoCD n'arrivait pas à rendre les manifestes dans son délai. `alloy` s'en est sorti seul, `loki` pas encore. **ArgoCD n'est pas géré depuis ce dépôt**, donc son délai ne se règle pas par une PR ici — c'est à arbitrer. Le remède propre est probablement de **retirer les releases Helm faites à la main** pour que git soit la seule source. ⚠ Je ne l'ai **pas** fait sans avis : au moment où je l'ai envisagé, ArgoCD ne pouvait pas rendre `alloy`, donc désinstaller aurait détruit une collecte qui marche sans garantie de la voir revenir. ## 2. La sonde de disponibilité de Loki exige une réponse en **1 seconde** ``` readinessProbe: { timeoutSeconds: 1, periodSeconds: 10, failureThreshold: 3 } ``` Mesuré : **30 échecs en 25 minutes**, `context deadline exceeded`, pendant que Loki tournait parfaitement — il finissait de rejouer son WAL, avec un `checkpoint done time=4m23s`. Conséquence : aucun endpoint derrière le service, donc **la source de données Grafana était inutilisable** tant que ça durait. Une seconde est un défaut amont raisonnable sur une machine rapide. Sur un Raspberry Pi qui rejoue un WAL, c'est un piège. ⚠ Le remède n'est pas d'assouplir sans réfléchir : c'est de séparer « Loki démarre » de « Loki répond mal », par exemple avec un `startupProbe` généreux et une `readinessProbe` qui reste stricte ensuite. ## 3. pi2 est saturé, et Loki y a atterri `kubectl top nodes` ce matin, avant Loki : pi1 77 %, **pi2 106 %**, pi3 64 %. Après : l'agent a mesuré **+3,2 points sur pi2**, ~645 Mio ajoutés au cluster. Le symptôme est arrivé tout de suite : le nouveau pod Grafana a été planifié sur pi2 et y est resté bloqué **20 minutes**, `Running` mais jamais prêt — **1 seconde de CPU consommée**. Il a fallu fermer pi2 à la planification pour qu'il démarre sur pi3, en 165 s. ⚠ La cause profonde est nommée par l'agent : **le planificateur ne voit que les `requests`, et elles ne sont presque pas déclarées sur ce cluster** (17 % demandés contre 108 % utilisés). Le planificateur croit donc pi2 libre. Tant que ce sera vrai, il continuera d'y poser des charges. ⚠ Et `metrics-server` a cessé de répondre par moments pendant ces manipulations — à surveiller, c'est peut-être le même symptôme. ## Ce que je n'ai pas vérifié - **Le compacteur n'a jamais été vu supprimer quoi que ce soit** : « le volume ne grossit pas indéfiniment » reste non démontré, avec 30 jours de rétention déclarés. - **Aucune gate ne démarre Loki sur la configuration rendue** — une config cassée passerait la CI. Les deux pannes de mise au point de ce lot seraient repassées. - Les premières lignes d'un pod très bref sont perdues : ~5 s séparent l'écriture de l'attachement du tailer. Un pod qui vit 2 s **n'est pas collecté** — je m'y suis fait prendre en le vérifiant. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Owner

Deux des trois restes sont traités

1. L'adoption par ArgoCD — résolue d'elle-même

loki et alloy sont maintenant Synced / Healthy tous les deux. Le ComparisonError: DeadlineExceeded du repo-server était bien transitoire : il n'arrivait pas à rendre les manifestes dans son délai pendant que le cluster était sous tension, et il y est arrivé une fois la pression retombée.

Je n'ai donc PAS eu à retirer les releases Helm posées à la main, et je ne l'ai pas fait. Les secrets sh.helm.release.v1.{loki,alloy}.* traînent encore dans tools : ArgoCD gère les ressources, mais ces secrets sont des orphelins. Sans conséquence connue, à nettoyer un jour de calme.

2. La sonde à 1 seconde — corrigée (PR #45, fusionnée)

Un startupProbe patient tient désormais le rejeu du WAL (jusqu'à 10 min), et la readinessProbe reste stricte une fois le démarrage acquis, avec 5 s au lieu de 1.

Ce que le rendu m'a appris : mon premier jet omettait initialDelaySeconds en croyant le supprimer, et helm template rendait quand même le 15 amont — le chart fusionne cette table au lieu de la remplacer. Il est maintenant écrit à 0, explicitement.

La preuve manque encore : le rendu prouve la configuration produite, pas que le pod passera Ready du premier coup sous charge. C'est au prochain redémarrage que ça se constatera.

3. pi2 saturé — intact, et ça dépasse ces charts

La cause de fond n'est pas Loki : les requests ne sont presque nulle part déclarées sur ce cluster (17 % demandés contre 108 % utilisés), donc le planificateur croit pi2 libre et continue d'y poser des charges. Loki, lui, déclare bien les siennes (cpu: 100m, memory: 256Mi, limite 512Mi).

C'est un chantier à part et un arbitrage : déclarer des requests pour l'ensemble des charges du cluster change la façon dont tout se planifie, et sur trois Raspberry Pi ça peut rendre des pods non planifiables là où ils tournaient. Ça ne s'improvise pas la nuit.

⚠ Reste aussi ouvert, et non expliqué : metrics-server a cessé de répondre par moments pendant ces manipulations (kubectl top nodes muet). Peut-être le même symptôme, peut-être autre chose.

🤖 Generated with Claude Code

## Deux des trois restes sont traités ### 1. L'adoption par ArgoCD — **résolue d'elle-même** `loki` et `alloy` sont maintenant **`Synced` / `Healthy`** tous les deux. Le `ComparisonError: DeadlineExceeded` du repo-server était bien transitoire : il n'arrivait pas à rendre les manifestes dans son délai pendant que le cluster était sous tension, et il y est arrivé une fois la pression retombée. ⚠ **Je n'ai donc PAS eu à retirer les releases Helm posées à la main**, et je ne l'ai pas fait. Les secrets `sh.helm.release.v1.{loki,alloy}.*` traînent encore dans `tools` : ArgoCD gère les ressources, mais ces secrets sont des orphelins. Sans conséquence connue, à nettoyer un jour de calme. ### 2. La sonde à 1 seconde — **corrigée** (PR #45, fusionnée) Un `startupProbe` patient tient désormais le rejeu du WAL (jusqu'à 10 min), et la `readinessProbe` reste stricte une fois le démarrage acquis, avec 5 s au lieu de 1. ⚠ **Ce que le rendu m'a appris** : mon premier jet *omettait* `initialDelaySeconds` en croyant le supprimer, et `helm template` rendait quand même le `15` amont — **le chart fusionne cette table au lieu de la remplacer**. Il est maintenant écrit à `0`, explicitement. ⚠ **La preuve manque encore** : le rendu prouve la configuration produite, pas que le pod passera `Ready` du premier coup sous charge. C'est au prochain redémarrage que ça se constatera. ### 3. pi2 saturé — **intact, et ça dépasse ces charts** La cause de fond n'est pas Loki : **les `requests` ne sont presque nulle part déclarées sur ce cluster** (17 % demandés contre 108 % utilisés), donc le planificateur croit pi2 libre et continue d'y poser des charges. Loki, lui, déclare bien les siennes (`cpu: 100m`, `memory: 256Mi`, limite `512Mi`). C'est un chantier à part et un arbitrage : déclarer des `requests` pour l'ensemble des charges du cluster change la façon dont tout se planifie, et sur trois Raspberry Pi ça peut rendre des pods **non planifiables** là où ils tournaient. Ça ne s'improvise pas la nuit. ⚠ Reste aussi ouvert, et non expliqué : **`metrics-server` a cessé de répondre par moments** pendant ces manipulations (`kubectl top nodes` muet). Peut-être le même symptôme, peut-être autre chose. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Author
Owner

Le point 3 se précise : pi2 porte ~2 cœurs de charge hors du cluster

metrics-server s'est remis à répondre (il avait redémarré — 35 redémarrages en 174 jours, le dernier il y a 7 h, ce qui explique son silence de tout à l'heure). Ce qu'il montre est plus intéressant que « pi2 est saturé » :

nœud CPU mémoire
pi1 17 % 81 %
pi2 155 – 188 % 109 %
pi3 12 % 63 %

Et en interrogeant le nœud directement (/stats/summary) :

  nœud entier    : 3740 m
  tous les pods  : 1236 m
  kubelet        :  421 m
  ─────────────────────────
  NON ATTRIBUÉ   : ~2080 m   ← deux cœurs

Deux cœurs sur pi2 ne sont ni dans un pod, ni dans le kubelet. La mémoire n'était donc que la moitié de l'histoire : ce nœud est d'abord saturé en calcul, par quelque chose que Kubernetes ne voit pas.

Ce que j'ai éliminé, en le mesurant

  • Ce n'est pas la CI. Deux jobs tournaient pendant la mesure : celui de kadans-api sur arcodange_global_runner_pi3 (et pi3 est à 12 %), celui du front sur arcodange_laptop_runner. Aucun sur pi2.
  • Ce ne sont pas les charges Kubernetes. Le plus gourmand des pods de pi2 est longhorn-system/instance-manager à 645 m ; tous ensemble ils font 1236 m.

Je n'ai pas trouvé le consommateur. Il faudrait regarder les processus de l'hôte, et je n'ai pas voulu poser un pod privilégié sur un nœud déjà tendu, la nuit, sans que ce soit nécessaire. ⚠ Une piste que je n'ai pas suivie : ce cluster tourne sous Docker, pas containerd, avec sa racine déplacée sur /mnt/arcodange/docker — tout conteneur Docker hors Kubernetes sur cet hôte compte dans le total du nœud sans apparaître nulle part côté k8s.

Pourquoi ça compte pour la suite

C'est probablement l'explication des deux symptômes de cette nuit, et ils n'étaient pas des coïncidences :

  • le pod Grafana bloqué 20 minutes sur pi2, Running mais jamais prêt, avec 1 seconde de CPU consommée ;
  • la sonde de Loki qui expirait en context deadline exceeded alors que Loki allait bien.

Deux charges affamées sur le même nœud, au même moment. Déclarer des requests partout — le remède que je proposais plus haut — n'y changerait rien : le planificateur ne réserve que ce qu'il connaît, et ces deux cœurs lui sont invisibles.

🤖 Generated with Claude Code

## Le point 3 se précise : pi2 porte ~2 cœurs de charge **hors du cluster** `metrics-server` s'est remis à répondre (il avait redémarré — 35 redémarrages en 174 jours, le dernier il y a 7 h, ce qui explique son silence de tout à l'heure). Ce qu'il montre est plus intéressant que « pi2 est saturé » : | nœud | CPU | mémoire | |---|---|---| | pi1 | 17 % | 81 % | | **pi2** | **155 – 188 %** | 109 % | | pi3 | 12 % | 63 % | Et en interrogeant le nœud directement (`/stats/summary`) : ``` nœud entier : 3740 m tous les pods : 1236 m kubelet : 421 m ───────────────────────── NON ATTRIBUÉ : ~2080 m ← deux cœurs ``` **Deux cœurs sur pi2 ne sont ni dans un pod, ni dans le kubelet.** La mémoire n'était donc que la moitié de l'histoire : ce nœud est d'abord saturé en **calcul**, par quelque chose que Kubernetes ne voit pas. ## Ce que j'ai éliminé, en le mesurant - **Ce n'est pas la CI.** Deux jobs tournaient pendant la mesure : celui de `kadans-api` sur `arcodange_global_runner_pi3` (et pi3 est à **12 %**), celui du front sur `arcodange_laptop_runner`. Aucun sur pi2. - **Ce ne sont pas les charges Kubernetes.** Le plus gourmand des pods de pi2 est `longhorn-system/instance-manager` à 645 m ; tous ensemble ils font 1236 m. ⚠ **Je n'ai pas trouvé le consommateur.** Il faudrait regarder les processus de l'hôte, et je n'ai pas voulu poser un pod privilégié sur un nœud déjà tendu, la nuit, sans que ce soit nécessaire. ⚠ Une piste que je n'ai pas suivie : **ce cluster tourne sous Docker, pas containerd**, avec sa racine déplacée sur `/mnt/arcodange/docker` — tout conteneur Docker hors Kubernetes sur cet hôte compte dans le total du nœud sans apparaître nulle part côté k8s. ## Pourquoi ça compte pour la suite C'est probablement l'explication des deux symptômes de cette nuit, et ils n'étaient pas des coïncidences : - le pod Grafana **bloqué 20 minutes** sur pi2, `Running` mais jamais prêt, avec **1 seconde de CPU consommée** ; - la sonde de Loki qui expirait en `context deadline exceeded` alors que Loki allait bien. Deux charges affamées sur le même nœud, au même moment. Déclarer des `requests` partout — le remède que je proposais plus haut — **n'y changerait rien** : le planificateur ne réserve que ce qu'il connaît, et ces deux cœurs lui sont invisibles. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: arcodange-org/tools#44