Uso de pipelines en Jenkins

Uso de pipelines en Jenkins

Uso de pipelines en Jenkins

Tiempo de lectura: 5

En este artículo vamos a ver el uso de uno de los componentes más utilizados de la industria.

Jenkins es un servidor open source para la integración continua, una herramienta que se utiliza para compilar y probar proyectos de software de forma continua. Esto facilita que los desarrolladores puedan integrar cambios en un proyecto y entregar nuevas versiones. Es un programa escrito en Java, multiplataforma y accesible mediante interfaz web.

Actualmente, es uno de los componentes más utilizados en la industria para este propósito.

Ventajas

Entre las ventajas más destacadas podemos nombrar:

CONFIGURACIÓN
SENCILLA

Fácil de instalar y no requiere instalaciones o componentes adicionales

AUTOMATIZACIÓN

Proporciona el beneficio de ahorro de tiempo y reducción de errores en los procesos de despliegue y prueba de software

MULTIPLATAFORMA

Jenkins está disponible para todas las plataformas y diferentes sistemas operativos, ya sea OS X, Windows o Linux

TROUBLESHOOTING

Cuenta con un sistema capaz de identificar y solucionar los fallos muy rápidamente

ARQUITECTURA DE COMPLEMENTOS

Fácilmente configurable; Jenkins se puede modificar y extender fácilmente

CÓDIGO ABIERTO

Jenkins tiene una gran comunidad de usuarios que la respalda

¿Qué es una Pipeline?

Una Pipeline es un conjunto de complementos que inicializan un flujo de integración y entrega continua en Jenkins. Según la documentación de Jenkins, una Pipeline, es una expresión automatizada para obtener el software desde un sistema de control de versiones hacia los equipos de destinos.

Cada cambio en el software pasa por un proceso complejo antes de ser entregado. Este proceso implica construirlo de manera confiable y repetible, y acto seguido hacerlo vascular entre las distintas etapas de la prueba de implementación.

La definición de una Pipeline de Jenkins se escribe en un archivo de texto (llamado Jenkinsfile) que, a su vez, existe en el repositorio de control de versiones del proyecto en cuestión. Esta es la base de “Pipeline-as-code”; trata el flujo de CD (entrega continua) como parte de la aplicación para ser versionada y revisada como cualquier otra pieza del código.

Crear un Jenkinsfile y enviarlo al servidor de control de versiones proporciona una serie de beneficios inmediatos:

Estructura básica de una Pipeline

La sintaxis básica de una Pipeline es la siguiente:

				
					<código>
pipeline {
    agent any #1
    stages {
        stage('Build') { #2
           steps {
                // #3
            }
        }

        stage('Test') { #4
            steps {
                // #5
            }
        }

        stage('Deploy') { #6
            steps {
                // #7
            }
        }

    }
}

</código>
				
			

01

Ejecuta la Pipeline y cualquiera de sus fases en cualquiera de los agentes disponibles.

02

Define la fase <Build>

03

Realiza los pasos relacionados a la fase <Build>

04

Define la fase <Test>

05

Realiza los pasos relacionados a la fase <Test>

06

Define la fase <Deploy>

07

Realiza los pasos relacionados a la fase <Deploy>

Shared libraries y su uso

A medida que se adopta una Pipeline para más proyectos en una organización, es probable que surjan patrones comunes. A menudo, es útil compartir partes de Pipelines entre varios proyectos para reducir las redundancias y mantener el código.

Una Pipeline da soporte a las “Librerías compartidas” que se pueden definir en repositorios de control de versiones externos.

Según la documentación de Jenkins, la estructura de directorios sería la siguiente:

¿Cómo se usan las Shared libraries ?

Las librerías compartidas configuradas por defecto en Jenkins se cargan implícitamente y permiten que Pipelines la use de manera automática. Jenkins es flexible, por lo que permite en un momento determinado hacer uso de otra librería. Para ello se debe de hacer uso de la connotación @Library, especificando el nombre de la biblioteca:

La connotación puede estar en cualquier parte del script según criterios de Groovy.  Cuando se hace referencia a bibliotecas de clases (con directorios src/), convencionalmente, la connotación va en una declaración de importación:

Simplificando Pipelines en Jenkins

En el punto anterior hemos visto la estructura de una Pipeline. Ahora vamos a hacerlo un poco más real. Quedaría de la siguiente manera:

				
					<código>

pipeline {

    agent any

 

    stages {

        stage('step_one') {

            steps {

                dir('Pipelines/PrimerPipeline/') {

                    script {

                        sh "docker build -t oscarenzo/mywebsite:0.0.1 ."

                               sh "echo \"Se va a crear una imagen de docker\""

                        sh "echo \"Nombre: oscarenzo/mywebsite\""

                        sh "echo \"Version: 0.0.1\""

                    }

                }

            }

        }

 

        stage('step_two') {

            steps {

                script {

                    sh "docker run -dit -p 80:80 --name nginx-server oscarenzo/mywebsite:0.0.1"

                }

            }

        }

 

        stage('step_three') {

            steps {

                script {

                    sh "nc -vz 127.0.0.1 80"

                    sh "echo \"Now you can join to the website using this link: http://127.0.0.1:8080\""

                }

            }

        }

    }

}

</código>
				
			

Usando las shared libraries de Jenkins, el Pipeline podría quedarnos de esta manera:

				
					<código>

pipeline {

    agent any

 

    stages {

        stage('step_one') {

            steps {

                script {

                    dir('Pipelines/PrimerPipeline/') {

                        dockerActions.build(

                            image: "oscarenzo/mywebsite",

                            version: "0.0.1"

                        )

                    }

                }

            }

        }

        stage('step_two') {

            steps {

                script {

                    dockerActions.runContainer(

                        image: "oscarenzo/mywebsite",

                        version: "0.0.1",

                        port: "80"

                    )

                }

            }

        }

        stage('step_three') {

            steps {

                script {

                    dockerActions.digest(

                        port: "80"

                    )

                }

            }

        }    

    }

}

</código>
				
			

Como se puede apreciar en los ejemplos, hemos pasado de tener esto:

				
					<código>
stage('step_one') {
            steps {
                dir('Pipelines/PrimerPipeline/') {
                    script {
                        sh "docker build -t oscarenzo/mywebsite:0.0.1 ."
                        sh "echo \"Se va a crear una imagen de docker\""
                        sh "echo \"Nombre: oscarenzo/mywebsite\""
                        sh "echo \"Version: 0.0.1\""
                    }
                }
            }
        }
</código>

A esto:

<código>
stage('step_one') {
            steps {
                script {
                    dir('Pipelines/PrimerPipeline/') {
                        dockerActions.build(
                            image: "oscarenzo/mywebsite",
                            version: "0.0.1"
                        )
                    }
                }
            }
        }
</código>
				
			

Con esto, hemos conseguido estandarizar parte del Pipeline, gestionando de una única manera la tarea de creación de una imagen de Docker y todas las tareas que podrían seguir de ella. Además de hacerlas más parametrizables, haciendo uso de variables, convertido la tarea, o conjunto de tareas, en una función.

Vamos a ir un poco más allá y simplificamos aún más la Pipeline, convirtiendo esto:

				
					<código>

pipeline {

    agent any

 

    stages {

        stage('step_one') {

            steps {

                dir('Pipelines/PrimerPipeline/') {

                    script {

                        sh "docker build -t oscarenzo/mywebsite:0.0.1 ."

                        sh "echo \"Se va a crear una imagen de docker\""

                        sh "echo \"Nombre: oscarenzo/mywebsite\""

                        sh "echo \"Version: 0.0.1\""

                    }

                }

            }

        }

 

        stage('step_two') {

            steps {

                script {

                    sh "docker run -dit -p 80:80 --name nginx-server oscarenzo/mywebsite:0.0.1"

                }

            }

        }

 

        stage('step_three') {

            steps {

                script {

                    sh "nc -vz 127.0.0.1 80"

                    sh "echo \"Now you can join to the website using this link: http://127.0.0.1:8080\""

                }

            }

        }

    }

}

</código>
				
			

En esto:

				
					<código>

dockerPipeline(

    image: "oscarenzo/mywebsite",

    version: "0.0.1",

    port: "80"

)
</código>
				
			

De esta forma logramos estandarizar toda la Pipeline para que gestione de una única manera las tareas, fases y formas de ejecución de estas. Siguiendo la línea de hacerlas más parametrizables con el uso de variables, hemos convertido toda la Pipeline en una sola función.

Acceder a secretos y variables de entorno

Función credentials

La función credentials cuelga del plugin credentials-plugin, con esta podemos acceder a los secretos disponibles en nuestro Jenkins únicamente indicando como valor el id de los mismos .Por ejemplo:

				
					<código>
AWS_ACCESS_KEY_ID     = credentials('jenkins-aws-secret-key-id')

echo ‘${AWS_ACCESS_KEY_ID_USR}’
echo ‘${AWS_ACCESS_KEY_ID_PSW}’
</código>
				
			

Esto es útil para casos en los que se desee disponer de la credencial durante todo el ciclo de la Pipeline. Con ello nos evitamos tener que realizar múltiples consultas cada vez que se requiera de un secreto.

Este plugin soporta los siguientes tipos de secretos:

Función with Credentials

La función with Credentials cuelga del plugin credentials-binding-plugin, con esta podemos acceder a los secretos disponibles en nuestro Jenkins indicando el id de los mismos y asignando variables a los campos disponibles en estos de manera dinámica. Por ejemplo:

				
					<código>

withCredentials([usernamePassword(credentialsId: 'amazon', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {

  sh 'echo $PASSWORD'

  // O También podemos imprimirlo como variable de Groovy

  echo USERNAME

}
</código>
				
			

Es importante tener en cuenta que ésta estará disponible sólo mientras dure la invocación, es decir, que esta credencial no estará disponible en la siguiente tarea, por lo que será necesario invocarla tantas veces sea necesario durante toda la vida de la Pipeline.

Accediendo a variables en Jenkins

Las variables de entorno en Jenkins son comunes para toda la Pipeline. Por ejemplo:

				
					<código>
pipeline {
    agent any

    environment {
        image = “oscarenzo/mywebsite”
        version = “0.0.1”
    }


    stages {
        stage('step_one') {
            steps {
                dir('Pipelines/PrimerPipeline/') {
                    script {
                        sh "docker build -t ${image}:${version} ."
                    }
                }
            }
        }

        stage('step_two') {
            steps {
                script {
                    sh "docker run -dit -p 80:80 --name nginx-server ${image}:${version}"
                }
            }
        }

        stage('step_three') {
            steps {
                script {
                    sh "nc -vz 127.0.0.1 80"
                    sh "echo \"Now you can join to the website using this link: http://127.0.0.1:8080\""
                }
            }
        }
    }
}
</código>

				
			
Enzo
Ana Ramírez

Ana Ramírez

¿Te ha resultado interesante?

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

SÍGUENOS

CATEGORÍAS

ÚLTIMAS ENTRADAS