Nodos dinámicos en Jenkins vía AWS Spot Fleet

Nodos dinámicos en Jenkins vía AWS Spot Fleet

Provisionar nodos slave de jenkins dinámicamente gracias a aws spot fleet.

Hace unas semanas, un cliente contactó con el equipo de Geko Cloud. Su petición era simple: ¿Qué podemos hacer para optimizar aún más nuestro proceso automatizado de CI/CD en Jenkins?

Tras estudiar cuidadosamente las necesidades y la infraestructura actual del cliente, nos decidimos por cambiar de paradigma en cuanto a ejecución de tareas se refiere. Hasta ahora Jenkins ejecutaba las tareas en su nodo principal (La máquina en la que se está ejecutando Jenkins), al cambiar este paradigma a uno basado en el aprovisionamiento dinámico de nodos subordinados ganaríamos los siguientes beneficios:

  • Ahorro de costes – Al mover todo el esfuerzo computacional de la compilación fuera del nodo principal. Este puede estar alojado en una maquina de menor rendimiento, y por lo tanto menor coste. Los nodos subordinados solo estarían en funcionamiento durante la ejecución de una tarea, ahorrando también dinero mientras no se ejecuta ninguna tarea.
  • Seguridad – Al no ejecutar ningún tipo de código en el nodo principal, aseguramos su integridad aunque el código haya sido comprometido, y como este solo se ejecuta en una instancia efímera el atacante estará aislado.
  • Rendimiento – El nodo subordinado existe solamente para ejecutar la tarea y puede dedicar todos los recursos del nodo para ella.

 

Requisitos

Para este cambio, realizaremos las siguientes tareas:

  • Creación de Spot Fleet en AWS
  • Instalación y configuración del plugin de Jenkins
  • Adaptación de Jenkinsfile

Para esta publicación haremos las siguientes suposiciones:

  • Contamos con una cuenta de AWS configurada
  • Alojamos Jenkins en un servidor EC2
  • Contamos con conocimiento sobre algunos conceptos base de Jenkins y AWS

Dicho esto, pongámonos manos a la obra.

 

En este artículo sobre «Nodos dinámicos en Jenkins vía AWS Spot Fleet» desarrollaremos todos los pasos necesarios para poder obtener un buen resultado.

Creación de launch template y spot fleet

Un Spot Fleet de AWS no es más que un AutoScaling Group de máquinas EC2 pero con instancias de tipo spot – excedente de Amazon que nadie está usando y que AWS deja a un precio económico. El primer paso consistirá en la creación de el Launch Template que utilizara el Spot Fleet:

 

Los puntos importantes a rellenar serán:

  1. Nombre y descripción
  2. AMI a usar – Por ejemplo Ubuntu 22
  3. Tipo de instancia a usar – Elegir una adecuada al desempeño
  4. Key Pair – Lo necesitaremos introducir mas adelante en Jenkins
  5. Network – Utilizar la misma Subnet que Jenkins si es posible
  6. Security Group – Para que se establezca la conexión entre nodos, es importante que demos acceso al puerto 22 desde el nodo principal. La manera mas fácil es crear una regla que permita el acceso o bien desde la IP del nodo principal (Si la tenemos estática) o desde su Security Group
  7. Storage – Asignar el almacenamiento que vayamos a necesitar
  8. User data – Para que el worker de Jenkins se ejecute en el nodo subordinado, tendremos que instalar JRE, nosotros además instalamos Docker como requisito:

 

#!/bin/bash
# Install docker
apt-get update
apt-get install -y apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"
apt-get update
apt-get install -y docker-ce
usermod -aG docker ubuntu

# Install docker-compose
curl -L https://github.com/docker/compose/releases/download/1.21.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

#Install JRE
sudo apt install -y default-jre

 

Una vez creado tendremos un Launch template parecido a este:

 

 

Ahora es momento de crear el Spot fleet como tal:

 

 

En este caso, los puntos a rellenar son:

  1. Launch parameters – Seleccionar nuestro Launch Template creado arriba
  2. Target Capacity – Asignar total a 0 por ahora, ya que lo controlará el plugin de Jenkins. Habilitar Maintain Target Capacity
  3. Network – Asignar la VPC en la que está alojado el nodo principal
  4. Instance type requirements – Elegir los tipos de instancia deseados en Manually select instance types, una mayor cantidad da una mejor pool entre la que iniciar instancias Spot
  5. Allocation strategy – Capacity Optimized

Y listo, ya tenemos creado nuestro Spot Fleet.

 

Instalacion y configuracion del plugin de Jenkins

Antes de instalar el plugin, tendremos que crear y asignar un nuevo rol de IAM a la instancia que aloja el nodo principal para que sea capaz de administrar nuestro Spot fleet:

 

{
   "Version":"2012-10-17",
   "Statement":[
      {
         "Effect":"Allow",
         "Action":[
            "ec2:DescribeSpotFleetInstances",
            "ec2:ModifySpotFleetRequest",
            "ec2:CreateTags",
            "ec2:DescribeRegions",
            "ec2:DescribeInstances",
            "ec2:TerminateInstances",
            "ec2:DescribeInstanceStatus",
            "ec2:DescribeSpotFleetRequests"
         ],
         "Resource":"*"
      },
      {
         "Effect":"Allow",
         "Action":[
            "autoscaling:DescribeAutoScalingGroups",
            "autoscaling:UpdateAutoScalingGroup"
         ],
         "Resource":"*"
      },
      {
         "Effect":"Allow",
         "Action":[
            "iam:ListInstanceProfiles",
            "iam:ListRoles",
            "iam:PassRole"
         ],
         "Resource":"*"
      }
   ]
}

 

Una vez hecho esto, instalaremos el plugin ec2-fleet-plugin y nos dirigiremos a configurar desde Administrar Jenkins > Administrar nodos > Configure Clouds > Añadir nueva nube > Amazon EC2 Fleet.

 

 

  1. Asignamos un nombre
  2. AWS Credentials – Lo dejamos en blanco, ya que nuestra maquina ya tiene el rol descrito más arriba
  3. Region – Seleccionamos la región en la que alojamos nuestros servidores.
  4. EC2 Fleet – Solo aparecerá cuando asignemos la región y el rol esté asignado a la máquina correctamente. Una vez rellenado, seleccionamos nuestro Spot fleet
  5. Subimos la Private key generada en el Launch template como SSH Username with private key
  6. Private IP – Seleccionar si queremos que el nodo maestro se conecte al subordinado usando la IP privada en vez de la publica
  7. Always Reconnect – Recomendamos no habilitarlo, ya que las instancias Spot son efímeras por naturaleza
  8. Restrict Usage – Restringirá el uso de este Spot Fleet solo a tareas que indiquen la etiqueta asignada, indistintamente de la configuración global de Jenkins
  9. Label – La etiqueta que identifica esta nube de nodos, útil para asignar qué tareas se ejecutarán en el Fleet
  10. Number of executors – La cantidad de trabajos simultáneos que podrá albergar cada instancia del Spot Fleet
  11. Max Idle Minutes Before Scaledown – La cantidad de minutos que Jenkins mantendrá una instancia activa sin tareas pendientes. Si se deja a 0 Jenkins jamás apagará instancias, aunque estén sin uso
  12. Minimum/Maximum cluster Size – El tamaño mínimo y máximo de instancias que queremos que tenga el Spot fleet. Lo ideal es dejar el mínimo a 0 para abaratar costes

Una vez guardada la configuración, tendremos una nueva sección en el dashboard de Jenkins, que nos indica el estado actual de nuestro Spot Fleet.

 

 

Adaptación de Jenkinsfile

Ahora que tenemos nuestro Spot fleet configurado, es hora de realizar unos pequeños cambios para que Jenkins use este nuevo tipo de nodo a su disposición. Si queremos que se use de manera sistemática a nivel global, podemos deshabilitar la ejecución de tareas en el nodo principal (el único otro nodo en nuestro caso) desde la pantalla de configuración de nodos.

 

 

Si por el contrario, queremos que ciertas tareas sean las que se ejecuten en los nodos subordinados, tendremos que especificarlos a nivel de JenkinsFile, a continuación un ejemplo de cómo hacerlo:

 

#!groovy
pipeline {
//Especificamos agente a nivel de Pipeline
  agent {
    label 'ec2-fleet'
  }

  stages {
        stage('Test') {
          agent {
            dockerfile {
              // Si usamos un agente en concreto, tendremos que volver a especificar la etiqueta
              label 'ec2-fleet'
              filename 'Dockerfile'
              dir 'docker/images/tests'
              args '-u root'
            }
          }
          steps {
            sh 'Hello World'
          }
        }
  }
}

Con este último paso, estamos listos para ejecutar cualquier tarea en nodos provisionados dinámicamente dentro de instancias Spot Fleet.

 

Desde Geko Consultoría Cloud, esperamos que este post te hayan gustado y sobre todo que te resulten útiles, nada nos alegraría más.
Te invitamos a que si necesitas información sobre el mundo Cloud y DevOps, nos contactes y sigas revisando nuestro blog para encontrar otras publicaciones útiles.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *