Освоение оптимизации исполнителей Jenkins для ускорения сборок

Оптимизируйте производительность CI/CD, освоив настройку исполнителей Jenkins. Это экспертное руководство объясняет, как рассчитать оптимальное количество исполнителей на основе ограничений ЦП и ввода-вывода, сокращая время ожидания в очереди сборок и максимизируя пропускную способность агентов. Изучите ключевые стратегии конфигурации, включая использование параллелизма Pipeline, управление статическими и динамическими агентами, а также выявление узких мест с помощью ключевых метрик, таких как длина очереди и ожидание ввода-вывода. Внедрите эти практические шаги для достижения более быстрых сборок и более эффективной среды Jenkins.

Освоение оптимизации исполнителей Jenkins для ускорения сборок

Оптимизация исполнителей Jenkins — это, по сути, планирование ресурсов в миниатюре. Исполнитель — это слот, в котором Jenkins может выполнять работу на узле. Если добавить слишком мало слотов, задания будут ждать в очереди. Если добавить слишком много, каждая сборка будет бороться за ЦП, память, диск, сеть, а иногда и за один и тот же кеш зависимостей.

Цель — не «больше исполнителей». Цель — сократить общее время получения обратной связи, не делая отдельные сборки ненадёжными.

Оставьте исполнители контроллера на нуле

Для большинства современных установок Jenkins контроллер должен планировать работу, обслуживать UI, хранить конфигурацию заданий и координировать агенты. Он не должен компилировать код или запускать тестовые наборы.

Установите количество исполнителей контроллера на 0, если только у вас нет небольшого, целенаправленного административного задания, которое должно выполняться там. Занятая сборка на контроллере может лишить ресурсов UI, задержать планирование очереди и усложнить диагностику проблем с плагинами. Если контроллер становится нестабильным, это чувствует каждая команда, использующая Jenkins.

Перенесите работу по сборке на агенты с чёткими метками:

linux && docker
linux && maven
windows && visualstudio
large-memory

Метки являются частью оптимизации исполнителей, потому что заданию, ожидающему linux && docker, всё равно, что у вас есть простаивающие исполнители Windows.

Начните с типа рабочей нагрузки, а не с универсальной формулы

Для сборок с интенсивным использованием ЦП начните с количества, близкого к числу физических или виртуальных ядер ЦП. Виртуальная машина для сборки с 8 ядрами, выполняющая компиляцию C++ или большие наборы тестов Java, может начать с 6–8 исполнителей, а затем уменьшить или увеличить их количество на основе измерений.

Для сборок с интенсивным вводом-выводом вы можете запускать больше исполнителей, чем ядер ЦП, потому что задания тратят время на ожидание загрузки из сети, выгрузки артефактов или тестовых сервисов. Агент с 8 ядрами и интенсивными зависимостями может хорошо справляться с 10–12 исполнителями. Он также может выйти из строя при 6, если диск медленный или памяти мало.

Память часто устанавливает реальный предел. Если каждая сборка может использовать 2 ГБ ОЗУ, а у агента 16 ГБ, то восемь исполнителей не оставляют места для ОС, демона Docker, сред выполнения языков, браузерных тестов или кеша файловой системы. В этом случае четыре или пять исполнителей могут быть быстрее, чем восемь, потому что машина избегает свопинга.

Используйте такую рабочую таблицу:

ОЗУ агента: 32 ГБ
резерв для ОС и демонов: 4 ГБ
доступно для сборок: 28 ГБ
типичный пик сборки: 3 ГБ
начальное количество исполнителей: 8 или 9, затем проверка под нагрузкой

Числа не обязательно должны быть идеальными. Они должны быть достаточно явными, чтобы вы могли скорректировать их после наблюдения за реальными сборками.

Следите за причинами очереди

Очередь сборок Jenkins сообщает вам, почему работа ожидает. «Ожидание следующего доступного исполнителя» отличается от «Нет узлов с такой меткой». Первое указывает на нехватку ресурсов. Второе указывает на проблемы с метками или предоставлением агентов.

Когда разработчики жалуются, что Jenkins медленный, проверьте:

  • Длину очереди по меткам.
  • Среднее время ожидания в очереди.
  • Частоту переключения агентов между онлайн/офлайн.
  • Сборки, застрявшие в ожидании заблокированных ресурсов.
  • Параллельные стадии Pipeline, потребляющие больше исполнителей, чем ожидалось.

Один Pipeline с десятью параллельными ветвями может занять десять слотов исполнителей. Это может быть именно то, что вам нужно для тестовой матрицы. Это также может лишить ресурсов все остальные задания в небольшой установке Jenkins.

Используйте один исполнитель на одноразовый агент

Для агентов Kubernetes и многих облачных агентов одна модель с одним исполнителем на под или экземпляр обычно является самой чистой. Под — это единица изоляции. Если вам нужно больше параллелизма, создавайте больше подов вместо того, чтобы упаковывать несвязанные сборки в одно и то же одноразовое рабочее пространство.

Эта модель делает важными запросы ресурсов:

resources:
  requests:
    cpu: "2"
    memory: "4Gi"
  limits:
    memory: "6Gi"

Если запросы слишком низкие, Kubernetes может упаковать слишком много агентов Jenkins на один узел. Jenkins видит доступных исполнителей, но узел кластера перегружен. Если лимиты слишком жёсткие, сборки завершаются с ошибками памяти, которые выглядят как проблемы приложения.

Для статических агентов VM несколько исполнителей могут быть вполне приемлемы. Просто убедитесь, что структура рабочего пространства, каталоги кеша и установки инструментов спроектированы для параллельных заданий.

Ограничьте «шумные» задания

Некоторые задания не должны запускать столько копий, сколько может запланировать Jenkins. Интеграционные тесты базы данных, браузерные тесты, нагрузочные тесты и сборки образов могут быстро насытить общие системы.

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

pipeline {
  agent { label 'linux && docker' }
  options {
    disableConcurrentBuilds()
  }
  stages {
    stage('Build Image') {
      steps {
        sh 'docker build -t app:${BUILD_NUMBER} .'
      }
    }
  }
}

Для общей промежуточной базы данных блокировка более понятна:

lock('staging-db') {
  sh './run-integration-tests.sh'
}

Это может заставить одно задание ждать, но предотвращает одновременный сбой пяти заданий и потерю всего времени их исполнителей.

Измеряйте агент, а не только Jenkins

Jenkins может сообщить вам о времени ожидания в очереди и использовании исполнителей. Операционная система сообщает вам, разумно ли количество исполнителей.

На агентах Linux проверьте:

uptime
mpstat 1
iostat -xz 1
free -h
df -h
docker system df

Высокая загрузка ЦП с низким ожиданием ввода-вывода означает работу, связанную с ЦП. Высокое ожидание ввода-вывода означает, что добавление исполнителей, вероятно, замедлит сборки. Использование свопа во время сборок — это сильный сигнал к уменьшению количества исполнителей или увеличению памяти. Почти полный диск может замедлить checkout, архивирование и сборки Docker.

Также смотрите на продолжительность отдельных сборок. Если время ожидания в очереди сокращается на одну минуту, но каждая сборка становится на пять минут медленнее, то большее количество исполнителей не является выигрышем.

Настраивайте небольшими изменениями

Изменяйте количество исполнителей постепенно. Переведите агент с 4 на 6, понаблюдайте несколько загруженных дней, затем примите решение. Большие скачки скрывают причину регрессий.

Ведите записи:

2026-05-24: linux-build-03 исполнители 4 -> 6
Причина: очередь для linux && maven в среднем 12 минут
Отслеживать: ЦП, iowait, конкуренция за кеш Maven, продолжительность сборки
Откат: установить на 4, если p95 продолжительности сборки вырастет больше ожидаемого

Эта короткая запись помогает, когда кто-то спрашивает, почему агент медленный две недели спустя.

Практическая цель

Здоровая установка Jenkins имеет большинство агентов занятыми в пиковые периоды, короткие очереди для общих меток, отсутствие свопа, предсказуемую продолжительность сборок и отсутствие сборок на контроллере. Она также имеет достаточную ёмкость для конкретных меток, чтобы отсутствие агента Docker не заставляло ждать несвязанные задания Maven.

Оптимизация исполнителей — это не разовая настройка. Она меняется, когда ваши команды добавляют параллельные стадии, переходят на более крупные тестовые наборы, внедряют сборки Docker или мигрируют со статических VM на агенты Kubernetes. Пересматривайте её, когда время ожидания в очереди становится заметным, когда агенты начинают использовать своп, или когда самое быстрое решение, которое предлагают люди, — это «просто добавьте больше исполнителей». Иногда это правильно. Часто лучшее решение — это новый пул агентов, лучшие метки или меньшее количество одновременных копий задания, которое вредит всем остальным.

Параллельные стадии нуждаются в бюджете

Параллелизм Pipeline может быть большим преимуществом, но он быстро расходует исполнителей. Матричная сборка для четырёх версий Java и трёх операционных систем может создать двенадцать ветвей. Если каждая ветвь выполняется на отдельном агенте, то одна такая сборка может потребить двенадцать исполнителей до того, как даже начнутся стадии развёртывания.

Это приемлемо, если команда запланировала это. Это болезненно, когда один pull request лишает ресурсов релизные сборки. Установите ограничения для заданий с большим разветвлением:

options {
  parallelsAlwaysFailFast()
}

Используйте метки, которые отправляют дорогие ветви в правильный пул, и рассмотрите возможность запуска полной матрицы в main, а на pull requests — меньшей матрицы. Цель — быстрая обратная связь там, где это важно, а не максимальный параллелизм везде.

Разделяйте интерактивные и пакетные ожидания

Не каждое задание Jenkins заслуживает одинакового целевого времени ожидания в очереди. Сборка исправления для продакшена, задание проверки pull request, ночная проверка безопасности и еженедельное задание очистки могут выполняться в Jenkins, но они не должны конкурировать на равных.

Вы можете разделить их с помощью меток, периодов тишины, ограничений и запланированных окон. Тяжёлые ночные задания должны выполняться, когда интерактивный PR-трафик низок. Долго выполняющиеся нагрузочные тесты должны иметь свои собственные агенты. Релизные задания могут заслуживать зарезервированную ёмкость или метку, которую не могут использовать обычные задания веток.

Это меньше связано с политикой и больше с надёжностью. Если каждая рабочая нагрузка использует один пул исполнителей, то самая шумная рабочая нагрузка определяет опыт для всех.

Что документировать

Для каждого пула агентов ведите небольшую запись:

метка: linux && docker
тип агента: статическая VM
исполнителей на агента: 4
основные рабочие нагрузки: сборки образов Docker, интеграционные тесты
известные ограничения: дисковый ввод-вывод и рост слоёв Docker
очистка: ночной docker prune, очистка рабочего пространства после сборки
владелец: платформенная команда

Эта запись значительно облегчает будущую настройку. Когда время ожидания в очереди растёт, вы можете решить, добавить ли больше таких же агентов, разделить рабочую нагрузку на новый пул или уменьшить параллелизм заданий, вызывающих конкуренцию.