Logo Craft Homelab Docs Нейросети Хостинг Контакты
GitLab в Docker: масштабирование от одного контейнера до кластера Трендовые github проекты в нашем телеграм канале. Подпишись →
18 сентября 2026 г.

Когда одного контейнера с GitLab уже недостаточно

Минимальная установка GitLab Community Edition умещается в один docker run с тремя volume и портами 80/22. Такая конфигурация держит нагрузку примерно до 20 RPS и 1000 пользователей — для небольшой команды этого хватает надолго. Дальше начинаются симптомы: страницы с ветками merge request отрисовываются медленно, git clone из CI падает с Gitaly is unreachable, sidekiq копит очередь фоновых задач. Стандартный совет в такой ситуации — переезд на новый кластер, желательно на отдельном железе. Это рабочий вариант, но дорогой и в развороте, и в сопровождении.

Альтернатива — постепенно выносить компоненты действующего инстанса в отдельные Docker-контейнеры, не трогая остальное. Официальный образ GitLab уже содержит все нужные для этого сервисы: PostgreSQL, Redis, Gitaly, Sidekiq, веб-приложение на Rails. Достаточно на каждом хосте включить в gitlab.rb только один компонент, а остальные явно выключить.

Один нюанс до начала: GitLab написан на Ruby, и его процессам нужна степень двойки по CPU. На конфигурациях с 7, 9 или 12 ядрами приложение работает с неполной нагрузкой даже при формально достаточных ресурсах — закладывайте 4, 8, 16 ядер.

PostgreSQL — первый кандидат на вынос

PostgreSQL хранит все метаданные: проекты, пользователей, merge request, issues. Тормозящий Web GUI при живой БД на диске — верный признак, что пора выносить базу на отдельный контейнер.

Единственный параметр, который стоит держать в голове отдельно от документации, — shm_size. По умолчанию Docker выделяет под shared memory 64 МБ, чего не хватает даже одному контейнеру с GitLab целиком. Без явного увеличения хотя бы до 256 МБ в логах появляется could not resize shared memory segment: No space left on device, а в браузере — 500-я ошибка.

Для переноса данных лучше подходит pg_dump/psql, чем rsync каталога данных: дамп переиспользуется при последующих обновлениях версии PostgreSQL, бинарная синхронизация каталога — одноразовое решение под конкретную версию.

docker exec -it single-node-gitlab gitlab-ctl stop puma
docker exec -it single-node-gitlab gitlab-ctl stop sidekiq
docker exec -t single-node-gitlab pg_dump -U gitlab gitlabhq_production > dump.sql
cat dump.sql | docker exec -i single-node-gitlab psql -h gitlab-pg-host -U gitlab -d gitlabhq_production

После переключения db_host в gitlab.rb и рестарта миграция обычно укладывается в 10 минут простоя.

Два момента ломают инстанс тихо, без явных ошибок в логах:

  • Часовой пояс. Если примонтировать /etc/localtime с хоста в контейнер PostgreSQL, веб-интерфейс покажет корректное время, авторизация продолжит работать, а в логах не будет ни одной строки об ошибке. Но в базе время уйдёт на несколько часов вперёд от UTC. Через какое-то время это проявится как 404 на странице создания gitlab runner, остановка партиционирования таблиц и забитые очереди sidekiq — эффект отложенный и его сложно связать с причиной постфактум.
  • Версия pg_dump при бэкапе. С 17-й версии GitLab годовой релиз (X.11) поддерживает сразу две мажорные версии PostgreSQL. Если обновили GitLab, но не прописали postgresql['version'] явно, плановый бэкап по cron может создаваться без дампа базы данных — и это не бросается в глаза, пока не понадобится восстановление.

Redis, объектное хранилище, Gitaly

Redis выносится проще всего: он хранит очередь задач sidekiq, кеш и пользовательские сессии, и отдельный контейнер разворачивается почти мгновенно. Прямого прироста производительности этот шаг не даёт, но обязателен для дальнейшей кластеризации Sidekiq.

Объектное хранилище — самая неоднозначная часть переезда. Технически можно просто расшарить каталог /var/opt/gitlab/gitlab-rails/shared между инстансами Rails и Sidekiq через сетевую папку и получить почти тот же эффект без отдельного S3-совместимого сервиса. Полноценное хранилище (например, MinIO) имеет смысл при больших объёмах LFS, артефактов CI и логов сборок — оно снимает эту нагрузку с диска приложения. Важное ограничение: GitLab работает с MinIO только по HTTPS, обычный HTTP запрос игнорируется, поэтому перед MinIO нужен nginx с TLS-терминацией. После миграции LFS-объектов в объектное хранилище GitLab начинает раздавать их напрямую оттуда — доступ к порту хранилища должен быть открыт всем, кто ходит по git lfs.

Логи GitLab CI (gitlab-ci/builds) в объектное хранилище не переезжают в принципе — их нужно чистить отдельным cron по возрасту файлов.

Gitaly — микросервис, управляющий git-репозиториями через gRPC: чтение, запись, поиск. Его вынос на отдельную дисковую подсистему снимает характерную проблему массового одновременного git clone — например, при пакетном запуске тестов по расписанию, когда в логах CI появляется Gitaly is unreachable. Перенос самих репозиториев делается через rsync в два прохода: первый — на горячую, пока GitLab работает и репозитории меняются, второй — уже с остановленным сервисом, для финальной синхронизации без выпавших коммитов.

Отдельная ловушка — несовпадение UID: GitLab, поставленный из rpm, создаёт пользователя git:git, а в Docker-образе обычно используется 998:998. После переноса файлов права на каталог репозиториев на новом хосте Gitaly нужно выставить вручную.

Sidekiq и Rails: горизонтальный рост без простоя

Sidekiq и Rails — единственные компоненты, масштабирование которых не требует прерывания сервиса: новые узлы добавляются и удаляются на лету. Но добавлять их смысл есть только после того, как вынесены PostgreSQL, Redis, Gitaly и хранилище — иначе новый узел просто упрётся в те же самые узкие места.

Для Sidekiq критичный параметр — sidekiq['queue_groups'] = ['*'] * N, где N обязано совпадать с числом доступных CPU на узле. Несовпадение не бросает ошибку, а просто оставляет часть ядер незадействованными.

Для Rails действует правило: наращивать число инстансов можно сколько угодно, кроме одного. Один экземпляр обязан оставаться «ведущим» — только через него идёт применение миграций базы при обновлении версии и снятие/восстановление бэкапов, и только у него разрешено прямое подключение к СУБД в обход пула соединений вроде pgbouncer. Удобно физически разместить этот ведущий узел на машине с балансировщиком и отключить на него маршрутизацию пользовательского трафика — тогда он не участвует в обслуживании запросов, но остаётся доступен для служебных операций. Документированный потолок — около 32 vCPU на узел Rails, после этого дальнейший рост даёт всё меньше отдачи и добавление узлов становится эффективнее вертикального апгрейда.

Замыкает схему HAProxy перед несколькими узлами Rails: балансировка по leastconn, отдельный frontend для HTTP/HTTPS и отдельный — в режиме tcp для SSH-доступа к репозиториям по git-протоколу.

Что в итоге

Ни один из этих шагов не требует одновременного простоя всей системы — PostgreSQL и Gitaly укладываются в минуты планового окна, Redis и объектное хранилище — в районе минуты на применение конфигурации, а Sidekiq и Rails добавляются без даунтауна вовсе. Выносить компоненты стоит по мере того, как каждый конкретный узкое место даёт о себе знать — медленный Web GUI указывает на PostgreSQL, ошибки Gitaly при параллельных клонах — на дисковую подсистему git-данных, забитые очереди — на Sidekiq и Redis. Постепенная кластеризация существующего контейнера обходится дешевле в деньгах и времени сопровождения, чем немедленный переезд на новую инфраструктуру, и оставляет откат к предыдущей конфигурации простым — через правку одного gitlab.rb и рестарт.