Jenkins: Jobs as Code

Jenkins: Jobs as Code

Una de las funcionalidades destacadas en la nueva versión de Jenkins es la posibilidad de definir los pipelines utilizando un lenguaje de dominio específico. Esto resulta muy útil cuando uno tiene que manejar una cantidad importante de jobs y al mismo tiempo facilita el control de configuración ya que permite que la definición de los jobs sea tratada como código en lo referente al almacenamiento y versionado.

Esta posibilidad de manejar los jobs como código es algo que está ahora más presente en Jenkins 2, pero la realidad es que es una funcionalidad que ya estaba disponible para las versiones anteriores de Jenkins mediante el uso de plugins y algunas otras herramientas.

En particular yo venia utilizando el Jenkins Job Builder, una herramienta de la gente de Open Stack que permite definir jobs en un archivo .yml, definiendo también relaciones “de herencia” entre los jobs. El Job build es básicamente un script python que parsea los .yml y utiliza la api de Jenkins para crear los jobs correspondientes.

Otra herramienta que provee una experiencia similar es el Job DSL Plugin. Este plugin provee la posibilidad de definir Job utilizando un DSL Groovy para la definición de Jobs. La forma de uso es: poner los scripts de definición de jobs en un repo y luego crear un job en Jenkins (típicamente llamado Seed) que monitorea el repo de scripts y que cuando detecta cambios ejecuta los scripts los cuales generan los jobs que hayamos definido.

Otras herramientas similares pero que no he probado lo suficiente son: el Build Flow Plugin y el Workflow plugin.

Delivery Pipeline con Jenkins 2

Si bien vengo trabajando con Jenkins2 desde que se publicó el release candidate, no había echado mano a la nueva funcionalidad nativa de pipelines. Sinceramente la miré por arriba apenas instalé Jenkins2, no me convenció y decidí seguir con la combinación de plugins que venia usando con Jenkins 1.6.

El lunes pasado me actualicé a Jenkins 2.8 y decidí investigar más en profundidad la funcionalidad de pipelines nativos.  Finalmente esta mañana logré completar una primera versión de mi pipeline utilizando el nuevo soporte nativo. Al hacerlo descubrí un montón de cosas interesantes que pueden llegar a disminuir considerablemente el costo de control de configuración de Jenkins (principalmente las cuestiones versionado, actualización y backups de jobs)

jenkins2-pipeline

 

Jenkins 2 ha llegado

Así es, Jenkins 2 finalmente está aquí. Según se cuenta en el sitio oficial los puntos destacados de esta nueva versión mayor (major release) son:

  • Soporte nativo para delivery pipelines
  • Mejoras de usabilidad
  • Completa compatibilidad con versiones anteriores

Tuve la oportunidad de verificarlos cuando pasé uno de mis proyectos a la versión pre-release que Jenkins publicó hace un tiempo. Adicionalmente a esto noté:

  • “Independencia” de Maven, lo pongo entre comillas porque no estoy seguro que el término apropiado sea independencia. Lo que noté es que en la versión anterior, al crear un nuevo job existía la opción “proyecto Maven”, cosa que ya no existe. Creo que inicialmente esto tenía sentido pues Jenkins había surgido en el mundo Java, pero en los últimos años creo que ha transcendido ampliamente el mundo Java y se ha convertido en la herramienta de-facto para integración continua.
  • Integración con Gradle para la definición de los pipelines, los cual tiene mucho sentido ya que Gradle es una herramienta que se posiciona como herramienta de build genérica capaz de buildear proyectos en distintos lenguajes.
  • Se removió el soporte nativo para repositorios CVS, quedándo nativamente soporte solo para Git y Subversion.

Más allá de estos puntos, un detalle que me llamó positivamente la atención fue que como parte del proceso de instalación, se ofrece instalar un conjunto de plugins que son los más comunmente utilizados por la comunidad.

A partir de esta nueva versión de Jenkins y de algunas charlas que he tenido con mi equipo actual de proyecto he decido empezar a trabajar en la preparación de un curso de Jenkins para dictar en Julio o Agosto.

Blue Green Deployment

En las últimas 2 semanas me encontré explicando la técnica de despliegue Blue/Green más de 5 veces. Cuando tomé conciencia de ello decidí hacer un video explicativo de modo de poder utilizarlo también en mis clases. Espero resulte de utilidad.

Un caso robusto de integración contínua en Java

En el proyecto en el que he estado trabajando los últimos meses tenemos montado un proceso de integración continua bastante completo en mi opinión, comparto aquí algunos detalles. Se trata de un proyecto Java, basado en Spring, Hibernate, Camel y algunos otros frameworks. A nivel de herramientas tenemos quality checks con PMD, pruebas unitarias y de aceptación con JUnit y pruebas de aceptación y carga con JMeter. Como herramienta de build usamos Maven, como servidor de integración continua usamos Jenkins y el código lo tenemos en Git (gitlab). Al mismo tiempo tenemos un ambiente de tests en la nube donde desplegamos nuestra aplicación periódicamente. También tenemos un ambiente de prueba en las oficinas del cliente, donde desplegamos nuestra aplicación al final de cada iteración. En el jenkins tenemos varios jobs:

  • integración continua: monitorea el branch develop y ante cada cambio compila, ejecuta las pruebas de JUnit (unitarias y de integración)
  • quality-check: se ejecuta a continuación del job de integración continua y caso_java_jenkins_2básicamente ejecuta análisis de código (pmd)
  • integración continua de branches: en algunos casos creamos feature-branches, para lo cual seguimos una convención de nombres y este job se encarga de ejecutar integración continua sobre estos branches. En general procuramos que estos branches no vivan más de 3 días.
  • inicialización: es el job que dispara el build pipeline, y como tal comienza por inicializar el ambiente de test. Se ejecuta periódicamente (varias veces al dia siempre que haya cambios en el repositorio)
  • deploy: son dos jobs que se encargan de desplegar las dos aplicaciones que forman parte de nuestro sistema.
  • pruebas de aceptación: ejecuta las pruebas de aceptación (codeadas con jmeter) luego de cada despliegue
  • pruebas de carga: ejecuta un conjunto de pruebas de carga. Este job lo ejecutamos manualmente al menos una vez por iteración para asegurarnos que los cambios realizados no haya impactado en la performance del sistema
  • generador de release: este job lo ejecutamos manualmente al final de cada iteración para generar un nuevo release lo cual implica: taggear el repo, generar y publicar los artefactos (wars y jars) y actualizar la versión en los archivos del proyecto (pom.xml)
  • generador de instalable: este job toma  los artefactos generados por el job de generación de release y los empaqueta junto con un grupo de scripts que luego se utilizarán para instalar  el sistema en los ambientes del cliente.

caso_java_jenkins

Reportes en Jenkins

Si bien últimamente vengo trabajando muy felizmente con Travis, una de las funcionalidades que extraño mucho son los reportes. Cuando trabajo con Jenkins suelo hacer que mis jobs de CI muestren diversos reportes.

Hay dos estrategias para mostrar reportes en Jenkins:

  1. Utilizar las capacidades de reporting de Jenkins, ya sea nativas o con plugins, o bien
  2. Hacer que cada herramienta que forma parte del build genere sus propios reportes y luego simplemente hacer que Jenkins provea acceso a dichos reportes.

Un ejemplo del primer caso es el plugin de Cucumber-jvm que genera un reporte como el de la figura 1.

cucumber_jenkins
Figura 1

Un ejemplo similar pero usando la segunda estrategia sería ejecutar cucumber especificando que genere output HTML y luego usar el plugin Sidebar link de Jenkins para linkear el reporte generado por cucumber. En figura 2 se ve un caso de esta estrategia donde Jenkins provee links un reporte de features generado por cucumber y otro de coverage generado por SimpleCov. La figura 3 muestra cómo configurar los links a los reportes una vez instalado el plugin.

reportes_jenkins
Figura 2

 

sidebar_links
Figura 3

 

Estrategia de Continuous Delivery: git + jenkins + heroku (parte 1)

En uno de los proyectos open source en los que participo hemos montado una infraestructura de continuous delivery que nos viene dando buenos resultados por ello quiero dedicar algunas líneas a describir su estructura.

El proyecto consta de 2 aplicaciones, una webapp y un servicio de backend pero cada una es manejada de forma independiente, por ello el resto de este artículo hablaré de forma genérica como si fuera una única aplicación. La aplicación web está construida con Ruby/Padrino, mientras que el servicio es Ruby “puro”.

El código de la aplicación es versionado en GitHub, donde tenemos un branch develop sobre el trabajamos y un branch master que tiene el código que está en producción. El código de develop es desplegado automáticamente por Jenkins en una instancia de prueba (que denominamos preview) mientras que el código de master es desplegado en la instancia de producción. Ambas instancia estan corriendo en Heroku.

En el proyecto somos tres programadores, que trabajamos en la aplicación en nuestros tiempos extra laborales. Cuando trabajamos en funcionalidades no tan chicas, que pueden llevarnos un par de días, creamos feature branches.

Adicionalmente al repositorio de GitHub, tenemos otro repositorio, privado, hosteado en BitBucket donde almacenamos la configuración de la aplicación. Aquí tenemos dos branches, uno por cada ambiente.

Jenkins se encarga de la correr la integración continua y los pasajes entre ambientes. Para ello tenemos los siguientas tareas:

  • CI: corre integración contínua sobre el branch develop. Básicamente corre pruebas unitarias y de aceptación.
  • CI_All: es análogo al CI, pero trabajo sobre todos los demás branches, lo que nos permite tener integración contínua incluso cuando trabajamos en feature branches.
  • Deploy_to_Preview: despliega en el ambiente de preview (test) el código proveniente del branch develop
  • Run_Preview_Pos_deploy: aplica la configuración actualizada y las correspondientes migraciones en el ambiente de preview y ejecuta un smoke test
  • Run_Pre_Production_deploy: mergea el branch develop en master
  • Deploy_to_production: despliega en el ambiente productivo el código proveniente del branch master
  • Run_Production_Pos_deploy: aplica la configuración actualizada y las correspondientes migraciones en el ambiente de preview y ejecuta un smoke test

Algunos detalles más para destacar son:

  • Todo commit a develop, se despliega automáticamente a preview.
  • No se hacen despliegues a preview y producción en forma manual, todo pasa por Jenkins
  • Ante cada build, Jenkins informa el resultado via chat (Jabber) a todos los miembros del equipo de desarrollo.
  • El monitoreo de la aplicación en el ambiente productivo lo hacemos con LogEntries y New Relic

Sin duda que este esquema no es una bala de plata, pero nos funciona muy bien en este proyecto. Algunas cuestiones a revisar en otros proyectos serían:

  • En caso de trabajar con un lenguaje compilado estáticamente (Java, C#, etc) habría que considerar utilizar un repositorio de binarios para que la aplicación se compile una única vez (algo tipo Artifactory)
  • En caso de aplicaciones con requerimientos de disponibilidad 7×24, había que considerar una estrategia de deploy distinta, tal vez algo del tipo blue-green deploy.

Si gustan conocer más detalles, no duden en consultar.