Проблемы сборки Arch Linux под ARM-архитектуру и как мы их решали
В декабре мы пополнили линейку конфигом Ampere Altra Max M128-30 с ARM-процессором внутри. Рассказываем, с какими проблемами мы столкнулись и какие решения нашли.
В декабре мы пополнили линейку конфигом Ampere Altra Max M128-30 (3 ГГц, 128 ядер) с ARM-процессором внутри. Перед введением в «эксплуатацию» нужно было разобраться с сетевой загрузкой, модифицировать служебный дистрибутив, адаптировать существующие процессы автоустановки ПО под новую архитектуру. В тексте расскажу, с какими проблемами мы столкнулись и какие решения нашли.
Вы можете подробнее почитать о том, зачем мы добавили ARM-серверы в линейку, а еще посмотреть на результаты бенчмарков тестовой платформы на Ampere Altra Q80-30 в классной статье от моего коллеги.
Если коротко, они хорошо показывают себя в многопоточных задачах и при этом намного энергоэффективнее процессоров от AMD и Intel. Основные задачи, которые решали клиенты во время тестов Ampere Altra, — работа с СУБД, высоконагруженные веб-сервисы, разработка мобильных приложений и embedded-разработка.
На первый взгляд, новые серверы мало чем отличаются от решений на архитектуре x86_64. Тем не менее, для их полноценной поддержки нужно было решить несколько проблем:
- разобраться с сетевой загрузкой,
- модифицировать служебный дистрибутив,
- адаптировать существующие процессы автоустановки ОС под новую архитектуру.
Сетевая загрузка, или часть, в которой все идет по плану
Это была самая простая часть процесса, где все работало именно так, как ожидалось.
Автоматизируем сборку и деплой PXE-клиента
Мы используем модифицированный iPXE (небольшие изменения во время сборки, чтобы DHCP-сервер мог опознать PXE-клиент), который уже поддерживает arm64. Единственной проблемой стала сборка бинарного файла для arm64 на x86_64 GitLab-worker с помощью кросс-компиляции. Для сборки используется стандартный Docker-контейнер linux/amd64 ubuntu:20.04, Dockerfile:
FROM ubuntu:20.04
LABEL name="ipxe-builder-aarch64" \
description="Image to build ipxe binary files" \
version="1.0.1"
# Install all necessary packages for compiling the iPXE binary files
RUN apt update && apt install -y git
gcc gcc-aarch64-linux-gnu make curl
Сборка выполняется из мастера iPXE GitHub следующей командой:
make CROSS=aarch64-linux-gnu- ARCH=arm64 bin-aarch64-efi/ipxe.efi EMBED=universal.ipxe
, где universal.ipxe — встраиваемый скрипт, который обрабатывает проблемы с сетью и работает со ссылками на загрузочные скрипты, которые генерирует наш бэкенд. На выходе получаем готовый arm64-binary, который деплоится на наши TFTP-серверы.
Убеждаемся, что DHCP-сервер отдаст ссылку на нужную сборку iPXE, в зависимости от архитектуры
Мы определяем архитектуру и/или режим загрузки (Legacy/UEFI) сервера с помощью опции 60 DHCP — Vendor class identifier:
Смотрим на часть PXE Client:Arch:00011, значения соответствуют RFC4578:
0 | Standard PC BIOS |
6 | 32-bit x86 EFI |
7 | 64-bit x86 EFI |
9 | 64-bit x86 EFI (obsolete) |
10 | 32-bit ARM EFI |
11 | 64-bit ARM EFI |
В зависимости от этого значения DHCP-сервер возвращает имя необходимого файла в DHCP Option 67 — Bootfile name.
После iPXE проходит по одноразовой ссылке и получает сгенерированный загрузочный скрипт. В зависимости от содержимого скрипта сервер либо загружается с диска, либо запускает переустановку ОС, либо загружается в дистрибутив для восстановления.
Проверьте ARM-процессор в своих задачах
Сервер с 128-ядерным процессором Ampere® Altra® Max M128-30 и 256 ГБ DDR4 памяти уже в Selectel.
Arch Linux для ARM, или грустный рассказ о неверных эстимейтах
Поскольку мы сдаем выделенные серверы в аренду и стараемся делать это очень быстро, мы автоматизируем многие процессы. Например, запуск операций зачистки дисков при завершении аренды сервера, проведение промежуточных тестов при завершении аренды и приемочных тестов после сборки сервера. Для всего этого мы использует кастомную сборку Arch Linux — внутри компании мы называем этот дистрибутив rescue. Помимо прочего, он используется для диагностики серверов и аварийных работ с ними.
Нам нужно было состыковать Arch Linux с ARM-процессором. Изначально никаких подводных камней не ожидалось: есть проект Arch Linux for ARM, на GitHub есть проект archiso-aarch64 – mkarchiso, адаптированный под aarch64. Казалось бы, собираем все вместе, заменяем архитектуру в существующих пайплайнах по сборке и деплою образа — готово. Мы оценили задачу примерно в 2 недели. Именно в этот момент все пошло не так.
Разумеется, такая потрясающая подготовка не могла не выйти боком: мы столкнулись сразу с пачкой проблем.
Проблема со сборкой артефактов для загрузки сервера в режиме UEFI
Разработчик archiso-aarch64 просто продублировал большинство классов, поменяв значения переменных на необходимые для архитектуры aarch64. Нам пришлось немного модифицировать сборочные скрипты archiso-aarch64. Модификация только одна: был изменен класс, который используется при сборке образа под legacy-режим загрузки. Дело в том, что тестируемые нами серверы с процессорами ARM работают только в UEFI, но при этом в оригинальных скриптах артефакты загрузки (vmlinuz и initramfs) собираются только в legacy-части скриптов.
В итоге в часть, ответственную за сборку UEFI-артефактов, был импортирован класс, ответственный за упаковку ядра и initramfs. В строку 622 мы добавили строку:
```_run_once _make_boot_on_iso9660```
Это позволило нам получить необходимые артефакты для сетевой загрузки.
Не поддерживалась кросс-архитектурная сборка образа
При сборке образа используется chroot, который ломается во время «добавления» пакетов «внутрь» образа.
Тут мы решили попробовать проект archboot, который обещал поддержку кросс-архитектурной сборки, но процесс интеграции наших инструментов сильно затягивался. Кроме того, в образе слишком много лишнего — например, графическая оболочка, доступ через VNC, огромная куча хуков mkinitcpio, с которой было решительно некогда разбираться. Из-за этого вернулись к использованию archiso-aarch64.
Мы пробовали собирать образ в эмулированной через QEMU виртуальной машине, но процесс занимал больше часа. Проблему со сборкой решили заведением GitLab-runners на Raspberry Pi 4. Наши раннеры для сборки x86-образа запущены на хостах облачной платформы Selectel, в то время как раннеры для этого проекта запускаются на физических RPi4, которые находятся в служебной стойке в одном из наших дата-центров.
Ранее мы писали, как внедряли серверы на «малинках» в дата-центры. Подробно описали каждый этап. Все тексты — в одной подборке.
В качестве executor в GitLab-runner используется Docker. Это сильно отличается от сборки x86-образа, где используется libvirt и QEMU. Дело в том, что последняя работает пока крайне медленно в режиме эмуляции aarch64, да и использование более простого в настройке исполнителя приветствуется. Dockerfile:
FROM agners/archlinuxarm-arm64v8
WORKDIR /archlinux
RUN mkdir -p /archlinux/rootfs
COPY pacstrap-docker /archlinux/
RUN ./pacstrap-docker /archlinux/rootfs \
bash sed gzip pacman
# Remove current pacman database, likely outdated very soon
RUN rm rootfs/var/lib/pacman/sync/*
FROM scratch
COPY --from=0 /archlinux/rootfs/ /
COPY rootfs/ /
ENV LANG=en_US.UTF-8
RUN locale-gen
RUN pacman-key --init
# Currently command crashes. Use key below for package sign
#RUN pacman-key --populate archlinuxarm
RUN pacman-key --recv-keys 68B3537F39A313B3E574D06777193F152BDBE6A6 && pacman-key --lsign-key 68B3537F39A313B3E574D06777193F152BDBE6A6
# Original image outdated, upgrade needed
RUN pacman -Syyu --noconfirm
# mkarchiso dependencies
RUN pacman -Sy --noconfirm arch-install-scripts awk dosfstools e2fsprogs erofs-utils findutils gzip libarchive libisoburn mtools openssl sed squashfs-tools
CMD ["/usr/bin/bash"]
Сборка выполняется стандартной командой ./mkarchiso -v -o $OUTDIR -w $WORKDIR -m $MODE $PROFILE
Недостаток готовых бинарных пакетов в репозиториях
Наш служебный образ достаточно сильно кастомизирован: начиная от вшитых ключей авторизации моих коллег — системных инженеров и сотрудников техподдержки, заканчивая кастомными загрузочными хуками по настройке сети и собственными скриптами и утилитами.
Первым делом нам пришлось сильно переработать список пакетов для установки в дистрибутив. Огромное количество ПО просто не существует в репозиториях, да и сам репозиторий Arch Linux, хоть и поддерживается некоторыми мейнтейнерами, — является скорее неофициальным.
Достаточно много пакетов — stress-ng, утилиты для прошивки материнских плат, SSD и NIC — мы собираем сами. Некоторые из них поставляются в виде бинарных файлов только под архитектуру x86_64. Из-за этого нам пришлось переписать скрипт автотестов, а также временно отказаться от некоторых используемых фич для архитектуры arm64. Например, автоматической прошивки материнских плат и периферии, а также автоматической настройки BMC .
Коротко об автотестах при передаче сервера клиенту
После сборки платформы, а также при передаче сервера новому клиенту мы проводим ряд автотестов.
После сборки сервера он должен пройти тест комплектующих. Проверяем, что CPU, RAM, GPU и блочные устройства корректно определяются, их характеристики соответствуют заявленным. Также проверяем SMART-параметры блочных устройств и убеждаемся, что их скорость чтения соответствует классу устройства. Для проверки SMART используем самописную утилиту на Python в сочетании с базой данных граничных значений. Выход за пределы значений вызывает автоматический вывод сервера из эксплуатации и его передачу на диагностику.
Дальше стресс-тест. В течение двух часов нагружаем CPU, RAM, GPU и смотрим, чтобы не было ошибок и троттлинга. Для стресс-тестов мы используем утилиту stress-ng.
Завершают цикл приемочные тесты (перед передачей сервера новому клиенту). Здесь также проверяем комплектацию сервера и SMART-параметры дисков.
Еще немного граблей, на которые мы наступили
Отсутствие логов
Эта проблема сильно осложнила дебаг rescue и тесты автоустановки. Вместо логов на экране отображалась очень симпатичная заставка с пингвинами — вроде такой, только пингвинов побольше:
Параметры ядра вроде debug и loglevel=7 не помогали. Через какое-то время мы догадались, в чем проблема, и решили ее добавлением параметра ядра console=tty0.
Проблема с тестами автоустановки Ubuntu 22.04
Вторая проблема была связана с тестами автоустановки Ubuntu 22.04. Нужно было быстро проверить, что сетевая установка будет работать. Но, поскольку Ubuntu перешел с использования debian-installer к subiquity, legacy-installer больше недоступен.
Мы попытались обмануть систему, вытащив необходимые файлы из установочного образа, и встретились с интересной проблемой — iPXE зависало при попытке загрузить initramfs. Оказалось, что initrd сжат с использованием Zstandart, который iPXE, судя по всему, не поддерживает. Примечательно, что версия для x86_64 не использует Zstandart.
Вывод file для initrd subiquity Ubuntu 22.04 amd64:
initrd: ASCII cpio archive (SVR4 with no CRC)
Вывод file для initrd subiquity Ubuntu 22.04 arm64:
initrd: Zstandard compressed data (v0.8+), Dictionary ID: None
Позже мы выяснили несколько вещей:
- установщику в любом случае необходимо выкачивать весь ISO для корректной работы,
- файлы preseed больше не поддерживаются, и необходимо использовать cloud-init/autoinstall.
В общем, без переписывания шаблонов не обойтись. Сейчас мы как раз модернизируем процесс автоустановки ОС, чтобы клиенты могли получить готовый к работе сервер с предустановленной Ubuntu 22.04 в течение нескольких минут.
Плавающие проблемы с дисками
Эти проблемы всплывали во время динамической разметки дисков, их объединения в программные рейды и добавления в LVM в установщике Ubuntu 20.04. В некоторых случаях установщик пытался создать лишние рейды, либо не мог очистить метаданные LVM. Мы не продолжили анализ проблемы, так как она будет решена вместе с модернизацией системы автоустановки.
Заключение
На первый взгляд, работа с серверами на архитектуре arm64 практически не отличается от x86_64-аналогов. Но чем глубже погружаешься, тем больше открывается нюансов.
Задача была интересна еще и тем, что из-за небольшого количества информации все решения принимались через практические эксперименты. Мы продолжаем работу над серверами с ARM-архитектурой и обязательно расскажем об этом в следующих материалах.