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

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

Когда нужны свои runners: приватная сеть, hardware, cost, compliance; labels, runner groups, lifecycle, обновления, изоляция и риски untrusted workloads.

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

Self-hosted runners

Когда нужны свои runners: приватная сеть, hardware, cost, compliance; labels, runner groups, lifecycle, обновления, изоляция и риски untrusted workloads.

Self-hosted runners

Self-hosted runner — это runner, который вы сами разворачиваете и обслуживаете, а GitHub Actions только отправляет на него jobs. Если в GitHub-hosted runners GitHub обычно выдаёт новую managed VM (кроме single-CPU runners, которые запускаются в контейнере на shared VM), то здесь машина принадлежит вам: физический сервер, VM в облаке, on-prem host, контейнерная среда или autoscaling pool. Workflow остаётся тем же GitHub Actions YAML, но runtime-контракт меняется радикально: вы отвечаете за OS, пакеты, сеть, изоляцию, обновления, capacity и уборку после job.

Это не «более профессиональная версия» hosted runner. Это другой компромисс. Self-hosted runners нужны, когда у workflow есть требование, которое hosted runner не закрывает: приватная сеть, специфичное железо, compliance-зона, уже оплаченная инфраструктура, большой build cache рядом с monorepo или софт, который нельзя/неудобно каждый раз ставить на чистую VM.

flowchart TD A[Workflow job] --> B{runs-on} B --> C[Runner group policy] C --> D{labels match?} D -->|yes| E[Self-hosted runner] D -->|no| Q[Job waits in queue] C --> R[Selected repositories only] E --> F[Private network / hardware / licensed tools] E --> G[Workspace and host state need cleanup] H[Public fork PR] -. risky if allowed .-> E
flowchart TD
  A[Workflow job] --> B{runs-on}
  B --> C[Runner group policy]
  C --> D{labels match?}
  D -->|yes| E[Self-hosted runner]
  D -->|no| Q[Job waits in queue]
  C --> R[Selected repositories only]
  E --> F[Private network / hardware / licensed tools]
  E --> G[Workspace and host state need cleanup]
  H[Public fork PR] -. risky if allowed .-> E
Как job попадает на self-hosted runner: сначала политика доступа через group, затем подбор по labels, затем выполнение в вашей инфраструктуре.

Когда self-hosted runner оправдан

Первый типичный случай — доступ к приватной сети. Например, deploy job должен сходить в закрытый Kubernetes API, внутренний package registry, staging-базу или on-prem сервис. Иногда это решается через Cloud deploy через OIDC и публичный endpoint с короткоживущими credentials. Но если endpoint принципиально не должен быть публичным, runner внутри нужной сети становится нормальным вариантом.

Второй случай — hardware. GPU, большой SSD, много RAM, ARM-железо, специфичный macOS host, embedded-девайсы, лицензированный EDA/CAD софт, локальный симулятор или аппаратный стенд. В Matrix strategy это обычно превращается в отдельную ось или отдельный job, а не заменяет весь pipeline.

Третий случай — cost и throughput. GitHub не берёт Actions minutes за self-hosted runner, но инфраструктура, поддержка, security и простои остаются на вас. Поэтому «бесплатно» здесь означает только «не тарифицируется как GitHub-hosted minutes». Для тяжёлого постоянного CI пул своих ephemeral VM может быть дешевле. Для маленькой команды один вручную настроенный сервер часто оказывается дороже по операционному времени, чем обычный ubuntu-24.04.

Четвёртый случай — compliance. Иногда build обязан выполняться в контролируемой среде: свой image baseline, свой audit, egress policy, private CA, endpoint protection, сегментированная сеть. Тогда runner — часть платформенной инфраструктуры, а не строка runs-on.

Labels: как job выбирает машину

Self-hosted runner обычно получает label self-hosted, а также labels вроде linux, x64, arm64, gpu, deploy, ios-signer, large-disk. При runs-on массив labels работает как AND: runner должен иметь все указанные labels.

jobs:
  gpu-tests:
    runs-on: [self-hosted, linux, x64, gpu]

    steps:
      - uses: actions/checkout@v4
      - run: ./scripts/run-model-tests.sh

Такой job не попадёт на любой Linux runner. Он ждёт runner, где одновременно есть self-hosted, linux, x64 и gpu. Это удобно, но labels быстро становятся скрытым API вашей CI-инфраструктуры. Называйте их по capability, а не по имени конкретной машины: gpu лучше, чем ci-box-17; prod-deploy лучше, чем alice-vm.

Для deploy jobs labels часто комбинируют с environment, permissions и условиями из Jobs, dependencies и conditions:

jobs:
  deploy:
    if: github.ref == 'refs/heads/main'
    runs-on: [self-hosted, linux, prod-deploy]
    environment: production
    permissions:
      contents: read
      id-token: write

    steps:
      - uses: actions/checkout@v4
      - run: ./deploy.sh

Здесь runs-on выбирает машину, environment подключает правила из Environments и protection rules, а permissions ограничивает Permissions и GITHUB_TOKEN. Эти механизмы не заменяют друг друга.

Runner groups: доступ не через labels, а через policy

Labels отвечают на вопрос «какая capability нужна job». Runner groups отвечают на вопрос «какие repositories вообще имеют право использовать этот pool». Это особенно важно на уровне organization или enterprise, где один runner может быть виден нескольким репозиториям.

jobs:
  private-network-check:
    runs-on:
      group: internal-network-runners
      labels: linux

    steps:
      - uses: actions/checkout@v4
      - run: curl --fail https://internal-api.example.local/health

Группа internal-network-runners должна быть доступна только тем repositories, которым действительно нужен доступ во внутреннюю сеть. Не надо складывать «все self-hosted runners» в одну default group и надеяться, что labels спасут от ошибки. Labels — routing hint. Groups — boundary доступа. В зрелой организации это уже тема Runner governance: кто создаёт groups, кто добавляет runners, какие repos разрешены, где audit trail, что происходит при инциденте.

Lifecycle: runner — не одноразовая VM по умолчанию

GitHub-hosted runner обычно стартует чистым для job и исчезает после выполнения. Self-hosted runner по умолчанию может переживать много jobs. Это ускоряет некоторые сценарии, но создаёт риск загрязнения workspace: оставшиеся файлы, локальные caches, daemon-процессы, credentials в history, toolchains после ручных экспериментов.

Минимальный lifecycle для постоянного runner выглядит так:

1. Provision: создать VM/host из известного image.

2. Register: добавить runner на repo/org/enterprise level через временный registration token.

3. Run as service: держать runner online после reboot.

4. Patch: обновлять OS, packages и base image; runner application может обновляться автоматически, но остальной софт — ваша ответственность.

5. Clean: чистить workspace, Docker state, временные файлы и локальные credentials.

6. Retire: удалять runner из GitHub и уничтожать host, если он больше не нужен.

ИнтерактивПеред подключением self-hosted runner#int-1
Шаг 1 из 51. Определите причину
Запишите конкретное требование: приватная сеть, hardware, compliance, cost или licensed tooling. Если причины нет, начните с GitHub-hosted runner.
Короткий walkthrough по вопросам, которые стоит закрыть до того, как runner начнёт принимать jobs.

Для untrusted workloads лучше смотреть на ephemeral или just-in-time runners: runner получает один job и потом удаляется из GitHub. Но даже JIT-подход не магия, если вы переиспользуете тот же грязный host или disk snapshot. Чистая среда должна быть реальной, а не только новым runner registration.

Главный риск: untrusted code на вашей машине

Self-hosted runner выполняет код из workflow и репозитория. Если runner имеет доступ к внутренней сети, metadata service, SSH keys, package registry, Docker socket или production credentials, то workflow получает путь к этим ресурсам. Поэтому GitHub прямо рекомендует использовать self-hosted runners прежде всего с private repositories и очень осторожно относиться к public repos и fork PRs.

Самый опасный анти-паттерн выглядит так: public repository, pull_request, self-hosted runner во внутренней сети, широкий GITHUB_TOKEN, checkout кода из PR и deploy/test scripts без review. Это уже зона pull_request_target и untrusted PR и Secure use и threat model. Проблема не в том, что Actions «небезопасен». Проблема в том, что вы дали чужому коду исполниться на своей инфраструктуре.

Практичные правила:

  • не используйте self-hosted runners для public fork PRs без жёсткой изоляции;
  • разделяйте runners по trust level: public CI, private CI, deploy, release signing;
  • не храните долгоживущие secrets на host, если можно использовать OIDC из OIDC и секреты облаков;
  • не монтируйте Docker socket в job без понимания последствий;
  • не давайте одному runner доступ и к untrusted test jobs, и к production deploy;
  • фиксируйте third-party actions и permissions так же строго, как на hosted runners.

Контейнеры не отменяют ответственность

Job container и service containers из Containers и service containers помогают стандартизировать runtime: Node/Python/Go версии, PostgreSQL, Redis, queues. Но контейнер внутри self-hosted runner не всегда является security boundary. Если job может управлять Docker daemon на host, он часто может влиять на host сильнее, чем кажется. Для isolation думайте уровнями: отдельный runner group, отдельный VM/host, ephemeral lifecycle, сетевые ACL, минимальные credentials.

Cache тоже требует дисциплины. В Dependency cache cache ускоряет зависимости; на self-hosted runner легко случайно начать полагаться на локальный state вне GitHub cache. Это делает build быстрым, но нерепродьюсибельным. Если job проходит только на одном «тёплом» runner, а на новом падает, у вас не оптимизация, а скрытая зависимость.

Эксплуатационная эвристика

Начинайте с GitHub-hosted runners, если нет конкретного требования к сети, железу, compliance или стоимости. Если требование есть, проектируйте self-hosted pool как инфраструктуру: groups до labels, clean image до ручной настройки, ephemeral там, где есть untrusted code, мониторинг до инцидента.

Для обычного CI pipeline self-hosted runner редко должен становиться единственным вариантом. Часто хорошая схема гибридная: lint, unit tests и build идут на hosted Ubuntu; аппаратные, private-network или deploy jobs идут на self-hosted pool; reports передаются через Artifacts и reports; производительность разбирается отдельно в Оптимизация времени CI.

Хороший self-hosted runner незаметен: jobs приходят только туда, куда должны, машина регулярно обновляется, workspace чистится, доступы минимальны, а при падении видно, это проблема workflow, runner status или сети. Плохой self-hosted runner становится не CI-инструментом, а неучтённым сервером с правом запускать произвольный код.

See also

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

Почему self-hosted runner — это не «более профессиональный hosted runner», а другой компромисс?

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

GitHub Actions только отправляет jobs, а за OS, пакеты, сеть, изоляцию, обновления, capacity и уборку отвечаете вы. Это имеет смысл только когда hosted runner не закрывает конкретное требование.

Источники

  1. GitHub Docs — Self-hosted runners
  2. GitHub Docs — Choose the runner for a job
  3. GitHub Docs — Managing access to self-hosted runners using groups
  4. GitHub Docs — Adding self-hosted runners
  5. GitHub Docs — Monitoring and troubleshooting self-hosted runners
  6. GitHub Docs — Secure use reference
  7. AWS DevOps Blog — Best practices working with self-hosted GitHub Action runners at scale on AWS