Introducción
Después del tutorial de instalación de Jenkins 2 en Ubuntu, continuamos con la configuración de Jenkins 2 con un sencillo pipeline de integración continua.
En este tutorial aprenderemos los fundamentos de los pipelines de Jenkins y configuraremos uno sencillo en el que integraremos una aplicación java con Spring Boot + Maven + GitHub. El pipeline lo mantendremos en el propio repositorio de GitHub y conectaremos GitHub con Jenkins de forma que cada vez que se realice un commit en el repositorio se ejecute de forma automática el pipeline de Jenkins.
Índice
- ¿Qué son los pipelines de Jenkins?
- ¿Qué elementos componen un pipeline?
- Creación del pipeline
- Comprobando funcionamiento
- Conclusión
¿Qué son los pipelines de Jenkins?
Definen el ciclo de vida de la aplicación de nuestro flujo de integración/entrega continua utilizando un lenguaje basado en Groovy. Debido a su gran flexibilidad y compatibilidad con numerosos plugins, permiten crear flujos complejos completos capaces de implementar sistemas de entrega continua (Continuos Delivery – CD). Además los pipelines pueden sobrevivir a reinicios del servidor y pueden ser pausados a la espera de que una persona realice una acción antes de que continúe la ejecución del flujo.
¿Qué elementos componen un pipeline?
Un pipeline se compone de distintas etapas (stages) secuenciales que ejecutan tareas (steps) que son lanzadas en nodos de trabajo (nodes).
Step
Son las tareas ó comandos que ejecutados de forma secuencial implementan la lógica de nuestro flujo de trabajo. Por ejemplo, un comando puede ser la escritura de un mensaje en la consola echo 'Mensaje de prueba'
ó por ejemplo la descarga de un repositorio checkout scm
.
Node
Los nodos son agrupaciones de tareas o steps que comparten un workspace. Los conjuntos de steps son añadidos a la cola de Jenkins para ser ejecutados cuando haya algún espacio libre entre los agentes de ejecución. Los agentes de ejecución pueden ser la misma máquina del servidor maestro de Jenkins o un Jenkins en otra máquina en modo esclavo dedicado a este propósito.
Es importante reseñar que el directorio de trabajo (workspace) es compartido por los steps del nodo, de forma que steps de un nodo pueden acceder a ficheros/directorios generados por steps de ese mismo nodo. Por el contrario un step de un nodo no puede acceder al workspace de otro nodo.
Stage
Son las etapas lógicas en las que se dividen los flujos de trabajo de Jenkins. Cada stage puede estar compuesto por un conjunto de steps, y cada node puede contener un conjunto de stages.
Es una práctica recomendada dividir nuestro flujo de trabajo en etapas ya que nos ayudará a organizar nuestros pipelines en fases. Además Jenkins nos muestra los resultados de la ejecución del pipeline divido en etapas, mostrando el resultado de la ejecución de cada una de ellas con un código de colores.
Creación del pipeline
Como paso previo debemos tener instalado un servidor Jenkins 2 con los plugins necesarios para la construcción del pipeline. Si aún no tienes un servidor Jenkins puedes seguir el Tutorial Jenkins 2 – Instalación para instalarlo de forma sencilla.
Vamos a crear un sencillo pipeline para controlar el ciclo de vida una aplicación Java desarrollada con Spring Boot y Maven. Utilizaremos el plugin de Jenkins Multibranch Pipeline
que nos permite definir el pipeline en el propio Git del proyecto, de forma que podemos mantener en el proprio SCM el historial de cambios del pipeline. Además vamos a configurar un hook en Github para que cada vez que se realice un commit en el repositorio se lance una nueva ejecución del pipeline.
A continuación detallamos los pasos para la creación del pipeline:
1.- Creación del repositorio en Github
En nuestro caso hemos creado el repositorio https://github.com/dmunozfer-blog/tutorial-jenkins-2 en el que hemos subido el código de una aplicación de ejemplo que muestra un mensaje de bienvenida al usuario.
2.- Definición del pipeline
La definición del pipeline la vamos a realizar en un fichero Jenkinsfile
que subiremos en el directorio principal de nuestro repositorio.
El fichero Jenkinsfile es un script basado en Groovy que permite definir un pipeline de Jenkins.
Se recomienda que la primera línea del script sea #!groovy
para que los editores puedan interpretar que es un script de Groovy y lo muestren correctamente.
En nuestro caso hemos definido un pipeline compuesto de cuatro etapas:
- Compilar: Descarga los fuentes del proyecto desde Github
checkout scm
y los compila con el comandomvn clean compile
. - Test: Ejecuta los test de la aplicación utilizando el plugin surefire de maven
mvn verify
. - Instalar: Genera los binarios de la aplicación, en nuestro caso un jar, utilizando el comando
mvn install -Dmaven.test.skip=true
. - Archivar: Archiva los paquetes generados para poder ser visualizados desde Jenkins.
Contenido del fichero Jenkinsfile:
#!groovy node { // ------------------------------------ // -- ETAPA: Compilar // ------------------------------------ stage 'Compilar' // -- Configura variables echo 'Configurando variables' def mvnHome = tool 'M3' env.PATH = "${mvnHome}/bin:${env.PATH}" echo "var mvnHome='${mvnHome}'" echo "var env.PATH='${env.PATH}'" // -- Descarga código desde SCM echo 'Descargando código de SCM' sh 'rm -rf *' checkout scm // -- Compilando echo 'Compilando aplicación' sh 'mvn clean compile' // ------------------------------------ // -- ETAPA: Test // ------------------------------------ stage 'Test' echo 'Ejecutando tests' try{ sh 'mvn verify' step([$class: 'JUnitResultArchiver', testResults: '**/target/surefire-reports/TEST-*.xml']) }catch(err) { step([$class: 'JUnitResultArchiver', testResults: '**/target/surefire-reports/TEST-*.xml']) if (currentBuild.result == 'UNSTABLE') currentBuild.result = 'FAILURE' throw err } // ------------------------------------ // -- ETAPA: Instalar // ------------------------------------ stage 'Instalar' echo 'Instala el paquete generado en el repositorio maven' sh 'mvn install -Dmaven.test.skip=true' // ------------------------------------ // -- ETAPA: Archivar // ------------------------------------ stage 'Archivar' echo 'Archiva el paquete el paquete generado en Jenkins' step([$class: 'ArtifactArchiver', artifacts: '**/target/*.jar, **/target/*.war', fingerprint: true]) }
El script es bastante sencillo de comprender pero se pueden destacar las siguientes líneas:
step([$class: 'JUnitResultArchiver', testResults: '**/target/surefire-reports/TEST-*.xml'])
Archiva los resultados de las pruebas realizadas con el plugin surefire de Maven para poder ser visualizados desde la interfaz web de Jenkins. Además este comando está envuelto en una estructura try/catch para que en el caso de que falle algún test los resultados también sean archivados.
step([$class: 'ArtifactArchiver', artifacts: '**/target/*.jar', fingerprint: true])
Archiva los ficheros jar generados por Maven para que estén disponibles desde la interfaz web de Jenkins.
3.- Dando de alta el pipeline en Jenkins
El primer paso será el acceso a la interfaz web de administración de nuestro servidor Jenkins, para ello navegamos a la URL de nuestro servidor http://IP_JENKINS:8080/ y tras identificarnos se nos mostrará la pantalla de bienvenida.
Cómo vamos a utilizar Maven para la construcción de nuestra aplicación tenemos que configurar la herramienta Maven en nuestro Jenkins. Para ello pulsamos sobre Adminsitrar Jenkins -> Global Tool Configuration
. Nos desplazamos hasta la parte inferior en la sección Maven y pulsamos en el botón Añadir Maven
. Introducimos como nombre M3
(el mismo que configuramos en el Jenkinsfile) y marcamos la opción Instalar automáticamente
. A continuación pulsamos sobre el botón Apply
.
El siguiente paso será añadir una nueva tarea para la definición del pipeline, para ello pulsamos sobre el enlace Back to Dashboard
y a continuación pulsamos sobre el enlace Nueva Tarea
.
Nos aparecerá una pantalla en la que debemos introducir el nombre y tipo de la tarea, en nuestro caso introduciremos tutorial-jenkins-2
y seleccionaremos la opción Multibranch Pipeline
. A continuación pulsamos el botón OK
.
Se nos mostrará la pantalla de configuración de la tarea. La configuración por defecto será suficiente, lo único que debemos configurar son los datos de el repositorio de GitHub. Para ello pulsamos sobre el desplegable Add source
debajo de la sección Branch Sources
y seleccionamos la opción GitHub
.
En el campo owner introducimos nuestro nombre de usuario de GitHub dmunozfer
y en repository seleccionamos el repositorio donde tenemos el código fuente de la aplicación junto con el fichero Jenkinsfile dmunozfer.es-tutorial-jenkins-2
. Pulsamos Save
para guardar la configuración.
Ya tenemos configurado la tarea de Jenkins con el pipeline que leerá desde el repositorio de GitHub.
4.- Configurando hook en GitHub
El siguiente paso será configurar un hook en el repositorio de GitHub para que cada vez que se realice un commit en el repositorio se inicie la tarea de Jenkins creada anteriormente. Para ello desde nuestro repositorio de GitHub pulsamos sobre el enlace Settings -> Webhooks & Services
. Se mostrará la pantalla de configuración de los hooks, pulsamos sobre Add service
y escribimos jenkins
, seleccionamos el servicio Jenkins (GitHub plugin)
.
Se nos mostrará la pantalla de configuración del servicio Jenkins de GitHub, en esta pantalla únicamente tenemos que introducir la URL al hook de nuestro Jenkins que será de la forma http://IP_JENKINS:8080/github-webhook/
y pulsamos el botón Add Service
.
¡Enhorabuena! En este momento ya tenemos configurado nuestro sistema de integración continua con Jenkins + Maven + GitHub.
Comprobando funcionamiento
Para comprobar que todo funciona correctamente únicamente tenemos que realizar un commit en nuestro repositorio. Una vez realizado, accedemos a la interfaz de Jenkins para ver el resultado de la ejecución de la tarea del pipeline tutorial-jenkins-2
.
Accedemos a la pantalla principal de Jenkins y se nos mostrará la tarea multibranch definida anteriormente con un sol espléndido para indicarnos que no tiene errores. Pulsamos sobre tutorial-jenkins-2 para ver el detalle de la tarea.
A continuación se mostrarán todas las ramas que contenga nuestro repositorio (en nuestro caso solo master) con la información de la última ejecución del pipeline para cada una de las ramas. Pulsamos en el enlace master
para ver en detalle la información de la rama.
En el detalle de la rama master podemos ver en la parte central las etapas de nuestro pipeline y el resultado de las últimas ejecuciones, en nuestro caso todas las etapas acabaron correctamente. En la parte superior podemos acceder al último .jar creado correctamente y en la parte inferior a los informes de test generados.
Para finalizar vamos a realizar un cambio en los test del programa para forzar un error. Una vez realizado el cambio realizamos un commit y un push a GitHub. De forma automática GitHub creará una nueva ejecución de nuestro pipeline. Si esperamos unos segundos veremos que se empieza a ejecutar una nueva instancia del pipeline y se nos muestran los resultados.
Como forzamos un fallo en los test vemos que la ejecución del pipeline se detuvo en la fase de Test
. Además podemos ver ha aparecido un gráfico de evolución de test fallidos en la parte superior derecha. En la parte inferior de la pantalla seguimos viendo el informe de los test, si accedemos en el enlace podremos ver en detalle los casos de prueba ejecutados y el resultado de cada uno de ellos.
Conclusión
Crear un pipeline sencillo en Jenkins no es complejo y nos será de gran ayuda a la hora de mantener y monitorizar el flujo de vida de nuestra aplicación desde el desarrollo hasta la puesta en producción. Para hacer flujos más complejos podéis consultar la documentación oficial, además dejo unos enlaces con documentación y ejemplos sobre los pipelines de Jenkins en el apartado recursos.
Espero que este tutorial os sirva como puerta de entrada en el mundo de los pipeline de Jenkins. A mí me ha resultado muy interesante hacerlo para fijar los conceptos y como base para el pipeline que estoy preparando para uno de mis próximos proyectos.
¡Nos vemos en la siguiente entrada!
Recursos:
- https://jenkins.io/doc/pipeline/
- https://github.com/dmunozfer-blog/tutorial-jenkins-2
- http://dmunozfer.es/jenkins-2-tutorial-instalacion/
- https://jenkins.io/doc/pipeline/jenkinsfile/
- https://github.com/jenkinsci/pipeline-plugin/blob/master/TUTORIAL.md
- https://jenkins.io/doc/pipeline/steps/
- https://jenkins.io/blog/2015/12/03/pipeline-as-code-with-multibranch-workflows-in-jenkins/
15 Comentarios
Puedes escribir comentarios en esta página.
Hola David!
Estoy siguiendo el tutorial, pero estoy teniendo un problema.
El problema es que en la pantalla Status del proyecto me muestra este mensaje:
https://uploads.disquscdn.com/images/c3f6941bb054570a10e815fd7f5ef0683b68bd95b008e7a9fb652a34903ee201.png
He seguido los pasos del tutorial, pero no se por que aparece ese mensaje.
Podrias ayudarme?
Muchas gracias.
Saludos.
AleGallagher hace 6 años
Hola, he visto que estaba mal el enlace al repositorio de github… había renombrado el repositorio y se me olvidó modificar la entrada del blog 🙁
Respecto al error que te está dando, parece que el fichero Jenkisfile no existe en el directorio raiz de tu repositorio git. ¿Has comprobado si realmente existe?
David Muñoz Fernandez hace 6 años
Mucha gracias por tu respuesta David!
Efectivamente, el archivo existe y está en el root del repositorio. Mi repositorio es el siguiente:
https://github.com/AleGallagher/Prueba1
https://uploads.disquscdn.com/images/6cf5126240dee0438fb2d69bebc5083cfe9079203ede478d332a1c37ade83a8c.png
Y la configuración del proyecto es como muestra esta imagen.
La verdad que no se cual puede ser el problema, hace días que estoy tratando de resolverlo.
Muchas gracias.
Saludos.
AleGallagher hace 6 años
Hola, he estado haciendo una prueba con tu proyecto y funciona bien… yo creo que el error que te está dando es por problema de configuración de las credenciales de acceso al repositorio de git.
Revisa en «Scan Multibranch Pipeline Log» a ver si está accediendo correctamente al repositorio.
Saludos!
David Muñoz Fernandez hace 6 años
Gracias por la respuesta David.
He probado poner «None» en Credentials y pasa el mismo problema. Intuyo que las credenciales no hacen falta ya que es un repositorio público.
No se que podrá ser. ¿Algo del plugin?
Muchas gracias.
Saludos.
AleGallagher hace 6 años
Igual el problema es del plugin de GitHub…. utiliza la API de GitHub y cuando no se accede con credenciales utiliza el acceso anónimo que ahora mismo parece que está limitado en número de peticiones (y ahora supera el límite).
Para configurarlo con tus credenciales:
– Genera un token en GitHub para poder acceder a la API con tus credenciales en este enlace https://github.com/settings/tokens
– Configura ese token en las opciones de «Configurar el Sistema» de Jenkins, como se muestra en las siguientes capturas:
https://uploads.disquscdn.com/images/7fe1d693b9f924f46c87ceb5125a88da051559ff46d7512aa2e1f972e55f6f24.png
https://uploads.disquscdn.com/images/8b33b5e69cb64d44adf95fdcfc1c0abf4be9e5d264670dd2a08270a10735c924.png
Espero que con eso se solucione el problema. Si te sigue fallando copia la salida de «Scan Multibranch Pipeline Log» a ver si da más pistas sobre el error.
Saludos
David Muñoz Fernandez hace 6 años
Gracias por la respuesta David.
Creé la nueva credencial, poniendo en el campo secret lo que generó el token, y dice que la misma se ha verificado:
https://uploads.disquscdn.com/images/d31b2533599809ad4ca31d44d1ab01a6c141216d269376dee4b1e1205964f097.png
La configuración del proyecto quedó como la siguiente imagen:
https://uploads.disquscdn.com/images/50afeb425b89c14a45ebc0fc493d1497a38a0c9531856e9d124032f5ede9b8e5.png
Y los logs dicen que fue un éxito, aunque a mi no me aparece con el nombre «Scan Multibranch Pipeline Log», me aparece como Repository Log, tendrá algo que ver?:
https://uploads.disquscdn.com/images/4856205a143a5b6a140c739b8b2e99c0ea8f947796dad046a98f74fb60f859e1.png
Y la pantalla de estatus sigue siendo la misma, con el mensaje de Folder empty.
No sé que podrá ser la verdad, ya me tiene loco.
Muchas gracias.
Saludos.
AleGallagher hace 6 años
PD: Estos son los plugins que tengo instalados, por si ayuda de algo (supongo que no será problema tener instalados plugins de GitLab y GitHub):
https://uploads.disquscdn.com/images/d0ac1c158f87c7bfe51a4883e668712711986e4363b79eae80209fb116f75fff.png
Muchas gracias.
Saludos.
AleGallagher hace 6 años
Hola, sinceramente no se cual puede ser el problema…. yo he configurado un nuevo pipeline en jenkins apuntando a tu repositorio de GitHub y me detecta correctamente el fichero Jenkinsfile. Te paso unas capturas de la configuración para que compruebaes si lo tienes igual:
https://uploads.disquscdn.com/images/81d23ae441ebc826540462404a65622a113d79791c6e3001beddc9b2bd106206.png
https://uploads.disquscdn.com/images/cedbdb3f7f37c2f992bdadebda15d9ad2f7b636a82407b42acd5634a29980f53.png
En mi log de escaneo del repositorio se puede ver que sí encuentra en Jenkinsfile
https://uploads.disquscdn.com/images/9f2e1c0b7fa48b9a9296e2b0b0fccff782858cf57a72fe70f0424eb0565abfea.png
Prueba a revisar los logs de jenkins a ver si encuentras algo extraño que te de pistas de lo que está pasando. Los logs de jenkins los puedes ver en http://IP_JENKINS:PUERTO_JENKINS/log/all
David Muñoz Fernandez hace 6 años
Hola David!
Gracias por la respuesta.
Funcionó al fin!!! 😀
Le puse los behaviours y ahí anduvo.
Te hago una última consulta, para el caso de Discover branches, que significa la opción «Exclude branches that are also filed as PRs» ?
Muchas gracias por todo.
Disculpá las molestias.
AleGallagher hace 6 años
Gracias , muy buen tutorial!
Eduardo Pinuaga hace 6 años
Buenos días, tengo el proyecto de ejemplo pero en bitbucket pero en el stage : «Tests» sale un error y no se que pueda ser al que me pueda ayudar muchas gracias.
y otra pregunta, si falla un paso en un stage no seguiria hasta el otro stage? https://uploads.disquscdn.com/images/5e78aa431bc75a8cfbd8e802014f2925bdfb34b0897d8367229162143b30eef9.png https://uploads.disquscdn.com/images/b555883f5b60de4bc0a9c7632597d1ab2423dfede44eee6d8f028fa319cc1f7f.png
Daniel Toro hace 6 años
Hola Daniel,
Tienes razón, hice el commit del cambio en los test para forzar el fallo y lo subí a la rama master, cuando pueda a ver si cambio el post para aclarar este tema. Puedes ver el diff del commit el cambio que hay que hacer para que pasen todos los test https://github.com/dmunozfer-blog/tutorial-jenkins-2/commit/ac1e9266ef238e2a713b8caada5e093b21fc1962.
Respecto a lo que comentas de los fallos, en cuanto uno de los stage da error se para la build y no continúa al siguiente. Es el comportamiento habitual, normalmente cuando falla uno de los pasos no quieres que siga ejecutándose los posteriores.
David Muñoz Fernandez hace 6 años
Buenas
Se ha cambiado la dirección IP por la migración y ahora cuando publico sobre FTP me sale el siguiente error:
FTP: Connecting from host [localhost.localdomain]
FTP: Connecting with configuration [ProgramaClientes] …
FTP: Disconnecting configuration [ProgramaClientes] …
ERROR: Exception when publishing, exception message [Could not write file. Server message: [550 0.716db6827518661177e7.chunk.js: Permiso denegado
]]
Build step ‘Send files over FTP’ changed build result to UNSTABLE
Finished: UNSTABLE
Alguien me podría decir como se puede solucionar?
gracias de antemano
Un saludo
Alvaro José Fernández de la Cu hace 5 años
Buenas
Acabo de crear una nueva tarea a ver si se solucionaba pero de nuevo al transferir sobre FTP:
ERROR: Exception when publishing, exception message [Could not write file. Server message: [550 3rdpartylicenses.txt: Permiso denegado
]]
Build step ‘Send build artifacts over FTP’ changed build result to UNSTABLE
Finished: UNSTABLE
Alvaro José Fernández de la Cu hace 5 años
Responder