GitLab CI es una herramienta muy potente y cada vez más utilizada. Si no la conoces, te recomiendo pasarte por este artículo que yo mismo escribí en mi blog personal hablando un poco de las bondades de esta herramienta. Hoy vamos a profundizar un poco más sobre los tipos de executors para tus runner en GitLab CI.
Como muchos sabréis, no es lo mismo utilizar una instalación de gitlab en un servidor propio que utilizar la web pública de gitlab.com, especialmente si usas la versión gratuita, mucho más limitada. Hace bastante tiempo, Rubén Aguilera ya nos contó cómo configurar GitLab CI en una instalación propia. Así que hoy vengo a contarte cómo es el entorno en la web pública de GitLab y qué limitaciones tiene. Vamos a enfrentar, principalmente, los executor de tipo SHELL (disponibles en instalaciones propias) frente a los executor DOCKER, (los que puedes usar en la versión gratuita de gitlab.com, y también en tu propio servidor).
Existen numerosos ejecutores, es decir, numerosas formas en las que tu runner puede comunicarse con la máquina servidora. En esta página de la documentación oficial puedes verlo detalladamente. No obstante, como este artículo es más un conjunto de recomendaciones en base a mi experiencia que un tutorial totalmente técnico, voy a centrarme principalmente en los dos ya mencionados.
Cómo funciona en Gitlab.com
Vamos a usar la capa gratuita para que tú mismo puedas probar. Para habilitar esta funcionalidad en un proyecto, accede primero a dicho proyecto y, después, en el menú de ajustes, busca la opción CI/CD:
Aquí podrás habilitar los shared runners. Es decir, que te vas a pelear con el resto de miles de usuarios de GitLab.com para conseguir ejecutar un job.
Además puedes configurar también el despliegue en runners de Kubernetes (o sea, más docker), pero para esto ya hay que pagar, porque serían tus propios contenedores. Así de simple. Ahora comprenderás más qué diferencias hay con un entorno SHELL en un servidor privado.
Ejecución en SHELL
Los shell executors son los más sencillos de todos. Repito que este tipo sólo es posible utilizarlo si tienes GitLab instalado en tu propio servidor.
El runner de GitLab hará simplemente de puente entre tu repositorio y una sesión de terminal del servidor. Supongamos un proyecto que utiliza java y maven. Para los tests de integración, con un plugin de maven o con test containers de Spring Boot, podemos levantar un contenedor. Para poder funcionar, por tanto, necesitamos que nuestro servidor tenga instalada la versión de java que necesitamos, también maven (aunque sería válido maven wrapper) y, por supuesto, docker.
De esta forma, los scripts de nuestros jobs se van a ejecutar directamente como si nosotros mismos estuviéramos escribiendo en la terminal del servidor. Esta imagen puede ayudarte:
Esto provoca que el archivo .yml de tu proyecto sea más simple. Ya que con una sola línea puedes hacer mucho:
script: - mvn clean install -U -P *profileName* sonar:sonar -Dsonar.login=$SONAR_TOKEN -Dsonar.sourceEncoding=UTF-8
La diferencia radica en que, a la hora de escribirlo, piensas igual que si estuvieras escribiendo en la propia terminal de la máquina.
Ventajas y desventajas
++ Menor complejidad en tu yml. Cuando escribas tu .gitlab-ci.yml, tendrás que pensar como si lo estuvieras ejecutando tú mismo en la máquina. Desde luego, es una perspectiva mucho más simple que otras.
++ Más libertad. Al tratarse de comandos ejecutados sobre una máquina, tienes toda la libertad que tu máquina te ofrece. Ten cuidado, eso sí, de que mayor libertad no signifique mayor inseguridad.
++ Control total. No dependes de contenedores o servicios externos y todo lo que gestionas está realmente instalado en la máquina.
++ Entornos reales 100%. No vas a ejecutar nada sobre un servicio virtualizado. Va a ser una máquina totalmente real, por lo que ganas en cuanto a parecidos a producción.
— Mayor curva de configuración inicial de tu servidor. La ventaja de tener archivos yml más simples se ve descompensada porque tienes que instalar todo lo necesario. Por tanto, solo compensa si el servidor va a alojar proyectos con tecnologías similares.
— Casi imposible garantizar que el entorno está limpio. A diferencia de los contenedores, que se crean y se destruyen.
— Implicará posiblemente mayor dificultad de gestión y mantenimiento a largo plazo. Si tienes proyectos en java, otros en typescript, otros en python, otros en C, etc… Vas a tener que tener todas esas herramientas, frameworks y tecnologías instaladas. Y claro, eso requiere sus actualizaciones, reparaciones, mantenimientos… Puedes volverte realmente loco.
Casos de uso
Vistas sus ventajas y desventajas, yo lo veo muy claro. Usa este tipo de runner si, y solo si, tu servidor va a estar ocupado por un número bajo de proyectos, y mejor si son proyectos que utilicen tecnologías similares. Por ejemplo, si todos tus proyectos back son Java y tu front React, el número de herramientas a instalar es muy bajo y puedes suplir muchos proyectos, por lo que sacas partido a la hora de crear tus scripts.
Ejecución en DOCKER
Aquí el paradigma cambia. Ahora, cuando escribas tus scripts, no puedes pensar que estas en una máquina real. Volvamos al ejemplo de antes con java, maven y tests de integración automatizados con test containers. Si lanzas tu ejecución en un contenedor java, no van a funcionar los tests container porque no puedes ejecutar un «docker run» dentro de otro contenedor. ¿Vas pillando? Por tanto, deberás configurar tus diferentes perfiles para que se adapten según el entorno y, como te explicaré más adelante, esto te hará pensar más a la hora de escribir tu yml.
Con esta imagen seguro que te queda mucho más claro:
Vale, seguimos con nuestro proyecto java, maven y todo eso. En este caso necesitas un perfil específico para el entorno de integración que deshabilite la creación de contenedores y lo que vas a hacer va a ser especificar algo así al principio de tu yml:
stages: - test - deploy services: - mysql:8 variables: MYSQL_DATABASE: mon3x MYSQL_ROOT_PASSWORD: root image: openjdk:13
Indicamos que necesitamos un servicio de mysql versión 8. Además, le especificamos dos variables: el nombre de la base de datos y la clave del usuario root. Da igual que esta clave se vea en el código del repositorio, solo va a estar ejecutada durante los tests.
Y, por supuesto, indicamos que la imagen que queremos en nuestro contenedor es java 13. De esta forma, sin que nuestros test container ni nadie levanten una base de datos, nos podremos conectar a una base de datos dockerizada cuyo nombre y contraseña root son los especificados.
Como ves, no es difícil, pero sí que resulta algo más engorroso. Sobre todo porque te enfrentas a ese cambio en la manera de pensar. Ya no vale pensar que estás en una máquina real, y puede resultar difícil de ver al principio.
Ventajas y desventajas
++ Los entornos siempre se iniciarán vacíos.
++ Menos problemas de configuración. Tu servidor solo necesitará GitLab y Docker.
++ Mayor polivalencia entre diferentes tecnologías. Si tienes cien proyectos y cada uno es de su padre y de su madre, te da igual. Van a tirar de Docker, punto pelota.
— Menor grado de libertad. Ahora estás en un sistema virtualizado, tus posibilidades se reducen a las extrictamente necesarias. Puede darte problemas según qué formas hayas diseñado para resolver ciertos problemas.
— Cuidado con ejecuciones masivas. He pasado por el problema de los dead containers y créeme, si todo tu sistema depende de docker, te vuelves loco. Cuidado también con cuánto espacio te ocupa docker en tu sistema. Vas a tener volúmenes e imágenes muertas a porrón. Pero bueno, nada que no se arregle con un buen prune programado, ¿no?.
— Más engorroso a la hora de pensar y escribir tus yml, que se ve compensado con la mayor facilidad a la hora de instalar y configurar todo el sistema en tu servidor.
Casos de uso
Seguramente pienses como yo y veas que los runners sobre docker son más usables. En general, siempre te va a funcionar con casi cualquier sistema. Si no sabes cuál utilizar, mi recomendación es que uses este. La ligera mayor curva en los yml, en cuanto te acostumbras, se te hace normal. Y docker tiene muchísimas imágenes, lo que te hace la vida muy fácil.