← blogENhome

Kubernetes · Networking · 2026-09-03 · 6 min de lectura

HA de egress en Cilium OSS sin pagar Enterprise

Un cluster Kubernetes on-premise (kubeadm sobre VMware) necesitaba una IP de egreso fija: los terceros whitelistean una sola dirección, y todo el tráfico saliente hacia ellos tiene que originarse desde ahí. El egress gateway de Cilium hace exactamente eso — dirige el tráfico que matchea a través de un nodo gateway, con SNAT a la IP designada.

El problema es la palabra un. En el Cilium que corre este cluster — 1.17 OSS — el egress gateway no hace failover. Lo verificamos en pruebas: con varios nodos candidatos etiquetados, el datapath elige uno de forma determinística, y cuando ese nodo muere, el tráfico que matchea se descarta hasta que alguien interviene. El HA del gateway con leader election viene en el egress gateway enterprise de Isovalent; la versión open-source deja la silla vacía. Así que tu IP de egreso cuidadosamente whitelisteada es ahora un single point of failure con un pager atado.

El diseño: flotar la IP, seguir al líder

Dos piezas móviles, cada una haciendo lo que sabe hacer:

kube-vip flota la IP de egreso. Corre en los workers candidatos, elige un líder a través de un lease de Kubernetes, y el ganador anuncia la VIP con ARP gratuito. Muere el nodo → expira el lease → otro nodo toma la IP y hace GARP a la red. Es plomería probada en batalla; el mismo mecanismo que muchos clusters ya usan para la VIP del control plane.

Un pequeño controller de label-sync sigue al lease. A Cilium no le importa dónde vive la VIP — rutea el tráfico de egress al nodo que tenga el label de gateway. Así que la pieza faltante es vergonzosamente específica: mantener exactamente un nodo etiquetado, y que sea el que hoy tiene el lease de kube-vip. El controller observa el lease y parchea los labels de los nodos en consecuencia. Esa es toda la descripción del puesto.

kube-vip solo no lo resuelve (el label seguiría apuntando al nodo muerto) y re-etiquetar solo tampoco (la IP no se movería). Juntos le dan a Cilium OSS lo que Enterprise vende: egress que sobrevive a la caída de un nodo.

Del PoC a algo por lo que se puede paginar gente

La prueba de concepto era un script bash corriendo en una sesión de tmux — validó el approach y habría muerto con mi conexión SSH. La versión productiva es un operator en Go sobre controller-runtime:

La parte interesante son las invariantes, porque mover labels tiene filos:

El susto del cold-start

Una observación del PoC casi mata el diseño entero: el primer failover hacia un nodo que nunca había tenido la IP de egreso tardó más de 30 minutos en converger — el agente de Cilium tardaba en reconocer la egress IP como local. Si eso se reproducía, un failover real cayendo en un nodo "virgen" significaba media hora de corte de egress, y el diseño no servía para producción.

Eso se convirtió en el spike que gateaba todo, por delante del trabajo de empaquetado: reproducirlo con un nodo genuinamente limpio, o enterrarlo. Resultado: no se reprodujo — un nodo virgen convergió en ~5 segundos, consistentemente. Lo que fuera que produjo la convergencia de 30 minutos era un artefacto del entorno del PoC, no una propiedad del diseño.

Dos lecciones en una: perseguí tu peor observación antes de construir nada encima del diseño que cuestiona — y acordate de que el entorno de un proof-of-concept es en sí mismo un sospechoso de primera. Podríamos haber shippeado maquinaria de pre-warming (rotar la VIP por todos los candidatos en el setup) para mitigar un problema que no existía.

Modos de falla, dichos en voz alta

Escribir esto explícitamente valió más que cualquier línea de código:

Qué muereQué pasa
Una réplica del controllerLa otra toma el relevo en ≤15 s; el egress ni se entera (el estado ya está aplicado en los nodos)
Las dos réplicasEl label queda congelado donde está; el egress sigue funcionando. Solo un failover de kube-vip durante esa ventana quedaría sin seguir
kube-vip se queda sin líderesLa VIP no vive en ningún lado y el egress se degrada al estado pre-HA. El controller no puede suplirlo — pero salta la alerta

Cuatro alertas de Prometheus cubren el sistema: holder del lease ≠ nodo etiquetado, cero-o-dos nodos etiquetados, controller caído, lease sin dueño. Cada una mapea a una fila de esa tabla.

Resultados

Failover medido end-to-end — desde matar el nodo gateway hasta que el tráfico de egress vuelve a salir con la IP whitelisteada como source, verificado con captura de paquetes en el nuevo gateway:

nodo C → nodo B:  5 s
nodo B → nodo C:  4 s

Para tráfico HTTP de vida corta, las conexiones en vuelo simplemente reintentan y nadie lo nota. El lado VMware necesitó su propia validación (el port group tiene que aceptar MAC changes / forged transmits para que la migración de IP por GARP funcione), que una VIP de control plane con keepalived ya existente convenientemente había probado. Y los dos planos quedan aislados: la peor falla posible de kube-vip devuelve el egress a su viejo comportamiento de IP estática — la VIP del apiserver, propiedad de keepalived en los nodos de control plane, nunca está en el blast radius.

Lecciones

Ciliumkube-vipcontroller-runtimeegressKubernetes