Как мы тестировали серверное железо под нагрузки CI и сборок

Как мы тестировали серверное железо под нагрузки CI и сборок

Когда сервер выбирают под CI и тяжёлые сборки, почти всегда ошибаются в одном и том же: смотрят только на частоту CPU и объём RAM, а потом удивляются, почему реальная скорость пайплайнов не соответствует ожиданиям. На практике важны не только ядра, но и поведение дисковой подсистемы, стабильность под длительной нагрузкой, параллельные I/O-операции и предсказуемость результатов на разных типах задач. Мы в лаборатории не раз сталкивались с тем, что железо, блестяще прошедшее синтетику, проваливалось на реальных CI-сценариях, поэтому выработали методику, которая позволяет увидеть картину целиком.

Зачем вообще тестировать железо именно под CI и сборки

Нагрузка CI принципиально отличается от «обычного сервера приложений». Здесь одновременно происходят короткие всплески CPU на компиляции и упаковке артефактов, активная работа с диском при распаковке зависимостей, кэшей и образов, множество мелких файловых операций, параллельный запуск нескольких job’ов и периодические пиковые скачки из-за тестов, линтеров и контейнерных сборок. В отличие от веб-сервера, где нагрузка более равномерна, CI-раннер постоянно переключается между фазами: то процессор на 100% занят компиляцией, то диск захлёбывается от тысяч мелких файлов при распаковке npm-пакетов или Docker-слоёв.

Из-за этого сервер может выглядеть мощным в синтетике, но проседать именно на Jenkins, GitLab CI или другом раннере. Поэтому мы тестировали не «сервер вообще», а конкретный сценарий: сборки, параллельные job’ы, кэширование и длительная стабильная работа под повторяющейся нагрузкой.

Что мы хотели проверить

Перед тестами мы сформулировали практические вопросы, ответы на которые важнее абстрактного «балла производительности»:

  • сколько сборок сервер выдерживает одновременно без заметной деградации — и не просто «держит», а сохраняет ли приемлемое время выполнения каждого job’а;
  • упирается ли система в CPU, память или диск — и в какой момент начинается перегрузка конкретного компонента;
  • как ведёт себя storage при постоянной записи и чтении мелких файлов, особенно когда параллельные job’ы создают конкурентный доступ;
  • есть ли просадки после прогрева, когда кэш уже заполнен и файловая система начинает работать с «грязными» данными;
  • насколько результаты стабильны от прогона к прогону — не «повезло» ли с одним замером;
  • что ломается первым: latency, I/O, температура или лимиты по памяти — и как это отражается на времени сборки.

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

Какой сценарий тестирования мы использовали

Мы разделили проверку на несколько уровней, чтобы последовательно выявлять ограничения.

1. Базовый прогон «одна сборка»

Сначала запускали одну типовую сборку, чтобы понять базовую скорость системы без конкуренции за ресурсы. В неё входили: сборка проекта (например, Node.js или Java), установка зависимостей, запуск unit-тестов и упаковка артефактов. Это даёт опорную точку — минимальное время, которое способен показать сервер в идеальных условиях.

2. Параллельные сборки

Дальше увеличивали число одновременно выполняемых job’ов. Именно тут обычно вскрываются проблемы с нехваткой ядер, слабым диском, медленным распаковочным I/O, нехваткой RAM и уходом в swap, а также перегревом и троттлингом. Мы начинали с двух параллельных задач и постепенно поднимали до 4–8, фиксируя момент, когда время сборки начинало расти нелинейно.

3. Длительная нагрузка

Короткий тест может выглядеть отлично, но через 30–60 минут всё меняется. Поэтому мы проверяли: сохраняется ли скорость после длительного прогона, не падает ли производительность при нагреве, не начинает ли система «пилить» по latency, не растёт ли время отдельных стадий. В некоторых конфигурациях уже через 20–30 минут проявлялся троттлинг, и сборка замедлялась на 15–20%.

4. Имитация смешанного CI-профиля

В реальном CI редко бывает только build. Обычно рядом идут тесты, контейнерные сборки, статический анализ, артефакты, кеши и репозитории. Поэтому мы смешивали сценарии: одновременно запускали линтеры, unit-тесты, сборку Docker-образов и упаковку артефактов. Это позволяло увидеть не только пиковую, но и прикладную нагрузку, когда диск и память работают в режиме жёсткой конкуренции.

Какие метрики мы смотрели

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

Метрика Что показывает Почему важна
Время полной сборки Итоговую скорость пайплайна от коммита до артефакта Главный бизнес-показатель: как быстро разработчик получает обратную связь
Время отдельных стадий Где именно теряется время — на компиляции, тестах или упаковке Помогает найти узкое место и понять, что апгрейдить в первую очередь
CPU usage Насколько упираемся в процессор Полезно при компиляции и тестах: если ядра загружены под 100%, а сборка всё равно медленная — проблема не в CPU
Load average Суммарное давление на систему, включая очереди на I/O Показывает перегрузку очередей: значение выше числа ядер говорит о том, что задачи ждут ресурс
RAM usage Хватает ли памяти под параллельные job’ы и контейнеры Помогает избежать swap, который для CI почти гарантированно убивает производительность
Disk IOPS и latency Как быстро диск обслуживает мелкие операции чтения/записи Критично для зависимостей, кэшей и артефактов: высокая latency при 4K-операциях мгновенно замедляет сборку
Network throughput Скорость загрузки пакетов и образов Важна для внешних зависимостей: если канал узкий, даже мощный сервер будет ждать скачивания
Температуры и частоты Есть ли троттлинг из-за перегрева Особенно важно у компактных серверов: через 15–20 минут частоты могут упасть, и все предыдущие замеры станут неактуальны

Что оказалось самым важным на практике

Диск часто важнее, чем кажется

Для CI слабое хранилище убивает производительность быстрее, чем недостаток ядер. Сборки постоянно создают, читают и удаляют тысячи мелких файлов — зависимости, кэши, временные артефакты. Если диск не держит I/O, процессор может простаивать, а пайплайн всё равно будет медленным. У нас был случай, когда сервер с 16 ядрами и HDD-массивом показывал худшее время сборки, чем 8-ядерный с NVMe, потому что диск не справлялся с параллельными операциями чтения/записи.

На практике это проявляется так: первая сборка ещё терпима; при параллельных job’ах время резко растёт; кэш начинает «не помогать», а мешать, потому что система тратит ресурсы на его сброс; тесты и упаковка артефактов становятся непропорционально долгими. Даже SATA SSD может упереться в свой предел IOPS, если параллельно работают 4–5 воркеров.

Ядра важны, но только в связке с памятью

Если CPU хватает, а RAM мало, система быстро уходит в swap. Для CI это почти гарантированная деградация. Одновременный запуск нескольких job’ов, контейнеров и тестов легко съедает память даже на, казалось бы, «мощном» сервере. Например, при запуске четырёх параллельных job’ов на сервере с 32 ГБ RAM, каждый из которых требовал по 8 ГБ под контейнеры, система уходила в swap уже на третьем job’е, и время сборки вырастало в разы. Поэтому объём памяти нужно считать с запасом, учитывая пиковое потребление всех параллельных задач плюс файловый кэш.

Температурный режим нельзя игнорировать

Некоторые конфигурации красиво показывают себя в коротком бенчмарке, но через 20–30 минут снижают частоты. Для CI это особенно неприятно: в начале всё быстро, потом стабильно медленнее. В компактных серверах 1U с пассивным охлаждением троттлинг наступал уже через 15 минут интенсивной работы, хотя в первые минуты частоты держались на максимуме. Поэтому долгий прогон важнее однократного синтетического теста — он показывает, как сервер поведёт себя в реальной эксплуатации, когда сборки идут одна за другой.

Как мы строили тестовую методику

Чтобы результаты были полезны, а не декоративны, мы придерживались нескольких правил, выработанных на практике.

Правило 1. Сравнивать нужно одинаковые сборки

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

Правило 2. Нужны повторные прогоны

Один результат ничего не значит. CI-нагрузка шумная: влияют кэш, фоновые процессы, температура, состояние storage. Поэтому мы смотрели не на одиночный рекорд, а на серию прогонов (минимум 5) и усреднённую картину, отбрасывая явные выбросы. Только так можно отделить случайность от закономерности.

Правило 3. Нельзя тестировать только «холодный старт»

Первый прогон часто показывает худшее или, наоборот, лучшее время. Важно отдельно смотреть cold run (без кэша), warm run (с прогретым кэшем) и многократный прогон после прогрева, а также результат после длительной серии job’ов. После 20–30 прогонов картина может снова измениться из-за фрагментации или заполнения диска, поэтому мы обязательно анализировали стабильность на длинной дистанции.

Правило 4. Нужно отделять сервер от инфраструктуры вокруг него

Иногда проблема не в железе, а в медленном репозитории пакетов, сетевом канале, некорректно настроенном кэше, лимитах контейнерной платформы или слишком агрессивных параллельных воркерах. Мы всегда проверяли, не упирается ли сеть в пропускную способность внешнего реестра (npm, Docker Hub), и при необходимости поднимали локальное зеркало, чтобы изолировать железо. Только так можно быть уверенным, что измеряешь именно сервер, а не скорость интернета.

Типовой план теста, который можно повторить у себя

Шаг 1. Определите профиль нагрузки

Сначала честно ответьте на вопросы: какие языки и фреймворки используются; сколько минут занимает средняя сборка; сколько job’ов одновременно запускается; есть ли контейнерные сборки; насколько важны тесты и статический анализ. Если у вас Java-проект с Maven, учтите, что локальный репозиторий .m2 может занимать гигабайты и активно читаться при каждой сборке — это создаёт дополнительную дисковую нагрузку.

Шаг 2. Зафиксируйте базовую конфигурацию

Запишите не только модель CPU и объём RAM, но и тип/модель диска, схему RAID, версию ОС, настройки файловой системы, параметры виртуализации (если есть), а также степпинг процессора и версию микрокода. Без этого сравнение бессмысленно: разные версии драйверов или режим энергосбережения в BIOS могут кардинально изменить результаты.

Шаг 3. Запустите один эталонный pipeline

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

Шаг 4. Увеличивайте параллелизм

Постепенно добавляйте job’ы (с шагом 1–2) и смотрите: где начинает расти очередь; при каком числе задач растёт latency; когда появляется swap; когда диск перестаёт успевать. Параллельно следите за утилизацией диска через iostat — как только avgqu-sz стабильно превышает 1–2, а await растёт, диск становится узким местом.

Шаг 5. Прогоняйте длительный тест

Нужно убедиться, что система держит нагрузку не 3 минуты, а час и дольше. Это особенно важно для build-ферм и общих раннеров. Мы обычно запускали непрерывную серию из 20–30 сборок и записывали температуры каждые 5 минут, чтобы отловить момент начала троттлинга.

Шаг 6. Снимайте метрики не только из CI, но и из ОС

Сами логи пайплайна не покажут всю картину. Нужны метрики уровня системы: загрузка CPU по ядрам, I/O wait, использование памяти, swap, температура, очередь на диск. Мы использовали sar, iostat, vmstat и lm-sensors — эти утилиты дают полный срез без лишнего оверхеда.

Какие ошибки чаще всего делают при оценке серверов под CI

  • Смотрят только на CPU и игнорируют диск. Потом удивляются, почему 32-ядерный сервер собирает медленнее ноутбука с NVMe.
  • Тестируют один раз и делают выводы по одному прогону. Один удачный замер не гарантирует стабильности.
  • Используют слишком короткий бенчмарк. 5-минутный тест не покажет троттлинг и деградацию кэша.
  • Не учитывают прогрев и кэш. Cold run может быть в разы медленнее, но это не всегда показатель реальной работы.
  • Не проверяют параллельную нагрузку. Сервер, отлично справляющийся с одной сборкой, может «лечь» при трёх одновременных.
  • Сравнивают серверы с разными условиями окружения. Разные версии ОС, драйверов или настройки RAID делают сравнение некорректным.
  • Не фиксируют версии ОС, драйверов и настроек. Потом невозможно воспроизвести результаты или понять причину различий.
  • Делают выводы по synthetic benchmark вместо реального pipeline. Geekbench не покажет, как сервер справится с npm install и Docker build одновременно.

Как интерпретировать результаты без самообмана

Если сервер показывает высокую скорость в одной сборке, но начинает резко деградировать при двух-трёх параллельных job’ах, это почти всегда означает, что узкое место находится не в «мощности», а в балансе ресурсов. Например, если при одном job’е время сборки 5 минут, а при двух — 12 минут, это не просто «линейный рост», а признак того, что система вошла в swap или диск перегружен. I/O wait в этот момент может достигать 40–50%.

Обычно картина такая: CPU загружен не полностью, но build тормозит; I/O wait растёт сильнее, чем CPU usage; память уходит в swap; время упаковки и распаковки растёт непропорционально; при длительной работе появляются температурные ограничения. В таких случаях апгрейд «ещё одного ядра» может дать меньше пользы, чем переход на более быстрый SSD или увеличение RAM.

Практический чек-лист перед покупкой сервера под CI

  • Проверить не только CPU, но и реальную скорость storage — особенно случайный доступ 4K и IOPS на глубине очереди 32.
  • Оценить объём RAM с запасом под параллельные job’ы и файловый кэш: сложите пиковое потребление всех одновременных задач и добавьте 20–30%.
  • Уточнить, есть ли NVMe и как устроен RAID: некоторые RAID-контроллеры добавляют latency, сводя на нет преимущества быстрых дисков.
  • Посмотреть на стабильность частот под длительной нагрузкой — запросите у вендора или протестируйте сами в течение часа.
  • Сравнить cold и warm run, чтобы понять влияние кэша.
  • Проверить, как сервер ведёт себя при 2–4 одновременных сборках — это минимальный реалистичный сценарий.
  • Убедиться, что ОС и файловая система подходят под интенсивный I/O: например, ext4 с опцией noatime или XFS могут дать прирост.
  • Сразу продумать мониторинг CPU, RAM, диска и температур — без него вы не узнаете, когда сервер начнёт деградировать.
  • Тестировать на реальном пайплайне, а не на абстрактном синтетическом бенчмарке.
  • Проверить, как сервер ведёт себя при заполнении диска на 80% — многие SSD теряют в производительности на заполненном томе.

Что бы мы рекомендовали для CI-инфраструктуры

Если задача — собрать предсказуемый CI-сервер, приоритеты обычно такие:

  1. Быстрый диск с низкой задержкой. CI-нагрузка генерирует огромное количество случайных мелких операций, и даже SATA SSD может не справиться, если параллельных job’ов много. NVMe здесь выигрывает за счёт более высокого IOPS и меньшей очереди.
  2. Достаточный объём RAM. Память должна покрывать пиковое потребление всех параллельных задач плюс файловый кэш, иначе swap убьёт производительность.
  3. CPU с запасом по ядрам. Количество ядер должно быть не меньше числа одновременно выполняемых тяжёлых job’ов, но без фанатизма — лишние ядра не помогут, если диск или память в дефиците.
  4. Стабильное охлаждение. Проверьте, что сервер не уходит в троттлинг при длительной работе. Для 1U-серверов это особенно критично.
  5. Нормальный сетевой канал. Если сборки тянут зависимости из внешних репозиториев, узкий канал станет бутылочным горлышком.
  6. Мониторинг и алерты. Без них вы рискуете узнать о проблемах только тогда, когда разработчики начнут жаловаться на медленные пайплайны.

Если нагрузка смешанная и включает контейнеры, тесты и сборку образов, дисковая подсистема и память становятся важнее «чистой» частоты процессора. Инвестиции в быстрый storage и достаточный объём RAM окупаются быстрее, чем погоня за гигагерцами.

Вывод

Тестировать серверное железо под CI и сборки нужно не по абстрактным цифрам, а по реальному сценарию работы. Самые частые узкие места — диск, память и температурная стабильность, а не только CPU. Если правильно построить методику, можно заранее понять, выдержит ли сервер параллельные pipeline’ы, как он поведёт себя после прогрева и где начнёт терять скорость. Наш опыт показывает, что грамотный выбор storage и достаточный запас RAM дают гораздо более предсказуемую и стабильную CI-среду, чем попытки выжать максимум из процессора в ущерб остальным компонентам.

FAQ

Можно ли оценить сервер под CI одним синтетическим бенчмарком?

Нет. Синтетика вроде Geekbench или PassMark покажет пиковую производительность CPU и памяти, но не отразит поведение дисковой подсистемы под смешанной нагрузкой, влияние кэша файловой системы и деградацию при длительной работе. Мы не раз видели, как сервер с отличными цифрами в бенчмарке проваливал реальную сборку из-за высокого I/O wait.

Что важнее для CI: CPU или SSD?

Для большинства сборочных сценариев SSD и подсистема хранения оказываются критичнее, чем кажется. CPU важен, но диск часто становится первым узким местом, особенно при параллельных job’ах и работе с тысячами мелких файлов. Если бюджет ограничен, лучше взять более быстрый NVMe-диск и чуть менее мощный процессор, чем наоборот.

Сколько прогонов нужно делать?

Минимум несколько повторов в одинаковых условиях. Один прогон легко искажает результат из-за кэша, температуры и фоновых задач. Мы рекомендуем делать не менее 5 прогонов и анализировать усреднённые значения, отбрасывая явные выбросы.

Почему сервер медленнее при длительной нагрузке?

Обычно причина в нагреве и троттлинге, заполнении кэша, росте очередей I/O или нехватке памяти. После 20–30 минут интенсивной работы многие системы снижают частоты, а диск может начать испытывать трудности со сбросом буферов, что ведёт к росту latency.

Что проверять перед покупкой в первую очередь?

Диск (реальные IOPS и latency на случайных операциях 4K), объём RAM с запасом под параллельные задачи, поведение под параллельной нагрузкой (хотя бы 2–4 одновременные сборки), температурную стабильность в течение часа и воспроизводимость результатов на реальном CI-пайплайне. Только после этого имеет смысл смотреть на частоты CPU.