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

Ядро Cloudflare — это централизованные центры обработки данных, где работают наши системы управления, биллинг и аналитика — в отличие от глобально распределенной периферии, обрабатывающей пользовательский трафик. Серверы ядра — это «голое железо», и когда при перезагрузке возникают проблемы, последствия могут нарастать лавинообразно.

Их последовательность загрузки orchestrated (управляется) UEFI — современным стандартом прошивки, который инициализирует оборудование и передает управление операционной системе. Небольшие особенности в этой передаче могут иметь огромные последствия.

После планового обновления прошивки некоторые из наших серверов ядра стали загружаться четыре часа вместо прежних нескольких минут. То, что должно было занять один день при развертывании на всем парке, растянулось на многодневные марафоны. Новые узлы проходили полный цикл ожидания тайм-аутов при самой первой загрузке. Окна технического обслуживания раздувались. Инженерные команды были вынуждены «нянчить» обновления, которые должны были выполняться без присмотра.

Эта проблема затронула весь парк Gen12 — почти 2000 устройств. Каждый неожиданный сбой во время обновления означал перезапуск всего цикла, а новые мощности простаивали в ожидании завершения «череды тайм-аутов».

Это история о том, как мы отследили причину до особенности прошивки и слишком усердного линейного поиска по всем доступным сетевым загрузочным интерфейсам, и как мы сократили общее время загрузки и обновления с часов обратно до минут. Попутно мы поделимся тем, что узнали о внутреннем устройстве UEFI, особенностях конкретных вендоров и стратегиях автоматизации, которые в конечном итоге решили проблему.

Интерфейс сетевой загрузки

Интерфейс сетевой загрузки позволяет серверу загружать операционную систему по сети, а не с локального хранилища. Это критически важно для централизованного, автоматизированного и масштабируемого управления запуском машин, особенно в глобально распределенном парке, обслуживающем разные рабочие нагрузки. Поскольку наши серверы находятся в разных средах и выполняют разные задачи, у них разные требования к конкретному интерфейсу сетевой загрузки. Двумя основными интерфейсами являются Preboot Execution Environment (PXE) и Unified Extensible Firmware Interface (UEFI) HTTPS boot.

В рамках нашего процесса перезагрузки серверы обычно используют PXE по разным причинам автоматизации. В Cloudflare мы используем открытое программное обеспечение iPXE — открытую прошивку для сетевой загрузки, поддерживающую современные протоколы, такие как HTTP и HTTPS. Это позволяет компьютерам загружать операционные системы напрямую с веб-серверов, облачных хранилищ или корпоративных сетей хранения данных со значительно более высокой скоростью и надежностью.

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

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

Линейный поиск

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

Чтобы исключить это, мы подключились к последовательной консоли на пострадавшей машине и в реальном времени наблюдали цикл загрузки. Самотестирование при включении (POST) прошло нормально, инициализация оборудования выглядела здоровой. Но затем, вместо того чтобы быстро перейти к этапу сетевой загрузки и загрузить образ ОС, сервер просто ждал. И ждал.

Вывод консоли рассказал историю: система пыталась выполнить сетевую загрузку через IPv4 HTTPS, получала тайм-аут через несколько минут, затем пробовала IPv4 iPXE, снова тайм-аут, затем повторяла обе попытки — и все это, прежде чем наконец добраться до интерфейса IPv6 HTTPS, который действительно срабатывал.

Каждая неудачная попытка сетевой загрузки сжигала около пяти минут ожидания ответа по тайм-ауту. Поскольку до правильного интерфейса накапливалось четыре попытки, один цикл загрузки тратил впустую около двадцати минут. Для обычной перезагрузки это больно. Для автоматизации обновления прошивки, требующей нескольких последовательных перезагрузок (по одной на компонент), эти двадцатиминутные штрафы накапливались почти до четырех часов простоя в ожидании на один сервер.

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

Никаких игр в поиск: укажи мой загрузочный интерфейс

После трассировки последовательности загрузки и изоляции паттерна тайм-аутов первопричина стала ясна: серверы вслепую перебирали один за другим все доступные интерфейсы сетевой загрузки, ожидая сбоя каждого, прежде чем перейти к следующему. Исправление заключалось в том, чтобы полностью исключить угадывание — указать правильный загрузочный интерфейс заранее, чтобы система никогда не тратила время на интерфейсы, которые никогда не ответят.

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

Наш рабочий процесс автоматизации загрузки

Наш поток автоматизации загрузки состоит из трех основных этапов: инициализация прошивки, предзагрузка и запуск ядра. После включения питания прошивка UEFI выполняет некоторую инициализацию оборудования и периферии, после чего следует среда предзагрузки PXE. Предзагрузка настраивает сетевую карту и выполняет небольшую программу, называемую загрузчиком, которая запускает ядро. Именно на этом этапе PXE происходит зондирование различных сетевых интерфейсов для поиска правильного. При первой загрузке в наш рабочий процесс автоматизации загрузки включены обновления прошивки.

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

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

Перестроив последовательность автоматизации так, чтобы порядок интерфейсов сетевой загрузки объявлялся на раннем этапе предзагрузочной стадии PXE для каждого аппаратного обеспечения/варианта использования, мы смогли сократить общее время примерно на час, поскольку процессу загрузки больше не нужно было тратить 20 минут на зондирование для каждого обновления прошивки.

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

Попытка объявить порядок интерфейсов сетевой загрузки ввела два конкретных ограничения:

  1. Поддержка устаревшего оборудования (Legacy Support): Упорядочивание загрузки не поддерживается в старых версиях UEFI.

  2. Постоянство (Persistence): Настройки конфигурации часто сбрасываются после обновления прошивки UEFI.

Для обработки этих граничных случаев мы реализовали этап проверки состояния. Теперь автоматизация прошивки проверяет конфигурацию после изменения: если обнаружено, что настройки были изменены, она повторно применяет конфигурацию и инициирует перезагрузку.

Хотя первая загрузка может занять немного больше времени, это изменение кардинально сокращает время, необходимое для всех последующих запусков, — с примерно 20 минут до менее чем минуты на каждую последующую загрузку.

Установка порядка загрузки отключена вендором

Внутренняя структура данных настроек сетевой загрузки — это структура данных EFI_IFR_REF3, которая загружалась лениво (lazy loading), то есть данные не создавались до тех пор, пока к ним не обращались явно через GUI-обратный вызов:

typedef struct _EFI_IFR_REF3 {
  EFI_IFR_OP_HEADER          Header;
  EFI_IFR_QUESTION_HEADER    Question;
  EFI_QUESTION_ID            QuestionId;
  EFI_GUID                   FormSetId;
} EFI_IFR_REF3;

Хотя это стандартная отраслевая практика для ускорения времени загрузки BIOS, она делала «Интерфейс сетевой загрузки» невидимым для наших программных сканирований. Поскольку структура еще не была «загружена», наша автоматизация не могла обнаружить приоритеты.

Мы работали с нашими вендорами, чтобы включить определенные токены внутри фиксированного «Модуля порядка загрузки» (Boot Order Module). Это заставляет обнаруживать интерфейс сетевой загрузки во время последовательности загрузки без необходимости ручного взаимодействия с GUI.

UEFI от наших производителей оборудования имел неизменяемую настройку Force Priority Httpv4 Httpv6 Pxev4 Pxev6, которая мешала нам изменить порядок загрузки.

Это потребовало новой версии BIOS от нашего вендора и сеанса отладки при установке порядка загрузки.

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

В зависимости от производителя сетевой карты (NIC) строки различались, что вызывало несоответствие при настройке порядка загрузки через iPXE.

Примеры:

UEFI: HTTPS IPv4 Ethernet Network Adapter XXX-XXX-Y for OCP 3.0 P1 UEFI: HTTPS IPv4 Network Adapter - 50:00:E6:8F:4F:32 P1

Чтобы обойти эту проблему, нам пришлось реализовать дополнительную функцию в инструменте CfHIIConfig_App, позволяющую устанавливать конфигурацию без полной строки:

.*HTTP.*IPv4.*P1

Затем конфигурация сопоставляется с принятыми строками конфигурации и выбирает правильный порядок загрузки. В настоящее время мы работаем с нашими поставщиками UEFI над стандартизацией строк сетевых интерфейсов, чтобы использовать только соответствующую информацию (например, протокол, тип передачи, номер порта и индекс физического слота) и отказаться от таких деталей продукта, как MAC-адрес. При необходимости детали продукта можно прочитать из встроенной информации о важных деталях продукта сетевой карты. Таким образом мы устраняем как дрейф конфигурации, так и использование подстановочных знаков.

Невозможность проверки конфигурации через iPXE 

Поскольку iPXE читает эту переменную как HEX, он считывал строковый вывод как шестнадцатеричный. Чтобы проверить, была ли изменена настройка сетевой загрузки, и сократить время загрузки (чтобы не выводить переменные перед их установкой), мы реализовали логический флаг uefi-same-hex, указывающий, изменилась ли конфигурация.

Это позволило нам выполнять одну команду set вместо того, чтобы сначала выполнять show для сравнения, а затем set, если конфигурация не была в нужном состоянии.

Это позволило нам выполнять одну команду set вместо того, чтобы сначала выполнять show для сравнения, а затем set, если конфигурация не была в нужном состоянии.

# construct path to read the update variable
set buffer-var-guid 91468514-75bc-4bb5-8f33-91efff9e9b1f
set var-upd-path efivar/CfHIIVarUpd-${buffer-var-guid}

#Run the config change command
imgexec <signed CF UEFI configuration App> set ${uefi-setting}=${uefi-value}

#Compare the update variable with the expected value if it has changed.
#If it has changed, set the local variable to reboot the system
iseq ${uefi-same-hex} ${${var-upd-path}} || set has-changed ${uefi-diff-hex}

Результат: более динамичная система

Устранив неопределенность из нашей последовательности сетевой загрузки, мы превратили четырехчасовую процедуру обратно в 3-минутный процесс. В результате получается система, в которой изменения динамичны и не требуется ручного взаимодействия с BIOS. Один образ прошивки BIOS обслуживает все SKU, обновления конфигурации развертываются в масштабе через наш существующий конвейер релизов, и весь рабочий процесс работает из iPXE.

Метрика

До изменения порядка

После изменения порядка

Автоматизация обновления прошивки

Почти 4 часа

3 минуты

Последующая однократная загрузка

Около 20 минут

Менее минуты

Ничего из этого не было бы возможным без глубокого изучения внутренностей UEFI, тесного сотрудничества с нашими OEM-поставщиками для раскрытия таких возможностей, как программное управление порядком загрузки, и использования инструментов с открытым исходным кодом, таких как iPXE, для создания масштабируемой автоматизации.