До основного вмісту

GitLab CI/CD: конвеєр автоматичного деплою на VPS

Хмара та DevOps · 29.09.2026

GitLab CI/CD перетворює ручний деплой "зайшов по SSH і оновив файли" на передбачуваний конвеєр: кожен push у гілку запускає збірку, тести і викладку на сервер без участі людини. Розберемо, як налаштувати робочий конвеєр деплою на звичайний VDS з нуля, включно з безпечною передачею SSH-ключа і відкатом при помилці.

Що таке CI/CD і навіщо він потрібен при деплої на VPS

CI/CD розшифровується як continuous integration і continuous delivery — безперервна інтеграція і безперервна доставка. На практиці це файл .gitlab-ci.yml у корені репозиторію, який описує стадії: зібрати проєкт, прогнати тести, викласти код на сервер. GitLab Runner виконує ці стадії на кожен push чи merge request автоматично.

Без CI/CD деплой залежить від людини: пропустив крок — зламав прод. З конвеєром послідовність кроків однакова щоразу, а лог виконання видно в інтерфейсі GitLab.

Мінімальний .gitlab-ci.yml для деплою по SSH

Базовий конвеєр з двох стадій — test і deploy — виглядає так:

stages:
  - test
  - deploy

run_tests:
  stage: test
  image: php:8.3-cli
  script:
    - php -l public/index.php

deploy_production:
  stage: deploy
  image: alpine:latest
  only:
    - main
  before_script:
    - apk add --no-cache openssh-client rsync
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | ssh-add -
    - mkdir -p ~/.ssh
    - ssh-keyscan -H $DEPLOY_HOST >> ~/.ssh/known_hosts
  script:
    - rsync -avz --delete ./app/ deploy@$DEPLOY_HOST:/var/www/app/

Як безпечно передати SSH-ключ у GitLab CI

Приватний ключ ніколи не зберігають у репозиторії. Його додають у розділі Settings → CI/CD → Variables як змінну SSH_PRIVATE_KEY з прапорцями Protected і Masked. Protected означає, що змінна доступна лише на захищених гілках, Masked — що значення не потрапить у відкритому вигляді до логу job.

  • Ключ генерують окремо для CI, а не переюзають особистий ключ розробника
  • На сервері для цього ключа створюють окремого користувача deploy з обмеженими правами
  • У authorized_keys на сервері ключ прив'язують командою з обмеженням command=, якщо потрібен доступ лише до одного скрипту

Стадії конвеєра: build, test, deploy

Стадія build збирає артефакт — наприклад, архів із залежностями Composer чи зібраний фронтенд. Стадія test прогонує лінтери і юніт-тести та зупиняє конвеєр при помилці, не даючи зламаному коду потрапити на сервер. Стадія deploy копіює вже перевірений артефакт на VDS.

СтадіяЩо робитьЩо відбувається при помилці
buildЗбирає залежності та артефакт деплоюКонвеєр зупиняється, деплою не буде
testЗапускає лінтери, юніт- і інтеграційні тестиJob позначається failed, деплой блокується
deployКопіює артефакт на сервер, перезапускає службиВидно лог job, реліз можна відкатити вручну

Деплой через rsync і симлінк на реліз

Надійна схема — деплой в окрему папку з датою і перемикання симлінка current лише після успішної викладки. Так сайт жодної секунди не дивиться на частково скопійовані файли.

ssh deploy@$DEPLOY_HOST "mkdir -p /var/www/releases/$CI_COMMIT_SHORT_SHA"
rsync -avz ./app/ deploy@$DEPLOY_HOST:/var/www/releases/$CI_COMMIT_SHORT_SHA/
ssh deploy@$DEPLOY_HOST "ln -sfn /var/www/releases/$CI_COMMIT_SHORT_SHA /var/www/current"
ssh deploy@$DEPLOY_HOST "systemctl reload php8.3-fpm"

Відкат при невдалому деплої

Якщо реліз зламав прод, відкат — це перемикання симлінка current на попередню папку в /var/www/releases/, а не повторний деплой старого коміту. Такий відкат займає секунди і не потребує нового прогону конвеєра.

  • Зберігати на сервері останні 5–10 релізів, а не лише поточний
  • Тримати окремий job rollback у .gitlab-ci.yml, який запускається вручну кнопкою в GitLab
  • Логувати кожен деплой із міткою часу і хешем коміту в окремий файл на сервері

Поширені помилки в конфігурації конвеєра

Перша помилка — деплой без стадії тестів: конвеєр викладає на прод код, який навіть не перевірено лінтером. Друга — спільний SSH-ключ на всіх середовищах одразу, через що компрометація одного ключа дає доступ до всіх серверів. Третя — забутий only чи rules у job деплою, через що викладка запускається з будь-якої гілки, а не лише з main.

Якщо після деплою код треба ще й налаштувати на рівні ОС — пакети, cron, конфіги, — цей крок логічно винести в окремий Ansible-плейбук, який конвеєр запускає наступним job. Для порівняння з альтернативною платформою CI дивіться статтю про деплой через GitHub Actions. Якщо сервером призначення виступає кластер із кількох машин, деплой зручніше націлити на Docker Swarm замість одиночного VDS.

Підсумок: чекліст робочого конвеєра

Робочий деплой на VPS через GitLab CI будується з чотирьох обов'язкових елементів, без яких конвеєр рано чи пізно підведе у проді.

  • Окремий SSH-ключ для CI з прапорцями Protected і Masked у змінних
  • Стадія тестів перед стадією деплою, а не деплой напряму
  • Релізи в окремих папках із симлінком current для миттєвого відкату
  • Обмеження деплою правилом only чи rules на потрібну гілку
← Назад до бази знань Поставити питання підтримці