Primeros pasos en Jenkins


Me he dado cuenta hace poco de que, con todos los artículos que he escrito y llevando más de 2 años y medio cuidando del Jenkins de Tuenti, aún no he escrito nada sobre este sistema de integración contínua.

Así que vamos a solucionar el problema.

Qué es Jenkins

Lo primero es saber qué es Jenkins. Para alguien que esté acostumbrado a los sistemas de integración contínua no hay mucho que explicar aquí. Sin embargo, quien no sepa qué es eso puede encontrarse con un problema XD

Realmente Jenkins no es más que un cron a lo bestia. Es un planificador de tareas. La diferencia está en que, además, permite la gestión de recursos y mostrar los resultados.

Como definición... puede valer, aunque seguramente quien no sabía lo que era Jenkins siga sin saberlo. Vamos con un ejemplo:

Supongamos que tenemos un programa, por ejemplo, colorize. Este programa tiene tests, o documentación que generar, o cualquier otra cosa que necesitemos. Lo que Jenkins nos ofrece es, justamente, una manera fácil de:

  • generar un workspace para desplegarlo.
  • descargarse el repositorio.
  • ejecutar órdenes que nos permitan realizar los tests, la documentación o lo que queramos.
  • mostrar los resultados de manera bonita.

Y todo esto con interfaz web, configurable, plugable, ...

Parece utópico, ¿no?

Vamos a ver cómo se hace.

Primeros pasos con Jenkins

Lo primero es descargárselo y ejecutarlo. Si no queréis "engorrinar" vuestra máquina, lo más sencillo es descargarse el war de la última versión de Jenkins y ejecutar:

java -jar jenkins.war

Eso nos levantará una instancia en el puerto 8080 (habitualmente), que utilizará un servidor web embebido (habitualmente winstone). Si os da conflicto deberéis seleccionar otro puerto.

Ahora abrimos un navegador y abrimos la url http://localhost:8080 y veremos que ya está todo montado para comenzar a probarlo. Si nos gustara y lo creéis necesario, podéis montarlo sobre un Tomcat o algún otro servidor más potente.

Plugins

En Jenkins todo funciona mediante plugins. Lo primero que necesitamos es soporte para nuestro sistema de control de versiones. Dado que colorize está en Git, tendremos que instalar el plugin de Git para Jenkins. Para ello, desde la interfaz de Jenkins, seleccionamos Administrar Jenkins->Administrar Plugins->Todos los plugins. Seleccionamos "Git plugin" (hay varios, pero nosotros usaremos éste). Podéis ayudaros del buscador de arriba a la derecha. Tras seleccionar el checkbox, pulsamos Instalar sin reiniciar.

Esto instalará el plugin y todas sus dependencias. Temo que alguna de éstas requiera reiniciar Jenkins.

Primera tarea

Ahora volvemos a la página principal de nuestra instancia de Jenkins y pulsamos en Nueva tarea. Le ponemos un nombre; por ejemplo "colorize" y seleccionamos Crear un proyecto de estilo libre. Pulsamos OK.

La tarea ya está creada pero tenemos que configurarla. Voy a ir a lo más básico, aunque podéis echar un ojo a la ayuda de los distintos apartados.

Por ello, sólo tendremos que modificar "Configurar el origen del código fuente": seleccionamos Repository URL "https://github.com/magmax/colorize.git" y dejamos el resto de los parámetros como están.

Ahora nos vamos al apartado "Ejecutar", donde añadimos un nuevo paso de tipo "Ejecutar línea de comandos (shell)". Éste puede ser sencillo o tan complejo como deseemos. Si lo queremos sencillo, tendremos que tener el entorno previamente preparado:

make

Sin embargo, es buena costumbre usar entornos aislados, por lo que quizá sea mejor preparar un poco el entorno. Para ello podríamos usar Puppet, Chef, SaltStack o algún otro software de automatización, pero en este pequeño ejemplo usaremos sólo VirtualEnv:

virtualenv venv
. venv/bin/activate
pip install -r requirements-dev.txt
python setup.py install
rm -rf results
mkdir results
make analysis
make run_unit_tests NOSE_OPTS="--with-xunit --xunit-file=results/unit.xml"
make run_integration_tests NOSE_OPTS="--with-xunit --xunit-file=results/integration.xml"
make run_acceptance_tests NOSE_OPTS="--with-xunit --xunit-file=results/acceptance.xml"
deactivate

Esto generará un pequeño entorno aislado, instalará dependencias y ejecutará las pruebas. Le pasamos opciones para que deje los resultados en el directorio "results".

Ahora tenemos que procesar éste directorio de resultados. Para ello, en la sección Acciones para ejecutar después añadimos un nuevo paso de tipo Publicar los resultados de tests de JUnit, y en el campo Ficheros XML con los informes de tests ponemos "results/*.xml".

Pulsamos "Guardar" y ya está listo.

Ejecución

Pulsamos sobre "Construir ahora" y Jenkins comenzará su magia. Podemos pulsar sobre la tarea que está corriendo para ver la salida estándar y la salida de errores de la ejeución.

Si todo termina correctamente, nos mostrará una bola azul. Si no, será roja si hubo un problema o bien amarilla si falló algún test.

Una vez terminado, podemos pulsar sobre la ejecución del trabajo (a la izquierda, en Historia de tareas) y ver qué tests se ejecutaron en Resultado de los tests, donde se muestran los tiempos de ejecución, tests fallidos, trazas si fallaron, diferencia en el número de tests, etc.

Aprendiendo un poco más de Jenkins

Hasta aquí lo básico. Hemos creado un trabajo y lo hemos ejecutado.

Pero es posible que tengamos muchas dudas ahora:

  • ¿Dónde se ha ejecutado?
  • ¿Puedo realizar ejecuciones paralelas?
  • ¿Puedo ejecutarlo remotamente?

La respuesta a estas preguntas es fácil: Se ha ejecutado en un nodo, y podemos configurar tantos nodos como queramos, incluso en remoto.

Para ello, desde Administrar Jenkins->Administrar Nodos podemos configurar nuevos nodos. Por defecto, Jenkins asume que queremos un nodo en la máquina donde está el servidor, pero puede que no lo queramos allí.

También tenemos la opción de configurar varios ejecutores en un mismo nodo. Si el nodo tiene mucha potencia, quizá pueda realizar dos tareas a la vez, de manera que una vaya a cada ejecutor.

Os invito a que naveguéis el resto de opciones, que instaléis nuevos plugins y que probéis lo que se os ocurra. Para limpiar toda la basura o si lo rompéis, basta con eliminar la carpeta ${HOME}/.jenkins" y reiniciar el servidor.

Advertencias

Llegados a este punto, veo necesario advertiros de cosas que me ha enseñado la experiencia:

  • Instalad la menor cantidad de plugins que podáis. Administrar los plugins es un coñazo: Habrá proyectos que hayan transformado bugs en características, se romperán cosas, etc.
  • Nunca actualicéis un plugin sin probarlo antes.
  • El número de tareas tiende a crecer. Usad tan pocas como podáis.
  • Los proyectos se guardan como XML en el disco. Haced copias de seguridad.
  • A veces los proyectos se corrompen cuando no pueden leerse correctamente. Basta reiniciar Jenkins para que esto... cambie (puede que los lea bien, puede que lea mal otros). Si esto ocurren, los trabajos fallidos no se mostrarán.
  • Los workspaces no se limpirán después de borrar un proyecto. Es necesario acceder al nodo y borrarlos a mano.
  • Jenkins tratará de reutilizar el mismo nodo para el mismo proyecto, si está libre. Eso es bueno y es malo: Por un lado, mejora la velocidad; por otro, si el entorno está corrupto, volverá a fallar.

Además, deberíais pensar en cómo va a funcionar vuestra organización. Existen estas arquitecturas básicas:

Un repositorio, un proyecto

Para cada repositorio en vuestra organización, se crea un proyecto Jenkins. De esta manera, todas las ramas se prueban en el mismo sitio.

Esto hace muy sencilla la gestión, pero resulta difícil saber cómo evoluciona un proyecto.

Un repositorio, dos proyectos

Una optimización es tener dos proyectos: Uno para la rama principal y otro para todo lo demás. Al fin y al cabo, las ramas son efímeras XD

Esta opción permite gestionar fácilmente los pull-requests y tener más historia de la rama principal.

Un proyecto por rama.

Finalmente, se puede crear un proyecto por rama. Esto permite que cada desarrollador/equipo pueda gestionar un proyecto y así tener histórico propio. Sin embargo, se generan distintos problemas:

  • Limpiar los workspaces obsoletos.
  • Crear nuevos proyectos.
  • Actualizar todos los proyectos de acuerdo a nuevas especificaciones (por ejemplo, añadir un nuevo paso, añadir un plugin, etc.).
  • Gestionar los recursos de forma apropiada.

Opinión personal

Los que me conocéis sabéis que no soy muy fan de Jenkins... Pero puede resultar un servidor de integración contínua estupendo para comenzar. Sin embargo, a medida que el número de plugins crece y lo mismo ocurre con el número de proyectos, su gestión se torna en más y más compleja.

Quizá llegado a este punto sea buena idea buscar otra alternativa. Pero eso no impide decir que Jenkins tal vez sea la mejor manera de comenzar a depender de un sistema de Integración Contínua. Pero si Jenkins llegara a quedarse corto, propongo echar un vistazo a otras alternativas:

  • BuildBot: Aunque no tiene a penas plugins, su versatilidad permite implementar cualquier necesidad en poco tiempo y de manera sencilla.
  • Travis: Quizá la mejor opción online, tanto para software libre como si estás dispuesto a pagar una módica cantidad. Es libre, pero puede ser complejo instalarlo localmente.
  • Drone: Otro sistema hosted, aunque se puede descargar, que utiliza dockers para generar entornos aislados en los nodos.
  • Go-CI: Una nueva opción, de ThoughtWorks, liberada hace poco.

Existen otras muchas alternativas; en la Wikipedia tenéis una lista de servidores de integración contínua bastante exhaustiva.


Comentarios

Comments powered by Disqus