Conceptos Iniciales
Lo primero que necesitamos entender es cómo funciona la organización de los códigos dentro de la plataforma de GitLab.
Grupos y Proyectos
En GitLab, la estructura de organización de los códigos se basa en grupos y repositorios, lo que facilita la gestión de equipos, permisos y proyectos.
-
Grupos: Un grupo es como una carpeta principal que puede contener varios proyectos (repositorios) e incluso otros subgrupos. Sirve para agrupar todo lo que pertenece a un mismo equipo, producto o área de la empresa.- Los permisos pueden ser asignados a nivel de grupo, y todos los proyectos heredan esos permisos.
- Ayudan en la organización por áreas (ej: frontend, backend, devsecops) y también en la aplicación de políticas y configuraciones en conjunto.
- Esta es una de las mayores ventajas que veo de GitLab frente a GitHub.
-
Subgrupos: Son como subcarpetas dentro de un grupo. En realidad son otros grupos dentro de grupos.- Permiten una organización aún más granular (ej: empresa/backend/servicios).
- Útiles para empresas grandes o con muchos equipos.
-
Repositorios(Proyectos)- Un repositorio, llamado proyecto en GitLab, es donde realmente vive el código fuente, el historial de commits, pipelines, issues y todo lo demás relacionado con ese proyecto específico.
- Cada proyecto tiene su propio control de versionado Git.
- Puede contener CI/CD configurado mediante .gitlab-ci.yml.
- Puede ser público o privado, y seguir
reglas heredadas del grupo.
empresa/
├── plataforma/ # grupo
│ ├── api-auth # repositorio
│ ├── dashboard # repositorio
│ └── infra/ # subgrupo
│ ├── terraform-network # repositorio
│ └── terraform-vm # repositorio
├── frontend/ # grupo
│ └── landing-page # repositorio
└── backend/ # grupo
├── user-service # repositorio
└── payment-service # repositorio
La jerarquía de grupos en GitLab es esencial cuando pensamos en escala y estandarización dentro de una organización. Los grupos permiten centralizar configuraciones que serán heredadas automáticamente por todos los repositorios hijos. Esto incluye desde variables de entorno, plantillas, hasta permisos de acceso y configuraciones de seguridad.
Cuando configuramos un pipeline en un proyecto, podemos definir variables y reglas directamente en él. Sin embargo, al utilizar grupos, conseguimos aplicar configuraciones globales que serán replicadas para todos los proyectos de ese grupo, evitando repetición y facilitando la gestión.
Cada vez que se ejecuta un pipeline, utiliza un Runner, que es la fuerza computacional responsable de ejecutar los trabajos. Estos runners pueden ser compartidos (a nivel de grupo) o dedicados (a nivel de proyecto), y es posible asociar variables de entorno en el grupo, que son automáticamente leídas por cualquier runner que ejecute pipelines en los proyectos hijos.
Es decir, la organización por grupos no es sólo una cuestión visual o estructural — es fundamental para escalar, estandarizar y mantener la gobernanza de los pipelines y de los entornos de ejecución dentro de GitLab.
Runners
El GitLab Runner es un servicio responsable de ejecutar los trabajos definidos dentro de los pipelines (.gitlab-ci.yml). En otras palabras: el runner es quien pone manos a la obra cuando se dispara un pipeline.
Tipos de Runners
-
Shared Runners (compartidos): Configurados por la instancia de GitLab, disponibles para múltiples grupos y proyectos.
-
Group Runners: Asociados directamente a un grupo. Todos los repositorios (proyectos) dentro de ese grupo pueden utilizarlo.
-
Project Runners: Exclusivos de un proyecto específico. Solo ese proyecto puede utilizarlo.
Esta división permite flexibilidad y control, principalmente cuando hablamos de equipos diferentes, cargas de trabajo distintas o entornos con requisitos específicos.
El runner es el entorno donde el código será ejecutado durante el pipeline. Este entorno puede ser genérico o específico, dependiendo de la necesidad del proyecto.
Por ejemplo, podemos tener un runner que inicie con una imagen básica, como un Ubuntu "limpio", sin ninguna herramienta instalada. Durante la ejecución de los trabajos, el propio pipeline se encarga de instalar todo lo necesario — como dependencias, lenguajes y herramientas — antes de ejecutar el proyecto.
Por otro lado, podemos usar imágenes preconfiguradas con todo lo que el proyecto necesita. Si vamos a ejecutar el proyecto A, podemos usar un contenedor A con todas las tecnologías ya instaladas. Si el proyecto B es una aplicación en Node.js, podemos usar una imagen oficial node que ya viene con Node.js listo para usar. Esto acelera la ejecución y evita repetición de instalación.
En algunos casos, puede tener sentido usar sólo una imagen base y dejar que cada proyecto configure lo que necesita mediante pipeline. Esto da más flexibilidad, pero puede aumentar el tiempo de ejecución y exigir más mantenimiento.
No existe correcto o incorrecto — lo que existe es arquitectura y gobernanza. Cada organización tiene sus propios requisitos de seguridad y cumplimiento, y esto puede impactar directamente cómo los runners son configurados y utilizados.
Por ejemplo, en algunos entornos más restrictivos, puede no estar permitido ejecutar imágenes personalizadas por cuestiones de auditoría o trazabilidad. En esos casos, la personalización necesita ocurrir en tiempo de ejecución, dentro del propio trabajo.
¿Y qué tiene esto que ver con los tipos de runner?
- Un runner compartido (shared), usado por varios proyectos, debe ser lo más genérico y neutral posible, garantizando compatibilidad amplia y seguridad.
- Un runner de grupo ya permite un nivel mayor de personalización, pues está bajo el control de un equipo específico y puede ser adaptado a las tecnologías comunes de ese grupo.
- Un runner de proyecto puede ser altamente especializado, creado a medida para las necesidades de ese proyecto específico — incluso con imágenes personalizadas y herramientas preinstaladas.
Durante la definición de un pipeline elegimos qué runners vamos a utilizar, pero eso es más adelante.
¿Dónde se ejecutan los Runners?
Los runners no son obligatoriamente ejecutados en contenedores, pero en la práctica, el uso de contenedores es muy común porque facilita el aislamiento, la portabilidad y la automatización.
Un runner no es más que un servicio que se comunica con GitLab, listo para clonar un repositorio y ejecutar el código de un pipeline. Si es un servicio en un sistema operativo, ¿qué tenemos normalmente?
| Ejecutor | ¿Dónde se ejecuta? | ¿Usa contenedor? |
|---|---|---|
| shell | En el propio host (como bash) | ❌ No |
| docker | Dentro de contenedores Docker | ✅ Sí |
| docker+machine | En contenedores Docker creados bajo demanda | ✅ Sí |
| kubernetes | En pods dentro de un clúster | ✅ Sí (pods) |
| ssh | En un servidor remoto vía SSH | ❌ No |
| custom | Tú defines | Depende |
¿Por qué usar contenedores entonces?
- Aislamiento: Nada de suciedad entre compilaciones.
- Seguridad: Cada trabajo en un entorno controlado.
- Portabilidad: El mismo contenedor se ejecuta en cualquier lugar.
- Reproducibilidad: "En mi ordenador funciona" pierde fuerza.
No es obligatorio que el runner use contenedor, pero en la mayoría de las veces, es el camino más seguro, limpio y escalable. Y la elección del ejecutor tiene todo que ver con la estrategia de tu infraestructura y con el nivel de control que quieres tener.
Variables de Entorno de los Runners
Un runner carga las variables de entorno definidas en un proyecto. Cuando defines variables de entorno en un proyecto en GitLab (definidas por proyecto o heredadas de grupos padres), esas variables son asociadas al runner durante la ejecución de un pipeline para ese proyecto específico. Esto significa que, al ejecutar el pipeline, el runner tendrá acceso a esas variables y podrá utilizarlas durante la ejecución de los trabajos.
Puedes definir variables de entorno directamente en la interfaz de GitLab, dentro de tu proyecto o de grupos. Estas variables pueden ser usadas para almacenar información como credenciales de API, claves de autenticación, configuraciones de base de datos, entre otras.
Estas variables quedan disponibles para cualquier trabajo dentro de ese proyecto, siempre que el runner esté ejecutando el pipeline.
Cuando se dispara el pipeline del proyecto, el runner que esté configurado para ese proyecto (puede ser un runner específico para el proyecto o un runner compartido) carga las variables de entorno definidas.
El runner pasa esas variables al entorno de ejecución donde el trabajo está siendo ejecutado, ya sea en un contenedor Docker, en un servidor vía SSH u otro tipo de ejecutor.
Desencadenadores de Pipeline
Arriba se comentó el siguiente fragmento "Cuando se dispara el pipeline del proyecto" y te puedes estar preguntando cómo funciona esto. Quien identifica los desencadenadores (triggers) es el propio GitLab de acuerdo con eventos que ocurren en un repositorio. Cuando GitLab identifique un evento, mirará en el archivo .gitlab-ci.yml y verá cómo está la configuración y si debe o no disparar el pipeline basado en las reglas que definiste.
Algunos posibles triggers:
-
Push al Repositorio: La manera más común de disparar un pipeline es a través de un push al repositorio. Siempre que hay un nuevo commit enviado al repositorio (un push), GitLab dispara automáticamente el pipeline para ejecutar los trabajos definidos en el archivo .gitlab-ci.yml.- Ejemplo: Cada vez que hagas un push en una rama (por ejemplo, main o develop), el pipeline será ejecutado.
- Lo que ocurre: GitLab va a buscar el archivo .gitlab-ci.yml y dispara el pipeline con los trabajos definidos en ese archivo.
- Es posible ejecutar cuando un push sea hecho para cualquier rama o alguna rama específica.
-
Merge Requests(MRs): Cuando creas o actualizas un merge request en GitLab, un pipeline puede ser automáticamente disparado para validar los cambios antes de fusionarlos en la rama de destino. Esto ayuda a garantizar que el código propuesto esté funcionando correctamente antes de ser integrado.- Ejemplo: Siempre que se abre un merge request, el pipeline se ejecuta automáticamente para probar y verificar el código de la rama que está siendo fusionada.
- Lo que ocurre: El pipeline se ejecuta para validar el código que será integrado y garantizar que esté en conformidad con las reglas de calidad y pruebas definidas en el .gitlab-ci.yml.
- Es posible ejecutar cuando se abra un merge, pero también se puede especificar para qué rama es ese merge.
-
Triggers Manuales: Puedes configurar triggers manuales para que los pipelines sean disparados solamente cuando un usuario los active manualmente. Esto es útil para acciones que no necesitan ser ejecutadas en cada commit, como despliegues o pruebas específicas que no forman parte del flujo estándar.- Ejemplo: Un trabajo de despliegue que sólo es disparado cuando un responsable autoriza, haciendo clic en el botón de "Play" en la interfaz de GitLab.
- Lo que ocurre: El trabajo es ejecutado sólo cuando el usuario lo solicita explícitamente, proporcionando mayor control sobre cuándo se realizan las acciones.
-
GitLab CI/CD Pipeline Schedules(Programaciones): Puedes configurar programaciones de pipeline, que permiten que un pipeline sea disparado en intervalos regulares o en horarios específicos. Esto es útil para tareas de mantenimiento, como actualizaciones de dependencias o compilaciones periódicas.- Ejemplo: Un pipeline puede ser configurado para ejecutarse todas las noches a las 3 de la mañana para hacer una limpieza o actualizar dependencias.
- Lo que ocurre: El pipeline será disparado automáticamente con base en la programación definida, sin intervención manual.
-
Triggers vía API: GitLab permite disparar pipelines desde fuera del sistema, utilizando triggers vía API. Con esto, puedes integrar GitLab con otros sistemas y herramientas que necesiten disparar pipelines en momentos específicos o después de ciertos eventos.- Ejemplo: Una herramienta de monitorización puede disparar un pipeline cuando se detecte una nueva versión del software o cuando se realice un nuevo commit.
- Lo que ocurre: Utilizas un token de trigger generado por GitLab para hacer un POST vía API y disparar el pipeline de forma externa.
-
Push en Ramas Específicas: Es posible configurar los pipelines para que sean disparados sólo cuando un push sea hecho en una rama específica. Esto puede hacerse configurando el .gitlab-ci.yml para escuchar cambios en ramas específicas.-
Ejemplo: Si quieres que el pipeline sea ejecutado sólo cuando se haga un commit en la rama main (o en otra rama específica), puedes configurar el pipeline para disparar sólo en esas circunstancias.
-
Lo que ocurre: El pipeline será ejecutado solamente si el push ocurre en las ramas que configuraste en el archivo .gitlab-ci.yml.
-
-
Pipeline Trigger vía Webhooks: Otra manera de disparar un pipeline es utilizando webhooks. Un webhook permite que envíes una petición HTTP a un endpoint de GitLab siempre que ocurra un evento en otro sistema. Esto es útil cuando quieres integrar GitLab con otras herramientas externas que pueden activar un pipeline.- Ejemplo: Si estás utilizando una herramienta de monitorización de código, como Sentry, o una herramienta de CI externa, puede disparar un pipeline en GitLab cuando detecte un evento específico.
- Lo que ocurre: El webhook envía una solicitud a GitLab, que a su vez dispara el pipeline con base en las condiciones configuradas.
Pueden existir otros, pero estos son los principales y más utilizados.
Todas estas configuraciones deben estar en el archivo .gitlab-ci.yml en la raíz del repositorio (proyecto)