open atlas
↑ К треку
CI/CD-пайплайны CICD · 11 · 02

Автоскейлинг через Actions Runner Controller: эфемерные поды, масштабируемые по очереди

Actions Runner Controller (ARC) гоняет self-hosted раннеры как эфемерные поды Kubernetes. Runner scale set масштабирует поды до maxRunners, каждый обслуживает один джоб, давая hosted-подобную изоляцию на кластере с ёмкостью, что следует за очередью, а не за фиксированным пулом.

CICD Senior ◷ 17 min
Уровень
ОсновыJuniorMiddleSenior

Пул был восемь статичных self-hosted раннеров на EC2, всегда включённых, и в 9 утра в понедельник он стал бутылочным горлышком: сорок инженеров запушили перед стендапом, очередь выросла до 60 джобов, а медианное время ожидания достигло одиннадцати минут для тест-сьюта, который бежал четыре. Раннеры никогда не простаивали и никогда не хватало. Первый инстинкт платформенной команды был добавить статичной ёмкости — но восемь машин, тарифицируемых 24/7, уже жгли деньги по ночам, когда очередь пуста, и удвоение удвоило бы простаивающие потери, чтобы починить двухчасовой в день всплеск. Вместо этого они перешли на Actions Runner Controller: раннеры стали эфемерными подами Kubernetes, масштабировавшимися от нуля до сорока, когда очередь наполнялась, и обратно до нуля, когда она опустошалась. Время ожидания в понедельник упало до менее минуты, а ночной счёт — до нуля, потому что ни один под не бежал, когда не было джобов в очереди. Фиксированный пул платил за пиковую ёмкость круглосуточно, чтобы обслужить всплеск длиной два часа.

Что такое ARC

Если ты когда-либо спрашивал «почему я плачу за простаивающие раннеры ночью ради двухчасового утреннего всплеска?» — ARC это ответ, но только если понимаешь, что именно управляет его решениями о масштабировании.

Actions Runner Controller — это оператор Kubernetes, управляющий self-hosted раннерами как подами. Ты ставишь контроллер и определяешь runner scale set — Helm-релиз, привязанный к репозиторию, организации или enterprise регистрационным токеном. Слушатель scale set держит длинный поллинг к сервису Actions GitHub; когда джобы назначаются runner-группе этого scale set, слушатель узнаёт, сколько их в очереди, и контроллер создаёт столько же эфемерных раннер-подов. Каждый под регистрируется, гоняет ровно один джоб, затем завершается и заменяется. Нет персистентного раннера, накапливающего состояние между джобами — та же изоляция «один джоб — один раннер», что делает hosted-раннеры безопасными, теперь на твоём кластере.

Масштабирование ограничено двумя значениями, которые ты задаёшь: minRunners (тёплый пол простаивающих подов, поглощающий задержку холодного старта планирования пода и pull образа) и maxRunners (потолок, ограничивающий и радиус поражения, и облачный счёт). Между ними число подов следует за очередью: джобы в очереди гонят поды вверх к maxRunners, опустошение очереди даёт подам завершиться и не быть заменёнными, откатываясь к minRunners (который может быть нулём — scale-to-zero при простое).

Викторина

В runner scale set ARC сколько джобов обрабатывает один эфемерный раннер-под, прежде чем будет заменён, и почему это важно?

Холодный старт, тёплый пол и компромисс ожидания в очереди

Цена эфемерности — холодный старт: свежесозданный под должен быть запланирован на узел (которому самому может понадобиться cluster autoscaler, чтобы добавить узел — десятки секунд до минут), сделать pull образа раннера, зарегистрироваться в GitHub и только потом подхватить джоб. Для четырёхминутного тест-сьюта 90-секундный холодный старт — это 38% накладных на первый джоб всплеска. minRunners — это рычаг: держи несколько подов тёплыми и простаивающими, чтобы фронт всплеска попал на готовый раннер, пока контроллер масштабирует остальные за ним. Компромисс прямой — тёплые поды стоят денег, пока простаивают, так что minRunners — это ручка между задержкой ожидания в очереди (ставь низко, плати меньше, жди больше в начале всплеска) и отзывчивостью (ставь выше, плати за простаивающее тепло, стартуй быстрее). Размеряй её под реальную форму твоего всплеска, а не под пик.

lesson.inset.connect

ARC накладывается на cluster autoscaler, и два холодных старта складываются. Когда очередь всплёскивает выше, чем вмещают текущие узлы, ARC создаёт ожидающие поды, которые ни один узел не может запланировать; cluster autoscaler Kubernetes видит непланируемые поды и провижинит новые узлы. Так что холодный всплеск может платить две задержки: провижининг узла, затем старт пода. Команды, которым нужна быстрая реакция на всплеск, держат и тёплый пол minRunners, и небольшой запас свободной ёмкости узлов, принимая устойчивую стоимость простоя, чтобы избежать сложенной задержки холодного старта — тот же компромисс фиксированной стоимости против задержки, что у прежнего статичного пула, теперь настраиваемый двумя ручками вместо одной.

Сайзинг maxRunners и образа раннера

maxRunners — твой потолок безопасности. Задай его из двух ограничений: твоего лимита параллелизма GitHub Actions (плановое ограничение на параллельные джобы — превышение просто ставит в очередь, так что maxRunners выше него тратит узлы впустую) и твоей ёмкости кластера и бюджета (каждый под потребляет CPU/memory requests; maxRunners, умноженный на per-pod request, должен влезть в твои узлы, а неограниченное масштабирование может нагнать облачный счёт или заголодить другие ворклоады на общем кластере). Образ пода тоже важен: лёгкий кастомный образ раннера с предзапечённым тулчейном резко срезает per-pod холодный старт против pull жирного образа и установки инструментов на старте джоба — та же логика, что у хорошо собранного CI base-образа, применённая к самому раннер-поду.

Викторина

Команда ставит minRunners: 0 для минимизации стоимости и сообщает, что первый джоб после периода простоя ждёт около двух минут, прежде чем вообще стартует. Какова наиболее вероятная причина и прямой фикс?

Вспомните перед уходом
  1. 01
    Опиши цикл масштабирования ARC и роли minRunners и maxRunners.
  2. 02
    Почему scale-to-zero (minRunners: 0) вводит задержку первого джоба, и что её усугубляет?
Итог

Actions Runner Controller — это оператор Kubernetes, заменяющий фиксированный пул всегда-включённых self-hosted раннеров эфемерными подами, масштабируемыми по очереди джобов. Ты определяешь runner scale set, привязанный к репозиторию, организации или enterprise; его слушатель длинно-поллит сервис Actions GitHub на джобы в очереди, и контроллер сверяет число подов. Каждый под регистрируется, гоняет ровно один джоб и завершается — та же изоляция «один джоб — один раннер», что делает hosted-раннеры безопасными, привнесённая на твой кластер. Масштабирование ограничено minRunners, тёплым полом простаивающих подов, что снижает задержку первого джоба ценой оплаты простаивающего тепла, и maxRunners, твёрдым потолком, ограничивающим радиус поражения и облачные траты. Цена эфемерности — холодный старт: свежий под должен быть запланирован, сделать pull образа и зарегистрироваться перед запуском, а когда cluster autoscaler должен сперва добавить узел, две задержки складываются. Ты настраиваешь это ненулевым тёплым полом, лёгким предзапечённым образом раннера и небольшим запасом ёмкости узлов — каждое меняет устойчивую стоимость простоя на более быстрые всплески. Размеряй maxRunners под лимитом параллелизма GitHub Actions (выше него просто очередь) и в пределах ёмкости кластера и бюджета, ведь каждый под потребляет CPU и memory requests, а неограниченное масштабирование может заголодить соседей или нагнать счёт. Результат — ёмкость, что следует за спросом — ноль подов, когда очередь пуста, масштабирование до потолка, когда она наполняется — вместо оплаты пиковой ёмкости круглосуточно ради обслуживания всплеска длиной пару часов. Теперь, когда встретишь команду, жалующуюся на медленный CI в понедельник утром и пустую очередь к полудню, знаешь ответ: ARC с откалиброванным тёплым полом, а не больше статичных раннеров.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 5 завершено

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.