RTP DesportoLiga das Nações. Jesus anuncia primeira lista de convocados à frente da seleçãoESPNFirst-month grades for all 20 Premier League teams: From an A+ to FThe Jerusalem PostUS government website used AI search tool from China that FBI said copied AnthropicDaily MaverickProtests erupt in Nigeria after 37 miners die in custody of paramilitary agencyInquirerCPD: 14.1% of Filipinos still use traditional family planningPunchAlexander-Arnold, Palmer return to England squad for Nations Leagueוואלהשלושה תלמידים נהרגו באירוע ירי בבית ספר בפיליפינים, בהם החשוד ביריEl Comercio“Estados Unidos tiene la tecnología, pero la guerra con Irán revela el límite de sus arsenales”VilaWebEl govern demana explicacions a l’empresa de traducció pel desgavell al judici de Roger EspañolIl Fatto QuotidianoOperaio 24enne colpito da un macchinario in una fonderia nel Bolognese: è grave. Sindacati proclamano uno sciopero di 4 ore7sur7L’Unesco peut être un “modérateur” dans le “débat global” sur l’IA, affirme son chefCBS NewsF-16 from Texas crashes in Michigan, evacuation order lifted
The Daily Newsstand · Free, Always
Friday, September 18, 2026

Как мигрировать Gitlab под рабочей нагрузкой из одного в десять Docker‑контейнеров

Translate

Привет! Меня зовут Илья, я занимаюсь автоматизацией разработки аппаратного обеспечения в YADRO. Эта статья будет посвящена масштабированию работающего в Docker-контейнере под рабочей нагрузкой инстанса Gitlab. По мере роста команды производительности Gitlab, развернутого в одном Docker-контейнере, становится мало. И масштабирование работающего под рабочей нагрузкой Gitlab, скажем, на десять контейнеров может стать нетривиальной задачей. Далее я на нашем примере расскажу, как это разумно организовать.

По ходу повествования мы оценим, на что влияет кластеризация того или иного компонента, взглянем на примеры минимально необходимых docker-compose.yml, а также прикинем время недоступности Gitlab, необходимое на выполнение той или иной процедуры

Gitlab — одно из самых популярных и комплексных решений для разработки и DevOps, и не просто так. Вот минимальная команда для запуска Gitlab Community Edition в Docker-контейнере:

docker run --detach \
  --hostname gitlab.example.com \
  --env GITLAB_OMNIBUS_CONFIG="external_url 'https://gitlab.example.com'" \
  --publish 80:80 --publish 22:22 \
  --name single-node-gitlab \
  --restart always \
  --volume /docker/gitlab/config:/etc/gitlab \
  --volume /docker/gitlab/logs:/var/log/gitlab \
  --volume /docker/gitlab/data:/var/opt/gitlab \
  --shm-size 256m \
  gitlab/gitlab-ce:<version>-ce.0

Ее хватит, чтобы мы получили open source веб-платформу, способную обеспечить:

  • хранение исходного кода в git-репозиториях;

  • совместную разработку и ревью кода;

  • инструменты управления проектами: отслеживание задач (issue tracker), встроенную вики, учет времени и т. д.;

  • встроенные инструменты CI/CD, хранения артефактов и контейнеров Docker.

Все это будет отлично работать при нагрузках до 20 RPS и/или 1000 пользователей. Но рано или поздно большинство команд вырастает, и производительности Gitlab в одном Docker-контейнере начинает не хватать. Интернет обычно предлагает сразу переезжать на новый кластер — желательно на новом железе. Это отличное, но дорогое решение в плане как развития, так и сопровождения.

Мы же выбрали другой вариант — не базе кластера Gitlab из Docker-контейнеров. Причин этому несколько:

  • Изначально наш Gitlab работал в единственном Docker-контейнере. Мы не переезжали на новый кластер, а понемногу дорабатывали существующий.

  • Все наше окружение контейнеризировано (Docker/Podman, k8s). Решили не дробить ландшафт.

  • По умолчанию в контейнерах все закрыто/отключено, так что нужно явно задать порты, прописывать ресурсы. Это помогает глубже понять инструмент.

  • Простота отката. Бывают случаи, когда минорный релиз выпущен с ошибкой.

  • Информационная безопасность. Отдельные компоненты в контейнере можно обновить быстрее, чем выйдет новый релиз Gitlab.

Такое масштабирование возможно, поскольку в официальном Docker-образе собраны все инструменты, необходимые для работы Gitlab. Всё, что нам нужно сделать – вынести компоненты на отдельные машины:

Инженерный контент > (на Хабр) Руководство по миграции Gitlab на кластер из Docker контейнеров > image-2026-8-17_20-26-46.png

Отмечу особенность Ruby-приложения Gitlab: количество CPU для компонентов Gitlab должно быть степенью двойки. На конфигурациях с 7/9/12 CPU приложение работает в лучшем случае с неполной нагрузкой.

Важные уточнения и ограничения перед началом:

  • Кластеризуем мы на примере Gitlab 18.11.

  • Docker-compose.yml немного упрощены для фокусирования на взаимодействии компонентов.

  • В docker-compose.yml и gitlab.rb включены Prometheus-экспортеры, но само подключение к системе мониторинга не описано. Это тема для отдельного обсуждения.

  • Централизованный сбор логов и отправка их ELK (или подобный стек) не описаны, но предполагаются. Это тема для отдельного обсуждения.

  • Работаем под root, Docker без rootless. На проде rootless удобнее, так как на id можно привязать пользователей ОС с понятными именами. Но это делает примеры слишком громоздкими.

PostgreSQL

PostgreSQL — единственная СУБД, поддерживаемая Gitlab. Она содержит все метаданные о проектах, пользователях, merge requests, issues и т. д. Важно помнить, что мажорной версии Gitlab соответствует конкретная мажорная версия PostgreSQL.

WebGUI может не хватать производительности: медленно отрисовываются страницы, заполняются ветки в merge request, комментарии или что-нибудь еще, связанное с метаданными. Чтобы привести производительность в норму в этом случае, достаточно будет провести миграцию на внешний PostgreSQL.

Подготовка окружения

Не будем детально останавливаться на процессе запуска PostgreSQL в Docker — при желании можно почитать хороший гайд на Хабре. Аргументы против такого подхода от администратора БД можно посмотреть на stackoverflow. Порядок действий таков.

Создадим на хосте gitlab-pg-host файл /docker/docker-compose/docker-compose.yml c содержанием:

services:
  postgres:
    image: company-local-docker-images/postgres:16.14
    container_name: gitlab-pg
    logging:
      options:
        max-size: "1g"
        max-file: "5"
    environment:
      POSTGRES_USER: gitlab
      POSTGRES_PASSWORD: "DB_PASSWORD"
      POSTGRES_DB: gitlabhq_production
    shm_size: 1g
    volumes:
      - /docker/postgres/16/data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U gitlab -d gitlabhq_production"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s
    restart: unless-stopped
deploy:
      resources:
        limits:
          cpus: '2'
          memory: 8G
     networks:
      - gitlabnet

  postgres-exporter:
    image: company-local-docker-images/postgres-exporter:0.17.1
    container_name: pg-exporter
    environment:
      DATA_SOURCE_NAME: "postgresql://gitlab:DB_PASSWORD@postgres:5432/gitlabhq_production?sslmode=disable"
    ports:
      - "9187:9187"
    depends_on:
      - postgres
     networks:
      - gitlabnet

networks:
  gitlabnet:
    driver: bridge

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

Запустим PostgreSQL:

docker compose -f /docker/docker-compose/docker-compose.yml up -d

Дожидаемся старта и редактируем /docker/postgres/16/data/postgresql.conf согласно рекомендациям вендора:

work_mem  8 MB
maintenance_work_mem 64 MB        
max_connections  400 # по умолчанию 100
shared_buffers       2 GB #25% от RAM сервера.
statement_timeout           60000
hot_standby_feedback    on

Перезапускаем контейнер и проверяем, что можно подключиться к PostgreSQL.

Миграция

Если все прошло успешно, то планируем прерывание в обслуживании. Без этого вынести PostgreSQL за пределы Gitlab не представляется возможным.

Вся миграция состоит из двух шагов:

  1. Перенести данные в новую базу PostgreSQL.

  2. Переключить Gitlаb на внешней PostgreSQL.

Технически перенести данные из одной базы в другую можно без прерывания в обслуживании. Но высок риск потери новых записей, созданных в момент копирования и переключения. Поэтому я рассматривать такой вариант не буду.

В PostgreSQL пишут два компонента Gitlab:

  • Puma — веб-сервер Ruby-приложений, собственно, сам Gitlab. Его процессы мы видим в дереве процессов.

  • Sidekiq — менеджер очередей на Ruby.

Остановим эти процессы:

docker exec -it single-node-gitlab gitlab-ctl stop puma
docker exec -it single-node-gitlab gitlab-ctl stop sidekiq

Проверим, что puma и sidekiq остановлены:

docker exec -it single-node-gitlab gitlab-ctl status
Инженерный контент > (на Хабр) Руководство по миграции Gitlab на кластер из Docker контейнеров > image-2026-8-17_20-58-2.png

Вариантов миграции данных PostgreSQL у нас несколько.

  • Через rsync перенести данные с одного хоста на другой:

rsync -avz -e ssh single-node-gitlab-host@/docker/gitlab/data/postgresql/data/ gitlab-pg-host@/docker/postgres/16/data/
  • Сделать полный бэкап Gitlab и восстановиться из него после переключения на новую базу:

docker exec -it single-node-gitlab gitlab-backup create
  • Сделать бэкап базы средствами Gitlab:

docker exec -it single-node-gitlab gitlab-backup create SKIP=tar,repositories,uploads,builds,artifacts,pages,lfs,terraform_state,registry,packages,ci_secure_files,agent_plan_content,external_diffs

В первом варианте я не смогу переиспользовать rsync при обновлении версии PostgreSQL, а снимать бэкап долго. Поэтому так я делать не буду, а вместо этого мигрирую данные через создание и восстановление данных докерезированного PostgreSQL.

1. Создаем бэкап на хосте с запущенным в одном Docker-контейнере Gitlab:

docker exec -t single-node-gitlab pg_dump -U gitlab gitlabhq_production > gitlabdatabase.sql. `date +%Y-%m-%d"_"%H_%M_%S`

2. Оставаясь на той же машине, восстанавливаем бэкап. На случай если на хосте нет psql, лучше делать все через контейнер с Gitlab.

cat gitlabdatabase.sql. `date +%Y-%m-%d"_"%H_%M_%S` | docker exec -i single-node-gitlab psql -h gitlab-pg-host -U gitlab -d gitlabhq_production

3. Проверяем, что восстановление прошло без ошибок и к базе можно подключиться удаленно. Если все хорошо, редактируем конфигурационный файл Gitlab gitlab.rb. В нашем случае файл будет располагаться в /docker/gitlab/config/ согласно инструкции:

# Disable the bundled Omnibus provided PostgreSQL
postgresql['enable'] = false

# PostgreSQL connection details
gitlab_rails['db_adapter'] = 'postgresql'
gitlab_rails['db_encoding'] = 'unicode'
gitlab_rails['db_host'] = gitlab-pg-host' # IP/hostname of database server
gitlab_rails['db_port'] = 5432
gitlab_rails['db_password'] = DB_PASSWORD'

4. Сохраняем изменения и перезапускаем docker-контейнер с Gitlab. По моему опыту, процесс миграции редко занимает более 10 минут.

Если получаем 500 ошибку и нет времени на разбор, комментируем ранее запущенные изменения и перезапускаем контейнер.

Инженерный контент > (на Хабр) Руководство по миграции Gitlab на кластер из Docker контейнеров > image-2026-8-17_21-2-25.png

Особенности работы с внешним PostgreSQL в Docker-контейнере

Часовой пояс

Стандартный для Gitlab часовой пояс — UTC. Менять его без необходимости не стоит. Особенно вот так:

volumes:

  • /etc/localtime : /etc/localtime : ro

Gitlab будет работать и даже не подаст вида, что что-то не так.

  • В WebGUI время корректное (подумаешь, у лейблов метка времени на 3 часа больше).

  • Авторизация через внешние сервисы работает.

  • В логах ошибок нет.

Но в базе вместо корректной временной зоны UTC...

Инженерный контент > (на Хабр) Руководство по миграции Gitlab на кластер из Docker контейнеров > image-2026-8-17_21-3-36.png

...будут данные на 3 часа больше:

Инженерный контент > (на Хабр) Руководство по миграции Gitlab на кластер из Docker контейнеров > image-2026-8-17_21-3-56.png

Такая на первый взгляд мелочь приводит к тому, что:

  • Пропадает возможность создавать gitlab runners через WebGUI — страница с токеном выдаст 404.

  • Сразу остановится создание партиций, и эффект от этого будет заметен через несколько месяцев.

  • Некоторые background jobs будут полностью забивать очереди sidekiq.

Версия при бэкапе

С 17 версии Gitlab придерживается политики ежегодного обновления версии PostgreSQL. Это значит, что 11-е версии в рамках года (напр. 17.11, 18.11) поддерживают работу с двумя версиями PostgreSQL. Gitlab 18.11 может работать с PostgreSQL 16 или 17, в то время как Gitlab 18.10 и ниже — только с PostgreSQL 16, а Gitlab 19.0 — только c PostgreSQL 17. По умолчанию Gitlab 18.11 использует PostgreSQL 16 в случае обновления с более ранних версий и PostgreSQL 17 для свежих инсталляций. По сути, вся поддержка заключается в версии pg_dump – 16 или 17. Если вы:

  • обновили Gitlab до 18.11,

  • мигрировали на PostgreSQL 17 в рамках подготовки к миграции на Gitlab 19,

  • не добавили в gitlab.rb опцию postgresql['version'] = 17,

...то резервная копия по CRON 0 2 * * * docker exec gitlab-sc gitlab-backup create CRON=1 будет создаваться без дампа базы. И со стороны это не будет видно. Изменится только размер, но при ежедневной ротации файлов резервных копий такое быстро выпадает из поля зрения.

Контейнер не дает полной изоляции

Работая с контейнерами, часто забываешь, что контейнер — это не виртуальная машина. И обновление операционной системы или пакетов хоста могут повредить данные внутри контейнера.

Redis

Redis — самый простой для выноса компонент Gitlab. Я не смог найти примеры, когда миграция на внешний Redis повышает производительность Gitlab, хотя здесь Redis:

  • хранит список задач (job) для очереди фоновых задач (Sidekiq),

  • кеширует данные,

  • управляет пользовательскими сессиями.

Подготовка окружения для внешнего Redis

Внешний Redis — обязательный шаг кластеризации и нужно пройти его до конца.

1. На хосте redis-host создаем /docker/docker-compose/docker-compose.yml с содержанием:

services:
  redis:
    image: company-local-docker-images/redis:7.4.9
    container_name: gitlab-redis
    logging:
      options:
        max-size: "1g"
        max-file: "5"
    volumes:
      - "/docker/redis/data:/data"
    command: redis-server --bind 0.0.0.0 --requirepass REDIS_PASS --appendonly yes --appendfsync everysec
    ports:
      - "6379:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "-a", "REDIS_PASS", "ping"]
      interval: 30s
      timeout: 10s
      retries: 5
    restart: unless-stopped
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 5G
     networks:
      - gitlabnet

  redis-exporter:
    image: company-local-docker-images/oliver006/redis_exporter:v1.88.0
    container_name: redis_exporter
    environment:
      REDIS_ADDR: "redis://redis:6379"
      REDIS_PASSWORD: " REDIS_PASS"
    ports:
      - "9121:9121"
    restart: unless-stopped
    depends_on:
        - redis
     networks:
      - gitlabnet

networks:
  gitlabnet:
    driver: bridge

2. Запускаем контейнер docker-compose:

-f /docker/docker-compose/docker-compose.yml up -d

Стартует он почти моментально, проверяем лог на наличие ошибок, подключаемся к Redis c помощью redis-cli.

3. Переходим на хост с single-node-gitlab. В конфигурационном файле /docker/gitlab/config/gitlab.rb указываем согласно инструкции:

### GitLab Redis settings
redis['enable'] = false
gitlab_rails['redis_host'] = "gitlab-redis-host"
gitlab_rails['redis_port'] = 6379
gitlab_rails['redis_password'] = “REDIS_PASS”

Миграция на внешний Redis

Планируем прерывание в обслуживании (около минуты). На хосте с single-node-gitlab применяем новые настройки:

docker exec -t single-node-gitlab gitlab-ctl reconfigure

Если в течение пары минут ошибка 500 не ушла и Gitlab не заработал штатно, нужно откатить изменения и читать логи.

Важно: проблемы с Redis и PostgreSQL в Gitlab проявляются одинаково — ошибкой 500. Для Postgres чаще всего нужно сразу смотреть лог, для Redis — упавшие задачи в очереди sidekiq.

Объектное хранилище

Объектное хранилище — неожиданно самая неоднозначная часть. Рассмотрим миграцию на внешнее объектное хранилище на примере MiniO, не забывая два важных фактора. Во-первых, фактически MiniO мертв. Во-вторых, почти то же самое можно получить, пошарив между rails-приложениями Gitlab и очередями sidekiq папку /var/opt/gitlab/gitlab-rails/shared из Docker-контейнера single-node-gitlab. В нашем примере на хосте она располагается по пути /docker/gitlab/data/gitlab-rails/shared/. Настройку я опишу ниже.

Для больших инсталляций Gitlab объектное хранилище предпочтительнее расшаренной папки, так как оно заберет на себя часть нагрузки, связанной с объектами LFS, логами CI и артефактами сборок.

Подготовка окружения для внешнего объектного хранилища

На хосте gitlab-minio создаем /docker/docker-compose/docker-compose.yml с содержанием:

services:
  minio:
    image: company-local-docker-images/minio:RELEASE.2025-07-18T21-56-31Z-cpuv1
    container_name: gitlab-mini0
    command: server /data --console-address ":9001"
    environment:
      - MINIO_ROOT_USER=S3User
      - MINIO_ROOT_PASSWORD=S3Password
    logging:
      options:
        max-size: "10g"
        max-file: "3"
    volumes:
      - "/docker/minio/data:/data"
    restart: unless-stopped
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 7G
    networks:
      - gitlabnet

  nginx-minio:
    image: company-local-docker-images/nginx:1.26.0-perl
    container_name: minio-nginx
    ports:
      - "443:443"
      - "9009:9009"
    volumes:
      - "/docker/nginx/etc/conf.d:/etc/nginx/conf.d"
      - "/opt/ssl:/opt/ssl"
      - "/docker/nginx/log:/var/log/nginx"
    restart: unless-stopped
    networks:
      - gitlabnet

networks:
  gitlabnet:
    driver: bridge

Gitlab работает с Mini0 только при наличии TSL шифрования. Запросы по http будут игнорироваться. Оборачиваем доступ в nginx, /docker/nginx/etc/conf.d/minio.conf:

server {
        listen                          80;
        server_name                     gitlab-mini0-host;
        return                          301 https://$server_name$request_uri;
}
server {
        listen                          443 ssl;
        listen                          [::]:443;
        server_name                     gitlab-mini0-host;

        # Allow special characters in headers
        ignore_invalid_headers off;
        # Allow any size file to be uploaded.
        # Set to a value such as 1000m; to restrict file size to a specific value
        client_max_body_size 0;
        # Disable buffering
        proxy_buffering off;
        proxy_request_buffering off;

        ssl_certificate                 /opt/ssl/my-cert.cer;
        ssl_certificate_key             /opt/ssl/my-cert.key;

        ssl_session_cache               builtin:1000 shared:SSL:10m;
        ssl_protocols                   TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers                     HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
        ssl_prefer_server_ciphers       on;

        access_log                      /var/log/nginx/gitlab-mini0.https.access.log;
        error_log                       /var/log/nginx/gitlab-mini0.https.error.log;

        location / {
proxy_set_header Host $http_host;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Proto $scheme;
  proxy_set_header X-NginX-Proxy true;
  real_ip_header X-Real-IP;
  proxy_connect_timeout 300;
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";
  chunked_transfer_encoding off;
  proxy_pass http://minio:9001/;
        }
}

server {
        listen                          9009 ssl;
        listen                          [::]:9009;
        server_name                     gitlab-mini0-host;

        # Allow special characters in headers
        ignore_invalid_headers off;
        # Allow any size file to be uploaded.
        # Set to a value such as 1000m; to restrict file size to a specific value
        client_max_body_size 0;
        # Disable buffering
        proxy_buffering off;
        proxy_request_buffering off;

        ssl_certificate                 /opt/ssl/my-cert.cer;
        ssl_certificate_key             /opt/ssl/my-cert.key;

        ssl_session_cache               builtin:1000 shared:SSL:10m;
        ssl_protocols                   TLSv1 TLSv1.1 TLSv1.2;
        ssl_ciphers                     HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
        ssl_prefer_server_ciphers       on;

        access_log                      /var/log/nginx/gitlab-mini0.https.access.log;
        error_log                       /var/log/nginx/gitlab-mini0.https.error.log;

        location / {
proxy_set_header Host $http_host;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header X-Forwarded-Proto $scheme;
  proxy_set_header X-NginX-Proxy true;
  real_ip_header X-Real-IP;
  proxy_connect_timeout 300;
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";
  chunked_transfer_encoding off;
  proxy_pass http://minio:9000/;
        }
}

Запускаем docker-compose:

-f /docker/docker-compose/docker-compose.yml up -d

Если все хорошо, видим страницу входа:

Инженерный контент > (на Хабр) Руководство по миграции Gitlab на кластер из Docker контейнеров > image-2026-8-17_22-43-50.png

Вручную создаем бакеты согласно списку:

Инженерный контент > (на Хабр) Руководство по миграции Gitlab на кластер из Docker контейнеров > image-2026-8-17_22-46-44.png

Для проверки можно что-то загрузить, но это непринципиально.

Переходим к настройке Gitlab. Для этого на хосте с single-node-gitlab редактируем конфигурационный файл Gitlab gitlab.rb согласно инструкции. В рассматриваемом случае файл будет располагаться по пути /docker/gitlab/config/gitlab.rb.

### Consolidated (simplified) object storage configuration
gitlab_rails['object_store']['enabled'] = true
gitlab_rails['object_store']['proxy_download'] = false
gitlab_rails['object_store']['connection'] = {
'provider' => 'AWS',
'region' => 'miran-gitlab-s3',                                    # Любой регион (MinIO игнорирует)
'endpoint' => 'https://gitlab-mini0:9009',
'aws_access_key_id' => 'S3User',                          		  # MinIO root user
'aws_secret_access_key' => 'S3Password',			  			  # MinIO root password
'path_style' => true                                              # Обязательно для MinIO!
}
gitlab_rails['object_store']['objects']['artifacts']['bucket'] = 'gl-artifacts'
gitlab_rails['object_store']['objects']['external_diffs']['bucket'] = 'gl-external-diffs'
gitlab_rails['object_store']['objects']['lfs']['bucket'] = 'gl-lfs'
gitlab_rails['object_store']['objects']['uploads']['bucket'] = 'gl-uploads'
gitlab_rails['object_store']['objects']['packages']['bucket'] = 'gl-packages'
gitlab_rails['object_store']['objects']['dependency_proxy']['bucket'] = 'gl-dependency-proxy'
gitlab_rails['object_store']['objects']['terraform_state']['bucket'] = 'gl-terraform-state'
gitlab_rails['object_store']['objects']['pages']['bucket'] = 'gl-pages'
gitlab_rails['object_store']['objects']['ci_secure_files']['bucket'] = 'gl-ci-secure-files'

Применяем новые настройки:

docker exec -t single-node-gitlab gitlab-ctl reconfigure

Будет короткое прерывание в обслуживании, около минуты на обновление конфигурации Gitlab. Теперь миграция:

docker exec -t single-node-gitlab gitlab-rake gitlab:uploads:migrate:all
docker exec -t single-node-gitlab gitlab-rake gitlab:artifacts:migrate
docker exec -t single-node-gitlab gitlab-rake gitlab:lfs:migrate
docker exec -t single-node-gitlab gitlab-rake gitlab:packages:migrate

Особенности работы с внешним miniO в Docker-контейнере

  • После подключения объектное хранилище работает сразу. Все новые файлы создаются в нем сразу. 

  • Если связь с хранилищем будет потеряна, Gitlab будет искать файлы на локальном диске в /var/opt/gitlab/gitlab-rails/shared без лишних запросов или разрешений.

  • После миграции LFS объектов на объектное хранилище обязательно откройте всем заинтересованным доступ к https://gitlab-mini0:9009, так как Gitlab будет раздавать git lfs именно с него.

  • При использовании самоподписанных сертификатов обязательно для single-node-gitlab добавьте их на хосте в /docker/gitlab/config/trusted-certs, иначе команды миграции выполняться не будут. Хоть новые объекты и будут создаваться и раздаваться без проблем.

  • Достаточно часто я сталкивался с тем, что не все объекты переезжают. Проще перенести руками либо пошарить /var/opt/gitlab/gitlab-rails/shared между rails (подробности будут далее).

  • Логи Gitlab CI /var/opt/gitlab/gitlab-ci/builds в объектное хранилище не переезжают. Я шарю эту папку и чищу по cron:

0 0 * * * find /mnt/gitlab-ci-builds/ -depth -mtime +14 -exec rm -rf {} +

Gitaly

Gitaly, сервис разработки команды Gitlab — микросервис в системе GitLab, управляющий хранением и обработкой всех Git-репозиториев. Он принимает запросы от веб-интерфейса и других частей платформы через протокол gRPC и выполняет операции с кодом: чтение, запись, поиск.

Миграция на внешний Gitaly — рекомендую сразу на отдельную дисковую подсистему — из Gitlab может быть достаточной для нормализации производительности в случаях активной единовременной нагрузки по выкачиванию репозиториев. Например, при массированном запуске тестов в Jenkins/Gitlab CI по расписанию, что характеризуется ошибками вида GitLab error: Gitaly is unreachable в логе git clone.

Gitaly для версий Gitlab Enterprise и Community Edition выглядят и ведут себя одинаково. Для Gitaly стоит отдать предпочтение Docker CE образу, так как он занимает меньше места.

Подготовка окружения для внешнего Gitaly

Приступаем к настройке внешнего Gitaly. Конфигурации Gitlab Rails, Sidekiq, Gitaly очень похожи, и будет много дублирования для тех, кто не читал другие пункты.

На хосте gitlab-gitaly-host создаем /docker/docker-compose/docker-compose.yml с содержанием:

services:
  gitaly:
    image: company-local-docker-images/gitlab/gitlab-ce:18.11.11-ce.0
    container_name: gitlab-gitaly
    logging:
      options:
        max-size: "1g"
        max-file: "5"
    volumes:
      - "/docker/gitlab/config:/etc/gitlab"
      - "/docker/gitlab/data:/var/opt/gitlab"
      - "/docker/gitlab/logs:/var/log/gitlab"
    ports:
      - "8075:8075"
      - "9236:9236"
    restart: unless-stopped
    deploy:
      resources:
        limits:
          cpus: '4'
          memory: 7G
     networks:
      - gitlabnet

networks:
  gitlabnet:
    driver: bridge

Запускаем docker-compose -f /docker/docker-compose/docker-compose.yml up -d. Ждем, когда создадутся все файлы конфигураций, и останавливаем контейнер docker-compose:

 -f /docker/docker-compose/docker-compose.yml down

Запрещаем обновление базы Gitlab при обновлении версии:

touch /docker/gitlab/config/skip-auto-reconfigure

Копируем файл с секретам /docker/gitlab/config/gitlab-secrets.json с хоста gitlab.example.com на gitlab-gitaly-host.

Переключаем Gitlab в режим Gitaly через редактирование /docker/gitlab/config/gitlab.rb согласно инструкции:

gitlab_rails['internal_api_url'] = 'https://gitlab.example.com'

# Disable all other services on the Gitaly node
postgresql['enable'] = false
redis['enable'] = false
nginx['enable'] = false
puma['enable'] = false
sidekiq['enable'] = false
gitlab_workhorse['enable'] = false
prometheus_monitoring['enable'] = false
gitlab_kas['enable'] = false

# Enable only the Gitaly service
gitaly['enable'] = true

# Enable Prometheus
prometheus['enable'] = true

# Disable database migrations to prevent database connections during 'gitlab-ctl reconfigure'
gitlab_rails['auto_migrate'] = false

gitaly['configuration'] = {
# Configure Gitaly to listen on network
   listen_addr: '0.0.0.0:8075',
   prometheus_listen_addr: '0.0.0.0:9236',
# Configure a strong auth_token
   auth: {
      token: 'GITALY_TOKEN',
   },
# Configure the storage location for Git data
   storage: [
     # Replace with appropriate name for each Gitaly nodes.
     {
       name: 'gitaly',
       path: '/var/opt/gitlab/git-data/repositories',
     },
   ]
}

Важно: при использовании самоподписанных сертификатов обязательно добавьте их на хосте gitaly-host в /docker/gitlab/config/trusted-certs. Иначе встроенная проверка будет отрабатывать корректно, но в WebGUI содержимого репозиториев не будет.

docker exec -it gitlab-gitaly /opt/gitlab/embedded/bin/gitaly check /var/opt/gitlab/gitaly/config.toml

Прописываем конфигурацию внешнего gitaly в gitlab.rb на хосте single-node-gitlab:

### Gitaly settings
gitaly['enable'] = false
gitlab_rails['repositories_storages'] = {
  "gitaly-external" => {"gitaly_address" => "tcp://gitaly-host:8075","gitaly_token" => 'GITALY_TOKEN'}
}

Так как мы выносим gitaly на внешний хост, то гибридную конфигурацию не рассматриваем. Подробнее можно самостоятельно прочитать в документации вендора.

Важно: при использовании самоподписанных сертификатов обязательно добавьте их на хосте gitaly-host в /docker/gitlab/config/trusted-certs:

docker exec -it gitlab-gitaly /opt/gitlab/embedded/bin/gitaly check /var/opt/gitlab/gitaly/config.toml

Миграция на внешний Gitaly

Вся миграция состоит из двух шагов:

  1. Перенести данные git репозиториев на новый gitaly.

  2. Переключить Gitlаb на внешний gitaly.

В нашем случае все данные git-репозиториев на хосте с single-node-gitlab лежат в папке /docker/gitlab/data/git-data/repositories. Нужно их перенести в ту же папку, но на внешний gitaly. Сделать это можно следующими способами:

  • Через создание и восстановление бэкапа. Все аналогично миграции PostgreSQL, не буду останавливаться, вариант не подходит.

  • Попроектная миграция (см. официальную документацию) — идеально, если проектов несколько, не требует длительного даунтайма. Но ручная миграция нескольких тысяч проектов может быть затруднительна. Этот вариант тоже не буду рассматривать.

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

Миграция на внешний Gitaly через rsync

На «горячую» (single-node-gitlab работает, gitlab-gitaly выключен) через rsync мигрируем основную массу данных:

rsync -avhz --delete --exclude='.gitaly-metadata' gitlab.example.com:/docker/gitlab/data/git-data/repositories/ gitlab-gitaly-host:/docker/gitlab/data/git-data/repositories/

Важно: Не забудьте удалить /docker/gitlab/data/git-data/repositories/.gitaly-metadata на gitlab-gitaly, если он существует по какой-то причине.

Планируем недоступность сервиса и останавливаем single-node-gitlab. Напомню, что изменения в его gitlab.rb внесены, но не применены. Затем финально синхронизируем данные с помощью rsync:

chown -R git:git /docker/gitlab/data/git-data/repositories/

Важно: на внешнем gitlab-gitaly-host нужно изменить права на репозитории. Gitlab в rpm создаёт пользователя git:git, Gitlab в Docker обычно 998:998.

Наконец, запускаем сервис single-node-gitlab на хосте gitlab.example.com. Проверить успешность переключения проще всего через WebGUI: если содержание любого репозитория отображается, изменения вносятся, то все работает. Если что-то пошло не так, на хосте внешнего gitaly проверяем:

docker exec -it gitlab-gitaly /opt/gitlab/embedded/bin/gitaly check /var/opt/gitlab/gitaly/config.toml

Откатываемся через изменение конфигурации.

Sidekiq

Sidekiq — обработчик очередей. Все задачи по срабатыванию веб-хуков, отправки писем, партиционированию таблиц и прочего проходят через него. Масштабирование Sidekiq приносит результат при большой Gitlab CI нагрузке — например, одновременный запуск нескольких тысяч Gitlab CI Pipelines. Прерывание в обслуживании не требуется. Новые обработчики очередей добавляются и удаляются на лету.

Важно: для масштабирования Sidekiq обязательно выполнение масштабирования всех вышеописанных элементов.

Подготовка окружения для внешнего Sidekiq

На хосте gitlab-gitaly-host создаём /docker/docker-compose/docker-compose.yml с содержанием:

services:
  sidkiq:
    image: company-local-docker-images/gitlab-ce:18.11.11-ce.0
    container_name: gitlab-sidekiq
    hostname: gitlab-sidekiq-host
    logging:
      options:
        max-size: "1g"
        max-file: "5"
    volumes:
      - "/docker/gitlab/config:/etc/gitlab"
      - "/docker/gitlab/data:/var/opt/gitlab"
      - "/docker/gitlab/logs:/var/log/gitlab"
    ports:
      - "8082:8082"
    restart: unless-stopped
    deploy:
      resources:
        limits:
          cpus: '4'
          memory: 15G
     networks:
      - gitlabnet

networks:
  gitlabnet:
    driver: bridge 

В image обязательно используйте CE или EE в зависимости от версии вашего Gitlab, у меня здесь и далее — СЕ. Hostname — необязательный, но удобный параметр, отвечает за отображение читаемого имени в WebGUI Gitlab

Далее запускаем docker-compose:

-f /docker/docker-compose/docker-compose.yml up -d

Ждем, когда создадутся все файлы конфигураций, и останавливаем контейнер docker-compose:

-f /docker/docker-compose/docker-compose.yml down

Запрещаем обновление базы Gitlab при обновлении версии:

touch /docker/gitlab/config/skip-auto-reconfigure

Копируем файл с секретом /docker/gitlab/config/gitlab-secrets.json с хоста gitlab.example.com на gitlab-sidekiq-host. Переключаем Gitlab в режим Sidekiq через редактирование /docker/gitlab/config/gitlab.rb согласно документации:

## GitLab configuration settings
external_url 'https://gitlab.example.com'
roles ['sidekiq_role']

### Prevent database migrations from running on upgrade automatically
gitlab_rails['auto_migrate'] = false

### Object storage configuration
gitlab_rails['object_store']['enabled'] = true
gitlab_rails['object_store']['proxy_download'] = false
gitlab_rails['object_store']['connection'] = {
'provider' => 'AWS',
'region' => 'gitlab-s3',
'endpoint' => 'https://gitlab-mini0:9009',
'aws_access_key_id' => 'S3User',
'aws_secret_access_key' => 'S3Password',
'path_style' => true
}
gitlab_rails['object_store']['objects']['artifacts']['bucket'] = 'gl-artifacts'
gitlab_rails['object_store']['objects']['external_diffs']['bucket'] = 'gl-external-diffs'
gitlab_rails['object_store']['objects']['lfs']['bucket'] = 'gl-lfs'
gitlab_rails['object_store']['objects']['uploads']['bucket'] = 'gl-uploads'
gitlab_rails['object_store']['objects']['packages']['bucket'] = 'gl-packages'
gitlab_rails['object_store']['objects']['dependency_proxy']['bucket'] = 'gl-dependency-proxy'
gitlab_rails['object_store']['objects']['terraform_state']['bucket'] = 'gl-terraform-state'
gitlab_rails['object_store']['objects']['pages']['bucket'] = 'gl-pages'
gitlab_rails['object_store']['objects']['ci_secure_files']['bucket'] = 'gl-ci-secure-files'

### Usage Statistics
gitlab_rails['usage_ping_enabled'] = false

### Gitaly settings
gitaly['enable'] = false
gitlab_rails['repositories_storages'] = {
  "default" => {
    "gitaly_address" => 'tcp://gitlab-gitaly-host:8075',
    "gitaly_token" => 'GITALY_TOKEN'
  }
}

### GitLab database settings
postgresql['enable'] = false
gitlab_rails['db_adapter'] = "postgresql"
gitlab_rails['db_encoding'] = "unicode"
gitlab_rails['db_database'] = "gitlabhq_production"
gitlab_rails['db_username'] = "gitlab"
gitlab_rails['db_password'] = "DB_PASSWORD"
gitlab_rails['db_host'] = "gitlab-pg-host"
gitlab_rails['db_port'] = 5432

### GitLab Redis settings
redis['enable'] = false
gitlab_rails['redis_port'] = '6379'
gitlab_rails['redis_host'] = 'gitlab-redis-host'
gitlab_rails['redis_password'] = 'REDIS_PASS'

### GitLab Sidekiq
sidekiq['listen_address'] = "0.0.0.0"
## Set number of Sidekiq queue processes to the same number as available CPUs
sidekiq['queue_groups'] = ['*'] * 4

##! Specifies where Prometheus metrics endpoints should be made available for Sidekiq processes.
sidekiq['metrics_enabled'] = true
sidekiq['exporter_tls_enabled'] = false
sidekiq['listen_address'] = '0.0.0.0'
sidekiq['listen_port'] = 8082

Важно: все дополнительные конфигурации (LDAP, KAS, Page) обязательно должны быть продублированы в настройке Sidekiq. Для настройки sidekiq['queue_groups'] = ['*'] * 4 обязательно указывайте число доступных CPU. В моем примере их четыре.

Теперь запускаем docker-compose:

-f /docker/docker-compose/docker-compose.yml up -d

Через 5 минут проверяем в WebGUI количество процессов. По умолчанию должно быть более 1:

Инженерный контент > (на Хабр) Руководство по миграции Gitlab на кластер из Docker контейнеров > image-2026-9-1_19-36-51.png

Инженерный контент > (на Хабр) Руководство по миграции Gitlab на кластер из Docker контейнеров > image-2026-9-1_19-36-51.png

Rails

Rails — основа системы, монолитное приложение, написанное на Ruby on Rails. В подавляющем большинстве случаев оно тождественно Gitlab. Масштабирование Rails приносит результат при большом количестве пользователей и обращений к Gitlab в целом. Имейте в виду: согласно архитектуре, бесконечно наращивать Gitlab Rails нельзя, рекомендуемый максимум на ноду — 32 vCPU.

Прерывание в обслуживании не потребуется. Добавляем новые ноды, без перехода на HAProxy изменения не вступят в силу.

Важно: для масштабирования Gitlab Rails обязательно выполнение масштабирования всех вышеописанных элементов.

Особенности масштабирования

При кластеризации Gitlab можно добавлять сколько угодно инстансов Gitlab Rails кроме одного. Вот ключевые особенности этого единственного инстанса. Назовем его ведущим, хотя это не совсем корректно:

  • прямое подключение к СУБД, работа через менеджер соединений (например, pgbouncer) не допускается;

  • используется для обновления Gitlab;

  • используется для создания и восстановления резервных копий Gitlab.

В нашем случае Docker-контейнер с ведущим Gitlab Rails мы разместим на машине с HAProxy и отключим на него маршрутизацию пользовательского трафика.

Подготовка окружения для Rails

Не забудьте заранее переопределить 22 порт для SSH. На каждом хосте gitlab-rails-(01/02)-host создаем /docker/docker-compose/docker-compose.yml с содержанием:

services:
  gitlab-rails-01:
    image: company-local-docker-images/gitlab-ce:18.11.11-ce.0
    container_name: gitlab-rails-01
    logging:
      options:
        max-size: "1g"
        max-file: "5"
    volumes:
      - "/docker/gitlab/config:/etc/gitlab"
      - "/docker/gitlab/data:/var/opt/gitlab"
      - "/docker/gitlab/logs:/var/log/gitlab"
      - "/network-mnt/gitlab-ci-builds:/var/opt/gitlab/gitlab-ci/builds"
    ports:
      - "22:22"
      - "80:80"
      - "9100:9100" #node exporter port
    restart: unless-stopped
    deploy:
      resources:
        limits:
          cpus: '8'
          memory: 8G
    networks:
      - gitlabnet

networks:
  gitlabnet:
    driver: bridge

/network-mnt/gitlab-ci-builds — сетевая папка с логами билдов, чистить будем по CRON на ведущем Gitlab Rails.

Запускаем docker-compose:

 -f /docker/docker-compose/docker-compose.yml up -d

Ждем, когда создадутся все файлы конфигураций, и останавливаем контейнер docker-compose: 

-f /docker/docker-compose/docker-compose.yml down

Запрещаем обновление базы Gitlab при обновлении версии: 

touch /docker/gitlab/config/skip-auto-reconfigure

Копируем файл с секретом /docker/gitlab/config/gitlab-secrets.json с хоста gitlab.example.com на gitlab-rails-(01/02)-host. Копируем файлы ssh-ключей — ssh_host_ecdsa_key, ssh_host_ecdsa_key.pub, ssh_host_ed25519_key, ssh_host_ed25519_key.pub, ssh_host_rsa_key, ssh_host_rsa_key.pub — из /docker/gitlab/config с хоста gitlab.example.com на gitlab-rails-(01/02)-host. При использовании самоподписанных сертификатов, обязательно добавьте их на хосте gitaly-host в /docker/gitlab/config/trusted-certs.

Переключаем Gitlab в режим Application через редактирование /docker/gitlab/config/gitlab.rb согласно документации:

## GitLab configuration settings
external_url 'https://gitlab.example.com'
roles ['application_role']

### Prevent database migrations from running on upgrade automatically
gitlab_rails['auto_migrate'] = false

### Object storage configuration
gitlab_rails['object_store']['enabled'] = true
gitlab_rails['object_store']['proxy_download'] = false
gitlab_rails['object_store']['connection'] = {
'provider' => 'AWS',
'region' => 'gitlab-s3',
'endpoint' => 'https://gitlab-mini0:9009',
'aws_access_key_id' => 'S3User',
'aws_secret_access_key' => 'S3Password',
'path_style' => true
}
gitlab_rails['object_store']['objects']['artifacts']['bucket'] = 'gl-artifacts'
gitlab_rails['object_store']['objects']['external_diffs']['bucket'] = 'gl-external-diffs'
gitlab_rails['object_store']['objects']['lfs']['bucket'] = 'gl-lfs'
gitlab_rails['object_store']['objects']['uploads']['bucket'] = 'gl-uploads'
gitlab_rails['object_store']['objects']['packages']['bucket'] = 'gl-packages'
gitlab_rails['object_store']['objects']['dependency_proxy']['bucket'] = 'gl-dependency-proxy'
gitlab_rails['object_store']['objects']['terraform_state']['bucket'] = 'gl-terraform-state'
gitlab_rails['object_store']['objects']['pages']['bucket'] = 'gl-pages'
gitlab_rails['object_store']['objects']['ci_secure_files']['bucket'] = 'gl-ci-secure-files'

### Usage Statistics
gitlab_rails['usage_ping_enabled'] = false

### Gitaly settings
gitaly['enable'] = false
gitlab_rails['repositories_storages'] = {
  "default" => {
    "gitaly_address" => "tcp://gitlab-gitaly-host:8075",
    "gitaly_token" => 'GITALY_TOKEN'
  }
}

### GitLab database settings
postgresql['enable'] = false
gitlab_rails['db_adapter'] = "postgresql"
gitlab_rails['db_encoding'] = "unicode"
gitlab_rails['db_database'] = "gitlabhq_production"
gitlab_rails['db_username'] = "gitlab"
gitlab_rails['db_password'] = "DB_PASSWORD"
gitlab_rails['db_host'] = "gitlab-pg-host"
gitlab_rails['db_port'] = 5432

### GitLab Redis settings
redis['enable'] = false
gitlab_rails['redis_port'] = '6379'
gitlab_rails['redis_host'] = 'gitlab-redis-host'
gitlab_rails['redis_password'] = 'REDIS_PASS'

### GitLab Sidekiq
sidekiq['enable'] = false

### GitLab NGINX
nginx['redirect_http_to_https'] = false
nginx['listen_port'] = 80
nginx['listen_https'] = false

### GitLab Logging
logging['svlogd_size'] = 200 * 1024 * 1024 # rotate after 200 MB of log data
logging['svlogd_num'] = 5 # keep 30 rotated log files
logging['svlogd_timeout'] = 24 * 60 * 60 # rotate after 24 hours
logging['svlogd_filter'] = "gzip" # compress logs with gzip
logging['logrotate_frequency'] = "daily" # rotate logs daily
logging['logrotate_size'] = 200 * 1024 * 1024 # rotate after 200 MB of log data
logging['logrotate_rotate'] = 5 # keep 30 rotated logs
logging['logrotate_compress'] = "compress" # see 'man logrotate'

### Prometheus
node_exporter['enable'] = true
node_exporter['listen_address'] = '0.0.0.0:9100'

Запускаем docker-compose. Через 5 минут проверяем логи: все уже должно работать без ошибок.

HAProxy

HAProxy — балансировщик нагрузки. Мы расширяем уже существующий Gitlab в Docker-контейнере, и на одном хосте с HAProxy будет работать ведущий Gitlab Rails, выведенный из-под пользовательской нагрузки.

Подготовка окружения для HAProxy

Предварительно проверяем, что 22 порт для SSH переопределен. Затем создаем /docker/docker-compose/docker-compose.yml с содержанием:

services:
  gitlab-haproxy:
    image: company-local-docker-images/haproxy:3.2.22
    container_name: gitlab-haproxy
    ports:
      - "22:22"
      - "80:80"
      - "443:443"
      - "8404:8404"
    volumes:
      - "/docker/haproxy:/usr/local/etc/haproxy"
      - "/docker/haproxy/stats:/var/lib/haproxy/stats"
      - "/opt/ssl:/opt/ssl"
    restart: unless-stopped
    networks:
      - gitlabnet

gitlab-rails-main:
    image: company-local-docker-images/gitlab-ce:18.11.11-ce.0
    container_name: gitlab-rails-01
    logging:
      options:
        max-size: "1g"
        max-file: "5"
    volumes:
      - "/docker/gitlab/config:/etc/gitlab"
      - "/docker/gitlab/data:/var/opt/gitlab"
      - "/docker/gitlab/logs:/var/log/gitlab"
      - "/network-mnt/gitlab-ci-builds:/var/opt/gitlab/gitlab-ci/builds"
    restart: unless-stopped
    deploy:
      resources:
        limits:
          cpus: '8'
          memory: 8G
    networks:
      - gitlabnet

networks:
  gitlabnet:
    driver: bridge

Теперь — файл для экспортера HAProxy, автоматически он не создается:

touch /docker/haproxy/stats/stats.state

Далее — конфигурационный файл HAProxy, /docker/haproxy/haproxy.cfg/haproxy.cfg:

# Configuration file for haproxy

#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
    expose-experimental-directives
    stats-file /var/lib/haproxy/stats/stats.state
    log 127.0.0.1:514 local0
    daemon

#---------------------------------------------------------------------
# Common defaults that all the 'listen' and 'backend' sections will
# use if not designated in their block
#---------------------------------------------------------------------
defaults
    log global
    mode http
    option httplog
    option dontlognull
    option http-server-close
    option redispatch
    timeout http-request 10s
    timeout queue 20s
    timeout connect 5s
    timeout client 20m
    timeout server 20m
    timeout http-keep-alive 10s
    timeout check 10s
    default-server inter 10s fall 3 rise 2
    balance leastconn

#---------------------------------------------------------------------
# statistic-http
#---------------------------------------------------------------------
frontend stats
    mode http
    bind *:8404 ssl crt /opt/ssl/my.company.ssl.pem
    http-request use-service prometheus-exporter if { path /metrics }
    stats enable
    stats uri /stats
    stats show-modules
    stats refresh 10s

#---------------------------------------------------------------------
# rails-http
#---------------------------------------------------------------------
frontend rails-http
    mode http
    bind *:80
    bind *:443 ssl crt /opt/ssl/my.company.ssl.pem
# 1. Process HTTP Request manipulations first
    http-request redirect scheme https code 301 unless { ssl_fc }
    http-response set-header X-Backend-Server %s
    option forwardfor except 127.0.0.0/8
# 2. Assign backends last
    use_backend rails-http-backend
    option httplog

#---------------------------------------------------------------------
# rails-http backend
#---------------------------------------------------------------------
backend rails-http-backend
    mode http
    server gitalb-rails-01 gitalb-rails-01:80 check
    server gitalb-rails-02 gitalb-rails-02:80 check

#---------------------------------------------------------------------
# rails-ssh
#---------------------------------------------------------------------
frontend rails-ssh
    mode tcp
    bind *:22
    use_backend rails-ssh-backend
    option tcplog

#---------------------------------------------------------------------
# rails-ssh backend
#---------------------------------------------------------------------
backend rails-ssh-backend
    mode tcp
        server gitalb-rails-01 gitalb-rails-01:22 check
        server gitalb-rails-02 gitalb-rails-01:22 check

Миграция на HAProxy

1. Планируем время недоступности. Сам перезапуск займет не более минуты, все текущие соединения не прервутся.

2. Останавливаем докер:

stop single-node-gitlab

3. Запускаем docker-compose:

 -f /docker/docker-compose/docker-compose.yml up -d

4. Не забываем проверить все свои CRON-задания.

Заключение

Спасибо всем дочитавшим: не ожидал, что материал по масштабированию и кластеризации Gitlab через Docker-контейнеры окажется таким обширным. Примеры конфигураций заняли много места, но официальная документация Gitlab аккуратно обходит некоторые вопросы, поэтому я разместил примеры в статье целиком. Старался составлять docker-compose.yml так, чтобы их при желании можно было объединить в один. Надеюсь, теперь вы сможете не переезжать сразу на следующую архитектуру, а самостоятельно решить, что конкретно нужно масштабировать в вашей инсталляции. Возможно, этим вы сэкономите время и деньги на развитие и сопровождение инфраструктуры.

Жаль, что совсем не осталось места на наш опыт по работе с кластером Gitlab в Docker. Если будет интересно, пишите, обязательно постараюсь рассказать.

Если вам интересна работа в DevOps, обратите внимание на наши вакансии:

View the original on Хабр

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.