Содержание

  1. Что такое CI/CD и зачем это нужно
  2. Как работает GitHub Actions
  3. Первый workflow: запуск тестов
  4. Добавляем сборку Docker
  5. Автодеплой на сервер
  6. Работа с секретами
  7. Советы по оптимизации

Разработчик делает изменение, пушит в GitHub — и через 5 минут оно уже на продакшн-сервере. Тесты прошли, Docker-образ собран, деплой выполнен. Всё автоматически. Звучит как мечта? Это реальность с GitHub Actions, и настроить это можно за пару часов.

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

CI (Continuous Integration) — автоматический запуск тестов при каждом изменении кода. Разработчик узнаёт о проблеме через 5 минут, а не через 2 дня когда уже всё сломалось.

CD (Continuous Delivery/Deployment) — автоматическая доставка кода на сервер после успешных тестов. Деплой перестаёт быть страшным ритуалом раз в месяц.

Без CI/CD типичная проблема выглядит так:

  • Разработчик пушит код в пятницу вечером
  • Деплой вручную — копирование файлов по SSH, перезапуск сервисов
  • Что-то сломалось — непонятно что именно и как откатить
  • Поддержка получает жалобы в выходные
Статистика: команды с CI/CD выпускают релизы в 200 раз чаще и восстанавливаются после сбоев в 24 раза быстрее (по данным DORA). Не потому что они умнее — просто автоматизация убирает страх перед деплоем.

2. Как работает GitHub Actions

GitHub Actions — это встроенная CI/CD платформа в GitHub. Бесплатно для публичных репозиториев, для приватных — 2000 минут/месяц бесплатно.

Конфигурация хранится в файлах YAML в папке .github/workflows/. Каждый файл — это workflow (рабочий процесс). Workflow состоит из jobs (задач), задачи из steps (шагов).

# Структура workflow-файла
name: CI/CD Pipeline          # Название

on:                             # Триггеры запуска
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:                           # Задачи
  test:                         # Имя задачи
    runs-on: ubuntu-latest      # Тип runner'а
    steps:                      # Шаги
      - name: Checkout code
        uses: actions/checkout@v4

3. Первый workflow: запуск тестов

Создаём файл .github/workflows/ci.yml:

name: CI — Tests

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      # 1. Получаем код репозитория
      - name: Checkout
        uses: actions/checkout@v4

      # 2. Устанавливаем Python
      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      # 3. Кэшируем зависимости (ускоряет последующие запуски)
      - name: Cache pip
        uses: actions/cache@v4
        with:
          path: ~/.cache/pip
          key: ${{ runner.os }}-pip-${{ hashFiles('requirements.txt') }}

      # 4. Устанавливаем зависимости
      - name: Install dependencies
        run: |
          pip install -r requirements.txt
          pip install pytest pytest-cov

      # 5. Запускаем линтер
      - name: Lint with flake8
        run: |
          pip install flake8
          flake8 . --max-line-length=100 --exclude=venv

      # 6. Запускаем тесты с покрытием
      - name: Run tests
        run: pytest --cov=. --cov-report=xml

      # 7. Загружаем отчёт о покрытии
      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          file: ./coverage.xml

4. Добавляем сборку Docker

Создаём Dockerfile для нашего Python-приложения:

# Dockerfile
FROM python:3.11-slim

WORKDIR /app

# Копируем зависимости отдельно для кэширования
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Копируем код
COPY . .

# Создаём непривилегированного пользователя
RUN adduser --disabled-password --gecos '' appuser
USER appuser

EXPOSE 8000
CMD ["python", "-m", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

Добавляем сборку и пуш Docker-образа в workflow:

# Добавляем job после тестов
  build:
    needs: test              # Запускается только после успешных тестов
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      # Авторизация в Docker Hub
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_TOKEN }}

      # Сборка и пуш образа
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            myapp/backend:latest
            myapp/backend:${{ github.sha }}

5. Автодеплой на сервер

После сборки образа деплоим на продакшн-сервер через SSH:

  deploy:
    needs: build
    runs-on: ubuntu-latest

    steps:
      - name: Deploy to server
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SERVER_SSH_KEY }}
          script: |
            # Обновляем образ
            docker pull myapp/backend:latest

            # Останавливаем старый контейнер
            docker stop myapp-backend || true
            docker rm myapp-backend || true

            # Запускаем новый
            docker run -d \
              --name myapp-backend \
              --restart unless-stopped \
              -p 8000:8000 \
              --env-file /home/deploy/.env \
              myapp/backend:latest

            # Проверяем что запустился
            sleep 5
            docker ps | grep myapp-backend

      # Уведомление в Telegram о деплое
      - name: Notify Telegram
        if: always()
        run: |
          STATUS="${{ job.status }}"
          EMOJI=$([ "$STATUS" = "success" ] && echo "✅" || echo "❌")
          curl -s -X POST "https://api.telegram.org/bot${{ secrets.TG_BOT_TOKEN }}/sendMessage" \
            -d "chat_id=${{ secrets.TG_CHAT_ID }}" \
            -d "text=$EMOJI Деплой $STATUS: ${{ github.repository }} @ ${{ github.sha }}"

6. Работа с секретами

Пароли, токены и ключи SSH нельзя хранить в коде. GitHub Secrets — правильное место для них.

Добавляем секреты: Settings → Secrets and variables → Actions → New repository secret.

Нужные секреты для нашего пайплайна:

  • DOCKER_USERNAME — логин Docker Hub
  • DOCKER_TOKEN — токен Docker Hub (не пароль)
  • SERVER_HOST — IP или домен сервера
  • SERVER_USER — пользователь для SSH
  • SERVER_SSH_KEY — приватный SSH-ключ
  • TG_BOT_TOKEN — токен Telegram-бота для уведомлений
  • TG_CHAT_ID — chat_id для уведомлений

7. Советы по оптимизации

Кэшируйте зависимости

Без кэша каждый запуск устанавливает зависимости заново — это 1–3 минуты. С кэшем — 10 секунд.

Запускайте jobs параллельно

Линтер и тесты можно запускать параллельно — это сокращает время пайплайна вдвое.

jobs:
  lint:
    runs-on: ubuntu-latest
    steps: [...]

  test:
    runs-on: ubuntu-latest
    steps: [...]

  # Build запускается только когда оба выше завершились успешно
  build:
    needs: [lint, test]
    steps: [...]

Используйте matrix для тестирования на нескольких версиях

jobs:
  test:
    strategy:
      matrix:
        python-version: ['3.10', '3.11', '3.12']
    runs-on: ubuntu-latest
    steps:
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
Не деплойте автоматически без проверки. Для production добавьте ручное подтверждение через environment protection rules в GitHub. Автодеплой подходит для staging, на production лучше нажимать кнопку осознанно.

Нужна настройка CI/CD под ключ?

Настраиваем GitHub Actions, GitLab CI и Jenkins для команд любого размера. От 40 000 ₽, срок 1–2 недели.

Обсудить проект