¡Después de unas merecidas vacaciones, volvemos a la carga!😁
En nuestra última publicación, hablamos de Backstage, la plataforma open-source de Spotify para gestionar infraestructura en AWS con Terraform.
Esta vez, vamos a hablar sobre uno de los temas que está siendo ‘trending topic’ en la gestión de escalado de Kubernetes: Karpenter. Durante este mes de Agosto se ha lanzado su primera versión estable y sin duda merecía un post relacionado. 🎉
Una de las grandes ventajas de Kubernetes es su capacidad de escalar según la demanda. Pero gestionar este escalado de los nodos puede ser complicado. Necesitas asegurarte de que haya suficientes nodos para desplegar tus aplicaciones, pero tampoco quieres pagar por más recursos de los necesarios. Aquí es donde entra Karpenter.🧙♂️
Karpenter ofrece un escalado rápido y flexible, seleccionando automáticamente las instancias más optimizadas y rentables en función de la demanda. Además, es una solución relativamente fácil de implementar... ¡suena difícil de creer, ¿verdad?!😱
Para desplegar esta solución, hemos utilizado el siguiente repositorio:
El código utilizado automatiza el despliegue de un clúster de EKS con 2 nodos m5.large, las VPC necesarias, y los addons coredns, eks-pod-identity-agent, kube-proxy, vpc-cni, además de la solución de Karpenter, incluyendo el NodePool y el EC2NodeClass necesarios para su funcionamiento. Adicionalmente, también despliega un Deployment que utilizaremos para pruebas.
Para su uso, debemos modificar las llamadas a los módulos para que apunten directamente al repositorio correspondiente.
Para ello copiaremos el fichero main.tf y realizaremos las siguientes modificaciones:
module "eks" {
source = "github.com/terraform-aws-modules/terraform-aws-eks"
}
module "karpenter" {
source = "github.com/terraform-aws-modules/terraform-aws-eks/modules/karpenter"
}
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "~> 5.0"
}
Una vez que tenemos el Terraform preparado, desplegamos la solución. El proceso tarda unos 15 minutos aproximadamente en estar completo y operativo.
Como podemos ver, ya tenemos nuestro clúster desplegado y configurado para utilizar Karpenter.
Actualizamos kubeconfig para poder interaccionar con el cluster:
aws eks --region eu-west-1 update-kubeconfig --name awtwins-cluster
Verificamos que tenemos acceso al clúster y que los pods se han desplegado correctamente, como hemos comentado con anterioridad el propio código de Terraform ya despliega los addons necesarios por lo que deberíamos tener el siguiente escenario:
kubectl get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system aws-node-8sd7q 2/2 Running 0 2m7s
kube-system aws-node-vjj8q 2/2 Running 0 2m11s
kube-system coredns-67d68bcfdc-hww5k 1/1 Running 0 6m39s
kube-system coredns-67d68bcfdc-vv5h6 1/1 Running 0 6m39s
kube-system eks-pod-identity-agent-5xzdr 1/1 Running 0 2m12s
kube-system eks-pod-identity-agent-hdg4k 1/1 Running 0 2m12s
kube-system karpenter-7868758ccf-7npdb 1/1 Running 0 3m56s
kube-system karpenter-7868758ccf-t564r 1/1 Running 0 3m56s
kube-system kube-proxy-9tj27 1/1 Running 0 3m2s
kube-system kube-proxy-ppkr5 1/1 Running 0 3m5s
También verificamos que se han creado tanto el Nodepool como el Nodeclass y el Deployment de prueba correspondiente
kubectl get nodepool
NAME NODECLASS NODES READY AGE
default default 0 True 6m34s
kubectl get ec2nodeclass
NAME READY AGE
default True 7m4s
kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
inflate 0/0 0 0 9m7s
Finalmente, habilitaremos nuestra cuenta de AWS para la creación de instancias spot, ejecutando el siguiente comando desde la CLI:
aws iam create-service-linked-role --aws-service-name spot.amazonaws.com || true
¡Llegados a este punto, ya tenemos un clúster completamente operativo! Ahora, podemos jugar con Karpenter. Antes de empezar, vamos a intentar dar algunas pinceladas sobre sus componentes clave:
NodePools
Los NodePools permiten configurar:
- Tipo de instancia: Puedes elegir entre tipos como c, m o r (según tus necesidades de cómputo, memoria, etc.).
- Arquitectura: Por ejemplo, amd64.
- Tipo de instancia: Selecciona entre instancias on-demand o spot.
- Sistema operativo: Configura el OS que mejor se adapte a tus aplicaciones.
- Tiempo de vida de los nodos o consolidación de los mismos.
Estas configuraciones permiten ajustar los recursos de manera precisa, optimizando el uso de la infraestructura.
Importante: Para que Karpenter funcione, debes tener al menos un NodePool creado.
NodeClasses
Permiten definir clases específicas de nodos dentro de un clúster de Kubernetes en AWS. Aquí puedes configurar aspectos como:
- Capacidad de almacenamiento.
- Opciones de red y configuraciones de seguridad.
Esto te permite ajustar los nodos a las necesidades de cada aplicación, mejorando tanto el rendimiento como el coste de la infraestructura.
Gracias a estas configuraciones, Karpenter puede gestionar los nodos de forma automática, ajustando el tamaño y las características de los nodos en tiempo real según la demanda. Esto asegura que siempre tengas los recursos óptimos para tus aplicaciones sin desperdiciar capacidad.
Al jugar con las configuraciones de NodePools y NodeClasses, puedes personalizar el comportamiento de Karpenter y adaptarlo a las necesidades específicas de tu clúster. Es fundamental ir probando y ajustando según tu caso de uso para encontrar la máxima eficiencia y reducir costes.
Dicho esto… 🕹️¡vamos a jugar!🕹️
Como hemos podido observar anteriormente el código de Terraform utilizado nos ha desplegado automáticamente los siguientes recursos de Karpenter:
- NodePool
- NodeClass
- Deployment de pruebas
Si analizamos los ficheros de configuración observaremos lo siguiente:
NodePool
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: default
spec:
template:
spec:
nodeClassRef:
name: default
requirements:
- key: "karpenter.k8s.aws/instance-category"
operator: In
values: ["c", "m", "r"]
- key: "karpenter.k8s.aws/instance-cpu"
operator: In
values: ["4", "8", "16", "32"]
- key: "karpenter.k8s.aws/instance-hypervisor"
operator: In
values: ["nitro"]
- key: "karpenter.k8s.aws/instance-generation"
operator: Gt
values: ["2"]
limits:
cpu: 1000
disruption:
consolidationPolicy: WhenEmpty
consolidateAfter: 30s
EC2NodeClass
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
metadata:
name: default
spec:
amiFamily: AL2023
role: ${module.karpenter.node_iam_role_name}
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: ${module.eks.cluster_name}
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery: ${module.eks.cluster_name}
tags:
karpenter.sh/discovery: ${module.eks.cluster_name}
Karpenter desplegará los nodos con las siguientes caracteristicas:
- Instancias de las categorías c, m o r (optimizadas para cómputo, uso general y memoria).
- Instancias con 4, 8, 16 o 32 vCPUs.
- Instancias que utilicen el hipervisor Nitro.
- Instancias de generaciones superiores a la segunda.
- El escalado de nodos no superará un total de 1000 vCPUs.
- Las instancias se eliminarán automáticamente cuando no tengan cargas de trabajo durante más de 30 segundos.
- Utilizará una Amazon Machine Image (AMI) de la familia AL2023.
- Se asignará el rol IAM definido por ${module.karpenter.node_iam_role_name}.
- Los nodos se desplegarán en subnets que tengan la etiqueta karpenter.sh/discovery: ${module.eks.cluster_name}.
- Los nodos estarán asociados a grupos de seguridad con la etiqueta karpenter.sh/discovery: ${module.eks.cluster_name}.
- Los nodos estarán etiquetados con karpenter.sh/discovery: ${module.eks.cluster_name} para facilitar su identificación.
Para verificar su funcionamiento vamos a escalar el numero de replicas del deployment generado (mediante Terraform) lo que forzará el despliegue de nodos puesto que el clúster requerirá de nuevos nodos para asignar la carga correspondiente.
Para ello, ejecutamos el siguiente comando:
kubectl scale deployment inflate --replicas=15
Como podremos observar disponemos de 15 pods en estado "pending"
NAMESPACE NAME READY STATUS RESTARTS AGE
default inflate-66fb68585c-4nzbw 0/1 Pending 0 11s
default inflate-66fb68585c-6hdft 0/1 Pending 0 11s
default inflate-66fb68585c-7h2vr 0/1 Pending 0 11s
default inflate-66fb68585c-bh66g 0/1 Pending 0 11s
default inflate-66fb68585c-c4w9r 0/1 Pending 0 11s
default inflate-66fb68585c-d2n7s 0/1 Pending 0 11s
default inflate-66fb68585c-hpzdw 0/1 Pending 0 11s
default inflate-66fb68585c-ljqgf 0/1 Pending 0 11s
default inflate-66fb68585c-lkp4t 0/1 Pending 0 11s
default inflate-66fb68585c-nbfcd 0/1 Pending 0 11s
default inflate-66fb68585c-pkwvb 0/1 Pending 0 11s
default inflate-66fb68585c-qx27z 0/1 Pending 0 11s
default inflate-66fb68585c-sxzs9 0/1 Pending 0 11s
default inflate-66fb68585c-tqwkd 0/1 Pending 0 11s
default inflate-66fb68585c-vw4dw 0/1 Pending 0 11s
kube-system aws-node-jb5sb 2/2 Running 0 11m
kube-system aws-node-wfj2q 2/2 Running 0 11m
kube-system coredns-67d68bcfdc-m5dg2 1/1 Running 0 15m
kube-system coredns-67d68bcfdc-tqj44 1/1 Running 0 15m
kube-system eks-pod-identity-agent-7hsb7 1/1 Running 0 11m
kube-system eks-pod-identity-agent-vzkls 1/1 Running 0 11m
kube-system karpenter-7ffd65448-nmld6 1/1 Running 0 13m
kube-system karpenter-7ffd65448-t94wh 1/1 Running 0 13m
kube-system kube-proxy-twfgp 1/1 Running 0 12m
kube-system kube-proxy-vpk6s 1/1 Running 0 12m
En este punto Karpenter estará desplegando nuevos nodos con los requisitos facilitados en los ficheros de configuración para poder cumplir con las cargas necesarias, accedemos a la consola y podemos ver que ya disponemos de una nueva instancia:
Si vemos el estado de los pods veremos como han sido desplegados y asignados correctamente y la instancia desplegada cumple con los requisitos facilitados en los ficheros de configuración.
NAMESPACE NAME READY STATUS RESTARTS AGE
default inflate-66fb68585c-4nzbw 1/1 Running 0 4m55s
default inflate-66fb68585c-6hdft 1/1 Running 0 4m55s
default inflate-66fb68585c-7h2vr 1/1 Running 0 4m55s
default inflate-66fb68585c-bh66g 1/1 Running 0 4m55s
default inflate-66fb68585c-c4w9r 1/1 Running 0 4m55s
default inflate-66fb68585c-d2n7s 1/1 Running 0 4m55s
default inflate-66fb68585c-hpzdw 1/1 Running 0 4m55s
default inflate-66fb68585c-ljqgf 1/1 Running 0 4m55s
default inflate-66fb68585c-lkp4t 1/1 Running 0 4m55s
default inflate-66fb68585c-nbfcd 1/1 Running 0 4m55s
default inflate-66fb68585c-pkwvb 1/1 Running 0 4m55s
default inflate-66fb68585c-qx27z 1/1 Running 0 4m55s
default inflate-66fb68585c-sxzs9 1/1 Running 0 4m55s
default inflate-66fb68585c-tqwkd 1/1 Running 0 4m55s
default inflate-66fb68585c-vw4dw 1/1 Running 0 4m55s
kube-system aws-node-gdx8l 2/2 Running 0 4m28s
kube-system aws-node-jb5sb 2/2 Running 0 16m
kube-system aws-node-wfj2q 2/2 Running 0 16m
kube-system coredns-67d68bcfdc-m5dg2 1/1 Running 0 20m
kube-system coredns-67d68bcfdc-tqj44 1/1 Running 0 20m
kube-system eks-pod-identity-agent-7hsb7 1/1 Running 0 16m
kube-system eks-pod-identity-agent-vzkls 1/1 Running 0 16m
kube-system eks-pod-identity-agent-w9tcq 1/1 Running 0 4m27s
kube-system karpenter-7ffd65448-nmld6 1/1 Running 0 18m
kube-system karpenter-7ffd65448-t94wh 1/1 Running 0 18m
kube-system kube-proxy-r65c2 1/1 Running 0 4m28s
kube-system kube-proxy-twfgp 1/1 Running 0 17m
kube-system kube-proxy-vpk6s 1/1 Running 0 17m
Si procedemos a eliminar las replicas desplegadas podremos ver que los nodos se eliminan bajo las condiciones facilitadas.
Para ello ejecutamos el siguiente comando:
kubectl scale deployment inflate --replicas=0
Esperamos 30 segundos y podremos ver como el nodo se elimina sin problemas.
Una vez visto el funcionamiento, podemos empezar a "jugar" con los archivos de configuración según las necesidades específicas de nuestro clúster. Al ajustar los parámetros de los NodePools y NodeClasses, es posible controlar aspectos clave como el tipo de instancias, la cantidad de CPU o memoria, el tiempo de vida de los nodos, o el uso de instancias spot para reducir costes.
En nuestra opinión, Karpenter es una de esas soluciones que no pueden faltar en tus proyectos si buscas optimizar la gestión de tus clústeres en Kubernetes. Su capacidad para automatizar el escalado y aprovisionamiento de nodos hace que la complejidad operativa y la sobrecarga manual no sean uno de nuestros problemas.
🚨Recomendación🚨
Antes de implementar Karpenter en tu clúster de Kubernetes, se recomienda configurar algunos elementos que optimizan el funcionamiento y aseguran una eficiente gestión de recursos como pueden ser:
Optimización de la infraestructura: Se recomienda configurar PodDisruptionBudgets (PDB) para garantizar la disponibilidad de las aplicaciones durante mantenimientos o interrupciones.
Alta disponibilidad: Tener al menos 2 réplicas por aplicación asegura redundancia en caso de fallos de nodos.
Escalado automático: Implementar el Vertical Pod Autoscaler (VPA) para ajustar recursos automáticamente y el Horizontal Pod Autoscaler (HPA) para gestionar el número de réplicas según las métricas.
Visualización del clúster: Herramientas como Kubernetes Dashboard, KubeOpsView y otras facilitan la administración y ofrecen una visión mas "amigable" del clúster.
👀Enlaces interesantes👀
Documentación Oficial de Karpenter
Post Community AWS - Christian Melendez (AWS)
Video Explicativo Karpenter
Workshop Karpenter (AWS)
¡Y hasta aquí llegamos por hoy! Como siempre, estamos deseando leer tus comentarios y preguntas. Nos ayudan a mejorar y seguir creando contenido interesante.
¡Nos vemos en el próximo post! 👌
Top comments (0)