¿Tienes tu aplicación desplegada sobre EKS y parece que te has quedado corto de IPs al asignar los CIDRs en las subredes? Vamos a analizar qué es lo que sucede debajo del servicio gestionado de Kubernetes de AWS.
Partiendo de una arquitectura de referencia como la siguiente:
Imagen: eks_blog-base.png
Típica arquitectura de referencia “tier 3 webapp” con su balanceador en subred pública, control plane de EKS, nodos gestionados por AWS, así como RDS y/o Elasticache todo ello en la subred privada.
Por diseño de AWS y su integración con el CNI, los nodos y los pods de EKS usan IPs de las subredes donde se hayan desplegado, en este caso, las subredes privadas en color azul.
Hace unos años, cuando se montaban los clústers the-hard-way, había que definir unas subredes “virtuales” que iban a usar el apiserver, los pods y los services.
A día de hoy, usando los servicios gestionados de nuestro proveedor cloud, en este caso AWS, él se encarga de esta labor y es totalmente transparente para nosotros una vez le facilitamos las subredes para desplegar nuestro clúster de EKS.
Exponiendo el problema
En un primer vistazo, nuestro diagrama tiene buena pinta y la provisión de IPs para nuestra pequeña aplicación es más que suficiente, al menos a corto plazo. Como hemos hecho bien los deberes, hemos dado un CIDR lo suficientemente grande, en nuestro caso un par de /22 lo que son 2048 IPS para nuestra aplicación que tiene unos pocos de pods y varios nodos para proporcionarnos HA entre zonas.
Imagen: red_eks1.png
¿Qué sucede cuando tengo un pico de carga? Al haber tenido esto en cuenta, hemos definido un HPA para que nuestros pods escalen y en caso de que sea necesario, el cluster-autoescaler levante más nodos para seguir dando servicio a este aumento puntual de peticiones.
Después de este pico de carga, nos damos cuenta que volvemos a tener niveles normales de pods y nodos, pero observamos que nos hemos quedado sin IPs disponibles en las subredes privadas.
Imagen: red_eks2.png
¿Y ahora qué hacemos? ¿Cómo recuperamos estas IPs?
Investigación
Para responder a estas preguntas, debemos entender cómo funciona “por debajo” la provisión de IPs en los ENI de cada nodo y como funciona el AWS VPC CNI.
1.Provisión de ENIs
Según la documentación, AWS VPC CNI es responsable de asignar direcciones IP de la VPC a los nodos de Kubernetes y realizar la configuración de la red necesaria para los pods en cada nodo.
Si nos fijamos en esta parte: “Daemon L-IPAM: responsable de crear interfaces de red y adjuntar las interfaces de red a las instancias de Amazon EC2, asignar direcciones IP secundarias a las interfaces de red y mantener un grupo activo de direcciones IP en cada nodo para asignarlas a los pods de Kubernetes cuando se programan. Cuando el número de pods que se ejecutan en el nodo supera el número de direcciones que se pueden asignar a una interfaz de red única, el complemento comienza a asignar una interfaz de red nueva, siempre y cuando el número máximo de interfaces de red para la instancia no se encuentre conectado.” conseguiremos tener muchas cosas claras.
Cada nodo puede ejecutar un número máximo de pods y esto va ligado al número máximo de ENIs que puede tener.
Si usamos como ejemplo un tipo de instancia m5.xlarge, vemos lo siguiente:
Imagen: eks_m5.png
2.Configuración de AWS VPC CNI
Tratemos de entender ahora cómo podemos configurar este componente de nuestro clúster.
Este componente se puede configurar para elegir cómo se mantienen activas las direcciones IP. El valor predeterminado es WARM_ENI_TARGET=1. Esto significa que EKS intentará mantener un ENI completo disponible en el nodo. Entonces, si un nodo tiene un ENI adjunto y se usa alguna de esas direcciones IP, se adjuntará otro ENI para que se mantenga esta configuración predeterminada.
Esto quiere decir, que cada nodo puede tener 4 ENIs y cada ENI 15 IPs, si hacemos una cuenta rápida, vemos que cada nodo puede reservar hasta 60 IPs, por lo que 60 IPs reservadas más 1 IP del propio nodo, estamos consumiendo hasta 61 IPs por nodo.
Soluciones
Las IPs no se pueden recuperar hasta que no se rotan los nodos, se pueden rotar a mano o ajustar el node_group size para que AWS lo haga por nosotros.
Tenemos dos alternativas para solucionarlo:
- Cambiar la variable de entorno que controla esto a: WARM_IP_TARGET=2 del daemonset aws-node de modo que cada nodo sólo reservará 2 IPs. De esta forma, la cuenta anterior sería: 4 ENIs y cada ENI 2 IPs, 8 IPs, más 1 IP del nodo, estaríamos consumiendo 9 IPs respecto a las 61 IPs con la configuración por defecto.
- Como buena práctica, si queremos mantener las WARM IPs puesto que es una buena idea y permite que tu aplicación escale más rápido, se puede ampliar el CIDR de la VPC con un nuevo CIDR sólo para los pods, de tal forma que los pods se desplieguen ahí y no usen nuestras valiosas IPs del CIDR principal.
a. Para ello, hay que adjuntar un nuevo CIDR a nuestra VPC y crear las subredes correspondientes que usaremos más adelante.
Imagen: red_eks3.png
b. Crear la variable AWS_VPC_K8S_CNI_CUSTOM_NETWORK_CFG con valor true en el daemonset de aws-node.
c. Desplegar una definición crd.k8s.amazonaws.com/v1alpha1 del tipo ENIConfig en nuestro clúster, del estilo:
Imagen: eniconfig.png
d. Rotar los nodos para que usen la nueva configuración.
Nuestra arquitectura pasaría a estar definida de la siguiente manera:
Imagen: eks_blog-extra.png
Si te has encontrado con estos problemas y necesitas que te ayudemos, nuestro equipo de especialistas en Cloud estará encantado de trabajar tu caso concreto.