Зазвичай усе починається так: код лежить на GitHub чи в хмарному GitLab, збірку хтось запускає з ноутбука, а на прод викладають через git pull по SSH. Поки розробник один, це навіть працює. Потім приходить другий, третій, з’являється тестовий сервер, і вже ніхто точно не скаже, яка версія зараз у проді й хто її туди виклав.
Друга половина болю — залежність від чужого сервісу. Ліміти на хвилини CI, ціна за користувача, яка росте разом із командою, образи контейнерів у чужому реєстрі. А для частини клієнтів ще й вимога тримати код і артефакти на своїй інфраструктурі.
Ми розгортаємо власний GitLab, налаштовуємо пайплайн і розводимо середовища так, щоб у прод потрапляло тільки те, що пройшло збірку, тести і чий реліз хтось свідомо підтвердив.
Що ми робимо
- GitLab CE на вашому сервері. Окрема віртуальна машина на Proxmox або виділений сервер в OVH чи Hetzner. Ставимо пакет Omnibus, HTTPS, пошту, вхід через SSH-ключі, 2FA для всіх.
- Container Registry. Вмикаємо вбудований реєстр образів. Раннер збирає образ і пушить його туди. Сервери тягнуть образ звідти за deploy-токеном лише з правом читання реєстру.
- Раннери. Docker executor для збірок і тестів: кожна задача в чистому контейнері. Окремий раннер під прод, який бере тільки захищені гілки й теги. Shell executor — там, де контейнер заважає, наприклад для деплою через Ansible.
- Пайплайн build → test → deploy. Збірка образу, тести, потім деплой: на сервер заходить окремий користувач
deploy, тягне потрібний тег образу і перезапускаєdocker compose. - Середовища dev, stage, prod. У кожного свої змінні, свій сервер і своя історія деплоїв у GitLab. Dev оновлюється з кожним комітом у робочу гілку, stage — з
main, prod — з тегу релізу. - Review-середовища (за бажанням). Для кожної гілки піднімається тимчасова копія застосунку на окремому піддомені й зникає після мерджу.
Як прод захищений від випадкових рук
Головне правило: у прод не можна викласти «з гілки, щоб глянути». Ось як це виглядає в .gitlab-ci.yml:
deploy_prod:
stage: deploy
tags: [prod] # тільки раннер прод-контуру
environment:
name: production
resource_group: production # два деплої одночасно не підуть
rules:
- if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/
when: manual # кнопку тисне людина
script:
- ssh deploy@$PROD_HOST "cd /opt/app && IMAGE_TAG=$CI_COMMIT_TAG docker compose pull && docker compose up -d"
До цього додаємо захищені гілки й теги: створити тег v* можуть тільки мейнтейнери. Середовище production теж захищене, тож кнопку деплою бачить лише визначене коло людей. Прод-раннер зареєстрований як protected і чужих задач не бере.
Секрети живуть у змінних CI/CD, а не в репозиторії. Ставимо їм прапорці masked (не вивалюються в лог) і protected (доступні тільки на захищених гілках). Змінні прив’язуємо до середовища: DATABASE_URL для stage і для prod — різні, і задача з dev-гілки прод-пароль просто не побачить.
Бекапи, оновлення й де тут граблі
Бекапи. gitlab-backup create за розкладом, архів одразу копіюємо за межі сервера. Тут перша пастка: у цей архів не потрапляють gitlab.rb і gitlab-secrets.json. Без другого файлу після відновлення не розшифруються змінні CI й токени. Тому зберігаємо їх окремо і хоча б раз проганяємо відновлення на чистій машині. Відновити бекап можна тільки на ту саму версію GitLab — це теж треба пам’ятати.
Оновлення. GitLab виходить щомісяця, і оновлювати його треба регулярно: там постійно закривають вразливості. Перескочити з дуже старої версії на свіжу одним кроком не вийде. Є обов’язкові проміжні версії (upgrade path), і після кожної треба дочекатися фонових міграцій. Перед оновленням робимо снапшот віртуальної машини. Після gitlab-ctl reconfigure перевіряємо, що піднялися всі сервіси: буває, що Sidekiq не стартує з першого разу і його доводиться перезапускати вручну.
Моніторинг. GitLab сам віддає метрики для Prometheus. Ми стежимо за пам’яттю, диском (реєстр і артефакти ростуть швидко, тому налаштовуємо політику очищення старих образів), чергами Sidekiq і живістю раннерів. Алерти приходять у месенджер або на пошту.
Чесно про ресурси. GitLab важкий. На 4 ГБ пам’яті він стартує тільки з урізаними налаштуваннями й помітно гальмує. Для комфортної роботи невеликої команди закладайте від 8 ГБ і 4 ядра, плюс окремі ресурси під раннери — збірки краще не ганяти на тій самій машині.
Коли GitLab не потрібен. Якщо у вас двоє розробників, один проєкт і достатньо простого CI — подивіться на Gitea або Forgejo. Вони вміщуються в кілька сотень мегабайт пам’яті, мають свій реєстр і CI, сумісний з GitHub Actions. Ми так і скажемо, якщо побачимо, що GitLab для вас завеликий.
Що ви отримуєте
- GitLab на вашому сервері з адмін-доступом для вас, а не для нас.
- Container Registry з політикою очищення старих образів.
- Раннери: загальні для збірок і окремий захищений під прод.
- Готовий
.gitlab-ci.ymlз етапами build → test → deploy і шаблон для нових проєктів. - Середовища dev, stage, prod з окремими змінними, захищеними гілками й ручним підтвердженням релізу.
- Бекапи GitLab разом із файлами конфігурації і перевірене відновлення.
- Моніторинг і алерти на пам’ять, диск, черги й раннери.
- Документацію: як додати проєкт, як випустити реліз, як відкотитися, як оновити GitLab.
Якщо у вас зараз деплой «руками по SSH» або рахунок за хмарний CI росте швидше за команду — напишіть нам. Розкажіть, скільки у вас людей і проєктів, і ми запропонуємо, що розгорнути: GitLab чи щось легше.