Новинка Виртуальный VDS сервер в Нидерландах от 390р
Выберите продукт

GitLab Runner: Docker executor vs Shell executor — что выбрать и как не словить сюрпризы

Docker executor и Shell executor в GitLab Runner подходят для разных задач: изоляция и воспроизводимость против простоты и максимальной скорости. Разберём безопасность, cache, права файлов, privileged, сборку Docker-образов и чек-лист выбора под команды и проекты.
GitLab Runner: Docker executor vs Shell executor — что выбрать и как не словить сюрпризы

Зачем вообще выбирать executor в GitLab Runner

GitLab Runner — агент, который забирает задания из GitLab CI/CD и исполняет их на вашей инфраструктуре. То, где и как выполняются команды job’а, определяет «исполнитель» (executor). Разберём два самых популярных варианта: docker executor и shell executor.

На практике выбор executor’а влияет на три вещи, которые обычно всплывают уже после первых инцидентов: воспроизводимость сборок, безопасность окружения и стоимость владения (сопровождение, чистка, отладка).

Если обобщать: Docker executor даёт изоляцию и предсказуемость окружения, Shell executor — минимальную прослойку и максимум прямого доступа к хосту. Но решают детали: как устроены cache, доступ к Docker, нужен ли privileged, какие права у файлов, где лежит workspace и как быстро накапливается «грязь» между сборками.

Модель исполнения: что реально происходит под капотом

Shell executor

Shell executor запускает команды job’а напрямую в ОС Runner’а (обычно через bash/sh или PowerShell). Всё выполняется на хосте: пакеты ставятся в систему, процессы живут в общей среде, а изменения окружения легко «перетекают» в следующие пайплайны.

Эффект: очень быстрый старт job’ов (нет pull образа и создания контейнера), но любая ошибка в pipeline может испортить хост или повлиять на соседние сборки.

Docker executor

Docker executor для каждого job’а поднимает контейнер на основе образа из image и запускает команды внутри него. Окружение должно быть описано образом (или этапами подготовки), поэтому сборки обычно более повторяемые: один и тот же образ — одни и те же версии инструментов.

Эффект: выше управляемость и изоляция, но добавляются накладные расходы на pull образов, тома, сеть, а также вопросы безопасности вокруг Docker daemon.

Ключевая разница такая: Shell executor доверяет job’у почти как процессу на сервере, а Docker executor пытается ограничить job контейнером. Но степень ограничения напрямую зависит от ваших настроек (особенно privileged, volume mount’ов и доступа к Docker socket).

Если runner у вас живёт на отдельной машине, чаще всего это будет VDS: так проще разделять доступы, квоты и риски между продом и CI.

Схема исполнения job: напрямую на хосте и внутри Docker-контейнера

Безопасность: где чаще всего ошибаются

Shell executor: риск «исполняем код на сервере»

С Shell executor угрозная модель максимально прямолинейная: любой, кто может менять .gitlab-ci.yml (или пушить код в ветки, где запускается CI), потенциально запускает произвольные команды на хосте runner’а. Даже если runner работает под отдельным пользователем, остаются риски утечек и боковых эффектов.

  • утечка секретов из файловой системы и переменных окружения;
  • подмена бинарников в PATH и «загрязнение» окружения;
  • доступ к локальной сети и ресурсам хоста (SSH-ключи, токены, конфиги);
  • пересечение кешей/артефактов между проектами при слабой сегментации.

Shell executor уместен, когда проекты доверенные, хост выделен строго под CI, а доступы и сеть зажаты: отдельный пользователь, минимальные права, сегментация, разные runner’ы под разные уровни доверия.

Docker executor: изоляция не равна безопасности

Docker добавляет изоляцию процессов и файловой системы контейнера, но «безопасность по умолчанию» легко ломается настройками. Самые частые источники проблем:

  • privileged режим;
  • проброс сокета Docker (/var/run/docker.sock) в контейнер;
  • слишком широкие volume mount’ы (например, чувствительные директории хоста);
  • контейнеры без ограничений по capabilities/seccomp и без AppArmor/SELinux-политик.

Важно понимать: если контейнер получает доступ к Docker daemon хоста (через сокет) или запускается с privileged, для атакующего это часто эквивалент root-доступа к хосту. В таких конфигурациях тезис «docker executor безопаснее shell» превращается в иллюзию.

Про privileged: когда нужен и почему опасен

privileged в docker executor обычно включают ради Docker-in-Docker (DinD) или действий, требующих расширенных прав. Включение privileged резко расширяет возможности контейнера: управление устройствами, сетевыми настройками и другие «почти хостовые» права.

Практическое правило: включайте privileged только на отдельных runner’ах, только для конкретных проектов и только при понятной необходимости. Для тестов, линтеров и компиляции он почти никогда не нужен.

FastFox VDS
Облачный VDS-сервер
Виртуальные серверы с быстрым запуском и гибкой конфигурацией от 390₽ / мес
Доступные локации
Россия Нидерланды

Воспроизводимость и дрейф окружения

Один из самых болезненных классов проблем в CI — «вчера собиралось, сегодня нет». Часто причина не в коде, а в изменившемся окружении.

Shell executor: дрейф неизбежен без дисциплины

Если вы ставите зависимости через apt/yum/pip/npm прямо на хост, версии со временем меняются. Даже «случайное» обновление пакетов на сервере может сломать пайплайны. Плюс остаются следы прошлых сборок: временные файлы, кеши, глобальные зависимости и остатки workdir.

Компенсация возможна, но требует дисциплины: иммутабельные образы VM, регулярная пересборка runner-хоста, запрет ручных установок, регламент уборки, разделение runner’ов по проектам.

Docker executor: окружение фиксируется образом

В docker executor вы фиксируете версии toolchain в Dockerfile/образе. Это делает pipeline повторяемым и переносимым: можно поднять такой же runner на другом хосте и получить то же поведение.

Рабочая практика: для каждого стека иметь базовый образ (node + конкретная версия менеджера пакетов, php + composer + расширения), обновлять его контролируемо и раскатывать изменения окружения как код.

Производительность: где быстрее и почему

«Быстрее» зависит от того, что вы делаете в job’ах и как устроен кеш.

  • Shell executor чаще выигрывает по времени старта и по I/O на локальном диске, потому что нет контейнерной прослойки и оверхеда на pull/создание контейнера.
  • Docker executor может быть очень быстрым на повторяющихся сборках при правильном использовании кешей зависимостей и локального кеша образов, но «холодный старт» (pull больших образов) иногда дорогой.

Типичные узкие места:

  • скачивание зависимостей (npm/composer/pip/maven);
  • сборка Docker-образов в CI;
  • скорость диска и случайные IOPS (особенно для node_modules и vendor);
  • параллельность: сколько job’ов одновременно на одном runner’е.

Если упираетесь в диск и кеширование зависимостей, полезно посмотреть и на серверный кеш: например, как организовать cache в Nginx через map и ключи (см. пример с map и кешем в Nginx).

Cache: как работает и почему «не ускоряется»

Слово cache в GitLab CI часто воспринимают как «волшебную папку». На деле кеш — это компромисс между скоростью, предсказуемостью и безопасностью.

Общий принцип cache в GitLab Runner

Кеш — набор директорий, которые runner пытается сохранить между job’ами. Важно помнить:

  • кеш бывает локальным (на диске runner’а) или через удалённое хранилище (зависит от настройки runner’а);
  • кеш легко «отравляется» после неудачной сборки, если ключи и пути выбраны неудачно;
  • кеш не заменяет фиксацию версий зависимостей в lock-файлах.

Cache в shell executor

С shell executor кеш часто оказывается просто директорией на том же диске, где workspace. Это быстро, но рискованно: при обслуживании нескольких проектов кеши могут конфликтовать, а права файлов — ломаться.

Отдельная неприятность: «полу-кеш» может жить в workspace даже без механизма cache, и со временем становится непонятно, что именно ускоряет сборку и почему поведение на другом runner’е отличается.

Cache в docker executor

С docker executor кеш реализуется через примонтированные тома и/или механизм cache, который runner архивирует и восстанавливает. Тут часто всплывают права: контейнер пишет файлы одним UID/GID, а следующий job запускается под другим (зависит от образа). Итог — permission denied и «внезапные» падения.

Практика: кешировать только детерминированные директории менеджеров пакетов и привязывать ключи к lock-файлам, а не кешировать весь рабочий каталог.

Docker executor и сборка Docker-образов: три подхода

Один из главных аргументов в пользу docker executor — удобно собирать контейнеры в CI. Но тут и появляются самые «острые» настройки: privileged и доступ к Docker daemon.

1) Docker-out-of-Docker (проброс docker.sock)

Контейнер job’а получает доступ к Docker daemon хоста через /var/run/docker.sock. Плюсы: быстро, хорошо работает с кешем слоёв. Минусы: по сути, даёт job’у административный доступ к Docker на хосте, а значит часто и к самому хосту.

2) Docker-in-Docker (DinD) и privileged

Поднимается отдельный Docker daemon внутри контейнера. Часто требует privileged. Плюсы: лучше изоляция от Docker хоста (в теории). Минусы: сложнее, больше накладных расходов, много нюансов с сетью и кешем, и всё равно остаются риски из-за privileged.

3) Rootless/выделенная сборочная нода

Третий вариант — избегать privileged и сокета, используя rootless-сборку (если позволяет окружение) или выделенную сборочную ноду. Обычно это безопаснее, но требует более аккуратной инфраструктуры и регламентов.

Типовые сценарии: что выбирать в реальной жизни

Когда чаще выигрывает docker executor

  • Несколько команд/проектов, нужна изоляция между ними.
  • Нужно воспроизводимое окружение (одни и те же версии инструментов).
  • Сложный toolchain (разные версии Node/Java/Python) и желание упаковать всё в образы.
  • Важна «чистота»: каждый job стартует в контролируемой среде.

Когда уместнее shell executor

  • Один доверенный проект и отдельный выделенный runner-хост.
  • Сборки упираются в диск/IO, а контейнеризация добавляет накладные расходы.
  • Нужна тесная интеграция с хостом: доступ к специфичному железу, системным сервисам, нестандартным сетевым настройкам.
  • Хочется «минимум слоёв» для отладки: что видите в логе, то и на машине.

Разделение runner'ов по проектам и уровням доверия в CI/CD

Чек-лист выбора: вопросы до внедрения

  1. Кто может менять pipeline? Если это не только админы — избегайте shell и особенно осторожно относитесь к docker.sock и privileged.
  2. Сколько проектов на одном runner? Чем больше мультиарендность, тем больше смысл в Docker executor и разнесении runner’ов.
  3. Нужно ли собирать Docker-образы? Если да — продумайте модель безопасности заранее.
  4. Где хранится cache? Локально или удалённо, какие ключи, что кешируем, кто может это читать.
  5. Как обновляете окружение? Образы (Docker) или управление конфигурацией хоста (Shell).

Практические грабли и как их обходить

Права файлов и «permission denied»

В docker executor разные образы могут работать под разными UID/GID. Если вы кешируете директории с результатами сборки, следующий job может не иметь прав их читать/перезаписать. Лечится унификацией пользователя в образах, аккуратным выбором кешируемых путей и разделением кешей по стеку/ветке/lock-файлу.

«Секреты утекли в кеш»

Плохая практика — кешировать домашний каталог целиком или директории, куда случайно попадают токены (конфиги CLI, файлы авторизации). Кеш должен содержать только то, что безопасно переносить между job’ами. Секреты — через защищённые переменные и scoped-токены, а не через файлы в кешах.

Непредсказуемость из-за смешивания окружений

С shell executor частая ситуация: «на этом runner’е когда-то ставили утилиту», поэтому сборка не падает, пока вы не перенесёте проект на другой runner. Если выбрали shell — документируйте зависимости хоста и автоматизируйте установку (конфигурация как код), иначе переносимость будет страдать.

Минимальные примеры конфигурации runner’ов

Ниже — ориентировочные фрагменты config.toml, чтобы визуально закрепить различия. Не копируйте без адаптации под вашу модель безопасности и изоляции.

Shell executor: базовая идея

[runners]
  name = "shell-runner-01"
  url = "GITLAB_URL"
  token = "REDACTED"
  executor = "shell"

Docker executor: базовая идея

[runners]
  name = "docker-runner-01"
  url = "GITLAB_URL"
  token = "REDACTED"
  executor = "docker"

  [runners.docker]
    image = "alpine:3.20"
    pull_policy = "if-not-present"
    privileged = false
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    shm_size = 0
FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Итог: короткая рекомендация без фанатизма

Если важны воспроизводимость, изоляция и масштабирование на несколько проектов — чаще всего начинайте с docker executor, но держите под контролем privileged и любые «дырки» вроде проброса Docker socket.

Если у вас один доверенный проект и нужен максимум скорости/простоты, а хост выделен под CI и хорошо защищён — shell executor может быть оправдан. Но относитесь к этому как к запуску кода на сервере, а не как к «безопасному CI по умолчанию».

В большинстве инфраструктур лучший результат даёт комбинированный подход: отдельные runner’ы под разные классы задач и доверия, чёткая политика cache и запрет привилегий там, где они не нужны.

Поделиться статьей

Вам будет интересно

ClickHouse vs TimescaleDB vs PostgreSQL в 2026: что выбрать для аналитики, метрик и VDS OpenAI Статья написана AI (GPT 5)

ClickHouse vs TimescaleDB vs PostgreSQL в 2026: что выбрать для аналитики, метрик и VDS

Разбираем, где ClickHouse сильнее PostgreSQL, когда TimescaleDB удобнее отдельного OLAP, и как оценить CPU, RAM, диск, бэкапы и со ...
SQLite, MariaDB или PostgreSQL в 2026: какую базу выбрать для VDS OpenAI Статья написана AI (GPT 5)

SQLite, MariaDB или PostgreSQL в 2026: какую базу выбрать для VDS

SQLite, MariaDB и PostgreSQL решают разные задачи, хотя часто оказываются в одном списке выбора. Разберём, что лучше поставить на ...
HestiaCP vs ISPConfig vs CloudPanel 2026: какую панель выбрать для VDS OpenAI Статья написана AI (GPT 5)

HestiaCP vs ISPConfig vs CloudPanel 2026: какую панель выбрать для VDS

Панель управления может сэкономить часы администрирования, а может усложнить жизнь лишними сервисами. Разбираем HestiaCP, ISPConfi ...