DEV Community

Rubén Rodríguez for AWS Español

Posted on

Optimiza tu cluster EKS con Karpenter

¡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:

Karpenter Example

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"
}


Enter fullscreen mode Exit fullscreen mode

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.

cluster EKS

Actualizamos kubeconfig para poder interaccionar con el cluster:



aws eks --region eu-west-1 update-kubeconfig --name awtwins-cluster


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode


kubectl get ec2nodeclass
NAME      READY   AGE
default   True    7m4s


Enter fullscreen mode Exit fullscreen mode


kubectl get deployments
NAME      READY   UP-TO-DATE   AVAILABLE   AGE
inflate   0/0     0            0           9m7s


Enter fullscreen mode Exit fullscreen mode

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.

Link NodePools

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.

Link NodeClasses

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


Enter fullscreen mode Exit fullscreen mode

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}


Enter fullscreen mode Exit fullscreen mode

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


Enter fullscreen mode Exit fullscreen mode

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:

EC2 Running

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


Enter fullscreen mode Exit fullscreen mode

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.

EC2 Shutdown

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)