GitLab Runner: Création d’images Docker

Construisez et poussez efficacement des images Docker à partir de vos pipelines GitLab CI/CD en utilisant les runners Stackhero et Docker-in-Docker

👋 Bienvenue dans la documentation Stackhero !

Stackhero offre une solution GitLab Runner cloud simple qui rend l’exécution de vos jobs GitLab CI/CD efficace et sans tracas. Voici ce que vous pouvez attendre :

  • Minutes CI/CD illimitées : exécutez vos pipelines aussi souvent que nécessaire, sans facturation à la minute ni frais imprévus.
  • Plusieurs jobs simultanés : accélérez votre développement en lançant plusieurs jobs en parallèle.
  • L’executor Docker avec prise en charge de Docker-in-Docker : construisez et poussez facilement des images de conteneurs dans vos processus CI/CD.
  • Fonctionne parfaitement avec GitLab.com et les instances GitLab autogérées.
  • Une infrastructure privée et dédiée avec stockage NVMe/SSD rapide assure des performances de build stables et prévisibles.
  • Disponible dans les régions 🇪🇺 Europe et 🇺🇸 USA pour répondre aux besoins de votre équipe.

Gagnez du temps : connectez votre premier GitLab Runner et commencez à exécuter vos pipelines en quelques minutes seulement !

Avec un GitLab Runner Stackhero, chaque job s’exécute dans un nouveau conteneur grâce à l’executor Docker. Vous pouvez créer vos propres images Docker directement dans votre pipeline en activant Docker-in-Docker (DinD). Cette configuration lance un démon Docker à côté de votre job, ce qui vous permet d’exécuter les commandes docker build et docker push dans votre processus CI/CD.

Chaque exécution bénéficie de minutes CI/CD illimitées : vous pouvez lancer autant de builds que nécessaire, sans vous soucier des limites d’utilisation. Votre cache de build est stocké sur le disque dédié du runner, ce qui permet de réutiliser les couches précédentes lors de builds répétés. Cela réduit considérablement les temps de build et accélère l’exécution de vos pipelines.

Vous pouvez ajouter l’exemple de .gitlab-ci.yml suivant à votre dépôt. Cette configuration construit le Dockerfile situé à la racine de votre projet :

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:
    # Remplacez "my-image" par le nom souhaité :
    - docker build -t my-image .
    # Optionnellement, lancez un test rapide sur l’image construite :
    # - docker run --rm my-image /path/to/tests

Nous utilisons la version 29 de l’image Docker dans cet exemple. Vous pouvez utiliser une version plus récente dès qu’elle est disponible. Vous trouverez les tags les plus récents sur la page officielle Docker image.

Dans cette configuration, le service docker:29-dind démarre un démon Docker à côté de votre job, et DOCKER_HOST: "tcp://docker:2375" indique au CLI docker de l’utiliser. Définissez toujours DOCKER_HOST lorsque vous déclarez un service docker:dind : sans cela, le CLI se connecte silencieusement à un autre démon, ce qui peut faire passer votre build même si le service est mal configuré, masquant ainsi de vrais problèmes (et cassant des outils comme Testcontainers qui dépendent du service). DOCKER_TLS_CERTDIR: "" permet de se connecter via le port interne non sécurisé 2375 du réseau du job.

En alternative plus simple, vous pouvez omettre le bloc services et les deux variables : le runner Stackhero expose aussi un démon Docker prêt à l’emploi via un socket monté, donc une simple commande docker build fonctionne sans configuration supplémentaire.

Testcontainers est une bibliothèque de tests, disponible pour Java, Go, Node.js, Python, .NET et d’autres langages, qui démarre de vrais services sous forme de conteneurs Docker jetables pendant l’exécution de vos tests. Plutôt que de simuler une base de données ou un message broker, vos tests d’intégration communiquent avec une véritable instance PostgreSQL, MySQL, Redis ou Kafka, créée avant les tests et supprimée juste après. C’est particulièrement populaire dans les projets Java et Spring.

Testcontainers a besoin d’un démon Docker, donc il fonctionne avec exactement la même configuration Docker-in-Docker que ci-dessus. Aucune variable supplémentaire n’est requise :

test:
  stage: test
  # Utilisez l’image dont vos tests ont besoin (ici un JDK), pas forcément l’image docker :
  image: gradle:jdk21
  services:
    - name: docker:29-dind
      alias: docker
  variables:
    DOCKER_HOST: "tcp://docker:2375"
    DOCKER_TLS_CERTDIR: ""
  script:
    - gradle test

Testcontainers lit la variable DOCKER_HOST pour trouver le démon, et il réutilise ce même hostname docker pour accéder aux ports exposés par les conteneurs qu’il démarre. Les deux fonctionnent car l’alias docker est résolu sur le réseau du job.

Gardez la variable DOCKER_HOST dans vos jobs Testcontainers. Sans elle, Testcontainers utilise le socket Docker monté par le runner, puis tente d’atteindre vos conteneurs de test via l’adresse IP de l’hôte, à laquelle votre job ne peut pas accéder. Le symptôme classique est l’échec du conteneur helper Ryuk avec Wait strategy failed. Container is removed, ou Timed out waiting for log output matching '.*Started.*'.

GitLab fournit des variables prédéfinies (CI_REGISTRY, CI_REGISTRY_USER, CI_REGISTRY_PASSWORD, CI_REGISTRY_IMAGE) pour que votre pipeline puisse s’authentifier et pousser des images vers le registre de conteneurs de votre projet de façon sécurisée. Aucun secret supplémentaire n’est requis.

Voici un exemple de job qui construit et pousse votre image :

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 vous êtes sur la branche par défaut, vous pouvez aussi taguer et pousser "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

Pour pousser vos images vers un autre registre (comme Docker Hub ou un registre privé), vous pouvez stocker les identifiants dans les variables CI/CD et les utiliser avec docker login de la même façon.

Le disque de votre runner persiste entre les pipelines, ce qui permet de réutiliser les couches d’image comme cache de build. Cela peut accélérer considérablement les builds successifs. Voici un exemple de configuration :

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:
    # Récupère la dernière image pour initialiser le cache (si elle 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"

Cette méthode permet à vos builds de profiter du cache de couches Docker, seules les nouvelles couches ou celles modifiées sont reconstruites.

Votre forfait détermine combien de jobs peuvent s’exécuter simultanément. Les jobs d’une même étape démarrent ensemble, jusqu’à la limite de parallélisme autorisée. Cela signifie que plusieurs jobs indépendants peuvent s’exécuter en parallèle et se terminer dès que le plus lent est terminé, sans attendre la fin de chaque job l’un après l’autre.

Exemple :

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 vous définissez votre parallélisme à 1 ou plus, les jobs unit, integration et e2e s’exécuteront en même temps.

Pour plus de détails sur la création d’images Docker dans les pipelines GitLab CI/CD, vous pouvez consulter la documentation officielle GitLab sur l’utilisation des builds Docker.