Гид по технологиям

Сборка и публикация Docker-образов в GitLab CI

10 min read DevOps Обновлено 02 Dec 2025
Docker-образы в GitLab CI: сборка и публикация
Docker-образы в GitLab CI: сборка и публикация

Быстрая навигация

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

Логотип GitLab, стилизованная голова лисы

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” для добавления новых значений.

Скриншот создания переменной CI в интерфейсе GitLab

Преимущества Shell исполнителя:

  • Простая настройка, если Docker уже установлен на хосте.
  • Полный доступ к кешу Docker на хосте — эффективное повторное использование слоёв.

Ограничения:

  • Менее изолированная среда: задания выполняются на хосте, что может быть рискованно для мультиарендных инстанций.
  • Требует управления версией Docker на хосте вручную.

Сборка с Docker исполнителем

Docker исполнитель запускает каждое задание в отдельном контейнере. Бинарь docker внутри такого контейнера по умолчанию недоступен для обращения к демону на хосте, поэтому доступны два подхода для сборки образов:

  1. Docker-in-Docker (DinD) — запуск Docker демона внутри контейнера.
  2. 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 сможет кешировать его слои.

Скриншот настроек прокси зависимостей группы в GitLab

Контроль безопасности и рекомендации

Выбор стратегии сборки напрямую влияет на безопасность 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:

  1. Сборка базового образа и пуш в реестр.
  2. Pull образа из реестра на другом хосте для проверки целостности.
  3. Сборка с кешем и без кеша для сравнения времени выполнения.
  4. Проверка поведения при одновременном запуске 3-х job-ов (в случае bind-mount сокета).

План отката в аварийной ситуации

Если новая конфигурация pipeline или Runner вызывает критические проблемы:

  1. Откатить .gitlab-ci.yml к предыдущему рабочему коммиту.
  2. Отключить проблемный Runner в интерфейсе GitLab (Settings > CI/CD > Runners) или временно пометить его как недоступный.
  3. Если проблема в настройках Runner на хосте, восстановить конфигурацию Docker/Runner из резервной копии.
  4. При проблемах с реестром — развернуть старую версию образа из существующих тегов в реестре.

Модель принятия решения: 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 помогают хранить и кешировать образы, уменьшая зависимость от внешних реестров.

Скриншот лога задания CI, собравшего Docker-образ

Важно: перед развёртыванием в продуктивную среду проверьте политику безопасности Runner и протестируйте кеширование в условиях, близких к боевым.

Дополнительные ресурсы и дальнейшие шаги

  • Проведите ревью Dockerfile на предмет секретов и ненужных больших слоёв.
  • Настройте правила ретенции артефактов и образов в реестре, чтобы не накапливать старые теги.
  • Разработайте SOP для добавления новых Runner и процедур обращения с привилегированными Runner.

Короткое резюме в 1 абзац: GitLab CI позволяет гибко интегрировать сборку и публикацию Docker-образов; выбор между Shell, DinD и bind-монтированием сокета зависит от баланса между безопасностью и производительностью. Используйте встроенный реестр и Dependency Proxy для надёжного хранения и ускорения сборок.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Как подключить AirPods к Android и Windows
Руководство

Как подключить AirPods к Android и Windows

Управление курсором с клавиатуры — настройка и советы
Инструменты

Управление курсором с клавиатуры — настройка и советы

CryptPad: защищённый офис с E2EE
Software

CryptPad: защищённый офис с E2EE

Перенос Gmail в ProtonMail — пошагово
Электронная почта

Перенос Gmail в ProtonMail — пошагово

Переключение с Wayland на Xorg
Linux

Переключение с Wayland на Xorg

Как скачать данные из Yahoo Groups перед удалением
Руководство

Как скачать данные из Yahoo Groups перед удалением