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на потрібну гілку