Learn (Almost) Anything
КаталогСкачать курс

Автоматизация в GitHub

Типовые CD-паттерны: deploy after CI, tag-based deploy, manual `workflow_dispatch`, promotion между environments, rollback jobs и `concurrency` для защиты продакшена.

ruЭнциклопедия33 урока6 модулей9
GitHub actionsАвтоматизация в GitHubМодель workflowсостояние и артефактыматрицы и runtimeрелизы и environmentsПереиспользование и собственные actionsgovernance и эксплуатацияgithubactions

Deployment workflows

Типовые CD-паттерны: deploy after CI, tag-based deploy, manual `workflow_dispatch`, promotion между environments, rollback jobs и `concurrency` для защиты продакшена.

Deployment workflows

Deployment workflow — это workflow, который не просто проверяет код, а доставляет уже выбранную версию в конкретную среду: staging, production, preview environment, registry или cloud service. В GitHub Actions он обычно собирается из трёх слоёв: trigger решает, когда вообще стартовать; needs, if и artifacts связывают CI с deploy; environment, protection rules и concurrency защищают цель деплоя. Поэтому эта тема стоит рядом с Environments и protection rules, но фокус здесь другой: не настройки environment как объекта, а рабочие CD-паттерны в YAML.

flowchart TD A[push / tag / workflow_dispatch] --> B[CI: test, build, package] B --> C[upload artifact или image] C --> D[deploy staging] D --> E{production gate} E -->|approved| F[deploy production] E -->|rejected| G[stop] F --> H[smoke check и deployment history] R[manual rollback] --> F
flowchart TD
  A[push / tag / workflow_dispatch] --> B[CI: test, build, package]
  B --> C[upload artifact или image]
  C --> D[deploy staging]
  D --> E{production gate}
  E -->|approved| F[deploy production]
  E -->|rejected| G[stop]
  F --> H[smoke check и deployment history]
  R[manual rollback] --> F
flowchart TD A[push / tag / workflow_dispatch] --> B[CI: test, build, package] B --> C[upload artifact или image] C --> D[deploy staging] D --> E{production gate} E -->|approved| F[deploy production] E -->|rejected| G[stop] F --> H[smoke check и deployment history] R[manual rollback] --> F
flowchart TD
  A[push / tag / workflow_dispatch] --> B[CI: test, build, package]
  B --> C[upload artifact или image]
  C --> D[deploy staging]
  D --> E{production gate}
  E -->|approved| F[deploy production]
  E -->|rejected| G[stop]
  F --> H[smoke check и deployment history]
  R[manual rollback] --> F
Типовая CD-цепочка: CI создаёт проверенный artifact, staging проверяет доставку, production проходит gate, rollback использует тот же deploy-контур.

Deploy after CI: самый обычный путь

Базовый CD-паттерн: сначала CI job проверяет и собирает артефакт, потом deploy job забирает именно этот артефакт. Это важнее, чем кажется. Если production job заново делает npm install && npm run build, вы уже деплоите не строго тот build, который прошёл тесты: могли измениться lockfile resolution, registry, системные пакеты runner image или переменные сборки. Надёжнее собрать один output и передать его через Artifacts и reports или опубликовать image/package, что дальше подробно связано с Packages и container images.

name: cd

on:
  push:
    branches: [main]

permissions:
  contents: read

jobs:
  ci:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - run: npm test
      - run: npm run build
      - uses: actions/upload-artifact@v4
        with:
          name: web-dist
          path: dist/

  deploy-staging:
    needs: ci
    runs-on: ubuntu-24.04
    environment:
      name: staging
      url: https://staging.example.com
    concurrency:
      group: deploy-staging
      cancel-in-progress: true
    steps:
      - uses: actions/checkout@v4
      - uses: actions/download-artifact@v4
        with:
          name: web-dist
          path: dist/
      - run: ./scripts/deploy.sh staging dist
        env:
          DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

needs: ci делает deploy зависимым от успешной сборки. environment: staging даёт environment secrets/vars и пишет deployment history. concurrency не связан с environment автоматически: это отдельный ключ. Если другой workflow тоже деплоит в staging, но не использует ту же concurrency group, GitHub его не остановит.

Production concurrency: очередь или отмена

Для production обычно нужно «не больше одного деплоя одновременно». GitHub Actions поддерживает concurrency на уровне workflow или job. В одной concurrency group одновременно может выполняться только один run или job; новый run в той же группе становится pending, а старый pending по умолчанию заменяется новым. Если указать cancel-in-progress: true, GitHub также отменит уже выполняющийся run/job в этой группе.

Для staging отмена часто нормальна: новый commit делает старый staging-deploy бесполезным. Для production это опаснее: прерванный deploy может оставить систему в промежуточном состоянии, если deploy script не атомарный. Поэтому production часто ставят без cancel-in-progress: true или используют явно спроектированный rollback.

deploy-production:
  needs: ci
  runs-on: ubuntu-24.04
  environment:
    name: production
    url: https://example.com
  concurrency:
    group: deploy-production
  permissions:
    contents: read
    deployments: write
  steps:
    - uses: actions/checkout@v4
    - uses: actions/download-artifact@v4
      with:
        name: web-dist
        path: dist/
    - run: ./scripts/deploy.sh production dist
      env:
        DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

Здесь protection rules из Environments и protection rules решают, кто и с каких refs может пройти в production, а concurrency защищает сам процесс от параллельного выполнения. Это разные предохранители.

Tag-based deploy

Tag-based deploy подходит для релизов: CI на main может быть быстрым и частым, а production стартует только от v1.8.0, v1.8.1 и похожих tags. Тогда tag становится явной точкой выпуска. Этот паттерн естественно продолжает Release automation: release notes, assets и публикация package часто привязываются к тому же tag.

name: release-deploy

on:
  push:
    tags: ['v*.*.*']

jobs:
  deploy-production:
    runs-on: ubuntu-24.04
    environment: production
    concurrency:
      group: deploy-production
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/deploy-release.sh "${GITHUB_REF_NAME}"
        env:
          DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

У tag-based deploy есть дисциплина: нельзя тихо «додеплоить main». Если нужно выкатить fix, создаётся новый tag. Если production environment разрешает только tags v*, branch/tag restrictions дополнительно страхуют от ручного запуска с неправильного ref.

Manual deploy через workflow_dispatch

workflow_dispatch — ручной запуск workflow из GitHub UI или API. Он полезен для promotion, emergency rollback, one-off maintenance и деплоя в выбранную среду. В inputs можно задать choice, boolean, string и другие типы; внутри workflow значения доступны через inputs.

name: promote

on:
  workflow_dispatch:
    inputs:
      target:
        description: Environment
        type: choice
        required: true
        options: [staging, production]
      version:
        description: Artifact version or image tag
        type: string
        required: true

jobs:
  deploy:
    runs-on: ubuntu-24.04
    environment: ${{ inputs.target }}
    concurrency:
      group: deploy-${{ inputs.target }}
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/promote.sh "$TARGET" "$VERSION"
        env:
          TARGET: ${{ inputs.target }}
          VERSION: ${{ inputs.version }}
          DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

Такой workflow выглядит универсально, но не должен превращаться в «кнопку всемогущества». Для production всё равно нужны required reviewers, branch/tag restrictions, минимальные Permissions и GITHUB_TOKEN и нормальная история, какой artifact или image tag был выкачен.

Promotion между environments

Promotion — это не «собрать ещё раз для production». Это «взять уже проверенную версию и продвинуть её дальше». В web-app это может быть artifact из CI; в container-пайплайне — image digest; в package-пайплайне — immutable package version. Смысл один: staging и production получают один и тот же build output, только с разными environment secrets/vars из Variables, env и secrets.

Одна и та же проверенная сборка проходит через staging и production, а меняются только настройки окружения.

Практический вариант:

jobs:
  deploy-staging:
    needs: ci
    environment: staging
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/deploy-image.sh staging "$IMAGE_DIGEST"
        env:
          IMAGE_DIGEST: ${{ needs.ci.outputs.image_digest }}

  deploy-production:
    needs: [ci, deploy-staging]
    environment: production
    runs-on: ubuntu-24.04
    concurrency:
      group: deploy-production
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/deploy-image.sh production "$IMAGE_DIGEST"
        env:
          IMAGE_DIGEST: ${{ needs.ci.outputs.image_digest }}

Если production требует ручного approval, job остановится перед доступом к production secrets. Это хорошо: reviewer видит, какой workflow и commit ждут approve, а deployment history потом покажет, что именно ушло в среду.

Rollback jobs

Rollback — это тоже deployment workflow, а не shell-команда «где-то на сервере». Его стоит делать отдельным workflow_dispatch с явным input: версия, image tag, release id или artifact id. Rollback должен использовать тот же environment и ту же concurrency group, что production deploy, иначе rollback может начать гонку с обычным deploy.

Rollback лучше понимать как переключение на уже сохранённую предыдущую версию, а не как новую сборку старого кода.
name: rollback

on:
  workflow_dispatch:
    inputs:
      version:
        description: Version to restore, for example v1.7.4
        type: string
        required: true

jobs:
  rollback-production:
    runs-on: ubuntu-24.04
    environment: production
    concurrency:
      group: deploy-production
    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/rollback.sh "$VERSION"
        env:
          VERSION: ${{ inputs.version }}
          DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

Хороший rollback не требует пересобирать старый commit. Он переключает систему на уже существующий artifact, image digest, package version или release asset. Если deploy в облако получает credentials через OIDC, rollback job должен использовать тот же подход: короткоживущий доступ через Cloud deploy через OIDC, а не старый аварийный cloud key в secrets.

Частые ошибки

Первая ошибка — объединить CI, staging, production, release publish и rollback в один огромный YAML без явных границ. GitHub Actions это позволит, но troubleshooting станет хуже: непонятно, где build, где promotion, где ручное решение. Лучше иметь понятные jobs и осмысленные names.

Вторая — ставить cancel-in-progress: true на production просто потому, что так делают в CI. Для линтера это удобно; для деплоя с миграциями БД может быть вредно. Production deploy должен быть либо безопасно прерываемым, либо сериализованным без отмены running job.

Третья — деплоить по push в main без environment gate. Это может быть нормально для маленького staging, но production обычно требует хотя бы protected branch, required reviewers или tag-based process. Даже если команда маленькая, automation должна переживать усталость, случайный re-run и неправильный branch.

See also

Внешние справки: GitHub Docs по control deployments, workflow syntax, events that trigger workflows, deploying to an environment и contexts.

Быстрое повторение #rc-1

Почему production job обычно не должен заново делать npm install && npm run build, если CI уже собрал проект?

Показать ответ

Так production может выкатить не тот build, который прошёл тесты: могли измениться registry, runner image, системные пакеты, lockfile resolution или переменные сборки. Надёжнее собрать один artifact/image/package и деплоить именно его.

Источники

  1. GitHub Docs — Deploying with GitHub Actions / Control deployments
  2. GitHub Docs — Workflow syntax for GitHub Actions
  3. GitHub Docs — Events that trigger workflows
  4. GitHub Docs — Deploying to a specific environment
  5. GitHub Docs — Contexts