Skip to content

ADR 019 — CNI : migration du VPC CNI vers Cilium (overlay, eBPF) (2026-06-18)

Statut

Accepté (2026-06-18) et validé live le 2026-06-18 sur un aws-start from-scratch (tous les critÚres en fin d'ADR cochés). Rend obsolÚte la Prefix Delegation (#79, ADR 012).

Le comment (pédagogique) est dans le guide Comprendre Cilium.

Contexte

Le cluster EKS utilise le VPC CNI (aws-node), CNI par défaut d'EKS. Chaque pod reçoit une IP du VPC portée par une ENI du node, ce qui plafonne la densité de pods à ~17 sur un t3.medium. On a contourné ce plafond avec la Prefix Delegation (#79) pour atteindre 110 pods, condition nécessaire à ArgoCD et à la stack d'observabilité.

Limites qu'on veut dépasser : densité dépendante des IP d'ENI (bricolage Prefix Delegation), NetworkPolicy L3/L4 seulement, routage des Services par kube-proxy (iptables), aucune observabilité réseau native, et un CNI spécifique à AWS alors qu'on vise la portabilité EKS / homelab / platform.

DĂ©cision 1 — Cilium en overlay VXLAN

On remplace le VPC CNI par Cilium en mode overlay (routingMode: tunnel, encapsulation VXLAN). Les IP des pods proviennent d'un pool géré par Cilium (ipam.mode: cluster-pool, clusterPoolIPv4PodCIDRList: ["10.42.0.0/16"]), hors du VPC.

  • 10.42.0.0/16 est choisi distinct du VPC (10.0.0.0/16) pour Ă©viter toute collision.
  • On se dĂ©tache du plafond ENI : la densitĂ© ne dĂ©pend plus des IP du VPC. La Prefix Delegation (#79) devient inutile.
  • eni.enabled: false (on est en overlay, pas en mode ENI/IPAM AWS).
  • L'overlay est portable (mĂȘme datapath sur n'importe quel cluster).

Alternative écartée : Cilium en mode ENI (IP du VPC, comme le VPC CNI). Rejeté car il garderait la dépendance aux IP d'ENI et au cloud AWS, soit l'inverse du but recherché.

DĂ©cision 2 — Remplacement de kube-proxy par eBPF

kubeProxyReplacement: true : Cilium route les Services en eBPF, on supprime kube-proxy.

  • Plus d'iptables gĂ©antes pour les ClusterIP (meilleure montĂ©e en charge, perf).
  • ConsĂ©quence opĂ©rationnelle : sans kube-proxy, l'agent Cilium ne peut plus joindre l'API server via le ClusterIP kubernetes. On lui donne l'adresse directe : k8sServiceHost = endpoint EKS (output Terraform cluster_endpoint), k8sServicePort: 443.

Alternative écartée : garder kube-proxy (Cilium pour le CNI seulement). Plus conservateur, mais on se prive de l'argument eBPF et d'une partie du gain de perf. On pourra y revenir en repli si un souci de routage Service apparaßt.

DĂ©cision 3 — Hubble + Hubble UI activĂ©s

hubble.enabled, hubble.relay.enabled, hubble.ui.enabled. Observabilité des flux réseau (L3/L4/L7), service map, visualisation des décisions NetworkPolicy.

  • ComplĂšte la stack d'observabilitĂ© du Sprint 4 (Prometheus/Grafana/Loki).
  • L'UI Hubble reste interne (port-forward), jamais exposĂ©e publiquement (cohĂ©rent avec Prometheus, #81).
  • Tradeoff : RAM supplĂ©mentaire sur le mono-node t3.medium dĂ©jĂ  chargĂ©. Requests basses, UI dĂ©sactivable sous pression.

DĂ©cision 4 — Migration, pas from-scratch pur (gestion de l'ordre de boot)

Un managed node group EKS exige des nodes Ready dans sa fenĂȘtre de santĂ©. Or un node n'est Ready qu'avec un CNI, et ArgoCD ne peut pas poser Cilium (il a lui-mĂȘme besoin d'un CNI). On retient donc une migration plutĂŽt qu'un from-scratch sans aucun CNI.

Clé : distinguer le composant vpc-cni (DaemonSet aws-node, installé par défaut par EKS) de l'addon managé aws_eks_addon.vpc_cni (la ressource Terraform qui l'adopte et le configure).

Décision
Gestion managĂ©e de l'addon vpc-cni RetirĂ©e (suppression de la ressource Terraform) → vpc-cni revient en self-managed
Composant vpc-cni au boot Conservé : amÚne les nodes Ready, amorce coredns (~17 IP suffisent)
AprĂšs installation de Cilium DaemonSet aws-node supprimĂ© (self-managed → aucun contrĂŽleur ne le recrĂ©e)

SĂ©quence : (1) boot avec vpc-cni self-managed → nodes Ready ; (2) l'amorce Ansible installe Cilium en premier ; (3) cutover : suppression d'aws-node + de kube-proxy, redĂ©marrage de coredns (reprend une IP overlay) ; (4) le reste de l'amorce (Envoy GW, cert-manager, ESO, ExternalDNS, ArgoCD) tourne sur Cilium. Cilium est une brique de socle posĂ©e par Ansible, pas par ArgoCD (mĂȘme motif que le reste de la plateforme).

Alternative Ă©cartĂ©e : garder l'addon managĂ©. RejetĂ© car son contrĂŽleur recrĂ©erait aws-node aprĂšs suppression → conflit avec Cilium.

PiÚges / Conséquences

  • iptables rĂ©siduels de kube-proxy : aprĂšs suppression, purger les rĂšgles (sinon routage incohĂ©rent jusqu'au reboot du node). Étape cĂąblĂ©e dans l'amorce.
  • maxPods: 110 (launch template nodeadm) : conservĂ©, dĂ©sormais servi par l'overlay Cilium (et non plus par la Prefix Delegation). Le commentaire Terraform est mis Ă  jour en consĂ©quence.
  • AmazonEKS_CNI_Policy (rĂŽle node) : conservĂ©e en l'Ă©tat. Le vpc-cni self-managed s'en sert encore pendant la phase d'amorce. Nettoyage possible si on passe un jour Ă  un Cilium prĂ©sent dĂšs le boot (pur from-scratch).
  • NetworkPolicy #55 : Cilium applique les NetworkPolicy Kubernetes standard, donc allow-fastapi reste valable sans modification. Ouvre la porte aux CiliumNetworkPolicy (L7), non utilisĂ©es pour l'instant.
  • Webhooks d'admission self-hosted (dĂ©couvert en live, INC-058) : le control plane EKS managĂ© n'est pas un node Cilium, il ne route pas vers une IP de pod overlay (10.42.x). Tout webhook d'admission self-hosted en failurePolicy: Fail devient injoignable (Address is not allowed) → l'amorce Ă©choue (cert-manager startupapicheck, ClusterIssuer, ESO). Fix : webhook.hostNetwork: true sur cert-manager (securePort: 10260) et ESO (port: 10261) → le pod prend l'IP du node (VPC), routable. Cluster mono-node ⇒ deux ports distincts (les deux webhooks bindent le mĂȘme rĂ©seau de node), hors du 10250 du kubelet. Le webhook Envoy Gateway (failurePolicy: Ignore) n'est pas concernĂ© (no-op si injoignable). RĂšgle gĂ©nĂ©rale : sous overlay non-natif sur EKS managĂ©, tout composant que l'API server doit joindre en retour doit ĂȘtre en hostNetwork.
  • Cluster Ă©phĂ©mĂšre : la migration se rejoue Ă  chaque aws-start. L'amorce doit donc ĂȘtre dĂ©terministe et idempotente. Rollback = git revert + aws-start (retour au VPC CNI + Prefix Delegation), blast radius faible.

Validation

  • Hors infra : tfsec 0 HIGH, terraform validate, helm template cilium (rendu des values), ansible-lint/syntax, mkdocs build.
  • Live, validĂ© le 2026-06-18 (deux passages : cutover le matin, bootstrap complet le soir aprĂšs les fixes INC-058/INC-059) :
    • plus aucun aws-node/kube-proxy, coredns en IP 10.42.x ✅
    • kubeProxyReplacement: True, DNS interne OK ✅
    • NetworkPolicy allow-fastapi : nĂ©gatif (egress port 80 → Network unreachable) + positif (/healthz/ready → 200) ✅ — Cilium enforce la NetworkPolicy K8s standard sans aws-node ni addon managĂ©
    • Hubble relay + UI Running ✅
    • https://api.devopsyouss.com/healthz/ready → 200 ✅
    • ArgoCD 6/6 Applications Synced/Healthy, stack observabilitĂ© (Prometheus/Grafana/Loki/ Alloy) debout, ESO SecretSynced ✅
  • PrĂ©-requis dĂ©couverts en live (sans rapport avec le datapath Cilium, voir INC-058 + INC-059) : les webhooks d'admission self-hosted (cert-manager, ESO) doivent ĂȘtre en hostNetwork sous overlay, et l'install ArgoCD ne doit pas utiliser helm --wait.

Date : 2026-06-18 Sprint : 4 Issue : #80