Как настроить CI/CD для Python-проекта с помощью GitHub Actions и Docker: пошаговое руководство

Введение

Разработка на Python — это не только написание чистого кода, но и постоянная борьба с рутиной: запустить тесты, собрать приложение, задеплоить на сервер. Каждый раз делать это вручную — терять время и рисковать ошибками. CI/CD (Continuous Integration / Continuous Delivery — непрерывная интеграция и доставка) решает эту проблему раз и навсегда. Связка GitHub Actions и Docker позволяет автоматизировать весь процесс: от пуша в репозиторий до запуска контейнера в продакшене. В этой статье я, как практикующий DevOps-инженер, покажу, как настроить пайплайн для типового бэкенда на FastAPI. Мы разберём конфигурации, лучшие практики и типичные ошибки — без воды, только код и реальный опыт.

Что такое CI/CD и зачем это Python-разработчику?

CI/CD — это методология, которая превращает разрозненные шаги (линтер, тесты, сборка, деплой) в единый автоматический конвейер. Для Python-проекта это особенно актуально: вы можете мгновенно проверять, не сломал ли новый коммит существующую логику, и доставлять обновления пользователям за минуты. GitHub Actions — это бесплатный встроенный инструмент GitHub, который запускает ваши скрипты в ответ на события (push, pull request). Docker же упаковывает приложение со всеми зависимостями, гарантируя, что оно будет работать одинаково на вашем ноутбуке, тестовом сервере и в облаке AWS.

Пошаговое руководство: от репозитория до продакшена

Шаг 1. Подготовка Python-проекта на FastAPI

Допустим, у нас есть простое FastAPI-приложение с одним эндпоинтом. Структура файлов:

my-python-app/
├── app/
│   ├── __init__.py
│   └── main.py
├── requirements.txt
├── Dockerfile
├── .github/
│   └── workflows/
│       └── ci-cd.yml
└── tests/
    └── test_main.py

Содержимое app/main.py:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"message": "Hello, DevOps!"}

Файл requirements.txt:

fastapi==0.111.0
uvicorn==0.30.1
pytest==8.2.0

Шаг 2. Создаём Dockerfile

Dockerfile — это рецепт сборки образа. Для Python используем официальный лёгкий образ python:3.12-slim:

FROM python:3.12-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]

Шаг 3. Настраиваем GitHub Actions для CI (тестирование и линтинг)

Создаём файл .github/workflows/ci-cd.yml. Начнём с этапа непрерывной интеграции (CI):

name: CI/CD Pipeline

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

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt

      - name: Run tests
        run: |
          pytest

Этот пайплайн запускается при каждом пуше в main или создании pull request. Он устанавливает зависимости и прогоняет тесты. Если тесты падают — деплой не произойдёт.

Шаг 4. Добавляем сборку и пуш Docker-образа (CD)

Теперь добавим этап доставки (CD). Для этого нам понадобится Docker Hub (или GitHub Container Registry). Создаём секреты в репозитории: Settings > Secrets and variables > Actions — добавляем DOCKER_USERNAME и DOCKER_PASSWORD.

Расширяем ci-cd.yml:

  build-and-push:
    needs: test
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ secrets.DOCKER_USERNAME }}/my-python-app:latest

Шаг 5. Автоматический деплой на AWS EC2

Для полной автоматизации добавим деплой на виртуальную машину (например, AWS EC2). Используем SSH:

  deploy:
    needs: build-and-push
    runs-on: ubuntu-latest
    steps:
      - name: Deploy to EC2
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ${{ secrets.EC2_USER }}
          key: ${{ secrets.EC2_SSH_KEY }}
          script: |
            docker pull ${{ secrets.DOCKER_USERNAME }}/my-python-app:latest
            docker stop my-app || true
            docker rm my-app || true
            docker run -d --name my-app -p 80:8000 ${{ secrets.DOCKER_USERNAME }}/my-python-app:latest

Этот шаг подключается к EC2, скачивает свежий образ и перезапускает контейнер. В реальном проекте стоит добавить health-чеки и откат при ошибке.

Таблица: сравнение этапов CI/CD

Этап Инструмент Действие Критичность
Тестирование GitHub Actions (pytest) Запуск unit-тестов Высокая — ловит баги до сборки
Сборка образа Docker + GitHub Actions Создание контейнера Средняя — упаковывает код
Пуш в registry Docker Hub Хранение версий Средняя — нужен для деплоя
Деплой на сервер SSH + Docker Запуск контейнера Высокая — доставляет фичи

Типичные ошибки и как их избежать

  1. Секреты в коде: Никогда не храните пароли или ключи в YAML-файлах. Используйте secrets GitHub. Если секрет утёк — немедленно отзовите его.
  2. Тяжёлые образы: Используйте slim-версии Python и многослойную сборку (multi-stage builds) — это ускоряет деплой.
  3. Отсутствие тестов: Если тесты не написаны, CI/CD теряет смысл. Хотя бы один тест должен быть.
  4. Игнорирование кэширования: GitHub Actions кэширует зависимости через actions/cache, что сокращает время пайплайна в 2-3 раза.

LSI-слова и практические рекомендации

В контексте CI/CD для Python важно учитывать следующие связанные термины (LSI):
- контейнеризация — основа Docker, позволяет изолировать окружение;
- оркестрация — управление несколькими контейнерами (например, Kubernetes);
- мониторинг — наблюдение за запущенным приложением (Prometheus + Grafana);
- инфраструктура как код (IaC) — описание серверов через Terraform или Ansible;
- логирование — сбор логов для отладки (ELK-стек);
- безопасность контейнеров — сканирование образов на уязвимости (Trivy);
- автоматизация деплоя — ключевая цель всего пайплайна.

Рекомендую добавить в пайплайн шаг сканирования безопасности Docker-образа — это защитит от известных CVE. Например, используйте aquasecurity/trivy-action.

Заключение

Вы только что настроили полноценный CI/CD пайплайн для Python-проекта: от тестирования до деплоя на AWS EC2. GitHub Actions и Docker — это мощный дуэт, который экономит часы ручной работы и снижает риск человеческой ошибки. Теперь ваш код проходит проверку автоматически, а пользователи получают обновления быстрее. Если вы хотите углубиться в тему и научиться управлять инфраструктурой на уровне профессионала — изучайте Kubernetes, Terraform и мониторинг. Начните с малого: добавьте кэширование зависимостей в свой workflow и посмотрите, как ускорится сборка. Удачи в автоматизации!

← Все статьи

Комментарии