Helm Charts / Application charts chart (pull_request) Successful in 11s
Helm Charts / Application charts alloy (pull_request) Successful in 45s
Helm Charts / Application charts loki (pull_request) Failing after 45s
Helm Charts / Application charts grafana (pull_request) Successful in 49s
Helm Charts / Detect changed charts (pull_request) Successful in 23s
Helm Charts / Library charts tool (pull_request) Skipped
Helm Charts / Application charts crowdsec (pull_request) Skipped
Helm Charts / Application charts hashicorp-vault (pull_request) Skipped
Helm Charts / Application charts minio (pull_request) Skipped
Helm Charts / Application charts pgbouncer (pull_request) Skipped
Helm Charts / Application charts pgcat (pull_request) Skipped
Helm Charts / Application charts prometheus (pull_request) Skipped
Helm Charts / Application charts redis (pull_request) Skipped
À supprimer. Ce commit casse volontairement loki/values.yaml (YAML malformé) pour vérifier que le job existe, s'exécute ET ÉCHOUE. Un gate incapable d'échouer rassure sans garder. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Fable 5.1 <[email protected]>
286 lines
13 KiB
YAML
286 lines
13 KiB
YAML
# -----------------------------------------------------------------------------
|
||
# Loki — binaire unique, stockage fichier, sur trois Raspberry Pi.
|
||
# -----------------------------------------------------------------------------
|
||
# ⚠⚠ LES DÉFAUTS AMONT SONT ÉCRITS POUR UN CLUSTER DE SERVEURS, PAS POUR CECI.
|
||
# Mesurés sur le chart 7.3.0 (`helm show values grafana/loki --version 7.3.0`) :
|
||
#
|
||
# deploymentMode SimpleScalable → read + write + backend, 3 jeux
|
||
# chunksCache.enabled true → un memcached à …
|
||
# chunksCache.allocatedMemory 8192 → … 8 Gio, sur un nœud qui en
|
||
# alloue 5,6 (pi2)
|
||
# resultsCache.enabled true → un second memcached
|
||
# gateway.enabled true → un nginx de plus
|
||
# lokiCanary.enabled true → un DaemonSet de plus
|
||
# test.enabled true → un pod de test
|
||
# loki.commonConfig.replication_factor 3 → 3 exemplaires de chaque flux
|
||
# loki.storage.type s3 → un magasin objet qu'on n'a pas
|
||
# loki.auth_enabled true → multi-locataire (en-tête
|
||
# X-Scope-OrgID exigé à chaque appel)
|
||
#
|
||
# Laisser ces défauts, c'est exactement la deuxième propriété de tools#38
|
||
# violée : « la collecte ne doit pas faire tomber ce qu'elle observe ». Chaque
|
||
# `false` ci-dessous est donc une décision, pas une omission.
|
||
# -----------------------------------------------------------------------------
|
||
loki:
|
||
# -- Un seul processus, une seule réplique. Le reste des modes est éteint plus bas.
|
||
deploymentMode: SingleBinary
|
||
|
||
loki:
|
||
# Locataire unique : personne d'autre que le homelab n'écrit ici. Laisser
|
||
# `true` obligerait chaque requête (et chaque source de données Grafana) à
|
||
# porter un en-tête X-Scope-OrgID, pour aucun bénéfice.
|
||
auth_enabled: false
|
||
|
||
commonConfig:
|
||
path_prefix: /var/loki
|
||
# -- UNE réplique de chaque flux : il n'y a qu'un ingesteur.
|
||
# Le défaut amont (3) ferait refuser toute écriture, faute de quorum.
|
||
replication_factor: 1
|
||
|
||
storage:
|
||
type: filesystem
|
||
filesystem:
|
||
chunks_directory: /var/loki/chunks
|
||
rules_directory: /var/loki/rules
|
||
# ⚠ PAS de `admin_api_directory` ici. Le chart l'accepte (il est dans
|
||
# ses valeurs par défaut), mais le binaire OSS le REFUSE :
|
||
# failed parsing config: field admin_api_directory not found in
|
||
# type common.FilesystemConfig
|
||
# C'est un champ de l'édition entreprise (GEL). Mesuré : le pod part en
|
||
# CrashLoopBackOff, 3 redémarrages en 4 min. Le chart ne valide pas la
|
||
# config qu'il écrit — seul le démarrage réel le dit.
|
||
|
||
# ⚠ Le chart n'écrit AUCUN schéma par défaut (`schemaConfig: {}`) et refuse
|
||
# de démarrer sans. tsdb + v13 est le couple courant de Loki 3.x.
|
||
#
|
||
# ⚠⚠ ET LA DATE `from` NE SE MET PAS AU JOUR DE L'INSTALLATION.
|
||
# C'est la faute qu'a faite le premier jet, et elle a coûté cher à
|
||
# diagnostiquer parce qu'elle ne ressemble à rien de ce qu'elle est.
|
||
#
|
||
# `from` dit « à partir de quand ce schéma s'applique », pas « depuis quand
|
||
# on garde ». Posée à la date du jour, Loki n'a AUCUN schéma pour les
|
||
# horodatages antérieurs et refuse la requête d'écriture ENTIÈRE :
|
||
#
|
||
# POST /loki/api/v1/push (500)
|
||
# failed to create stream: no schema config found for time 1786622254
|
||
#
|
||
# Or l'agent lit aussi les fichiers ROTATÉS encore présents sur le disque
|
||
# (4.log, 9.log, 10.log…), qui contiennent des lignes vieilles de plusieurs
|
||
# mois — une remontait à 2026-04-13. ⚠ Et comme une poussée est un LOT, le
|
||
# 500 emporte les lignes FRAÎCHES qui voyageaient avec les vieilles.
|
||
#
|
||
# Symptôme observé : `kubectl logs` montrait 19 lignes du pod témoin, Loki
|
||
# en montrait 0, l'agent se déclarait sain, ses compteurs `dropped` étaient
|
||
# tous à zéro — et `loki_write_sent_entries_total` valait 0. Rien ne
|
||
# DÉSIGNAIT la cause côté agent : elle n'était lisible que dans le journal
|
||
# de Loki. (Journal qu'on ne pouvait lire que parce que le pod n'avait pas
|
||
# redémarré. L'ironie est le sujet même de l'issue #38.)
|
||
#
|
||
# La date est donc largement antérieure à tout ce que le cluster peut
|
||
# porter. Ce qui borne la conservation, c'est `retention_period` +
|
||
# le compacteur, pas ceci.
|
||
schemaConfig:
|
||
configs:
|
||
- from: "2020-01-01"
|
||
store: tsdb
|
||
object_store: filesystem
|
||
schema: v13
|
||
index:
|
||
prefix: loki_index_
|
||
period: 24h
|
||
|
||
# ⚠⚠ UNE RÉTENTION QUI NE SUPPRIME RIEN EST UNE RÉTENTION QUI MENT.
|
||
# `limits_config.retention_period` seul ne fait RIEN : c'est le compacteur
|
||
# qui efface, et il ne le fait que si `retention_enabled` est vrai. Le
|
||
# couple est indissociable — l'un sans l'autre laisse le volume grossir
|
||
# jusqu'à saturation, en silence.
|
||
compactor:
|
||
working_directory: /var/loki/compactor
|
||
retention_enabled: true
|
||
delete_request_store: filesystem
|
||
|
||
limits_config:
|
||
# -- 30 jours. Voir `RETENTION` en fin de fichier pour la mesure.
|
||
retention_period: 720h
|
||
# ⚠⚠ CE N'EST PAS UN RÉGLAGE DE CONSERVATION, ET LE CONFONDRE BLOQUE TOUT.
|
||
#
|
||
# Posé à 720 h pour « coller » à la rétention, il a produit un second
|
||
# blocage complet, juste après celui du schéma : l'agent lit aussi les
|
||
# fichiers rotatés présents sur le disque, dont certains remontent à
|
||
# AVRIL. Loki refusait alors chaque lot —
|
||
#
|
||
# entry ... has timestamp too old: 2026-04-13T13:47:05Z,
|
||
# oldest acceptable timestamp is: 2026-08-08T15:41:50Z
|
||
#
|
||
# — et comme l'agent REJOUE un lot refusé, la file ne se vidait jamais :
|
||
# `loki_write_sent_entries_total` restait à 0 pour toujours. Un lot
|
||
# définitivement refusé bloque la file derrière lui.
|
||
#
|
||
# Ce garde-fou existe contre les horloges DÉCALÉES, pas pour borner la
|
||
# conservation. On l'ouvre donc largement au-delà de ce que le disque
|
||
# peut porter ; ce qui borne vraiment, c'est `retention_period` + le
|
||
# compacteur, plus bas.
|
||
#
|
||
# ⚠ Conséquence assumée : au tout premier démarrage, l'agent remonte
|
||
# l'historique encore sur le disque, et le compacteur supprimera dans
|
||
# l'heure ce qui dépasse 30 jours. On paie une ingestion inutile UNE
|
||
# fois, en échange d'une file qui se vide. Le bénéfice de bord est
|
||
# qu'on récupère au passage l'histoire déjà écrite.
|
||
reject_old_samples: true
|
||
reject_old_samples_max_age: 8760h
|
||
max_cache_freshness_per_query: 10m
|
||
split_queries_by_interval: 15m
|
||
query_timeout: 300s
|
||
volume_enabled: true
|
||
# Débit mesuré du cluster : ~305 Mio/jour TOUS pods confondus, soit
|
||
# ~3,6 Kio/s. Les bornes amont (4 Mio/s par flux) sont hors sujet ici ;
|
||
# on les laisse, elles ne mordent pas.
|
||
allow_structured_metadata: true
|
||
|
||
# Pas de vitrine : personne d'extérieur n'interroge ce Loki.
|
||
analytics:
|
||
reporting_enabled: false
|
||
|
||
singleBinary:
|
||
replicas: 1
|
||
persistence:
|
||
enabled: true
|
||
# ⚠ DEUX StorageClass portent le drapeau `(default)` sur ce cluster
|
||
# (`local-path` ET `longhorn`, mesuré le 2026-09-07). Un PVC sans classe
|
||
# explicite est donc un tirage au sort — et `local-path` ne survit pas au
|
||
# déplacement du pod, ce qui viderait la mémoire qu'on vient de bâtir.
|
||
# On NOMME la classe.
|
||
storageClass: longhorn
|
||
# 30 j × 305 Mio/j = 8,9 Gio BRUTS ; Loki compresse (facteur 5 à 10 sur
|
||
# du journal), donc 0,9 à 1,8 Gio attendus. 10 Gi laisse 5 à 10× de marge
|
||
# pour la croissance de kadans, sans immobiliser le disque.
|
||
size: 10Gi
|
||
resources:
|
||
requests:
|
||
cpu: 100m
|
||
memory: 256Mi
|
||
limits:
|
||
# Pas de limite CPU : l'étranglement d'un compacteur est pire que sa
|
||
# lenteur. La mémoire, elle, est PLAFONNÉE — c'est la ressource rare
|
||
# (pi2 mesuré à 109 % de son allouable le 2026-09-07).
|
||
memory: 512Mi
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Tout ce qui suit existe pour un déploiement distribué. Ici, chacun de ces
|
||
# blocs est du poids mort sur des Raspberry Pi — on les éteint NOMMÉMENT
|
||
# plutôt que par un `deploymentMode` qui « devrait » suffire.
|
||
# ---------------------------------------------------------------------------
|
||
write: { replicas: 0 }
|
||
read: { replicas: 0 }
|
||
backend: { replicas: 0 }
|
||
ingester: { replicas: 0 }
|
||
distributor: { replicas: 0 }
|
||
querier: { replicas: 0 }
|
||
queryFrontend: { replicas: 0 }
|
||
queryScheduler: { replicas: 0 }
|
||
indexGateway: { replicas: 0 }
|
||
compactor: { replicas: 0 }
|
||
bloomPlanner: { replicas: 0 }
|
||
bloomBuilder: { replicas: 0 }
|
||
bloomGateway: { replicas: 0 }
|
||
patternIngester: { replicas: 0 }
|
||
ruler: { replicas: 0 }
|
||
overridesExporter: { replicas: 0 }
|
||
|
||
# Les deux memcached. Le premier réclame 8 Gio à lui seul par défaut.
|
||
chunksCache:
|
||
enabled: false
|
||
resultsCache:
|
||
enabled: false
|
||
|
||
# Un nginx devant un service que seul Grafana interroge, depuis le cluster.
|
||
gateway:
|
||
enabled: false
|
||
|
||
# Un DaemonSet qui écrit des journaux pour vérifier qu'on lit les journaux :
|
||
# trois pods de plus sur trois nœuds, pour une propriété que le sabotage de
|
||
# la PR démontre mieux (écrire une ligne, tuer le pod, la relire).
|
||
lokiCanary:
|
||
enabled: false
|
||
|
||
test:
|
||
enabled: false
|
||
|
||
# Le chart sait déployer SON PROPRE MinIO. On en a déjà un, et on n'en veut
|
||
# pas ici : le stockage est le disque.
|
||
minio:
|
||
enabled: false
|
||
|
||
monitoring:
|
||
dashboards:
|
||
enabled: false
|
||
rules:
|
||
enabled: false
|
||
serviceMonitor:
|
||
enabled: false
|
||
metricsInstance:
|
||
enabled: false
|
||
selfMonitoring:
|
||
enabled: false
|
||
grafanaAgent:
|
||
installOperator: false
|
||
|
||
# Pas d'opérateur de déploiement progressif pour une réplique unique.
|
||
rollout_operator:
|
||
enabled: false
|
||
|
||
# ⚠ UN SECOND CONTENEUR SANS PLAFOND ANNULE LE PLAFOND DU PREMIER.
|
||
# Le chart ajoute au pod un side-car `loki-sc-rules` (kiwigrid/k8s-sidecar)
|
||
# qui surveille des ConfigMaps de règles d'alerte. Mesuré sur le premier
|
||
# déploiement : il arrive avec `resources: {}` — ni requête ni limite — dans
|
||
# un pod posé sur pi2, le nœud à 108 % de son allouable. On l'éteint : le
|
||
# `ruler` est à 0 réplique, il n'y a aucune règle à ingérer, donc ce side-car
|
||
# surveille des ConfigMaps qui n'existent pas.
|
||
sidecar:
|
||
rules:
|
||
enabled: false
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# RETENTION — l'arbitrage, et la mesure qui le porte
|
||
# -----------------------------------------------------------------------------
|
||
# Mesuré le 2026-09-07 sur ce cluster, par l'API (donc ce que Loki ingérerait) :
|
||
#
|
||
# fenêtre de 10 min, tous pods : 2 182 015 octets
|
||
# fenêtre de 1 h, tous pods : 7 897 505 octets
|
||
#
|
||
# Les deux instruments ne s'accordent pas (l'heure ne vaut que 0,60 fois six
|
||
# fois les dix minutes) — et le désaccord est LE résultat. Par conteneur, le
|
||
# rapport 1 h / 10 min vaut ~6 partout (débit stable) SAUF :
|
||
#
|
||
# kube-system/traefik ratio 1,18 → la fenêtre d'une heure est TRONQUÉE
|
||
# tools/clickhouse-0 ratio 3,98 → tronquée aussi
|
||
#
|
||
# Vérifié directement, en demandant à chaque journal jusqu'où il remonte :
|
||
#
|
||
# traefik pod démarré il y a 12 j — journal remontant à 10 MINUTES
|
||
# clickhouse pod démarré il y a 8 j — journal remontant à 2 MINUTES
|
||
#
|
||
# Autrement dit, les deux composants les plus bavards du cluster gardent entre
|
||
# deux et dix minutes d'histoire. C'est l'issue tools#38 en un chiffre.
|
||
#
|
||
# Estimation corrigée (max(1 h, 10 min × 6) par conteneur, pour ne pas hériter
|
||
# de la troncature) : 13 333 645 octets/h = 305 Mio/jour bruts.
|
||
#
|
||
# 30 j bruts 8,94 Gio
|
||
# 30 j compressés (facteur 5) 1,79 Gio
|
||
# 30 j compressés (facteur 10) 0,89 Gio
|
||
#
|
||
# POURQUOI 30 JOURS et pas 7 : les deux pannes qui motivent ce lot ont duré des
|
||
# SEMAINES avant d'être vues (la mise à l'abri de kadans#1033) ou trois jours
|
||
# (tools#36). Une rétention de 7 jours aurait perdu le début des deux. 30 jours
|
||
# couvre le délai réel entre « ça casse » et « quelqu'un s'en aperçoit », et
|
||
# coûte 1 à 2 Gio — sur 99 Gio libres au plus serré des trois nœuds Longhorn.
|
||
#
|
||
# ⚠ CE QUI RENDRAIT CE CHIFFRE FAUX : la mesure est une photo d'un mardi
|
||
# après-midi. Elle ne dit rien d'un pod qui part en boucle d'erreur, cas où le
|
||
# débit d'un seul conteneur peut dépasser à lui seul tout le reste du cluster.
|
||
# La marge de 5 à 10× est là pour ça, pas pour la croissance ordinaire.
|
||
# -----------------------------------------------------------------------------
|
||
|
||
malforme: [ceci ne ferme pas
|