Pipelines en Gitlab, el camino hacia el CI/CD (parte I)

Pipelines en Gitlab, el camino hacia el CI/CD (parte I)

En esta entrada se dará una visión general sobre qué es y cómo configurar un Gitlab Runner. También se explicará qué es y cómo configurar un pipeline. Por último, se enseñará un pequeño proyecto-ejemplo para poner en práctica los conocimientos adquiridos.

En una entrada anterior explicamos la teoría del CI/CD, en qué consiste y para qué sirve. La teoría está bien, pero no hay nada mejor para terminar de asimilar la teoría que un buen ejemplo de uso práctico.

Hemos elegido Gitlab para este fin ya que, en nuestra opinión, es muy fácil de instalar, configurar, mantener y visualmente es muy sencillo de comprender.

Nota: Este artículo no cubrirá el proceso de instalación de Gitlab en sí. Damos por hecho que el público al que este artículo está dirigido dispone de una instalación de Gitlab o, en su defecto, cuenta con los conocimientos y habilidades suficientes para llevar al cabo la instalación por su cuenta.

Nota: Todas las explicaciones en este artículo se harán partiendo de la premisa de que se dispone de una instalación de Gitlab CE 12.8, es decir, la última versión gratuita de Gitlab disponible en este momento. No se mencionarán características disponibles únicamente en la versión de pago.

Nota: Los ejemplos están basados en un Debian Buster (10).

Nota: Se da por hecho que el público al que este artículo está dirigido tiene unas nociones básicas sobre el uso de Git y Gitlab: entiende lo que es un grupo, un proyecto, un commit, etc…

# Gitlab Runners

El proyecto de Gitlab Runner es un complemento de Gitlab. Se trata de un entorno al que Gitlab puede enviar tareas para su ejecución y recibir los resultados de las mismas. Dicho de otra manera, nosotros configuramos una serie de tareas en cada uno de nuestros proyectos (mediante el archivo «.gitlab-ci.yml»), Gitlab las interpreta y transforma en pipelines, se las envía al Runner, éste las ejecuta y devuelve los resultados de las tareas a Gitlab, que a su vez los representa de manera gráfica sobre los mismos pipelines.

image

El primer paso para poder trabajar con Gitlab Runner es instalarlo. Existen varias formas de hacerlo, en este caso optaremos por usar el gestor de paquetes.

$ curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash
$ sudo apt-get install gitlab-runner

Al instalar Gitlab Runner, se creará un usuario con el mismo nombre. Para el correcto funcionamiento del ejemplo que se describe más adelante en esta entrada, debemos añadir dicho usuario a la lista de «sudoers». Esto nos permitirá poder ejecutar comandos con «sudo» desde nuestro pipeline.

$ sudo sh -c "echo 'gitlab-runner ALL=NOPASSWD: ALL' >> /etc/sudoers"

Atención: Añadir el usuario del Gitlab Runner a la lista de «sudoers» y usar la opción «NOPASSWD» es una mala práctica. Se usa en esta entrada en pos de rebajar la dificultad de la misma.

Por desgracia, existe un bug en Gitlab Runner que causará fallos en nuestro pipeline a menos que borremos los archivos de configuración de entorno del usuario «gitlab-runner»:

$ sudo rm /home/gitlab-runner/.bashrc
$ sudo rm /home/gitlab-runner/.bash_logout
$ sudo rm /home/gitlab-runner/.profile

El segundo paso es registrar el Runner con nuestra instalación de Gitlab. Tenemos que hacer una especie de conexión entre ambos para que puedan comunicarse.

Cabe mencionar que un Runner puede ejecutar una única tarea a la vez. Aunque en esta entrada registraremos y usaremos un único Runner, existe la posibilidad de registrar múltiples Runners contra la misma instalación de Gitlab, lo cual nos permite ejecutar múltiples tareas en paralelo.

image

El comando que usaremos durante el proceso de registro tiene varias partes que debemos entender, ya que cada una de esas partes afectará a las capacidades que va a tener nuestro Runner.

# Conectividad

El servidor donde esté instalado nuestro Runner debe poder tener una comunicación por red con el servidor donde esté instalado nuestro Gitlab. En nuestro caso esto no es problema, ya que ambos componentes se encuentran instalados en el mismo servidor, pero en caso de no ser así, deberemos plantear y planificar bien este punto.

# Tipo

El tipo determinará qué tareas puede ejecutar nuestro Runner. Puede ser:

  • shared - Podrá ejecutar tareas de cualquier grupo y proyecto.
  • group - Podrá ejecutar tareas de cualquier proyecto dentro del grupo en el que lo hemos registrado.
  • specific - Podrá ejecutar únicamente tareas del proyecto al que lo hemos asignado.

# Token

Por motivos de seguridad, para registrar un Runner necesitamos un token. Hay varios sitios en la interfaz de nuestro Gitlab desde los cuales podemos obtener dicho token. El sitio adecuado dependerá del tipo de Runner que queramos registrar.

  • Si queremos registrar un Runner del tipo «shared», tendremos que ir a la administración de Gitlab, en el apartado «Overview» y copiar el token desde la sección «Runners».

image

Si en cambio queremos registrar un Runner del tipo «group», deberemos ir al grupo de Gitlab que nos interesa, en el apartado «Settings» y copiar el token desde la sección «CI/CD».

image

Por último, si lo que buscamos es registrar un Runner del tipo «specific», deberemos ir al proyecto en concreto al que queremos asociar el Runner, en el apartado «Settings» y copiar el token desde la sección «CI/CD».

image

# Tags

Como acabamos de ver, dependiendo del tipo de Runner que registremos en nuestro Gitlab, este podrá ejecutar solo determinadas tareas. Sin embargo, gracias a los tags, podemos filtrar todavía más las tareas que nuestros Runners pueden ejecutar. Básicamente, a la hora de registrar nuestro Runner podemos especificar una serie de «palabras clave». Haciendo eso, el Runner podrá ejecutar únicamente las tareas que tengamos definidas con esas mismas «palabras clave».

# Executor

El último punto es definir el tipo de mecanismo que nuestro Runner deberá usar para ejecutar las tareas. Como ya dijimos al principio de la entrada, un Runner es la puerta a un entorno donde podemos ejecutar nuestras tareas. Dicho entorno puede ser el mismo servidor donde se está ejecutando nuestro Runner («shell»), algún tipo de maquina virtual dentro del servidor («Parallels», «VirtualBox», «Docker», «Kubernetes») o un servidor remoto («SSH»).

Cada una de estas opciones tiene sus ventajas y sus desventajas, pero dado que el objetivo de esta entrada es ofrecer una visión global acerca del funcionamiento de los Runners de Gitlab, no entraremos a describirlas ni compararlas.

Resumiendo, para no complicar demasiado este ejemplo, vamos a registrar un Runner compartido entre todos los grupos y proyectos, sin tags y del tipo shell.

[email protected]:~$ sudo gitlab-runner register
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/):
https://gitlab-tests.develat.io/
Please enter the gitlab-ci token for this runner:
*******************
Please enter the gitlab-ci description for this runner:
[gitlab-tests]: Mi primer runner
Please enter the gitlab-ci tags for this runner (comma separated):

Registering runner... succeeded                     runner=VyrsXzqX
Please enter the executor: docker-ssh, parallels, ssh, kubernetes, docker+machine, docker-ssh+machine, custom, docker, shell, virtualbox:
shell
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!

Tras registrar el Runner, podemos comprobar si realmente se ha realizado con éxito la operación yéndonos al área de administración, en el apartado «Overview», en la sección «Runners».

image

# Pipelines

A estas alturas ya tenemos nuestro Gitlab y nuestro Runner, pero todavía nos falta un componente: nuestro «pipeline».

Todos los proyectos tienen una serie de requisitos para ser ejecutados. Ya sean librerías o programas instalados en el propio entorno, ya sean archivos de configuración específicos. Nuestro objetivo es automatizar la creación de un entorno idóneo para la ejecución de nuestro proyecto y después automatizar la ejecución del proyecto en sí. Para conseguir esto, debemos definir varias tareas, como la instalación de dependencias o librerías, los comandos necesarios para ejecutar nuestro proyecto, etc…

Todas estas tareas las vamos a separar en «etapas» o «stages». Las etapas son agrupaciones de tareas y tienen un orden específico (definido por nosotros). Cada etapa puede tener una o más de una tarea y las tareas de las etapas no se ejecutan hasta que todas las tareas de la etapa anterior hayan finalizado su ejecución con éxito, a menos que nosotros especifiquemos lo contrario. Un pipeline es la manera abreviada de referirse a dicho cúmulo de etapas en un proyecto.

image

# Definiendo tareas - .gitlab-ci.yml

El ejemplo que vamos a plantear será muy simple en cuanto a funcionalidad. Insistimos en que esta entrada no pretende profundizar demasiado en todas las posibilidades que nos ofrecen los pipelines, sino que pretende explicar de manera simple y visual las partes mas fundamentales del proceso de automatización de tareas.

Por lo tanto, vamos a crear un simple programa hecho con la librería Pandas que va a interpretar una serie de datos y va a imprimir los resultados de realizar un par de operaciones sobre dichos datos.

main.py

import pandas as pd

data = {
    "country": ["Brazil", "Russia", "India", "China", "South Africa"],
    "capital": ["Brasilia", "Moscow", "New Dehli", "Beijing", "Pretoria"],
    "area": [8.516, 17.10, 3.286, 9.597, 1.221],
    "population": [200.4, 143.5, 1252, 1357, 52.98]
}

frame = pd.DataFrame(data)
print(frame)

print('------------------------------')

print("Sum of all the populations", sum(frame['population']))

Por último, vamos a definir dos etapas en nuestro pipeline. En la primera etapa vamos a definir una tarea que va a instalar las dependencias que necesitamos para ejecutar nuestro proyecto. En este caso estas dependencias son «pip» (el gestor de paquetes de Python) y la librería «Pandas». En la segunda etapa vamos a definir una tarea que va a ejecutar nuestro programa.

Nota: Los archivos de configuración de los pipelines son extremadamente flexibles y cuentan con un abanico muy amplio de opciones.

.gitlab-ci.yml

stages:
  - Preparación
  - Ejecución

Instalar_dependencias:
  stage: Preparación
  allow_failure: false
  script:
    - sudo apt update
    - sudo apt install -y python3-pip
    - pip3 install pandas

Ejecutar_programa:
  stage: Ejecución
  allow_failure: false
  script:
    - python3 main.py

Si añadimos estos dos archivos a nuestro proyecto y subimos los cambios a Gitlab, se ejecutará el pipeline que acabamos de definir, ya que Gitlab interpretará de manera automática el contenido del archivo «.gitlab-ci.yml».

image

De ahora en adelante nuestro pipeline se ejecutará cada vez que hagamos un cambio en nuestro proyecto y lo subamos a Gitlab. Por supuesto, podemos inspeccionar el historial de ejecuciones así como los detalles de ejecución de cada tarea.

image

Entrando en los detalles de la ejecución del pipeline, podemos ver el resultado de cada tarea («Instalar_dependencias» y «Ejecutar_programa») en cada etapa («Preparación» y «Ejecución»).

image

image

# Notas y aclaraciones

El tipo de «executor» que se usa en este ejemplo tiene serias carencias y limitaciones, pero también es el mas fácil de comprender, razón por la cual lo hemos elegido para esta entrada. Como explicamos más arriba, los comandos de las tareas se ejecutan directamente sobre el servidor, lo cual significa que en la tarea «Instalar_dependencias» instalamos las dependencias directamente en el servidor, lo cual puede generar conflictos con otros proyectos. Es más, si tuviéramos varios proyectos y varios Runners, todos del tipo «shell», nos encontraríamos con situaciones de conflicto y fallos prácticamente cada vez que se diera la casualidad de subir de manera simultánea a Gitlab cambios de dos proyectos.

En siguientes entradas explicaremos en profundidad los distintos tipos de Runners que se pueden registrar, así como varias maneras de ejecutar múltiples Runners de manera paralela.

¡Hola! ¿Te ha gustado el contenido de esta entrada? ¿Te ha aclarado las dudas que tenias sobre el tema? Si crees que podemos ayudarte a resolver los problemas técnicos en tu negocio o si crees que podemos trabajar juntos en tus proyectos o mejorarlos de alguna manera, escríbenos a [email protected].