Проблемы сборки Arch Linux под ARM-архитектуру и как мы их решали

Проблемы сборки 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:

DHCP DISCOVER от ARM64-EFI сервера.
DHCP DISCOVER от x86_64-EFI сервера.

Смотрим на часть PXE Client:Arch:00011, значения соответствуют RFC4578:

0Standard PC BIOS
632-bit x86 EFI
764-bit x86 EFI
964-bit x86 EFI (obsolete)
1032-bit ARM EFI
1164-bit ARM EFI

В зависимости от этого значения DHCP-сервер возвращает имя необходимого файла в DHCP Option 67 — Bootfile name. 

После iPXE проходит по одноразовой ссылке и получает сгенерированный загрузочный скрипт. В зависимости от содержимого скрипта сервер либо загружается с диска, либо запускает переустановку ОС, либо загружается в дистрибутив для восстановления.

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-архитектурой и обязательно расскажем об этом в следующих материалах.