Visión general
La implementación de servicios de Integración Continua / Entrega Continua en nuestro proceso de desarrollo se ha convertido en una expectativa muy típica al impulsar nuestra línea de producción. Ahora es muy común ver que se requieren múltiples entornos a lo largo de las líneas de producción.
Jenkins es bien conocida como una herramienta de orquestación para CI / CD que se puede ampliar mediante el uso de máquinas de nodos y una amplia variedad de complementos. Esto nos brinda algunas características excelentes que ayudan cuando se trata de admitir múltiples trabajos. Sin embargo, equilibrar adecuadamente la carga de trabajo entre los nodos se vuelve clave para aprovechar al máximo este enfoque.
Este artículo le mostrará cómo implementar un enfoque de equilibrio de carga en una infraestructura de Jenkins maestro / esclavo.
Definiciones
Establezcamos un terreno de conocimiento común con el que trabajar.
Maestro y metadatos.
Un Jenkins Master es la máquina donde se ejecuta nuestro servicio Jenkins. Este servidor maestro contendrá todos los metadatos relacionados con el trabajo, como el historial de compilación, la descripción del trabajo y los pasos para realizar la tarea designada.
La información sobre la tarea en sí, como los archivos extraídos del repositorio y los binarios compilados, se almacena en el espacio de trabajo del proyecto (es decir, JENKINS_HOME / workspace / myjenkinsproject), que se encuentra dentro de la máquina a cargo de la ejecución del proyecto.
Nodos de ejecución.
Los nodos de ejecución (esclavos) pueden darle a nuestro entorno CI / CD un conjunto de sistemas operativos para ejecutar lo que nos plazca. Un maestro de Jenkins puede tener tantos nodos de ejecución como necesite. Existen múltiples métodos para conectarlos: SSH, agentes Java o Gestión Remota. Las máquinas con prácticamente cualquier sistema operativo se pueden utilizar como esclavas.
Tanto los maestros como los nodos pueden ejecutar trabajos (proyectos de canalización o estilo libre), que es un conjunto finito de tareas, como extraer un repositorio, crear un archivo, construir un proyecto, etc. Los nodos de ejecución también proporcionarán el espacio de trabajo para los trabajos. corremos.
Ejecutores.
Los trabajos se crean utilizando ejecutores. Un ejecutor es básicamente un proceso y tanto el maestro como los esclavos pueden tener cualquier número de ejecutores.
Si decidimos darle a nuestro maestro dos ejecutores, eso significa que Jenkins podrá crear dos procesos diferentes en cualquier momento para construir dos tareas diferentes. Podríamos terminar ejecutando dos tareas del mismo trabajo, en los dos ejecutores diferentes en la misma máquina.
Construyendo nuestro escenario.
Para comenzar a demostrar los beneficios de una configuración maestro / esclavo, asumiremos que tenemos tres nodos Centos7 conectados a un maestro Jenkins.
Puede identificar sus nodos según las etiquetas asignadas. Definir un esquema de etiquetas es importante, ya que esto ayudará a Jenkins a delegar la ejecución del trabajo de manera eficiente en nuestra plataforma. Aspectos como el sistema operativo, la ubicación física o el propósito se pueden utilizar para definir nuestro esquema de etiquetas.
En este caso, nuestros nodos se identificarán en función del tipo de tecnología que admiten, así:
Desde la perspectiva de la etiqueta, cuando un trabajo o una tarea solicita explícitamente un nodo capaz de crear y ejecutar una aplicación "NodeJS", Jenkins debe asignar la ejecución a un nodo identificado con una etiqueta "Nodo JS" (el nodo 1 es el único que cumple este requisito).
Por otro lado, si quisiéramos ejecutar una tarea con la etiqueta "Linux", Jenkins seleccionará cualquiera de los 3 nodos disponibles, dependiendo del número de ejecutores inactivos en cada nodo.
Configurando nuestros nodos.
Una vez que hayamos definido nuestro esquema de etiquetas, podemos comenzar a agregar nodos de ejecución a nuestro maestro de Jenkins. Supongamos que tenemos tres máquinas Centos7 inactivas en funcionamiento en nuestra red con Java y Git instalados y nos gustaría usarlas como nodos de ejecución.
En el lado izquierdo de la pantalla del panel de Jenkins, podemos ver nuestros nodos y agregar otros nuevos. Haga clic en Build Executor Status -> New Node.
Este paso crea una nueva instancia de nodo en nuestro servidor Jenkins Master (un marcador de posición para recibir la conexión de nodo real). Necesitaremos agregar algunos valores de parámetros:
-
Defina un nombre para su nodo (algo simple pero descriptivo).
-
Seleccione "Agente permanente".
-
Defina el número de ejecutores que le gustaría en cada nodo.
-
Agregue las etiquetas de acuerdo con el esquema que definimos anteriormente.
-
En Uso, seleccione Solo crear trabajos que soliciten explícitamente un nodo con la etiqueta de este nodo.
En sistemas Unix (Ubuntu, Centos y MacOS entre otros) el método de conexión estándar es SSH. Querremos conectarnos a través de SSH a nuestros nodos de ejecución con un usuario que tenga privilegios de ejecución en la carpeta del espacio de trabajo que estamos usando para Jenkins (es decir, / opt / jenkins) y evitar el uso de root, ya que no queremos exponer los permisos de root a través de Trabajos de Jenkins en nuestras máquinas.
Repita este proceso para las otras máquinas que utilizará como nodos de ejecución. Después de eso, su lista de nodos debería verse así:
Prueba de rendimiento sin equilibrio de carga.
Para probar el manejo de solicitudes de rendimiento en Jenkins, usaremos este script de canalización declarativo, simulando una extracción simple, con una etapa de compilación (solo un comando de suspensión) y limpieza del espacio de trabajo para evitar quedarse sin espacio en disco.
pipeline{
agent { label 'linux'}
stages {
stage("Pull Repository"){
steps{
git credentialsId: '78307373-e926-xxxx-xxxx-2b065a6a52d7',
url: 'http://git.url/user.name/project.git'
}
}
stage("Wait Stage"){
steps{
sh """
#!/bin/bash
sleep 60;
"""
}
}
stage("Clean directory"){`
steps{
deleteDir()
}
}
}
}
Para comprender el rendimiento de la ejecución, ejecutemos dos escenarios:
-
Ejecute el trabajo en un solo nodo con 10 ejecutores disponibles, activando 10 compilaciones al mismo tiempo (10 ejecutores, 1 nodo).
-
Elimine 5 ejecutores en el nodo original, agregue un nodo más con 5 ejecutores (2 nodos, 5 ejecutores cada uno) y active las mismas 10 compilaciones al mismo tiempo.
Sus resultados probablemente serán similares a los descritos en la tabla:
El uso de 2 nodos en lugar de uno redujo el tiempo medio de ejecución del trabajo. ¿Qué pasa si agregamos un enfoque de equilibrio de carga en nuestro proceso?
Expandiendo la solución con Load Balancing.
A medida que crezcan nuestras solicitudes de ejecutores, tendremos que definir un criterio para asignar esclavos y considerar el rendimiento mientras asignamos recursos en nuestra pila limitada. Usaremos este complemento https://plugins.jenkins.io/scoring-load-balancer para ese propósito. Como su nombre lo indica, puntuará cada nodo coincidente en función de circunstancias definidas, como tener un ejecutor inactivo o si el trabajo falló anteriormente en un nodo determinado.
Para agregar este complemento, vaya a Administrar Jenkins -> Administrar complemento, seleccione la pestaña "Disponible", busque el complemento "Scoring Load Balancer", selecciónelo e instálelo.
Agregar registro al equilibrio de carga.
Una vez instalado, agregaremos un registrador a nuestros jenkins para verificar el algoritmo de puntuación en acción.
Para agregar el registrador:
Vaya a Administrar Jenkins -> Registro del sistema.
Luego vaya a "Nueva grabadora de registros".
Una vez allí, nombre el registro como desee.
La parte importante es establecer el nivel de registro en "INFO" y establecer el valor del registrador como "jp.ikedam.jenkins.plugins.scoringloadbalancer.ScoringLoadBalancer".
Esto nos dará información relacionada con las puntuaciones de los nodos cada vez que disparamos una compilación, será útil para depurar y comprender la asignación de nodos.
Agregar criterios al equilibrio de carga.
Vaya desde Manage Jenkins -> Configure System y desplácese hasta Scoring Load Balancer para establecer nuestras reglas de puntuación.
De forma predeterminada, el complemento ofrece 2 tipos de puntuación
Puntuación basada en nodos: puntúa cada ejecutor inactivo y cada ejecutor ocupado, multiplicado por un factor.
Puntuación basada en el trabajo: use el historial de compilaciones en cada nodo para definir dónde se debe asignar el trabajo. Esto es útil si necesitamos establecer algún tipo de afinidad para ciertos nodos.
Aquí nos gustaría penalizar a los ejecutores ocupados. Como se vio anteriormente, cada ejecutor ocupado agrega tiempo de compilación a cada ejecución simultánea.
De forma predeterminada, Jenkins no asigna compilaciones en función de ningún criterio, por lo que queremos que este comportamiento sea predecible y registrable. Para este ejemplo, usaremos el primer tipo de puntuación, pero ambos se pueden usar juntos.
Probando la solución:
La primera imagen de la izquierda muestra cinco compilaciones simultáneas sin equilibrador de carga al principio, lo que, en este caso, dejó un nodo completamente inactivo y asignó tres ejecuciones a un nodo.
En la imagen de la derecha, Jenkins logró asignar nuestras solicitudes a ambos nodos por igual. Jenkins podría haber llenado todos los ejecutores en un nodo, dejando dos nodos inactivos, que es exactamente la razón por la que queremos controlar la asignación de nodos.
Ahora, si vamos a Manage Jenkins -> System Logs -> Node Score Logger, podemos ver la puntuación de cada ejecución.
Conclusiones.
En entornos donde se aplica un esquema maestro / esclavo, lo que queremos es tener un equilibrador de carga para permitir un uso adecuado y predecible de los recursos junto con un buen rendimiento.
Definir un patrón de etiqueta para sus nodos, incluida la información mencionada anteriormente, hará un uso eficiente de su plataforma CI / CD.