Если вы когда-нибудь деплоили проект вручную — через FTP, SSH или копипаст команд в терминале — вы знаете, как это выглядит. Один раз что-то пошло не так, и полчаса потрачено на поиск того, какой файл не обновился. CI/CD решает эту проблему радикально: код попадает на сервер автоматически, после каждого коммита или пуша в нужную ветку.

Что такое CI/CD и зачем это нужно

CI — Continuous Integration, непрерывная интеграция. Это когда каждый пуш в репозиторий автоматически запускает тесты, линтер, сборку. Цель — поймать ошибку до того, как она попадёт на прод.

CD — Continuous Delivery или Continuous Deployment. Delivery — значит артефакт готов к деплою, но его нужно запустить вручную. Deployment — деплой тоже автоматический, без участия человека.

На практике чаще говорят «CI/CD» как об одном процессе: код → тесты → сборка → деплой. И это работает не только в крупных командах. Даже для одного разработчика автоматизация деплоя экономит время и убирает человеческий фактор.

Из чего состоит пайплайн

Пайплайн — это цепочка шагов, которые выполняются последовательно (или параллельно). Типичный выглядит так:

  1. Checkout — скачать код из репозитория
  2. Install — установить зависимости
  3. Lint — проверить стиль кода
  4. Test — запустить тесты
  5. Build — собрать приложение
  6. 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 как часть инфраструктуры проекта — это снижает риски при обновлениях и убирает ручную работу на этапе сопровождения.

Итого: с чего начать

  1. Если репозиторий на GitHub — создайте .github/workflows/deploy.yml
  2. Настройте SSH-ключ для деплоя, добавьте секреты
  3. Напишите пайплайн: checkout → install → test → deploy
  4. Проверьте на тестовом коммите
  5. Добавьте кэширование зависимостей
  6. Настройте уведомления о результате

Первый рабочий пайплайн — это час работы. Зато потом деплой занимает ровно столько, сколько нужно серверу, без вашего участия.