Con anterioridad estudiamos las formas de escalar la infraestructura de jenkins para un sistema de integración continua distribuido. En este post veremos mas a detalle como configurar los agentes de compilación que hacen posible la integración continua distribuida.
Para que un agente se comunique con el master se necesita establecer comunicación bidireccional y existen un numero de opciones sobre como puede ser iniciada:
- SSH: Un jenkins master se conecta a un slave usando un protocolo SSH estándar. Jenkins tiene un cliente SSH incluido, entonces el único requerimiento es que el servidor SSHD este configurado en los slaves. Este es el método mas conveniente porque usa mecanismos estándar de UNIX
- Java Web Start: Una aplicación java es iniciada en cada agent y la conexión es establecida entre un slave y la aplicación master. Este método es usado a menudo si los agents están protegidos por un firewall y master no puede iniciar la comunicación
- Windows Service: Un master registra un agente en una maquina remota como un servicio de windows. Este método no es recomendable porque la configuración es complicada y tiene algunas limitaciones en el uso de la interfaz gráfica.
Configurando los agentes
A un nivel bajo, los agents se comunican con el master usando uno de los protocolos descritos con anterioridad. De cualquier modo, en un nivel mas alto, podemos adjuntar slaves a un master de varias maneras. Las diferencias consisten en dos aspectos:
- Estático vs Dinámico: La solución mas simple es añadir slaves permanentemente en el master. El único problema con tal solución es que si se necesita hacer un cambio manualmente cuando necesitamos mas (0 menos) slaves. Una mejor solución es aprovisionar slaves dinámicamente cuando se necesite.
- Especifico vs Propósito General: Los agents pueden ser específicos; por ejemplo diferentes agents pueden correr proyectos en java 8 y otros distintos corren proyectos en java 7. Cuando se trata de agents de propósito general, estos actuan como hosts de Docker y compilan basándose en un flujo dentro de un contenedor docker
Estas diferencias resultaron en 4 diferentes estrategias de como los agents pueden ser configurados:
- Agentes permanentes
- Agentes Docker Permanentes
- Agentes Jenkins Swarm
- Agentes Docker dinámicamente aprovisionados
Examinemos cada una de las soluciones:
Agents Permanentes
Comencemos por la opción mas simple que es agregar nodos agent específicos de forma permanente. Esto puede lograrse por completo desde la interfaz web de jenkins.
En el jenkins master, navegamos a Manage Jenkins y despues a Manage Nodes, desde ahí podemos ver todos los nodos que este master tiene adjuntos. Entonces, hacemos click en New Node, Le damos un nombre, y confirmamos con el botón que pone: OK. Deberíamos ver la página de configuración de agents:
Veamos todos los campos:
- Name Es el nombre único que le pondremos este agent
- Description Una descripción humana para los agentes
- # of executors Se refiere a la cantidad de compilaciones que pueden correr al mismo tiempo en este agente
- Remote root directory Es un directorio dedicado en la maquina del slave que puede usarse para compilar los proyectos (por ejemplo /var/jenkins). Mas adelante los datos mas importantes son transferidos de regreso al master así que no este directorio no es critico.
- Labels Esto incluye las etiquetas que coinciden con compilaciones especificas. por ejemplo: proyectos basados en java 8
- Usage Es la opción para decidir si el agent debería usarse solo para las etiquetas que coincidan. O para todos los tipos de proyecto.
- Launch Method Este incluye lo siguiente:
- Launch agent via java web start Aquí, la conexión será establecida por el agente. Es posible descargar un archivo JAR y las instrucciones de como correrlo en un slave
- Launch via execution of command on the master Este es un comando personalizado que se corre en el master para inicializar el slave. por ejemplo: ssh <host_slave> java -jar ~/bin/app.jar.
- Launch slave agents via SSH Aquí el master se conectará al slave usando el viejo y conocido protocolo SSH
- Let Jenkins control this Windows slave as a Windows service Aquí el master iniciará un sistema de gestión remoto incluido en windows.
- Availability Esta es la opción que decide si este agent estará activo todo el tiempo o el master lo deberá encender bajo ciertas condiciones.
Cuando los agents están configurados correctamente, es posible apagar el nodo master, para que ninguna compilación se ejecute en el y sirva única y exclusivamente como una interfaz gráfica y como coordinador de integración.
Como funcionan los agentes permanentes
Como ya mencionamos una dificultad que encontramos al usar agentes permanentes es que esta solución no es tan sencilla de mantener cuando tenemos muchos slaves para diversos tipos de proyectos.
En este ejemplo, se tienen tres tipos de proyectos etiquetados correspondientemente (java7, java8, ruby) entonces necesitamos mantener tres tipos de slaves diferentes separados por etiquetas. Es el mismo problema que tendríamos manteniendo múltiples servidores de producción. En posts anteriores vimos como solucionar este problema instalando docker en los servidores de producción. De manera similar podemos solucionarlo instalando agentes permanentes basados en docker.
Agentes Docker Permanentes
La idea detrás de esta solución es agregar permanentemente slaves de propósito general. Cada slave esta configurado idénticamente con docker instalado y cada compilación esta definida con la imagen de docker sobre la cual correra.
Configurando agentes docker permanentes
La configuración es en esencia estática. Se hace de la misma manera a como lo hicimos anteriormente para los agentes permanentes. La unica diferencia reside en que necesitamos instalar docker en cada agent que sera usado como slave. Después usualmente no necesitamos las etiquetas porque todos los slaves son lo mismo. Despues de que los slaves han sido configurados, definimos la imagen de docker con un script en cada pipeline.
pipeline { agent { docker { image 'openjdk:8-jdk-alpine' } } ... }
Cuando la compilación inicia, los slaves arrancan un contenedor desde la imagen de docker openjdk:8-jdk-alpine y despues ejecuta los pasos del pipeline adentro de ese contenedor. De este modo, siempre sabemos que la configuración de cada ambiente independientemente de que tipo de proyecto vamos a correr.
Como funcionan los agentes docker permanentes
Si vemos el escenario para los contenedores docker permanentes el diagrama se vería como este:
Cada slave es exactamente el mismo y si quisiéramos compilar un proyecto que depende de java 8, entonces definiríamos la imagen docker apropiada en el script del pipeline en lugar de especificar la etiqueta como lo hicimos con los agentes permanentes.
Agentes Jenkins Swarm
Hasta ahora hemos definido de manera permanente cada uno de los agents en el Jenkins master. Esta solución, a pesar de ser buena para muchos casos, puede volverse complicada si necesitamos escalar con frecuencia el numero de maquinas slave. Jenkins Swarm permite agregar slaves dinámicamente sin la necesidad de configuración en el master.
Configurando Agentes Jenkins Swarm
El primer paso que usamos para configurar Jenkins Swarm es instalar el plug-in Swarm en Jenkins. Esto lo podemos lograr a través de la interfaz gráfica bajo el menú Manage Jenkins -> Manage Plugins. Despues de este paso, el master debería estar preparado para que se le adjunten slaves de forma dinámica. El segundo paso es correr Swarm en cada máquina que actuará como un slave. Esto lo hacemos usando el swarm-client.jar que pueden descargar.
El comando es el siguiente:
$ java -jar swarm-client.jar -master <url_jenkins_master> -username <usuario_jenkins_master> -password <pasword_jenkins_master> -name jenkins-swarm-slave-1
Después de ejecutar exitosamente esta aplicación, deberíamos ver que el nuevo slave apareció en el master de la siguiente forma
Como funcionan los agentes Swarm
Jenkins Swarm permite agregar agentes dinámicamente, pero no especifica si usar slaves específicos o basados slaves docker, así que podemos usarlos para ambos casos. En primera instancia Swarm podría no parecer muy útil. Después de todo, podemos configurar agentes como slaves, sin embargo, lo siempre lo tenemos que hacer manualmente. La verdadera utilidad de Swarm viene cuando tenemos que escalar dinámicamente slaves en clusters de servidores.
Agentes Docker dinámicamente provisionados
Otra opción es instalar Jenkins para que cree nuevos agentes dinámicamente cada vez que una compilación inicia. Esta solución es obviamente mas flexible ya la cantidad de slaves se ajusto dinámicamente a la cantidad de compilaciones que se están llevando a cabo en determinado momento.
Configurando agentes docker dinámicamente provisionados
Para configurar dichos agentes, tenemos que instalar el plug-in Docker. Como siempre con Plug-ins de Jenkins, podemos hacerlo desde la consola de administración de la interfaz gráfica y podemos seguir los siguientes pasos:
- Abrir la página de Manage Jenkins
- Clic en Configure System
- En el fondo de la página encontramos que hay una seccion Cloud
- Clic en Add new cloud y elegir Docker
- Llenar los detalles del agente
Aquí casi ningún parámetro necesita ser cambiado ya que todos son inferidos a partir de la configuración actual. Sin embargo necesitamos llenar dos de ellos. Estos son:
- Docker URL: La dirección de la maquina anfitrión donde los agentes correrán.
- Credentials: Las credenciales en caso de que dicho anfitrión las requiera.
6. Clic en Add Docker Template y luego seleccionar Docker Template
7. Llenamos los detalles y guardamos la imagen del slave
Podemos usar los siguientes parámetros:
- Docker Image: La imagen mas popular dentro dela comunidad es publicisworldwide/jenkins-slave
- Credentials: las credenciales para esta imagen son:
- username: jenkins
- password: jenkins
- Instance Capacity: Define el numero de agentes que correrán al mismo tiempo. Para iniciar recomiendo que sea 10.
Después de guardar todo esta listo. Podríamos correr un pipeline y observar que la ejecución realmente se llevo a cabo en el agente docker.
Como funcionan los agentes docker dinámicamente aprovisionados
Los agentes docker dinámicamente aprovisionados pueden ser tratados como una capa sobre el mecanismo estándar de los agents. No cambian ni los protocolos de comunicación ni como el agente es creado. Entonces, que hace diferente un agente Docker con la configuración dinámica que estudiamos?
El siguiente diagrama lo explica mejor:
Entonces paso a paso lo que hace es:
- Cuando el jenkins job inicia, el master corre un nuevo contenedor desde la imagen jenkins-slave en el host docker
- El contenedor jenkins-slave es realmente una imagen de ubuntu con un servidor SSHD instalado
- El master agrega el agente automaticamente a la lista de agentes (justo como lo hicimos manualmente en la sección anterior de este post).
- El agente es accedido usando el protocolo de comunicación SSH y realiza la compilación
- Después de compilar, master detiene y remueve el contenedor slave
La solución es de alguna forma similar a los agentes de docker permanentes, pero varia en el sentido de que en este corremos una compilación dentro de un contenedor de docker. La diferencia esta en la configuración del slave. Aquí TODO el slave esta dockerizado y no solo el ambiente de compilación. Entonces tenemos dos grandes ventajas:
- Ciclo de vida del agente automatizado El proceso de crear, agregar y remover los agentes ha pasado a ser trabajo automatizado
- Escalabilidad Un solo host nos permite correr varios slaves (hasta donde tenga capacidad). De manera que la capacidad se ve multiplicada sobremanera.
[…] Utiliza cualquier agent disponible […]