Skip to content

Latest commit

 

History

History
255 lines (191 loc) · 17.8 KB

File metadata and controls

255 lines (191 loc) · 17.8 KB

← Оглавление

CI/CD

«Если что-то больно делать — делай это чаще» — принцип непрерывной интеграции


CI/CD — практика, при которой каждое изменение кода автоматически проверяется и доставляется на сервер: прогоняются тесты и линтеры, собирается образ, приложение выкатывается в прод — без ручных шагов и «выложу вечером руками». Это часть культуры DevOps (сближение разработки и эксплуатации). Даже одиночному разработчику базовые навыки CI/CD нужны, чтобы автоматически публиковать свои проекты.

CI и CD — две половины

  • CI (Continuous Integration, непрерывная интеграция) — часто вливать код в общую ветку, и на каждое вливание автоматически гонять сборку, тесты и линтеры. Ломающее изменение ловится сразу, а не через неделю.
  • CD расшифровывают двояко:
    • Continuous Delivery (доставка) — пайплайн автоматически готовит готовый к выкатке артефакт, но кнопку «в прод» нажимает человек.
    • Continuous Deployment (развёртывание) — выкатка в прод тоже автоматическая, без ручного подтверждения.
CI                                  CD
push ─▶ сборка ─▶ тесты ─▶ линт ─▶ [артефакт] ─▶ деплой на сервер
└──────── проверка кода ────────┘   └──── доставка в прод ────┘

Workflow и gitflow

Workflow — принятая в команде схема веток и того, что происходит при действиях в них. Самый распространённый — gitflow, где у каждой ветки своя роль и своё окружение:

Ветка Версия Окружение
main / master финальная, стабильная production (боевой сервер)
develop сырая, в активной разработке dev / тестовый сервер
stage кандидат в релиз staging (QA и бизнес-тесты)
feature/… отдельная фича локально, без деплоя

Смысл в том, что ветка определяет, куда катить: пуш в develop собирается на тестовый сервер, а main всегда соответствует тому, что крутится в проде. Правила «какая ветка → какой пайплайн» задаются в конфиге (ключ branches).

Инструменты интеграции

Все они работают по одному принципу: конфигурационный файл описывает, по какому событию (push, pull request, тег) на чистой машине-раннере выполнить набор команд. Отличаются лишь тем, встроены ли в git-хостинг.

Тип Инструменты
Встроенные в хостинг GitHub Actions, GitLab CI/CD, Bitbucket Pipelines
Внешние, подключаемые Jenkins, CircleCI, TeamCity, Semaphore CI

Встроенные не требуют отдельной инфраструктуры — конфиг лежит в репозитории и запускается там же. Внешние (особенно Jenkins) гибче и мощнее, но их надо разворачивать и обслуживать самому. Инфраструктуру, на которую катят, часто готовят через Terraform (IaC) прямо из пайплайна.

Пайплайн — этапы проверки

Пайплайн (pipeline) — последовательность этапов, через которые прогоняется каждое изменение. Если этап падает — пайплайн останавливается, дальше код не идёт.

checkout ─▶ install ─▶ lint ─▶ test ─▶ build ─▶ deploy
(забрать   (поставить  (стиль) (тесты) (Docker-  (выкатить
 код)       зависимости)               образ)    на сервер)

GitHub Actions — устройство

GitHub Actions — встроенный в GitHub CI/CD-инструмент. Он сканирует папку .github/workflows/ и запускает найденные там .yml-скрипты по описанным в них событиям.

Структура workflow-файла:

  • on — событие-триггер (push, pull_request, schedule) и фильтр по веткам;
  • jobs — задачи (сборка, тесты, деплой); по умолчанию идут параллельно;
  • runs-on — образ раннера, на котором крутится job (ubuntu-latest);
  • steps — последовательность шагов внутри job;
    • uses — подключить готовый экшен (actions/checkout@v4);
    • run — выполнить команду в шелле;
    • name — человекочитаемое имя шага.

Secrets — секреты пайплайна

Пароли, ключи и токены нельзя класть в .yml (он в репозитории). Их хранят в настройках репозитория: Settings → Secrets and variables → Actions → New repository secret — и подставляют в конфиг через ${{ secrets.ИМЯ }}.

env:
  SECRET_KEY: ${{ secrets.SECRET_KEY }}
  DB_PASSWORD: ${{ secrets.DB_PASSWORD }}

Типичные секреты: SECRET_KEY, DB_PASSWORD, SSH_HOST, SSH_USER, SSH_KEY. GitHub маскирует их значения в логах, но только если они пришли из secrets, а не напечатаны в коде.

Сквозной пример: ci.yml для Python-проекта

Создаём .github/workflows/ci.yml. Пайплайн: на каждый пуш в main поднимается PostgreSQL, ставятся зависимости, гоняются линтер и тесты, и при успехе код выкатывается на сервер.

name: Тесты и деплой

on:
  push:
    branches: [main]              # запускать только на пуш в main

jobs:
  build:
    runs-on: ubuntu-latest        # раннер — свежая Ubuntu
    env:                          # секреты доступны всем шагам job
      SECRET_KEY: ${{ secrets.SECRET_KEY }}
      DEBUG: "False"
      DB_NAME: ${{ secrets.DB_NAME }}
      DB_USER: ${{ secrets.DB_USER }}
      DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
      DB_HOST: localhost
      DB_PORT: 5432

    services:                     # сопутствующие контейнеры на время job
      postgres:
        image: postgres:16
        env:
          POSTGRES_DB: ${{ secrets.DB_NAME }}
          POSTGRES_USER: ${{ secrets.DB_USER }}
          POSTGRES_PASSWORD: ${{ secrets.DB_PASSWORD }}
        ports: ["5432:5432"]
        options: >-              # ждать, пока база реально готова принимать соединения
          --health-cmd pg_isready
          --health-interval 5s
          --health-timeout 5s
          --health-retries 5

    steps:
      - name: Забрать код
        uses: actions/checkout@v4

      - name: Установить Python
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Установить зависимости
        run: pip install -r requirements.txt

      - name: Линтинг
        run: ruff check .

      - name: Тесты
        run: python manage.py test

      - name: Деплой на сервер
        uses: appleboy/ssh-action@v1     # зайти по SSH и выполнить команды
        with:
          host: ${{ secrets.SSH_HOST }}
          username: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_KEY }}     # приватный ключ, НЕ пароль
          script: |
            cd /home/deploy/project
            git pull origin main
            source venv/bin/activate
            pip install -r requirements.txt
            python manage.py migrate --noinput
            python manage.py collectstatic --noinput
            sudo systemctl restart gunicorn

Как это читать. Шаги идут сверху вниз и падение любого останавливает пайплайн: не прошёл линт — до тестов не дойдёт, не прошли тесты — не будет деплоя. services поднимает временный PostgreSQL, чтобы тестам было куда ходить. Последний шаг заходит на сервер по SSH-ключу и выполняет тот же redeploy, что делался бы руками.

Вход по ключу, а не по паролю. В appleboy/ssh-action передавай key (приватный SSH-ключ из secrets), а не password. Подход с паролем и утилитой expect (скрипты pull.exp, автоматически отвечающие на запрос пароля) хрупок и небезопасен — храни ключ в secrets и забудь про пароли в пайплайне.

Матрица версий

Matrix (матрица) — прогнать одну job на нескольких вариантах окружения сразу: разные версии Python, ОС, зависимостей. GitHub размножит job по всем комбинациям и запустит параллельно — библиотеку так проверяют на всех поддерживаемых версиях за один пуш.

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.11", "3.12", "3.13"]   # 3 параллельные job
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}   # своя версия в каждой
      - run: pip install -r requirements.txt
      - run: pytest

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

Каждый запуск ставит зависимости с нуля — на большом проекте это минуты. Кэш сохраняет скачанные пакеты между запусками и переиспользует, пока не изменился файл зависимостей. У setup-python он встроен параметром cache:

      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
          cache: pip                    # кэшировать кэш pip
          cache-dependency-path: requirements.txt   # ключ = хеш этого файла

Ключ кэша — хеш файла зависимостей: пока requirements.txt не менялся — берётся готовый кэш, при правке — пересобирается. Так CI не качает одно и то же на каждый пуш.

CD: сборка и публикация Docker-образа

Другой стиль CD (вместо git pull на сервере) — на релиз собрать Docker-образ и опубликовать его в реестр (Docker Hub, GHCR), откуда сервер его забирает. Триггер — публикация релиза или тег:

on:
  release:
    types: [published]        # запускать при создании релиза

jobs:
  docker:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: docker/login-action@v3            # логин в реестр
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: docker/build-push-action@v6       # собрать и запушить
        with:
          push: true
          tags: ghcr.io/${{ github.repository }}:${{ github.ref_name }}   # тег = имя релиза

Тег образа привязывают к версии релиза (github.ref_name) — так в реестре хранится история версий, и откат в прод — это просто запуск предыдущего образа.

CI как обратное давление для агентов

Появилось новое применение, которого у пайплайна изначально не было: сдерживать ИИ-агента.

Агент, работающий без присмотра часами и днями, склонен расползаться — придумывать себе задачи, менять то, о чём не просили, постепенно уходить от правил проекта. Просьба в промпте «не выходи за рамки» этому мешает слабо: это уговор, который модель трактует сама.

CI работает иначе — он не советует, а не пропускает. Упавший тест или линтер агент видит как факт и обязан отработать. В этой роли пайплайн называют backpressure (обратное давление).

Чтобы это работало, нужны две вещи:

  • Правило прописано в AGENTS.md (или CLAUDE.md) — файле-инструкции, который агент читает при входе в репозиторий: какие проверки обязательны и что делать при падении.
  • Проверка автоматическая и жёсткая. Ручное ревью в этой схеме не помогает: агент делает сотни коммитов, человек не успевает.

Общее правило автономной работы: чем дольше агент работает без человека, тем важнее внешний критерий «получилось». Без него длинная работа неотличима от длинного мусора. Подробнее — CI как обратное давление и Страховки от слопа.

Связи

  • Развертывание проекта — то, что автоматизирует шаг деплоя (gunicorn, nginx, systemd);
  • Работа с сервером — сервер, на который заходит ssh-action; там же настраиваются SSH-ключи для входа;
  • Docker — образ, который CI собирает и публикует на этапе build;
  • GIT — события в ветках (push, PR) и есть триггеры пайплайна; сам конфиг лежит в репозитории;
  • pytest, линтеры — то, что гоняется на этапах test и lint;
  • Автономность агента — CI как одно из трёх условий, при которых агента можно отпустить без присмотра.

Источники