GitLab CI DinD: сборка Docker-образов с кэшированием BuildKit
Published: 2026-03-25
Docker-in-Docker (DinD) в GitLab CI позволяет собирать и публиковать контейнерные образы в рамках пайплайна. Настройка включает TLS, сервис Docker daemon и общий том для сертификатов. С кэшированием слоёв BuildKit через registry инкрементальные сборки занимают 30–60 секунд вместо 5 минут.
Базовая настройка DinD
yaml# .gitlab-ci.yml
variables:
DOCKER_TLS_CERTDIR: "/certs"
DOCKER_BUILDKIT: "1"
REGISTRY: registry.example.com
services:
- name: docker:26-dind
command: ["--registry-mirror", "https://mirror.example.com"]
build:
image: docker:26
stage: build
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$REGISTRY"
script:
- |
docker buildx build \
--cache-from type=registry,ref=${REGISTRY}/${CI_PROJECT_NAME}:cache \
--cache-to type=registry,ref=${REGISTRY}/${CI_PROJECT_NAME}:cache,mode=max \
--platform linux/amd64 \
--tag ${REGISTRY}/${CI_PROJECT_NAME}:${CI_COMMIT_SHORT_SHA} \
--tag ${REGISTRY}/${CI_PROJECT_NAME}:latest \
--push \
.
Сервис docker:dind и образ docker:26 должны использовать одну мажорную версию. DOCKER_TLS_CERTDIR: "/certs" включает TLS между клиентом и daemon автоматически.
Проблема с TLS-сертификатами
Без общего DOCKER_TLS_CERTDIR Docker CLI не может подключиться к daemon:
Cannot connect to the Docker daemon at tcp://docker:2376.
Решение: DOCKER_TLS_CERTDIR: "/certs" — GitLab автоматически монтирует том между сервисом и контейнером задачи. Оба видят /certs.
Для отладки без TLS: DOCKER_TLS_CERTDIR: "" и tcp://docker:2375 — только не в production.
Registry mirror
Registry mirror — pull-through кэш для Docker Hub. Без него каждый DinD-runner тянет alpine:3.20 напрямую с Docker Hub, упираясь в rate limit и добавляя 30–60 секунд.
yamlservices:
- name: docker:26-dind
command: ["--registry-mirror", "https://mirror.example.com"]
mirror.example.com — собственный Registry proxy (registry:2 с proxy-конфигом):
yamlproxy:
remoteurl: https://registry-1.docker.io
Кэширование BuildKit: inline или registry
inline — встраивает метаданные кэша в слой образа. Просто, но вынуждает тянуть весь образ.
registry — хранит кэш как отдельный манифест в registry. Тег cache маленький (только метаданные + diff). mode=max кэширует все стадии сборки, а не только финальную.
Для multi-stage-сборки .NET:
restoreменяется редко (только при изменении.csproj)buildменяется каждый коммит- С
mode=maxстадияrestoreберётся из кэша даже на раннерах без локального кэша
Multi-arch сборки
yamlbefore_script:
- docker run --privileged --rm tonistiigi/binfmt --install all
- docker buildx create --use --name multiarch
script:
- |
docker buildx build \
--platform linux/amd64,linux/arm64 \
--cache-from type=registry,ref=${REGISTRY}/${CI_PROJECT_NAME}:cache \
--cache-to type=registry,ref=${REGISTRY}/${CI_PROJECT_NAME}:cache,mode=max \
--tag ${REGISTRY}/${CI_PROJECT_NAME}:${CI_COMMIT_SHORT_SHA} \
--push \
.
binfmt регистрирует QEMU-эмуляторы. Сборка под эмулируемую архитектуру идёт в 3–5 раз медленнее.
Сканирование Trivy в том же пайплайне
yamlscan:
image:
name: aquasec/trivy:latest
entrypoint: [""]
stage: scan
needs: [build]
script:
- |
trivy image \
--format json \
--output trivy-report.json \
--severity HIGH,CRITICAL \
--exit-code 1 \
${REGISTRY}/${CI_PROJECT_NAME}:${CI_COMMIT_SHORT_SHA}
artifacts:
when: always
reports:
container_scanning: trivy-report.json
--exit-code 1 — пайплайн падает при HIGH/CRITICAL уязвимостях. Артефакт container_scanning отображается в MR во вкладке Security.
Kaniko как альтернатива
Если привилегированные контейнеры недоступны (так бывает в части Kubernetes-инсталляций):
yamlbuild:
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [""]
script:
- |
/kaniko/executor \
--context . \
--dockerfile Dockerfile \
--destination ${REGISTRY}/${CI_PROJECT_NAME}:${CI_COMMIT_SHORT_SHA} \
--cache=true \
--cache-repo=${REGISTRY}/${CI_PROJECT_NAME}:cache
Kaniko не требует privileged mode. Компромисс: работает медленнее BuildKit и не поддерживает multi-arch.