Как мы сократили время загрузки серверов с нескольких часов до нескольких минут
Основная инфраструктура (core) Cloudflare — это централизованные дата-центры, в которых работают управляющая плоскость (control plane), биллинг и аналитика. В отличие от глобально распределённой периферии (edge), обрабатывающей пользовательский трафик, core-серверы представляют собой bare-metal машины, и любые проблемы при перезагрузке способны быстро вызвать цепную реакцию сбоев.
Последовательностью загрузки управляет UEFI — современный стандарт прошивки, который инициализирует оборудование и передаёт управление операционной системе. Даже незначительные особенности этой передачи могут иметь серьёзные последствия.
После очередного обновления прошивки часть наших core-серверов стала возвращаться в строй за четыре часа вместо привычных нескольких минут. То, что должно было занять один день при обновлении всего парка, растянулось на несколько дней. Новые узлы с первой же загрузки проходили через весь марафон таймаутов. Окна технического обслуживания разрастались, и инженерам приходилось вручную сопровождать обновления, которые в норме выполняются без какого-либо участия людей.
Проблема обнаружилась при вводе в эксплуатацию узлов, долгое время находившихся в выключенном состоянии. Прошивка на них устарела и требовала нескольких последовательных обновлений. В сочетании с недавними изменениями в протоколах загрузки, используемых серверами в ряде наших точек присутствия, время загрузки на затронутых узлах стало совершенно неприемлемым.
Эта статья — о том, как мы докопались до причины: особенности прошивки в сочетании с линейным перебором всех доступных сетевых интерфейсов загрузки. И о том, как мы сократили суммарное время загрузки и обновления с нескольких часов до нескольких минут. По ходу дела мы расскажем о том, что узнали о внутреннем устройстве UEFI, особенностях прошивок конкретных вендоров и стратегиях автоматизации, которые в итоге решили задачу.
Сетевая загрузка и её роль в нашей инфраструктуре
Сетевой интерфейс загрузки (network boot interface) позволяет серверу загружать операционную систему по сети, а не с локального накопителя. Это критически важно для централизованного, автоматизированного и масштабируемого управления запуском машин — особенно в глобально распределённом парке, обслуживающем разные типы нагрузок. Поскольку наши серверы расположены в разных средах и выполняют разные задачи, у них разные требования к конкретному сетевому интерфейсу загрузки. Два основных варианта — это Preboot Execution Environment (PXE) и HTTPS-загрузка через UEFI.
В рамках нашего процесса перезагрузки серверы, как правило, проходят через PXE по ряду причин, связанных с автоматизацией. В Cloudflare мы используем iPXE — программу сетевой загрузки с открытым исходным кодом, поддерживающую современные протоколы HTTP и HTTPS. Она позволяет компьютерам загружать операционные системы напрямую с веб-серверов, из облака или корпоративных сетей хранения данных — быстрее и надёжнее традиционных решений.
Для организаций iPXE превращает процесс загрузки в программируемый рабочий процесс. Расширенные возможности скриптования позволяют ИТ-командам автоматизировать сложные сценарии развёртывания: например, подготавливать серверы в зависимости от конкретной аппаратной конфигурации или управлять защищёнными бездисковыми рабочими станциями.
Часть нашего оборудования поддерживает UEFI HTTPS-загрузку: прошивка материнской платы самостоятельно скачивает файлы операционной системы по защищённому каналу.
С чего всё началось
Наша история начинается с того самого злополучного обновления прошивки. Вскоре после его применения во внутренних каналах появились первые сообщения: серверы не возвращаются в строй. На мониторинговых дашбордах было видно, что машины застревают в состоянии до загрузки ОС значительно дольше ожидаемого. Первоначальная версия — регрессия в прошивке: возможно, само обновление привнесло баг, вешающий процесс загрузки.
Чтобы это проверить, мы подключились к серийной консоли одного из затронутых серверов и в реальном времени понаблюдали за циклом загрузки. Самотестирование при включении (Power On Self Test, POST) завершилось в штатном режиме, инициализация оборудования выглядела нормально. Но затем, вместо того чтобы быстро перейти к этапу сетевой загрузки и скачать образ ОС, сервер просто ждал. И ждал.
Вывод консоли говорил сам за себя: система пыталась выполнить HTTPS-загрузку по IPv4, уходила в таймаут спустя несколько минут, затем пробовала IPv4 iPXE — снова таймаут, затем повторяла обе попытки, и лишь потом добиралась до интерфейса HTTPS-загрузки по IPv6, который в итоге срабатывал.
Каждая неудачная попытка сетевой загрузки сжигала около пяти минут ожидания ответа таймаута. При четырёх таких попытках перед тем, как система наконец достигала нужного интерфейса, один цикл загрузки терял около двадцати минут впустую. Для обычной перезагрузки это болезненно. Но для автоматизации обновления прошивки, требующей нескольких последовательных перезагрузок — по одной на каждый компонент — эти двадцатиминутные штрафы суммировались почти в четыре часа простоя на каждый сервер.
Корневая причина и направление решения
Отследив последовательность загрузки и выделив паттерн таймаутов, мы чётко установили причину: серверы вслепую перебирали все доступные сетевые интерфейсы загрузки по одному, ожидая отказа каждого из них перед переходом к следующему. Решение — полностью устранить этот перебор: заранее объявить правильный интерфейс загрузки, чтобы система никогда не тратила время на заведомо неработающие варианты.
Однако реализовать это оказалось непросто. Как мы объясним далее, мы столкнулись с несколькими препятствиями: порядком нашего рабочего процесса автоматизации, настройкой, которую мы не могли изменить, и различающимися форматами строк у разных вендоров сетевых карт.
Архитектура нашего процесса загрузки
Процесс автоматизации загрузки состоит из трёх широких этапов: инициализация прошивки, pre-boot (предзагрузка) и запуск ядра. После подачи питания прошивка UEFI выполняет инициализацию оборудования и периферии, после чего запускается среда предзагрузки PXE. На этапе предзагрузки настраивается сетевая карта и выполняется небольшая программа — загрузчик (bootloader), который запускает ядро. Именно на этапе PXE и происходит перебор сетевых интерфейсов в поисках нужного. При первой загрузке в рабочий процесс автоматизации включены обновления прошивки.
Поскольку каждое обновление прошивки требует перезагрузки (а значит, и всей последовательности попыток сетевой загрузки), именно так мы и пришли к ситуации, когда суммарное время загрузки приближалось к четырём часам.
Переструктурировав последовательность автоматизации так, чтобы порядок сетевых интерфейсов загрузки объявлялся заранее на этапе PXE — с учётом конкретного оборудования и сценария использования, — мы смогли сократить суммарное время примерно на час: загрузчику больше не нужно тратить по 20 минут на зондирование при каждом обновлении прошивки.
Ограничения при задании порядка загрузки
Попытка явно задать порядок сетевых интерфейсов загрузки выявила два конкретных ограничения:
-
Поддержка устаревших версий: управление порядком загрузки недоступно в старых версиях UEFI.
-
Сохранение настроек: конфигурационные параметры нередко сбрасываются после обновления прошивки 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.
Неизменяемые настройки прошивки
В прошивке от наших производителей оборудования была неизменяемая настройка 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-адрес, следует исключить. При необходимости эти сведения можно получить из встроенных данных о жизненно важных характеристиках продукта (vital product data) сетевой карты. Это позволит избавиться и от рассинхронизации конфигураций, и от необходимости использовать wildcards.
Флаг для отслеживания изменений конфигурации
Поскольку iPXE читает соответствующую переменную в шестнадцатеричном формате, строковый вывод воспринимался им как HEX. Чтобы проверять, изменилась ли настройка сетевой загрузки, и сокращать время загрузки (не выводя переменные перед их установкой), мы реализовали булев флаг uefi-same-hex, указывающий, изменилась ли конфигурация.
Это позволило выполнять одну команду 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}
Результат
Убрав угадывание из последовательности сетевой загрузки, мы превратили четырёхчасовую пытку в трёхминутный процесс. Теперь система полностью динамична: никакого ручного взаимодействия с BIOS не требуется. Один образ BIOS обслуживает все конфигурации оборудования (SKU), обновления конфигурации разворачиваются в масштабе через существующий пайплайн выпусков, а весь рабочий процесс управляется из iPXE.
| До | После |
|---|---|
Почти 4 часа (полное время обновления) |
3 минуты |
Около 20 минут (время каждой последующей загрузки) |
Менее одной минуты |
Ничего из этого не было бы возможным без глубокого погружения во внутреннее устройство UEFI, тесного взаимодействия с OEM-вендорами для открытия таких возможностей, как программное управление порядком загрузки, и использования инструментов с открытым кодом — в частности, iPXE — для построения масштабируемой автоматизации.
Команда OpenBMC в Cloudflare продолжает каждый день изучать, экспериментировать и оптимизировать процесс загрузки в нашем core-парке. Если вы управляете bare-metal инфраструктурой и сталкиваетесь с медленной загрузкой серверов — надеемся, эта статья даёт вам практическую основу для выявления и устранения лишних задержек в вашей последовательности сетевой загрузки. Тем, кто хочет узнать больше об iPXE и автоматизации сетевой загрузки, рекомендуем заглянуть сюда!