м. Київ, вул. Кирилівська 102

8 липня 2026 р.

Побудова CI/CD через GitLab Runner

Побудова CI/CD через GitLab Runner

GitLab Runner має сенс розгортати не як окремий службовий процес "десь на сервері", а як контрольовану точку виконання pipeline, де зрозумілі права, теги, мережевий доступ і межі відповідальності. Більшість проблем з CI/CD починаються не з YAML-файлу, а з невдалого старту: runner запускається від root без чіткої моделі доступу, виконує збірки в змішаному середовищі, тягне артефакти куди завгодно або отримує занадто широкі привілеї на deploy-вузлі.

У цій статті розберемо практичну побудову CI/CD через GitLab Runner: від підготовки вузла і встановлення runner до реєстрації, прив'язки тегів, написання `.gitlab-ci.yml`, організації stages і базового deploy. Такий підхід добре вкладається в DevOps послуги, регулярну автоматизацію через CI/CD і вже знайому базову інфраструктуру контейнеризації, якщо перед цим на вузлі акуратно виконано встановлення Docker та Docker Compose на Ubuntu. Якщо runner налаштований правильно з самого початку, команда отримує не просто кнопку "Run pipeline", а передбачуваний процес збірки, тесту й доставлення змін у цільове середовище.

Що варто підготувати перед встановленням

Перед побудовою CI/CD потрібно визначити три речі: де саме буде запускатися runner, що він повинен уміти робити і які середовища він має право чіпати. Якщо ці межі не зафіксувати одразу, через кілька ітерацій pipeline перетвориться на набір винятків, ручних змін і неконтрольованих секретів.

На практиці корисно одразу відповісти на такі питання:

- runner буде shared, group чи project-specific - потрібен shell executor чи docker executor - чи має runner доступ до production, чи тільки до staging - де зберігатимуться deploy-ключі, токени і змінні середовища - які теги будуть використовуватися для розмежування pipeline

Базова перевірка вузла перед встановленням runner може виглядати так:

bash
hostnamectl
uname -r
df -h
free -h
ip a
systemctl list-units --type=service | grep -E 'docker|gitlab-runner'

Якщо runner планується для docker executor, важливо одразу перевірити, чи не конфліктує локальна конфігурація Docker з майбутніми job-контейнерами. Якщо потрібен shell executor, потрібно ще уважніше дивитися на права користувача, від якого підуть збірки, тому що shell job фактично працює в операційній системі напряму.

Ще одна важлива підготовка це структура доступів. Не варто віддавати одному й тому ж runner однакові права на тестові збірки, deploy у staging і ручні production-задачі. Краще одразу рознести сценарії через теги, окремих runner або хоча б через чіткі rules у pipeline, щоб згодом не довелося виправляти саму модель доставки змін.

Встановлення GitLab Runner на Linux-вузол

Для Linux найпростіше ставити GitLab Runner з офіційного репозиторію GitLab. Це дає передбачувану схему оновлень, нормальну інтеграцію з `systemd` і менше шансів отримати випадковий бінарник з неперевіреного джерела.

bash
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh \
  | sudo bash

sudo apt update
sudo apt install -y gitlab-runner

Після встановлення корисно відразу перевірити сервіс, версію та системного користувача:

bash
sudo systemctl enable gitlab-runner
sudo systemctl start gitlab-runner
sudo systemctl status gitlab-runner --no-pager
gitlab-runner --version
id gitlab-runner

Якщо використовується docker executor, зазвичай потрібен доступ користувача `gitlab-runner` до Docker socket. Але робити це слід усвідомлено: додавання в групу `docker` фактично розширює можливості job-процесів на вузлі.

bash
sudo usermod -aG docker gitlab-runner
sudo systemctl restart gitlab-runner

Після цього важливо не просто побачити активний сервіс, а зрозуміти, де зберігається конфігурація runner. Основний файл це `/etc/gitlab-runner/config.toml`. Саме там з'являються зареєстровані runner, теги, executor, обмеження кешу та інші параметри. Редагувати його вручну можна, але краще робити це після реєстрації і з чітким розумінням, що саме змінюється, інакше легко розсинхронізувати локальні правки з тим, що очікує GitLab.

Shell executor чи Docker executor

Shell executor простіший у старті, тому що job запускається прямо на вузлі. Це зручно для deploy-сценаріїв, де потрібно мати прямий доступ до локального filesystem, SSH-ключів або сервісів на машині. Але такий підхід слабший з точки зору ізоляції: збірки впливають на спільне оточення, можуть лишати артефакти, пакети, кеші й непередбачувані побічні ефекти.

Docker executor дає чистіше середовище для кожного job і краще підходить для repeatable builds. Job стартує у контейнері, а це означає менше дрейфу між різними запускaми pipeline. Проте тут одразу треба вирішити, де живуть кеші, чи є доступ до Docker-in-Docker, і як job взаємодіє з образами та registry.

На практиці часто працює така схема:

- shell runner для deploy-операцій на конкретному вузлі - docker runner для build/test етапів - окремі теги для різних контурів

Такий поділ дозволяє не змішувати збірку артефактів з безпосереднім розгортанням і спрощує контроль доступу.

Реєстрація runner і прив'язка до проєкту

Після інсталяції runner потрібно зареєструвати в GitLab. У нових версіях зазвичай використовується registration token або runner authentication token залежно від поточної моделі GitLab. Точне джерело токена залежить від того, чи runner створюється на рівні instance, group або окремого проєкту.

Базовий приклад реєстрації для docker executor:

bash
sudo gitlab-runner register

Інтерактивно GitLab Runner запросить:

- URL GitLab - token - ім'я runner - теги - executor - базовий image для docker executor

Для неінтерактивної реєстрації це можна оформити так:

bash
sudo gitlab-runner register --non-interactive \
  --url "https://gitlab.example.com" \
  --token "GLRT-XXXXXXXXXXXXXXXX" \
  --name "staging-docker-runner" \
  --tag-list "docker,staging" \
  --executor "docker" \
  --docker-image "alpine:3.20" \
  --run-untagged="false" \
  --locked="true"

Параметри `run-untagged=false` і `locked=true` корисні там, де runner не повинен випадково брати будь-які job без явної прив'язки. Це особливо важливо для staging або deploy runner, який має доступ до чутливого контуру.

Після реєстрації варто перевірити, що runner бачить GitLab і проходить self-check:

bash
sudo gitlab-runner verify
sudo gitlab-runner list
sudo cat /etc/gitlab-runner/config.toml

Якщо runner зареєстрований, але job не підхоплюються, проблема часто не в самому сервісі, а в тегах. Наприклад, job очікує `staging`, а runner має `stage`. Або runner заблокований під конкретний проєкт, а pipeline запускається з іншого. Такі дрібниці з'їдають багато часу, якщо не звірити їх одразу після реєстрації.

Побудова базового `.gitlab-ci.yml`

Коли runner уже встановлений і зареєстрований, головне завдання це зробити pipeline прозорим. Хороший CI/CD-файл не повинен виглядати як магічний набір job, де незрозуміло, що саме запускається, в якій послідовності й чому deploy пішов без перевірки. Мінімальна практична структура це `stages`, зрозумілі job-назви, `rules` і теги під конкретний runner.

Нижче базовий приклад для проєкту, де є збірка, тест і deploy у staging:

yaml
stages:
  - build
  - test
  - deploy

variables:
  APP_IMAGE: registry.example.com/team/app:$CI_COMMIT_SHORT_SHA

build_image:
  stage: build
  tags:
    - docker
  script:
    - docker build -t "$APP_IMAGE" .
    - docker push "$APP_IMAGE"
  rules:
    - if: '$CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "stage"'

run_tests:
  stage: test
  tags:
    - docker
  script:
    - npm ci
    - npm run lint
    - npm run test
  rules:
    - if: '$CI_PIPELINE_SOURCE == "push"'

staging_deploy:
  stage: deploy
  tags:
    - staging
  script:
    - cd /var/www/project-staging
    - git pull origin stage
    - docker compose pull
    - docker compose up -d
  rules:
    - if: '$CI_COMMIT_BRANCH == "stage"'
      when: on_success

Це не універсальний шаблон на всі випадки, але він добре показує базову логіку: build і test запускаються окремо, deploy не змішується з ними, а job прив'язуються до runner через теги. Якщо змішати все в один job, pipeline формально працюватиме, але керованість втратиться дуже швидко: складніше знайти збій, важче повторно виконати окремий етап і майже неможливо нормально масштабувати схему під кілька середовищ.

Що варто продумати одразу в pipeline

На старті корисно зафіксувати ще кілька практичних правил:

- не зберігати секрети прямо в `.gitlab-ci.yml` - використовувати CI/CD Variables у GitLab - розділити автоматичний staging deploy і ручний production deploy - задавати `environment` для deploy job, якщо потрібно бачити історію розгортань - не давати одному job одразу і build, і cleanup, і deploy

Для shell deploy runner окремо важливо тримати скрипти і шляхи в передбачуваному вигляді. Якщо job залежить від невидимих ручних правок на сервері, такий CI/CD перестає бути справжньою автоматизацією і перетворюється на дистанційний запуск нестабільного shell-сценарію.

Типові помилки

Найчастіша помилка це реєстрація одного runner на все підряд без тегів і без меж доступу. Друга проблема це використання shell executor для всіх задач, включно зі збірками, через що вузол поступово забруднюється пакетами, кешами та артефактами минулих job. Третя це спроба зберігати токени, SSH-ключі або паролі прямо в репозиторії. Четверта це pipeline, де deploy виконується навіть тоді, коли тести не відпрацювали або взагалі не запускалися. П'ята це відсутність розділення між staging і production runner, через що випадковий job може піти не в той контур.

Окремо часто помиляються в дрібницях конфігурації: не збігаються теги між job і runner, `config.toml` змінюють вручну без перезапуску сервісу, Docker executor не має доступу до потрібного registry, а shell runner запускає команди у каталозі, де лишилися старі артефакти від попереднього деплою. Ще одна типова проблема це відсутність cleanup-стратегії: образи накопичуються, кеш росте, місце на диску закінчується, а pipeline раптово починає падати не через код, а через стан вузла.

Висновок

Побудова CI/CD через GitLab Runner починається не з красивого YAML, а з правильно обраної моделі виконання: де працює runner, які він має права, який executor використовується і як відокремлені build, test та deploy. Якщо ці рішення прийняті акуратно, далі pipeline стає передбачуваним, повторюваним і зрозумілим для команди.

Практичний наступний крок після базової інтеграції це не нескінченне додавання нових job, а стабілізація процесу: привести теги до єдиної схеми, винести секрети у змінні GitLab, розділити контури, описати deploy-кроки як керовані сценарії і регулярно перевіряти стан runner-вузлів. Саме тоді GitLab Runner перестає бути просто технічним агентом і стає опорною точкою для нормального CI/CD-процесу.

📞