PetClinic

Construcción y ejecución en local

Una vez que hemos configurado correctamente nuestro entorno de CI/CD con Jenkins, vamos a estudiar varios ejemplos tanto en Java como en NodeJs. Para comprender los ejemplos, primero los vamos a construir y ejecutar en local, para después automatizar el proceso de construcción y despliegue con Jenkins.

En este primer ejemplo en Java, nos vamos a basar en el proyecto PetClinic con Spring Boot, disponible en GitHub. Petclinic es una aplicación Spring Boot construida usando Maven como herramienta de soporte a la gestión y construcción (build).

Spring Boot es un framework de código abierto para el desarrollo de aplicaciones Java basadas en Spring. Spring Boot genera una proyecto Maven/Gradle con todo lo necesario y que se autoconfigura en el arranque. Por ejemplo, si decimos que queremos una aplicación web, Spring Boot automáticamente embebe un Tomcat y lo configura con el servlet de Spring. Toda la configuración la añade al archivo de la herramienta de build que indiquemos, en caso de Maven, al archivo pom.xml.

Recordemos que Maven es una herramienta software para la gestión y construcción de proyectos Java. Apache Maven estandariza la configuración de un proyecto en todo su ciclo de vida, como son todas las fases de compilación, ejecución de pruebas, empaquetado, etc. Maven permite la gestión de dependencias entre módulos y distintas versiones de librerías, simplemente indicando los módulos que componen el proyecto, y las dependencias utiliza el software que estamos desarrollando, en un fichero XML de configuración llamado POM (Project Object Model).

Para el proyecto PetClinic, en tu máquina de desarrollo local puedes construir el .jar (empaquetado) y ejecutarlo:

Requiere JDK 11 en la máquina local. Si estás en Windows y tienes varias instalaciones, cambia la version de Java predeterminada a la 11.

git clone https://github.com/spring-projects/spring-petclinic.git
cd spring-petclinic
./mvnw package (1)
java -jar target/*.jar (2)
1 Llama a mvnw, el warper de Maven que instala Maven (si es necesario), y ejecuta el goal package que se encarga de compilar, ejecutar los test y empaquetar la aplicación en un único archivo ejecutable .jar. La primera vez que lances la construcción tardará más de 5 minutos, ya que tiene que descargar todas las dependencias necesarias desde los repositorios de Maven (Maven Central), y después lanzar los tests. Toda la configuración necesaria está contenida en el archivo pom.xml de Maven.
2 Ejecuta la aplicación a partir del .jar. Puedes acceder a PetClinic en: http://localhost:8080/
petclinic homepage
Fig. 1. Página principal de PetClinic

En su configuración predeterminada, Petclinic utiliza una base de datos en memoria (H2) que se inicia con datos predeterminados, y los nuevos datos que se guarden se pierden al reiniciar la aplicación.

En caso de necesitar persistencia de los datos, PetClinic también está preconfigurada para usar MySql. Para cambiar el tipo de base de datos, la aplicación debe ejecutarse con un perfil de MySql: spring.profiles.active=mysql.

java -Dspring.profiles.active=mysql -jar target/*.jar

Recuerda que para ejecutarla en este modo, debes tener un MySql funcionando en local, o bien lanzar MySql como contenedor con docker o con docker-compose. Existe un archivo docker-compose.yml disponible en el repositorio del proyecto PetClinic, por tanto puedes iniciar MySql así:

docker-compose up -d

El archivo docker-compose.yml que permite iniciar MySql puedes consultarlo en la carpeta raíz del proyecto, y es el siguiente:

mysql:
  image: mysql:5.7
  ports:
    - "3306:3306"
  environment:
    - MYSQL_ROOT_PASSWORD=
    - MYSQL_ALLOW_EMPTY_PASSWORD=true
    - MYSQL_USER=petclinic
    - MYSQL_PASSWORD=petclinic
    - MYSQL_DATABASE=petclinic
  volumes:
    - "./conf.d:/etc/mysql/conf.d:ro"

Creación del Pipeline para PetClinic

Una vez que hemos probado la ejecución y funcionamiento de la aplicación PetClinic en local, vamos a configurar el proyecto en el servidor Jenkins de CI/CD para que este se encargue de la construcción y el despliegue automatizados.

Conecta a tu Jenkins, y crea un nuevo item de tipo Pipeline. Dale el nombre 'spring-petclinic-pipeline':

new item pipeline petclinic
Fig. 2. New Item, PetClinic pipeline

En el bloque Pipeline, pega la configuración siguiente:

pipeline {
  agent any (1)

  tools {
    // Previamente has debido instalar Maven con el nombre "Default Maven"
    maven "Default Maven" (2)
  }

  stages { (3)
    stage('Git fetch') { (4)
      steps {
        // Get some code from a GitHub repository
        git branch:'main', url:'https://github.com/spring-projects/spring-petclinic.git'
      }
    }
    stage('Compile, Test, Package') { (5)
      steps {
        // Run goal 'package' includes compile, test and package.
        sh "mvn clean package -Dcheckstyle.skip"
      }
      post { (6)
        // If Maven was able to run the tests, even if some of the test
        // failed, record the test results and archive the jar file.
        success {
          junit '**/target/surefire-reports/TEST-*.xml'
          archiveArtifacts 'target/*.jar'
        }
      }
    }
  }
}
1 agente o nodo de Jenkins en que ejecuta la construcción del proyecto. En el ejemplo, any indica que se ejecutará cualquier nodo, en nuestro caso será en master ya que es el único nodo que hay definido en nuestro Jenkins.
2 como herramienta para la construcción se usará maven. Pon aquí el nombre que diste a tu instalación de Maven configurada previamente en Tools Configuration.
3 Bloque de stages: fases o etapas que conforman el pipeline
4 Fase de descarga del repositorio git
5 Fase de compilación, ejecución de test y empaquetado de la aplicación. Se realizarán con los goals clean package: primero se elimina todo lo generado en la construcción anterior, y a continuación se lanza la construcción con package tal y como está definida en el archivo pom.xml. La opcion -Dcheckstyle.skip anula el análisis de CheckStyle, que lo añadiremos en una fase posterior.
6 Paso posterior que guarda los resultados de los test de JUnit para generar la gráfica de evolución de los test.

Tras ejecutar el pipeline, con "Build now", el resultado debe ser el siguiente:

petclinic pipeline build 1
Fig. 3. Construcción del pipeline PetClinic

Si realizamos una segunda ejecución, ya aparecerá la gráfica de evolución de los tests de JUnit.

Informe de Cobertura de código

Jenkins nos permite publicar métricas asociadas al proyecto. Una de ellas, es la cobertura de código ejecutado por las pruebas.

La Cobertura de código nos indica el porcentaje de código de producción que está siendo ejecutado por los test. Es deseable tener un valor de cobertura lo más próximo posible al 100%

El proyecto PetClinic contiene 40 test unitarios en JUnit, y está configurado (ver pom.xml) para que se calcule la cobertura cuando se lanzan los tests mediante el plugin JaCoCo (Java Code Coverage). Puedes visualizar el resultado de la cobertura en tu construcción local, en la carpeta target/site/jacoco:

jacoco local results
Fig. 4. Archivos generados por Jacoco
jacoco local html
Fig. 5. Informe html de la cobertura Jacoco

Y si haces clic en el nombre de una clase, verás el código coloreado:

plugins jacoco class details
Fig. 6. Detalle la cobertura de las lineas de código
1 Las lineas verdes están cubiertas, es decir, han sido ejecutadas por al menos 1 test.
2 Las lineas amarillas están parcialmente cubiertas (missed branches): un resultado de la condición (verdadero/falso) ha sido ejecutado por algún test pero el otro no ha sido ejecutado por ningún test.
3 Las líneas rojas no están cubiertas, no han sido ejecutadas por ningún test.

Para visualizar el resultado de la cobertura en Jenkins:

  1. Instala el plugin de Jacoco y el plugin Code Coverage API

plugins jacoco install
Fig. 7. Instalación del plugin Jacoco
plugins code coverage api install
Fig. 8. Instalación del plugin Code Coverage API
  1. Añade las dos siguientes lineas al bloque post para que se guarde y muestre el informe de cobertura.

  ...
  success {
    junit '**/target/surefire-reports/TEST-*.xml'
    archiveArtifacts 'target/*.jar'
    jacoco(execPattern: 'target/jacoco.exec') (1)
    publishCoverage adapters: [jacocoAdapter('target/site/jacoco/jacoco.xml')] (2)
  }
  ...
1 Añade el informe Coverage Trend
2 Añade el informe Coverage Report

Tras la construcción de nuevo del proyecto, verás la gráfica de los resultados de los test y debajo la gráfica de evolución de cobertura:

plugins jacoco dashboard result
Fig. 9. Informe de cobertura en el dashboard

Haciendo clic sobre la gráfica accedes a los detalles:

plugins jacoco details result
Fig. 10. Detalle de de cobertura

Análisis estático de código: Checkstyle

Para mantener y aumentar la calidad de nuestro código debemos ayudarnos, entre otras herramientas, de técnicas de análisis estático de código. Básicamente, se encargan de buscar defectos en el código sin necesidad de que este se ejecute. En Java una de las más habituales es Checkstyle, aunque hay otras como FindBugs, PMD, y SonarQube que integra a los anteriores.

CheckStyle valida el estilo del código respecto al estilo oficial de Java.

El proyecto PetClinic tiene configurado el plugin de CheckStyle en el pom.xml:

    ...
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-checkstyle-plugin</artifactId>
        <version>3.1.0</version>
        ...
      </plugin>
    ...

Para ejectutar CheckStyle en local, ejecuta el comando de maven (mvn) con los siguietnes goals: mvn checkstyle:checkstyle site -DgenerateReports=false

Tras la ejecución, en la carpeta target/site/ verás el archivo checkstyle.html:

checkstyle report html
Fig. 11. Informe de CheckStyle

Sería labor del equipo de desarrollo revisar los errores detectados y tratar de corregirlos, siempre que realmente supongan una mejora para la calidad del código.

Para ejecutar y visualizar el informe en Jenkins:

  1. Instalar el plugin Warnings Next Generation.

  2. Añadir al pipeline un nuevo stage con la siguiente descripción:

  stage ('Analysis') {
    steps {
      // Warnings next generation plugin required
      sh "mvn checkstyle:checkstyle site -DgenerateReports=false"
      recordIssues enabledForFailure: true, tool: checkStyle()
    }
  }

Tras la construcción, el pipeline tiene una nueva fase y además en el menú tenemos acceso al informe de CheckStyle.

checkstyle report dashboard
Fig. 12. Pipeline con la nueva fase de Análisis
checkstyle report details
Fig. 13. Detalles del informe de CheckStyle
Saber más…​

Si estás interesado en profundizar en este tema, te recomiendo integrar SonarQube con Jenkins, ya que SonarQube realiza un análisis mucho más detallado de la calidad y seguridad del código, realizando tanto análisis estático de código (CheckStyle y otros), como de análisis de seguridad (vulnerabilidades), y definiendo lo que denomina Quality Gates que permiten definir condiciones que se deben cumplir basadas en los valores de las métricas del proyecto (por ejemplo, que la cobertura de código sea mayor del 80%). Puedes encontrar mucha documentación online sobre cómo hacerlo:

Además, Si tu proyecto está en un repositorio público en GitHub, puedes ahorrarte tener que instalar tu propio SonarQube utilizando SonarCloud, el servicio de SonarQube en la nube (SaaS) gratuito para proyectos públicos, con el que evitas tener que instalar y mantener tu propio SonarQube.

Para lanzar el análisis de Sonar con maven:

  1. Genera el login TOKEN

  2. Ejecuta los goals de maven: clean verify sonar:sonar -Dsonar.login=$SONAR_LOGIN_TOKEN

Incluso puedes configurar SonarCloud y Jenkins para que analizar los pull request de tu repositorio y conocer el resultado del análisis de Sonar antes de hacer el merge del pull request.

Despliegue en la VM

Para desplegar la aplicación PetClinic en la instancia de despliegue vamos a copiar sobre ella el archivo JAR y a continuación ejecutaremos en ella la orden de java para ponerla en marcha:

Copia este nueva fase en tu pipeline, sustituyendo DEPLOY_MACHINE por la IP o el nombre DNS de tu instancia:

  stage('Deploy'){
    steps {
      sh '''
        ssh -i ~/.ssh/id_rsa_deploy ubuntu@DEPLOY_MACHINE "mkdir -p ~/spring-petclinic" (1)
        scp -i ~/.ssh/id_rsa_deploy $WORKSPACE/target/*.jar ubuntu@DEPLOY_MACHINE:~/spring-petclinic (2)
        ssh -i ~/.ssh/id_rsa_deploy ubuntu@DEPLOY_MACHINE "if pgrep java; then pkill java; fi" (3)
        ssh -i ~/.ssh/id_rsa_deploy ubuntu@DEPLOY_MACHINE "nohup java -jar ~/spring-petclinic/*.jar > ~/spring-petclinic/yourservice.log 2>&1 &" (4)
      '''
    }
  }
1 Crea la carpeta spring-petclinic dentro de la carpeta HOME del usuario ubuntu en la máquina de despliegue
2 Copia con scp el archivo .jar, que se ha generado tras la construcción con maven, en la máquina de despligue
3 Detiene el proceso java si existe de un despliegue anterior.
4 Ejecuta la aplicación java empaquetada en el .jar, en background y con nohup, que hace que el proceso siga funcionando incluso si el usuario que lo inició cierra la sesión. De esta manera finaliza el comando ssh y el proceso sigue funcionando, es decir, la aplicación PetClinic estará desplegada y funcionando.