Сборка и публикация Docker-образов в GitLab CI
Быстрая навигация
- Сборка с Shell исполнителем
- Сборка с Docker исполнителем
- Публикация образов в реестр GitLab
- Использование Dependency Proxy
- Контроль безопасности и рекомендации
- Чек-листы, критерии приёмки и отладка
- Краткое резюме

GitLab CI часто используется для автоматической сборки Docker-образов, которые затем разворачиваются на серверах или в облаке. Платформа GitLab упрощает этот процесс благодаря встроенному реестру контейнеров и возможности проксирования образов через Dependency Proxy — это ускоряет сборки и снижает вероятность превышения лимитов Docker Hub.
Ниже приведено пошаговое руководство по настройке сборок Docker-образов в GitLab CI. Мы рассмотрим два основных варианта исполнителя GitLab Runner: Shell и Docker, а также операционные и безопасностные аспекты каждого подхода.
Кому это нужно
- Разработчикам, которым нужно автоматически собирать образы вместе с изменениями кода.
- DevOps-инженерам, которые хотят централизованно хранить образы в GitLab и использовать кеширование.
- Администраторам, которым важно контролировать безопасность Runner и снижения зависимостей от публичных реестров.
Важно: если ваша GitLab-инстанция публичная или в ней множество внешних проектов, обратите особое внимание на безопасность Runner.
Сборка с Shell исполнителем
Если GitLab Runner настроен с исполнителем Shell, он будет запускать обычные shell-команды на хосте. В этом случае требуется, чтобы на машине-исполнителе был установлен Docker.
Перейдите в репозиторий проекта и создайте файл .gitlab-ci.yml в корне. Этот файл описывает pipeline, который будет запускаться при push в репозиторий.
Пример минимальной конфигурации, которая выполняет сборку и пуш образа:
stages:
- build
docker_build:
stage: build
script:
- docker build -t example.com/example-image:latest .
- docker push example.com/example-image:latestПояснения:
- GitLab Runner автоматически клонирует репозиторий в среду сборки, поэтому
docker buildвидитDockerfileи контекст проекта. docker pushотправляет образ в реестр. Безdocker pushобраз останется только на хосте Runner.
Если вы публикуете в приватный реестр, авторизуйтесь заранее:
script:
- docker login -u $DOCKER_REGISTRY_USER -p $DOCKER_REGISTRY_PASSWORD
- docker build -t example.com/example-image:latest .
- docker push example.com/example-image:latestЗадайте переменные $DOCKER_REGISTRY_USER и $DOCKER_REGISTRY_PASSWORD в веб-интерфейсе GitLab: Settings > CI/CD > Variables. Нажмите “Add variable” для добавления новых значений.

Преимущества Shell исполнителя:
- Простая настройка, если Docker уже установлен на хосте.
- Полный доступ к кешу Docker на хосте — эффективное повторное использование слоёв.
Ограничения:
- Менее изолированная среда: задания выполняются на хосте, что может быть рискованно для мультиарендных инстанций.
- Требует управления версией Docker на хосте вручную.
Сборка с Docker исполнителем
Docker исполнитель запускает каждое задание в отдельном контейнере. Бинарь docker внутри такого контейнера по умолчанию недоступен для обращения к демону на хосте, поэтому доступны два подхода для сборки образов:
- Docker-in-Docker (DinD) — запуск Docker демона внутри контейнера.
- Bind-монтирование Docker сокета хоста в контейнер.
Оба подхода имеют плюсы и минусы — выбирайте в зависимости от требований безопасности, производительности и конкуренции заданий.
Docker-in-Docker
DinD создаёт изолированную среду: Docker-демон запускается внутри сервиса docker:dind, а процесс сборки происходит в дочернем демон-процессе этого контейнера.
При регистрации Runner укажите флаг --docker-privileged, чтобы разрешить привилегированное выполнение контейнера:
sudo gitlab-runner register -n \
--url https://example.com \
--registration-token $GITLAB_REGISTRATION_TOKEN \
--executor docker \
--description "Docker Runner" \
--docker-image "docker:20.10" \
--docker-volumes "/certs/client" \
--docker-privilegedВ pipeline добавьте сервис docker:dind, чтобы job получил доступ к Docker-демону сервиса:
services:
- docker:dind
docker_build:
stage: build
image: docker:latest
script:
- docker build -t example-image:latest .Преимущества DinD:
- Изоляция: каждый job имеет собственный Docker-демон и файловую систему.
- Безопаснее в сценариях, где требуется изоляция между проектами.
Ограничения DinD:
- Кеширование слоёв сложнее: по умолчанию слои предыдущих сборок недоступны.
- Производительность может падать, особенно без оптимизаций.
Как частично решить проблему кеширования: попытаться подтянуть предыдущий образ и использовать его как cache source:
docker_build:
stage: build
image: docker:latest
script:
- docker pull $CI_REGISTRY_IMAGE:latest || true
- docker build --cache-from $CI_REGISTRY_IMAGE:latest -t $CI_REGISTRY_IMAGE:latest .Это позволяет использовать ранее загруженные слои как источник кеша при сборке.
Bind-монтирование Docker сокета
Альтернативный и часто более быстрый вариант — смонтировать сокет Docker хоста /var/run/docker.sock внутрь контейнера job. Тогда команды docker внутри контейнера будут обращаться к демону на хосте.
При регистрации Runner укажите --docker-volumes /var/run/docker.sock:/var/run/docker.sock:
sudo gitlab-runner register -n \
--url https://example.com \
--registration-token $GITLAB_REGISTRATION_TOKEN \
--executor docker \
--description "Docker Runner" \
--docker-image "docker:20.10" \
--docker-volumes /var/run/docker.sock:/var/run/docker.sockТеперь в job вы можете использовать docker build как обычно. Достоинства:
- Полный доступ к кешу слоёв на хосте — высокоперформантные сборки.
- Простота настройки без сервисов DinD.
Недостатки и риски:
- Безопасность: job получает доступ к Docker демону на хосте, что фактически даёт возможность запускать команды с привилегиями хоста. Мошеннический CI проект может выполнить опасные команды.
- Проблемы при параллельном выполнении: операции, зависящие от уникальных имён контейнеров, могут конфликтовать между параллельными job.
Рекомендация: использовать этот подход только в доверенной среде, где все проекты, запускающиеся на Runner, проверены.
Публикация образов в реестр GitLab
GitLab предоставляет встроенный реестр контейнеров для каждого проекта. Его можно включить в Settings > General > Visibility, Project, Features & Permissions, включив опцию Container registry. После этого ссылка в боковом меню проекта появится в разделе Packages & Registries > Container Registry.

GitLab автоматически задаёт набор переменных окружения в job-е, которые помогают взаимодействовать с реестром:
$CI_REGISTRY— URL реестра (сервер).$CI_REGISTRY_IMAGE— путь до реестра для текущего проекта (база для тегов).$CI_JOB_TOKEN— токен, который job может использовать для входа в реестр какgitlab-ci-token.
Пример script секции для входа и публикации образа в реестр проекта:
script:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- docker build -t $CI_REGISTRY_IMAGE:latest .
- docker push $CI_REGISTRY_IMAGE:latestЕсли нужно, чтобы сторонние клиенты могли подтягивать образы, создайте персональные Access Tokens в Settings > Access Tokens с правом read_registry и используйте их для docker login.
Совет: используйте теги образов, отражающие CI-метаданные, например $CI_COMMIT_REF_NAME или $CI_COMMIT_SHORT_SHA, чтобы можно было воспроизводимо связывать образы с версиями кода.
Использование Dependency Proxy
Dependency Proxy в GitLab кеширует образы из внешних реестров (например Docker Hub) на уровне группы. Это снижает количество запросов к Docker Hub и помогает избежать ограничения частоты запросов.
Включите Dependency Proxy в настройках группы: Settings > Packages & Registries > Dependency Proxy. После включения в CI можно ссылаться на переменную $CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX для загрузки образов через прокси.
docker_build:
stage: build
image: $CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX/docker:latest
services:
- name: $CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX/docker:dind
alias: dockerВажно: Runner автоматически логинится в Dependency Proxy, поэтому вручную авторизовываться не нужно.
Проблема: инструкция FROM ubuntu:latest в Dockerfile по умолчанию не будет проходить через Dependency Proxy. Решение — передавать аргументы сборки и переписывать базовый образ:
В Dockerfile:
ARG GITLAB_DEPENDENCY_PROXY
FROM ${GITLAB_DEPENDENCY_PROXY}/ubuntu:latestА в script указать аргумент при docker build:
script:
- docker build \
--build-arg GITLAB_DEPENDENCY_PROXY=${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX} \
-t example-image:latest .Так базовый образ будет загружаться через прокси, и Dependency Proxy сможет кешировать его слои.

Контроль безопасности и рекомендации
Выбор стратегии сборки напрямую влияет на безопасность Runner и всей платформы. Основные контрольные точки:
- Минимизируйте количество Runner, доступных внешним проектам. Если Runner используется для множества неизвестных проектов, запускайте его в ограниченной изолированной среде.
- Используйте DinD с привилегиями только в тех случаях, когда изоляция важнее производительности.
- Bind-монтирование сокета Docker даёт полный доступ к хосту — ограничивайте этот вариант только доверенными проектами.
- Ограничьте права пользователей в GitLab: ветки с правом выполнения CI и доступ к секретам стоит настраивать аккуратно.
- Логи и артефакты проверяйте на наличие секретов. Используйте встроенные возможности GitLab для маскировки переменных.
Рекомендация безопасности: для публичных инстанций и мультиарендных сред используйте DinD в привилегированном режиме или выделенные Runner для каждого доверенного набора проектов.
Производительность и кеширование
Как добиться быстрого времени сборки:
- Организуйте Dockerfile так, чтобы редко меняющиеся слои располагались выше, а часто меняющийся код — ближе к концу. Это увеличит шанс повторного использования кеша.
- Используйте
--cache-fromи попыткуdocker pullпредыдущих образов в DinD для частичного восстановления кеша. - Для bind-монтирования сокета используйте локальный кеш слоёв хоста.
- Многоуровневые сборки (multi-stage builds) помогают уменьшить размер итогового образа и ускорить перенос.
Пример использования многоступенчатой сборки:
# build stage
FROM golang:1.19 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp ./...
# final stage
FROM alpine:3.16
COPY --from=builder /app/myapp /usr/local/bin/myapp
CMD ["/usr/local/bin/myapp"]Чек-листы и рольные обязанности
Чек-лист для разработчика:
- Добавить упрощённый
Dockerfileв корень проекта. - Обновить
.gitlab-ci.ymlс базовым job-ом сборки и пуша. - Пометить переменные окружения (например теги, версии).
- Добавить тестовую задачу для проверки развёртывания образа.
Чек-лист для DevOps-инженера:
- Настроить Runner (Shell или Docker) в соответствии с политиками безопасности.
- Протестировать сборки в высоконагруженной среде.
- Настроить реестр проекта и проверить права доступа.
- Включить Dependency Proxy на уровне группы при необходимости.
Чек-лист для администратора GitLab:
- Ограничить доступ к переменным CI и токенам.
- Мониторить работу Runner и использование ресурсов.
- Проводить периодические проверки безопасности конфигурации Runner.
Критерии приёмки
- Job собирает образ и отправляет его в реестр без ошибок.
- Тег образа соответствует политике версионирования (например содержит
$CI_COMMIT_SHORT_SHA). - Размер итогового образа не превышает установленных лимитов.
- Автоматические тесты, зависящие от образа, успешно проходят при развёртывании.
Примеры конфигурации и шаблоны
Минимальный шаблон .gitlab-ci.yml для Shell исполнителя:
stages:
- build
docker_build:
stage: build
script:
- docker login -u $DOCKER_REGISTRY_USER -p $DOCKER_REGISTRY_PASSWORD
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
only:
- mainШаблон для Docker исполнителя с DinD и кешированием:
image: docker:20.10
services:
- docker:dind
variables:
DOCKER_DRIVER: overlay2
stages:
- build
docker_build:
stage: build
script:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- docker pull $CI_REGISTRY_IMAGE:latest || true
- docker build --cache-from $CI_REGISTRY_IMAGE:latest -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHAШаблон с использованием Dependency Proxy для base image:
image: $CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX/docker:latest
services:
- name: $CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX/docker:dind
alias: docker
stages:
- build
docker_build:
stage: build
script:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- docker build --build-arg GITLAB_DEPENDENCY_PROXY=${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX} -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHAОтладка и распространённые ошибки
Сценарии и способы диагностики:
- Ошибка “permission denied” при доступе к
/var/run/docker.sock— проверьте, что сокет смонтирован и права доступа корректны. - Сборка не использует кеш — убедитесь, что
docker pullпредыдущего образа выполняется успешно и--cache-fromуказывает на существующий образ. - Плохая производительность DinD — проверьте настройки
overlay2и доступность ресурсов хоста (CPU, RAM). - Параллельные job ломают контейнерные имена — избегайте жёстко закодированных имён контейнеров в сценариях работы с сокетом.
Минимальные тест-кейсы при интеграции в pipeline:
- Сборка базового образа и пуш в реестр.
- Pull образа из реестра на другом хосте для проверки целостности.
- Сборка с кешем и без кеша для сравнения времени выполнения.
- Проверка поведения при одновременном запуске 3-х job-ов (в случае bind-mount сокета).
План отката в аварийной ситуации
Если новая конфигурация pipeline или Runner вызывает критические проблемы:
- Откатить
.gitlab-ci.ymlк предыдущему рабочему коммиту. - Отключить проблемный Runner в интерфейсе GitLab (Settings > CI/CD > Runners) или временно пометить его как недоступный.
- Если проблема в настройках Runner на хосте, восстановить конфигурацию Docker/Runner из резервной копии.
- При проблемах с реестром — развернуть старую версию образа из существующих тегов в реестре.
Модель принятия решения: DinD или сокет
- Если требуется изоляция между job-ами и безопасность важнее производительности — выбирайте DinD.
- Если приоритет — скорость и эффективное кеширование, и Runner используется доверенными проектами — выбирайте bind-монтирование сокета.
- Для публичных или мультиарендных инстанций рекомендуем DinD с ограниченным набором Runner.
Mermaid диаграмма для выбора стратегии:
graph TD
A[Нужно ли изоляция между проектами?] -->|Да| B[Выбрать DinD]
A -->|Нет| C[Можно использовать сокет]
C --> D[Проверить доверие к проектам]
D -->|Надёжны| E[Использовать сокет]
D -->|Не надёжны| BСоветы по миграции и совместимости
- При переходе с Shell на Docker исполнитель убедитесь, что все необходимые инструменты доступны внутри образов job-ов или добавьте их через
before_script. - Если вы используете привязку сокета, проверьте, что версии Docker в контейнерах и на хосте совместимы.
- Планируйте фазу тестирования при миграции Runner, чтобы обнаружить неожиданные различия в окружении.
Короткая справка по основным CI-переменным
- $CI_REGISTRY — URL реестра GitLab проекта.
- $CI_REGISTRY_IMAGE — путь к репозиторию образов проекта.
- $CI_JOB_TOKEN — временный токен для доступа job-а к API и реестру.
- $CI_COMMIT_SHORT_SHA — короткий хэш коммита для тегирования образов.
- $CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX — префикс для обращения к Dependency Proxy.
1-строчный глоссарий:
- Runner — агент, выполняющий CI-задания.
- DinD — Docker-in-Docker, запуск Docker-демона внутри контейнера.
- Dependency Proxy — кеш GitLab для внешних образов.
Риски и меры смягчения
- Риск: удаление образов/контейнеров хостом из-за недоверенного job-а. Митигирование: сегрегировать Runner по проектам и окружениям.
- Риск: превышение лимитов Docker Hub. Митигирование: включить Dependency Proxy и хранить часто используемые образы локально.
- Риск: конфликты при параллельном запуске job-ов с общими именами контейнеров. Митигирование: использовать случайные или уникальные имена.
Краткое резюме
Docker-образы можно эффективно собирать в GitLab CI с помощью docker build и docker push прямо в job-ах. Shell исполнитель даёт простоту и доступ к кешу хоста, Docker исполнитель обеспечивает изоляцию. Для безопасных публичных инстанций предпочтительнее DinD; для высокой производительности и доверенных сред подойдёт bind-монтирование сокета. Встроенный реестр GitLab и Dependency Proxy помогают хранить и кешировать образы, уменьшая зависимость от внешних реестров.

Важно: перед развёртыванием в продуктивную среду проверьте политику безопасности Runner и протестируйте кеширование в условиях, близких к боевым.
Дополнительные ресурсы и дальнейшие шаги
- Проведите ревью Dockerfile на предмет секретов и ненужных больших слоёв.
- Настройте правила ретенции артефактов и образов в реестре, чтобы не накапливать старые теги.
- Разработайте SOP для добавления новых Runner и процедур обращения с привилегированными Runner.
Короткое резюме в 1 абзац: GitLab CI позволяет гибко интегрировать сборку и публикацию Docker-образов; выбор между Shell, DinD и bind-монтированием сокета зависит от баланса между безопасностью и производительностью. Используйте встроенный реестр и Dependency Proxy для надёжного хранения и ускорения сборок.
Похожие материалы
Как подключить AirPods к Android и Windows
Управление курсором с клавиатуры — настройка и советы
CryptPad: защищённый офис с E2EE
Перенос Gmail в ProtonMail — пошагово
Переключение с Wayland на Xorg