GitLab Runner: Crear imágenes Docker

Compila y publica imágenes Docker de forma eficiente desde tus pipelines de GitLab CI/CD utilizando los runners de Stackhero y Docker-in-Docker

👋 ¡Bienvenido a la documentación de Stackhero!

Stackhero ofrece una solución sencilla de GitLab Runner en la nube que facilita la ejecución de sus trabajos de GitLab CI/CD de manera eficiente y sin complicaciones. Esto es lo que puede esperar:

  • Minutos ilimitados de CI/CD: ejecute sus pipelines tantas veces como necesite, sin facturación por minuto ni cargos inesperados.
  • Varios trabajos concurrentes: acelere su desarrollo ejecutando varios trabajos en paralelo.
  • El ejecutor Docker con soporte para Docker-in-Docker: construya y publique fácilmente imágenes de contenedores como parte de su proceso CI/CD.
  • Funciona perfectamente tanto con GitLab.com como con instancias de GitLab autogestionadas.
  • Una infraestructura privada y dedicada con almacenamiento NVMe/SSD rápido garantiza un rendimiento de compilación estable y predecible.
  • Disponible en las regiones de 🇪🇺 Europa y 🇺🇸 USA para adaptarse a las necesidades de su equipo.

Ahorre tiempo: puede conectar su primer GitLab Runner y empezar a ejecutar pipelines en solo unos minutos.

Con un Stackhero GitLab Runner, cada job se ejecuta dentro de un contenedor nuevo utilizando el ejecutor Docker. Puedes construir tus propias imágenes Docker directamente en tu pipeline habilitando Docker-in-Docker (DinD). Esta configuración lanza un daemon Docker junto a tu job, lo que te permite ejecutar los comandos docker build y docker push como parte de tu proceso CI/CD.

Cada ejecución se beneficia de minutos CI/CD ilimitados: puedes compilar tantas veces como necesites, sin preocuparte por límites de uso. Tu caché de compilación se almacena en el disco dedicado del runner, lo que significa que las compilaciones repetidas pueden reutilizar capas anteriores. Esto reduce significativamente los tiempos de compilación y ayuda a que tus pipelines terminen más rápido.

Puedes añadir el siguiente ejemplo de .gitlab-ci.yml a tu repositorio. Esta configuración compila el Dockerfile que se encuentra en la raíz de tu proyecto:

build-image:
  stage: build
  image: docker:29
  services:
    - name: docker:29-dind
      alias: docker
  variables:
    DOCKER_HOST: "tcp://docker:2375"
    DOCKER_TLS_CERTDIR: ""
  before_script:
    - docker info
  script:
    # Sustituye "my-image" por el nombre que desees:
    - docker build -t my-image .
    # Opcionalmente, ejecuta una prueba rápida sobre la imagen construida:
    # - docker run --rm my-image /path/to/tests

En este ejemplo estamos usando la versión 29 de la imagen Docker. Puedes utilizar una versión más reciente si está disponible. Puedes consultar las etiquetas más recientes en la página oficial de la imagen Docker.

En esta configuración, el servicio docker:29-dind inicia un daemon Docker junto a tu job, y DOCKER_HOST: "tcp://docker:2375" indica al CLI de docker que lo utilice. Debes establecer siempre DOCKER_HOST cuando declares un servicio docker:dind: si no lo haces, el CLI se conecta silenciosamente a otro daemon, por lo que tu compilación puede pasar incluso si el servicio está mal configurado, ocultando problemas reales (y rompiendo herramientas como Testcontainers que dependen del servicio). DOCKER_TLS_CERTDIR: "" conecta a través del puerto interno sin TLS 2375 en la red interna del job.

Como alternativa más sencilla, puedes omitir el bloque services y ambas variables: el runner de Stackhero también expone un daemon Docker listo para usar a través de un socket montado, por lo que un simple docker build funciona sin configuración adicional.

Testcontainers es una biblioteca de testing, disponible para Java, Go, Node.js, Python, .NET y otros lenguajes, que inicia servicios reales como contenedores Docker desechables mientras se ejecutan tus tests. En lugar de simular una base de datos o un broker de mensajes, tus tests de integración se comunican con una instancia real de PostgreSQL, MySQL, Redis o Kafka, creada antes de los tests y eliminada justo después. Es especialmente popular en proyectos Java y Spring.

Testcontainers necesita un daemon Docker, por lo que funciona exactamente con la misma configuración de Docker-in-Docker que la anterior. No se necesita ninguna variable adicional:

test:
  stage: test
  # Utiliza la imagen que necesiten tus tests (aquí un JDK), no necesariamente la imagen docker:
  image: gradle:jdk21
  services:
    - name: docker:29-dind
      alias: docker
  variables:
    DOCKER_HOST: "tcp://docker:2375"
    DOCKER_TLS_CERTDIR: ""
  script:
    - gradle test

Testcontainers lee DOCKER_HOST para encontrar el daemon, y reutiliza ese mismo hostname docker para acceder a los puertos publicados por los contenedores que inicia. Ambos funcionan por sí solos, ya que el alias docker se resuelve en la red de tu job.

Mantén DOCKER_HOST en tus jobs que usen Testcontainers. Si no lo haces, Testcontainers recurre al socket Docker montado por el runner y luego intenta acceder a tus contenedores de test a través de la dirección IP del host, a la que tu job no puede conectarse. El síntoma habitual es que el contenedor auxiliar Ryuk falla con Wait strategy failed. Container is removed, o Timed out waiting for log output matching '.*Started.*'.

GitLab proporciona variables predefinidas (CI_REGISTRY, CI_REGISTRY_USER, CI_REGISTRY_PASSWORD, CI_REGISTRY_IMAGE) para que tu pipeline pueda autenticarse y publicar imágenes en el registro de contenedores de tu proyecto de forma segura. No se requieren secretos adicionales.

Aquí tienes un ejemplo de job que construye y publica tu imagen:

build-and-push:
  stage: build
  image: docker:29
  services:
    - name: docker:29-dind
      alias: docker
  variables:
    DOCKER_HOST: "tcp://docker:2375"
    DOCKER_TLS_CERTDIR: ""
  before_script:
    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
  script:
    - docker build -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA" .
    - docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA"
    # Si estás en la rama principal, también puedes etiquetar y publicar "latest":
    - |
      if [ "$CI_COMMIT_BRANCH" = "$CI_DEFAULT_BRANCH" ]; then
        docker tag "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA" "$CI_REGISTRY_IMAGE:latest"
        docker push "$CI_REGISTRY_IMAGE:latest"
      fi

Para publicar tus imágenes en otro registro (como Docker Hub o un registro privado), puedes almacenar las credenciales como variables CI/CD y usarlas con docker login de la misma manera.

El disco de tu runner se mantiene entre pipelines, lo que te permite reutilizar las capas de imagen como caché de compilación. Esto puede hacer que las compilaciones repetidas sean mucho más rápidas. Aquí tienes un ejemplo de configuración:

build-cached:
  stage: build
  image: docker:29
  services:
    - name: docker:29-dind
      alias: docker
  variables:
    DOCKER_HOST: "tcp://docker:2375"
    DOCKER_TLS_CERTDIR: ""
  before_script:
    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
  script:
    # Descarga la última imagen para inicializar la caché (si existe):
    - docker pull "$CI_REGISTRY_IMAGE:latest" || true
    - docker build --cache-from "$CI_REGISTRY_IMAGE:latest" -t "$CI_REGISTRY_IMAGE:latest" .
    - docker push "$CI_REGISTRY_IMAGE:latest"

Este enfoque permite que tus compilaciones aprovechen la caché de capas de Docker, por lo que solo se reconstruyen las capas nuevas o modificadas.

Tu plan determina cuántos jobs pueden ejecutarse de forma concurrente. Los jobs en la misma etapa se inician juntos, hasta el límite de concurrencia que tengas. Esto significa que varios jobs independientes pueden ejecutarse en paralelo y finalizar tan pronto como termine el más lento, en lugar de esperar a que cada uno termine en secuencia.

Ejemplo:

stages:
  - test

unit:
  stage: test
  image: node:22
  script: npm run test:unit

integration:
  stage: test
  image: node:22
  script: npm run test:integration

e2e:
  stage: test
  image: node:22
  script: npm run test:e2e

Si configuras tu concurrencia en 1 o superior, los jobs unit, integration y e2e se ejecutarán al mismo tiempo.

Para más detalles sobre cómo construir imágenes Docker en pipelines de GitLab CI/CD, puedes consultar la documentación oficial de GitLab sobre el uso de Docker builds.