GitLab Runner: Creazione di immagini Docker

Crea e pubblica immagini Docker in modo efficiente dalle tue pipeline GitLab CI/CD utilizzando i runner Stackhero e Docker-in-Docker

👋 Benvenuto nella documentazione di Stackhero!

Stackhero offre una soluzione GitLab Runner cloud semplice che rende l'esecuzione dei tuoi job GitLab CI/CD efficiente e senza complicazioni. Ecco cosa puoi aspettarti:

  • Minuti CI/CD illimitati: esegui le tue pipeline tutte le volte che vuoi, senza costi al minuto o spese impreviste.
  • Più job in contemporanea: accelera lo sviluppo eseguendo diversi job in parallelo.
  • L'executor Docker con supporto Docker-in-Docker: costruisci e pubblica facilmente immagini container come parte del tuo processo CI/CD.
  • Funziona perfettamente sia con GitLab.com che con istanze GitLab self-managed.
  • Un'infrastruttura privata e dedicata con storage NVMe/SSD veloce garantisce prestazioni di build stabili e prevedibili.
  • Disponibile nelle regioni 🇪🇺 Europa e 🇺🇸 USA per soddisfare le esigenze del tuo team.

Risparmia tempo: puoi collegare il tuo primo GitLab Runner e iniziare a eseguire pipeline in pochi minuti!

Con uno Stackhero GitLab Runner, ogni job viene eseguito all'interno di un container isolato tramite l'executor Docker. Puoi creare le tue immagini Docker direttamente nella pipeline abilitando Docker-in-Docker (DinD). Questa configurazione avvia un demone Docker accanto al tuo job, consentendoti di eseguire i comandi docker build e docker push come parte del processo CI/CD.

Ogni esecuzione beneficia di minuti CI/CD illimitati: puoi effettuare build tutte le volte che vuoi, senza preoccuparti di limiti di utilizzo. Il build cache viene memorizzato sul disco dedicato del runner, il che significa che le build ripetute possono riutilizzare i layer precedenti. Questo riduce notevolmente i tempi di build e accelera il completamento delle pipeline.

Puoi aggiungere il seguente esempio di .gitlab-ci.yml al tuo repository. Questa configurazione costruisce il Dockerfile presente nella root del progetto:

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:
    # Sostituisci "my-image" con il nome desiderato:
    - docker build -t my-image .
    # Facoltativamente, esegui un test rapido sull'immagine appena creata:
    # - docker run --rm my-image /path/to/tests

In questo esempio utilizziamo la versione 29 dell'immagine Docker. Puoi scegliere una versione più recente non appena disponibile. Trovi i tag più aggiornati sulla pagina ufficiale Docker image.

In questa configurazione, il servizio docker:29-dind avvia un demone Docker accanto al job, e DOCKER_HOST: "tcp://docker:2375" indica alla CLI docker di utilizzarlo. Imposta sempre DOCKER_HOST quando dichiari un servizio docker:dind: senza questa variabile, la CLI si collega silenziosamente a un altro demone, quindi la build può andare a buon fine anche se il servizio è configurato in modo errato, nascondendo problemi reali (e interrompendo strumenti come Testcontainers che dipendono dal servizio). DOCKER_TLS_CERTDIR: "" permette la connessione tramite la porta non-TLS 2375 sulla rete interna del job.

In alternativa, puoi omettere il blocco services e le due variabili: il runner Stackhero espone anche un demone Docker già pronto tramite un socket montato, quindi un semplice docker build funziona senza configurazioni aggiuntive.

Testcontainers è una libreria di testing, disponibile per Java, Go, Node.js, Python, .NET e altri linguaggi, che avvia servizi reali come container Docker temporanei durante l'esecuzione dei test. Invece di simulare un database o un message broker, i test di integrazione interagiscono con una vera istanza di PostgreSQL, MySQL, Redis o Kafka, creata prima dei test e rimossa subito dopo. È particolarmente popolare nei progetti Java e Spring.

Testcontainers necessita di un demone Docker, quindi funziona con la stessa configurazione Docker-in-Docker vista sopra. Non sono richieste variabili aggiuntive:

test:
  stage: test
  # Usa l'immagine necessaria per i tuoi test (ad esempio una JDK), non necessariamente l'immagine docker:
  image: gradle:jdk21
  services:
    - name: docker:29-dind
      alias: docker
  variables:
    DOCKER_HOST: "tcp://docker:2375"
    DOCKER_TLS_CERTDIR: ""
  script:
    - gradle test

Testcontainers legge la variabile DOCKER_HOST per individuare il demone, e riutilizza lo stesso hostname docker per raggiungere le porte pubblicate dai container che avvia. Entrambe le funzionalità sono operative perché l'alias docker viene risolto sulla rete del job.

Mantieni DOCKER_HOST nei job che utilizzano Testcontainers. Senza questa variabile, Testcontainers utilizza il socket Docker montato dal runner e poi tenta di raggiungere i container di test tramite l'indirizzo IP host, a cui il job non può connettersi. Il sintomo tipico è il fallimento del container helper Ryuk con Wait strategy failed. Container is removed, oppure Timed out waiting for log output matching '.*Started.*'.

GitLab mette a disposizione variabili predefinite (CI_REGISTRY, CI_REGISTRY_USER, CI_REGISTRY_PASSWORD, CI_REGISTRY_IMAGE) affinché la pipeline possa autenticarsi e pubblicare immagini nel container registry del progetto in modo sicuro. Non sono necessari segreti aggiuntivi.

Ecco un esempio di job che costruisce e pubblica la tua immagine:

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"
    # Se sei sul branch di default, puoi anche taggare e pubblicare "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

Per pubblicare le immagini su un altro registry (come Docker Hub o un registry privato), puoi memorizzare le credenziali come variabili CI/CD e utilizzarle con docker login allo stesso modo.

Il disco del runner persiste tra le pipeline, consentendo di riutilizzare i layer delle immagini come cache di build. Questo può rendere le build ripetute molto più rapide. Ecco un esempio di configurazione:

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:
    # Recupera l'ultima immagine per inizializzare la cache (se esiste):
    - 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"

Questo approccio permette di sfruttare la cache dei layer Docker, ricostruendo solo i layer nuovi o modificati.

Il tuo piano determina quanti job possono essere eseguiti contemporaneamente. I job appartenenti allo stesso stage vengono avviati insieme, fino al limite di concorrenza previsto. Questo significa che più job indipendenti possono essere eseguiti in parallelo e terminare non appena il più lento si conclude, senza dover attendere la fine sequenziale di ciascun job.

Esempio:

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

Se imposti la concorrenza a 1 o superiore, i job unit, integration ed e2e verranno eseguiti contemporaneamente.

Per ulteriori dettagli sulla creazione di immagini Docker nelle pipeline GitLab CI/CD, puoi consultare la documentazione ufficiale GitLab sull'utilizzo dei Docker build.