Если вы когда-нибудь деплоили проект вручную — через FTP, SSH или копипаст команд в терминале — вы знаете, как это выглядит. Один раз что-то пошло не так, и полчаса потрачено на поиск того, какой файл не обновился. CI/CD решает эту проблему радикально: код попадает на сервер автоматически, после каждого коммита или пуша в нужную ветку.
Что такое CI/CD и зачем это нужно
CI — Continuous Integration, непрерывная интеграция. Это когда каждый пуш в репозиторий автоматически запускает тесты, линтер, сборку. Цель — поймать ошибку до того, как она попадёт на прод.
CD — Continuous Delivery или Continuous Deployment. Delivery — значит артефакт готов к деплою, но его нужно запустить вручную. Deployment — деплой тоже автоматический, без участия человека.
На практике чаще говорят «CI/CD» как об одном процессе: код → тесты → сборка → деплой. И это работает не только в крупных командах. Даже для одного разработчика автоматизация деплоя экономит время и убирает человеческий фактор.
Из чего состоит пайплайн
Пайплайн — это цепочка шагов, которые выполняются последовательно (или параллельно). Типичный выглядит так:
- Checkout — скачать код из репозитория
- Install — установить зависимости
- Lint — проверить стиль кода
- Test — запустить тесты
- Build — собрать приложение
- Deploy — отправить на сервер
Если любой шаг падает — следующие не запускаются. Это и есть смысл: плохой код не попадёт на прод.
Инструменты: что выбрать
GitHub Actions — очевидный выбор, если репозиторий на GitHub. Конфигурация в YAML-файле внутри проекта, бесплатный план покрывает большинство нужд (2000 минут в месяц для публичных репозиториев — бесплатно). Огромный маркетплейс готовых actions.
GitLab CI/CD — встроен в GitLab, синтаксис чуть отличается, но логика та же. Хорошо работает с self-hosted раннерами.
Jenkins — старый добрый вариант, который всё ещё жив. Много плагинов, можно под себя настроить что угодно. Но требует отдельного сервера и административных усилий. Для нового проекта лучше не выбирать — GitHub Actions проще.
Woodpecker CI / Drone CI — лёгкие self-hosted альтернативы, если не хочется отдавать код в облако.
Для большинства проектов рекомендация простая: если код на GitHub — берите GitHub Actions, на GitLab — встроенный CI.
Пример: деплой Node.js-приложения через GitHub Actions
Предположим, у вас есть Node.js-приложение, которое нужно деплоить на VPS после каждого пуша в ветку main.
Создайте файл .github/workflows/deploy.yml:
name: Deploy
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Deploy to server
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /var/www/myapp
git pull origin main
npm ci --production
pm2 restart myapp
Что здесь происходит: при каждом пуше в main GitHub запускает Ubuntu-машину, устанавливает Node.js, прогоняет тесты, а потом по SSH заходит на ваш сервер и обновляет код.
Переменные SERVER_HOST, SERVER_USER, SSH_PRIVATE_KEY — это секреты. Их нужно добавить в Settings → Secrets and variables → Actions в настройках репозитория. Никогда не пишите credentials прямо в YAML.
Как правильно хранить секреты
Секреты в GitHub Actions зашифрованы и не отображаются в логах — даже если вы случайно напишете echo ${{ secrets.MY_SECRET }}, в логах увидите ***. Это нормально.
Для SSH-ключей: генерируйте отдельную пару ключей специально для деплоя. Не используйте свой личный ключ. Публичный ключ добавьте в ~/.ssh/authorized_keys на сервере, приватный — в секреты GitHub.
Для production-переменных окружения (пароли базы, API-ключи) — держите их в файле .env прямо на сервере, не тащите через CI. В пайплайне просто перезапускайте приложение.
Разные окружения: staging и production
Хорошая практика — иметь два окружения. Staging — для тестирования, production — для пользователей. Пайплайн можно настроить так:
- Пуш в
develop → деплой на staging
- Пуш в
main (или merge request) → деплой на production
В GitHub Actions это делается через условия:
on:
push:
branches:
- main
- develop
jobs:
deploy-staging:
if: github.ref == 'refs/heads/develop'
# ...
deploy-production:
if: github.ref == 'refs/heads/main'
# ...
Так вы не деплоите сырой код на прод и всегда можете проверить изменения на staging перед релизом.
Docker в CI/CD
Если приложение упаковано в Docker — деплой становится ещё чище. Пайплайн собирает образ, пушит в registry (Docker Hub, GitHub Container Registry, свой), а сервер просто скачивает новый образ и перезапускает контейнер.
- name: Build and push Docker image
run: |
docker build -t ghcr.io/yourname/myapp:${{ github.sha }} .
docker push ghcr.io/yourname/myapp:${{ github.sha }}
- name: Deploy
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
docker pull ghcr.io/yourname/myapp:${{ github.sha }}
docker stop myapp || true
docker run -d --name myapp -p 3000:3000 ghcr.io/yourname/myapp:${{ github.sha }}
Преимущество: сервер не нужно настраивать под конкретный стек. Нужен только Docker.
Типичные ошибки при настройке CI/CD
Деплой без тестов. Если тестов нет — CI/CD просто быстрее доставляет сломанный код. Хотя бы базовые smoke-тесты должны быть.
Один пайплайн на всё. Тесты и деплой лучше разделять на разные jobs. Тогда при падении тестов деплой просто не запустится, а логи будут чище.
Не проверяют логи CI. Пайплайн запустился, всё зелёное — хорошо. Но стоит периодически смотреть, сколько времени занимает каждый шаг. Если установка зависимостей занимает 3 минуты — настройте кэширование.
Кэш зависимостей. Для Node.js:
- uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
Это сократит время установки с 2-3 минут до 10-15 секунд.
Секреты в коде. Звучит очевидно, но случается. Если случайно закоммитили секрет — сразу его ротируйте, история в git остаётся навсегда.
Мониторинг после деплоя
Автоматический деплой — это удобно, но нужно знать, что происходит после. Минимум:
- Уведомление в Telegram или Slack о результате деплоя (успех/ошибка)
- Health check — через несколько секунд после деплоя проверить, что приложение отвечает
Простой health check в пайплайне:
- name: Health check
run: |
sleep 10
curl --fail https://yoursite.com/health || exit 1
Если сайт не отвечает — деплой считается упавшим, и вы сразу получите уведомление.
Когда CI/CD настраивать не нужно
Если это лендинг на статическом HTML, который обновляется раз в год — CI/CD избыточен. Настройка займёт больше времени, чем сэкономит.
Но как только появляется регулярная разработка, несколько веток, командная работа — автоматизация деплоя окупается очень быстро.
В REEXY при разработке корпоративных сайтов и интернет-магазинов мы настраиваем CI/CD как часть инфраструктуры проекта — это снижает риски при обновлениях и убирает ручную работу на этапе сопровождения.
Итого: с чего начать
- Если репозиторий на GitHub — создайте
.github/workflows/deploy.yml
- Настройте SSH-ключ для деплоя, добавьте секреты
- Напишите пайплайн: checkout → install → test → deploy
- Проверьте на тестовом коммите
- Добавьте кэширование зависимостей
- Настройте уведомления о результате
Первый рабочий пайплайн — это час работы. Зато потом деплой занимает ровно столько, сколько нужно серверу, без вашего участия.