К основному содержимому

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 на нужную ветку
← Назад в базу знаний Задать вопрос поддержке