Skip to content

ADR 026 — Segmentation des node groups : core (on-demand) / observability (Spot) (2026-06-26)

Statut

ValidĂ© live (2026-06-27) — validation from-scratch complĂšte : les deux node groups montent, la stack obs atterrit sur le nƓud Spot taintĂ©, prometheus passe enfin Running, le trafic reste stable sur core. ADR compagnon : ADR 025 (gouvernance des ressources, #112) traite le volet « discipline des ressources » ; le prĂ©sent ADR traite le volet « capacitĂ© matĂ©rielle ». Issue #114 (nĂ©e de #112 / INC-060). #112 clos avec #114.

Contexte

La validation from-scratch de la gouvernance (#112, ADR 025, 2026-06-25) a mesurĂ© le mono-node t3.medium Ă  99 % de requests mĂ©moire (3286Mi / ~3319 allouables), avec prometheus-kps-prometheus-0 non planifiable (FailedScheduling: Insufficient memory, MemoryPressure: False → sur-rĂ©servation, pas manque de RAM rĂ©elle). DĂ©cision assumĂ©e (ADR 025) : garder des requests honnĂȘtes plutĂŽt que de baisser un plancher-jeton. ConsĂ©quence directe : sans capacitĂ© supplĂ©mentaire, la stack gouvernĂ©e ne planifie pas entiĂšrement.

S'ajoutent deux faiblesses structurelles révélées par INC-060 (le pic Grafana qui a coupé l'ingress ~10 min) :

  1. ObservabilitĂ© co-localisĂ©e au chemin de trafic : le pic mĂ©moire de Grafana a tuĂ© le nƓud qui portait aussi Envoy/NLB → api + argocd + grafana Ă  000. L'obs et le trafic critique partageaient le mĂȘme destin.
  2. Arrivée de Tempo (#108) : l'ingestion de traces va alourdir encore la stack obs, déjà la plus gourmande.

DĂ©cision — Deux managed node groups sĂ©parĂ©s par taint / tolerations / nodeSelector

Node group Capacité Type de départ Porte Pourquoi
core on-demand t3.medium API FastAPI, Envoy/NLB, ArgoCD, cert-manager, ESO, ExternalDNS stabilitĂ© du trafic + plateforme : pas d'Ă©viction Spot sur le chemin critique. Une fois l'obs partie, core ≈ 2Gi sur 3.3 → marge ample pour l'HPA de l'app
observability Spot (taintĂ©) t3.large diversifiĂ© (t3.large / t3a.large / m5.large) Prometheus, Loki, Tempo (#108), Grafana, Alloy gourmand et croissant (Tempo), mais jetable (pas de persistance, perdu au teardown 20h) → on met le gros nƓud sur le moins cher (Spot ~-70 %)

Mécanisme d'isolation :

  • Le node group observability porte le taint workload=observability:NoSchedule + le label workload=observability.
  • CĂŽtĂ© k8s (MR2), les workloads obs portent une toleration de ce taint + un nodeSelector workload=observability : la toleration autorise l'atterrissage sur le nƓud taintĂ©, le nodeSelector l'y force (sinon ils pourraient retomber sur core).
  • Les workloads core ne portent rien : le taint les repousse automatiquement du nƓud obs.
  • PiĂšge DaemonSets : node-exporter (mĂ©triques nƓud) et alloy (logs) doivent tourner sur tous les nƓuds → toleration OUI, nodeSelector NON (sinon on perd la tĂ©lĂ©mĂ©trie du nƓud core). Cilium tolĂšre dĂ©jĂ  tout nativement.

Pourquoi ce dimensionnement, anti-intuition assumĂ©e : l'instinct prod « mettre l'app sur le gros nƓud » ne s'applique pas ici. L'API est volontairement lĂ©gĂšre (128Mi req, HPA plafonnĂ© Ă  5 ≈ 1.3Gi pic) et sans trafic rĂ©el (portfolio) : la dimensionner gros, c'est provisionner pour une charge qui ne viendra pas. Le vrai gourmand mĂ©moire est l'observabilitĂ© (Prometheus/Grafana + Tempo) — c'est elle qui a saturĂ© le nƓud, donc elle qui va sur le gros nƓud Spot. Le principe « trafic sur on-demand stable » reste respectĂ© : core est on-demand et l'app y a la place de scaler. Les types sont un point de dĂ©part Ă  confirmer en live (vraies mesures par pod).

Spot — prĂ©cautions

  • Interruption AWS avec 2 min de prĂ©avis → Spot pour le jetable uniquement, on-demand pour le critique.
  • Pas de stateful in-cluster (la DB est sur RDS) → la contrainte Spot est souple : core reste on-demand pour ne pas couper le trafic Ă  l'Ă©viction, pas pour de la persistance.
  • Diversifier les instance types du node group Spot (t3.large / t3a.large / m5.large) → meilleure disponibilitĂ©, moins d'interruptions.
  • Sans autoscaler, une interruption = pods obs Pending jusqu'au prochain start. Acceptable pour un lab Ă©phĂ©mĂšre ; comblĂ© par Karpenter (Sprint 6) qui reprovisionnerait sur un autre type Spot.

Conséquences

  • #112 devient structurellement impossible : un pic obs ne peut plus tuer le nƓud du trafic (nƓuds distincts). L'isolation des pannes est matĂ©rielle, pas seulement logique.
  • La stack gouvernĂ©e (requests honnĂȘtes, ADR 025) planifie enfin : la capacitĂ© dĂ©bloque prometheus (et la place pour Tempo).
  • Le nƓud le plus cher (celui qui absorbe Tempo) passe Ă  ~-70 % via Spot.
  • CoĂ»t : deux nƓuds au lieu d'un, mais infra Ă©phĂ©mĂšre (teardown 20h) et le gros nƓud est Spot → surcoĂ»t marginal.
  • Renommage Terraform : l'ancien node group unique main devient core → destroy/create cĂŽtĂ© Terraform, sans consĂ©quence sur infra Ă©phĂ©mĂšre (rien Ă  migrer, recréée Ă  chaque start).

Alternatives écartées

  • Un seul nƓud plus gros (ex. t3.xlarge) : rĂšgle la planification mais ne traite pas l'isolation (un pic obs tuerait toujours le trafic co-localisĂ©) et coĂ»te plein pot en on-demand.
  • Tout en Spot : exposerait le trafic aux interruptions (api Ă  000 le temps du reschedule) — inacceptable pour le chemin critique.
  • Karpenter tout de suite : trĂšs fort en entretien mais gros morceau (IAM, NodePool, consolidation) et inutile sans requests honnĂȘtes (il scale sur le Pending, donc sur les requests). SĂ©quencĂ© aprĂšs. Sprint 6 dĂ©diĂ©.

Validation live (✅ 2026-06-27, from-scratch)

Les six points cochés sur un cluster monté de zéro :

  • ✅ kubectl get nodes -L workload → deux nƓuds, labels core (ip-10-0-4-89) et observability (ip-10-0-3-32).
  • ✅ Taints → workload=observability:NoSchedule prĂ©sent sur le nƓud obs uniquement (core <none>).
  • ✅ prometheus-kps-prometheus-0 2/2 Running, plus grafana, loki-0, alertmanager, kube-state-metrics, operator → tous sur le nƓud obs. Le verdict de l'ADR : le 2026-06-25, ce mĂȘme prometheus restait Pending (Insufficient memory) sur le mono-node ; ici il planifie.
  • ✅ fastapi, argocd-server, envoy → sur le nƓud core ; curl https://api.devopsyouss.com/healthz/ready → 200, trafic stable.
  • ✅ node-exporter (2) et alloy (2) prĂ©sents sur les deux nƓuds (toleration joker operator: Exists).

Capacité mesurée aprÚs segmentation

NƓud Type requests mĂ©moire requests CPU
core t3.medium on-demand 75 % (2504Mi) 65 %
observability t3.large Spot 21 % (1492Mi) 31 %
mono-node (2026-06-25, pour mĂ©moire) t3.medium seul 99 % → prometheus Pending —

Deux nƓuds qui respirent lĂ  oĂč le mono-node saturait Ă  99 % de requests mĂ©moire. La marge Ă  21 % sur le t3.large Spot valide le dimensionnement « large Ă  dessein » : Tempo (#108) y rentrera sans nouvelle capacitĂ©. (Rappel pĂ©dagogique : les limits cumulĂ©es peuvent dĂ©passer 100 % — overcommit volontaire et sain ; ce qui gouverne la planification, ce sont les requests, pas les limits.)

  • Facultatif (non fait) : simuler une interruption Spot et observer le reschedule (Pending sans autoscaler). ReportĂ© Ă  Karpenter / Sprint 6.

Date : 2026-06-26 Sprint : 5 Issue : #114 (capacité, né de #112 / INC-060)