.Net: infraestructura de desarrollo & test

Quiero compartir la infraestructura que estamos usando en el proyecto que estoy trabajando en este momento.

Como repositorio de código estamos usando Git, más específicamente GitLab (aunque a pedido del cliente en breve migraremos a BitBucket).  Dado que estamos usando una arquitectura “micro-servicios-like” tenemos un repositorio por cada micro-servicio, más un repositorio general para configuración, más un conjunto de repositorios adicionales para algunas librerías de uso común y finalmente un repositorio para los scripts de operaciones.

Estamos usando Nexus como repositorio de binarios NuGet y Maven, ya que tenemos componentes .Net y Java.

El servidor de integración continua y orquestador de deployments es Jenkins 2. El proceso de Build lo tenemos armado con MSBuild de manera que Jenkins simplemente obtiene el código de Git, dispara MSBuild y genera reportes y notificaciones basados en el resultado del proceso.

Los deployments los hacemos con MSDeploy y los disparamos desde Jenkins.

De esta forma, los componentes comunes compartidos entre distintos micro-servicios están publicados en nuestro NuGet/Nexus y quien necesita utilizarlos simplemente agrega una referencia a nivel binario usando NuGet. Esto nos permite evitar almacenar binarios en Git y al mismo tiempo que nos habilita a que cada parte de la arquitectura pueda ser versionada en forma independiente.

Para la gestión del backlog utilizamos Jira que a su vez lo tenemos integrado con Hiptest para el manejo de los casos de prueba. Personalmente nunca antes había trabajado con Hiptest y algo que me gusta mucho es que se integra con SpecFlow para la definición de los casos de prueba y el registro centralizado del resultado de la ejecución de las pruebas reportado por SpecFlow.

Para comunicarnos tenemos una lista de correo, un Slack y Skype/Hangout para las video conferencias.

pipeline_net

Anuncios

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

 

El placer de trabajar con quien uno gusta

Varios son los beneficios de trabajar de manera independiente, pero dudo que alguno sea más gratificante que el hecho de poder elegir con quien trabajar.

En este sentido ayer tuve  el gusto de compartir una mañana de trabajo con Pablito Tortorella (@pablitux). Pablo y yo estamos trabajando en un cliente, Pablo enfocado en cuestiones de adopción de Scrum y mejora organizacional y yo en cuestiones de integración continua y pruebas automatizadas. Si bien son temas distintos, tienen puntos de contacto y complementación. Ayer en particular estuvimos trabajando armando la infraestructura de prueba con un equipo que está físicamente distribuído.

Fue una mañana intensa, trabajamos todos juntos sentados frente a dos pantallas: una con el Hangout (teníamos miembros del equipo conectados en forma remota) y otra con el código del proyecto.

Logramos el objetivo que nos habíamos propuesto (tener una prueba corriendo sobre la nueva arquitectura), aprendimos, la pasamos bien y acordamos repetirlo la semana próxima.

¡Gracias Pablitux!

Integración continua, principio #3: el principio del tio Ben

Cuando el tío Ben está apunto de morir en los brazos de su sobrino Peter Parker (spiderman) le dice una frase que lo marcará para siempre:

Grandes poderes conllevan grandes responsabilidades

Amo esta frase, creo que aplica a todos los ámbitos de la vida y también al desarrollo de software. En este sentido Tobias Meyer en su libro People Scrum la utiliza para describir la situación de un equipo autoorganizado: el equipo tiene el poder de autoorganizarse y eso automáticamente aumenta su responsabilidad. Analicemos esta situación por el contrario: si a un equipo se le imponen compromisos ¿cuán responsable será ese equipo por esos compromisos impuestos?. Si en lugar de eso fuera el propio equipo el que asumiera los compromisos ¿no se sentiría ese equipo automáticamente más responsable por el cumplimiento de esos compromisos que el propio equipo eligió? Mi experiencia me ha demostrado que efectivamente es de esta forma, lo cual confirma la frase del tío Ben.

Llevando esto al contexto de integración continua creo que la frase puede incluso aplicarse a la inversa, grandes responsabilidades requieren grandes poderes. O sea, si se pretende que un equipo asuma la responsabilidad de mantener un build sano, debe entonces darse a ese equipo las herramientas (poderes) para poder tomar esa responsabilidad. En concreto ello implica, entre otras cosas, que el equipo debe tener acceso total al ambiente de integración continua. Sin excepción puedo decir que todas las implementaciones fallidas que he visto de esta práctica tenían en común que el equipo no tenía acceso completo al ambiente de integración continua.

Integración continua, principio #2: build everywhere

Particularmente la tarea de Build debe poder ejecutarse tanto en el servidor de integración continua como en la máquina del programador. Esto es fundamental para que en caso de falla del build, los programadores sean capaces de reproducir la falla en sus ambientes locales ejecutando la misma tarea que ejecutó el servidor.

Al mismo tiempo esto reduce la dependencia con el servidor de integración continua, ya que el mismo solo agrega un paso previo y uno posterior al build. En concreto:

  • El servidor de IC monitorea al repositorio de código y dispara nuevas ejecuciones en caso de detectar cambios
  • Una vez disparado el build y descargado el código fuente, el servidor de IC ejecutará la tarea de build que es justamente la que debe poder ejecutarse en la máquina de los programadores
  • Finalmente el paso adicional que ejecuta el servidor de IC es la notificación del resultado del build

Integración continua, principio #1: Independencia de IDE

Desde el punto de vista conceptual esta práctica está muy bien descripta en el artículo de Martin Fowler (#lecturaObligatoria), pero a la hora de intentar implementar la práctica, no suele alcanzar con la teoría. En este sentido un libro que me parece muy útil es Jenkins: The definitive guide de John Ferguson.

Luego de la lectura de los mencionados recursos y de haber recorrido un largo camino con esta práctica he identificado algunos principios fundamentales para la efectiva implementación de esta práctica. A partir de hoy y durante los próximos días voy a compartir una serie de post explicando cada uno de dichos principios. Si bien los principios los voy a ir numerando, su numeración no tiene correlación alguna con su importancia. Aquí va el primero.

Independencia del IDE

La generación del build no puede depender de un IDE. Es sorprendente la cantidad de programadores que no son capaces siquiera de compilar su aplicación sin usar un IDE. Esta es una situación que veo en muchos ambiente Java (alta dependencia de Eclipse) y .Net (alta dependencia de Visual Studio). En la actualidad todos los lenguaje de programación cuentan con alguna herramienta de build e incluso si no existe una herramienta específica para algún lenguaje, siempre es posible utilizar alguna herramienta genérica como el noble make.

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