Как я настраиваю раннеров для GitLab CI
Настроил несколько раннеров под свои проекты и понял: вопрос не «shell или docker», а «где этот раннер физически живёт и что ему позволено трогать».
На каком хосте размещать
Мой принцип сейчас такой: раннер для деплоя живёт на том же хосте, куда деплоит.
Это позволяет использовать DooD — job-контейнер управляет хостовым докером
через смонтированный /var/run/docker.sock, SSH не нужен вообще.
Раннеры для линта и тестов можно хоть на другом континенте — им хост не нужен, им нужны только CPU и сеть.
Какой экзекьютор выбирать
Docker executor — 90% случаев
Job выполняется в чистом контейнере из образа image: node:22 и т.п.
Состояние между job'ами не течёт, чисто и предсказуемо.
[[runners]]
name = "shared-check"
executor = "docker"
[runners.docker]
image = "alpine"
volumes = ["/cache"]
Docker executor с DooD — для деплоя
Добавляем сокет и, для группового раннера, каталог релизов:
[[runners]]
name = "deployer"
executor = "docker"
[runners.docker]
image = "docker:28"
user = "runner" # uid:gid — чтобы job не был root на сокете
volumes = [
"/var/run/docker.sock:/var/run/docker.sock",
"/opt/apps:/mnt/deploy:ro", # /opt/apps — общий каталог релизов всех проектов
"/cache"
]
Что потребуется дополнительно:
- пользователь
runnerв группеdockerна хосте (usermod -aG docker runner); - каталоги
$DEPLOY_DIRпринадлежат этому пользователю — иначеmkdirв job'е упадёт с Permission denied (проверено на собственной грабле); - для job'ов, где нужен docker-compose — образ с CLI внутри (собираю свой
kovss-ci-deploy).
Shell executor — только для особых случаев
Job выполняется прямо на хосте, без изоляции. Я его почти не использую: нечисто, job'ы видят всё. Исключение — когда надо собирать что-то, что упорно не лезет в контейнер.
Как я ограничиваю раздувание кеша
Ранеры жрут диск тремя способами: кеш GitLab (/cache или S3),
кеш слоёв сборки buildkit, сами образы. Моя система защиты:
- Кеш GitLab — в S3 или с лимитом. Локальный
/cacheна диске хоста я ограничиваю таймером:
и таймер раз в сутки (OnCalendar=daily, Persistent=true).# /etc/systemd/system/gitlab-cache-cleanup.service ExecStart=/bin/sh -c "find /cache -maxdepth 3 -type d -mtime +7 -exec rm -rf {} +" - Кеш сборки — в after_script build-джоб:
docker builder prune -af --filter until=72h. - Образы —
docker image prune -fтам же + раз в неделюdocker system prune -afруками по ssh, когда смотрю на алерты. - Алерт по диску на 80% — без него любые таймеры рано или поздно проигрывают.
Ещё одна мелочь, которая сэкономила мне нервы: теги раннеров называю по смыслу
(check, test, build, deploy), а в пайплайне привязываю job'ы к тегам —
тогда deploy случайно не уедет на раннер без сокета, а линт не будет ждать
освобождения деплойной машины.