Использование buildx со встроенным эмулятором
Коллега написал небольшую статью по его ресёрчу и реализации мультиарч сборки докер контейнеров, с его согласия публикую.
Как работает
BuildKit имеет встроенный механизм QEMU-эмуляции, который срабатывает только во время выполнения RUN-инструкций внутри сборки (docker buildx build/bake). Он не зависит от binfmt_misc ядра хоста — работает "из коробки" на любой ноде, где запущен сам buildkitd (включая Talos, где host binfmt_misc физически отключён на уровне ядра).
Плюсы
• Не требует --privileged доступа к хосту для регистрации эмуляторов.
• Не зависит от того, включён ли CONFIG_BINFMT_MISC в ядре ноды — работает даже там, где классический tonistiigi/binfmt --install падает с no such device.
• Не нужно ничего настраивать на уровне кластера/DaemonSet — эмуляция инкапсулирована внутри самого buildkit-контейнера.
• Достаточно для большинства задач сборки (компиляция, установка пакетов, копирование файлов).
Недостатки
• Эмуляция работает только на этапе сборки образа, но не распространяется на docker run уже собранного образа — классический Docker Engine (dockerd) для запуска контейнеров по-прежнему полагается на host binfmt_misc, которого здесь нет. Это значит:
— собранный arm64-образ нельзя протестировать через docker run/testinfra на amd64-хосте в этой же конфигурации;
— для тестирования нужен либо host binfmt_misc (см. ниже про binfmt), либо native-раннер нужной архитектуры.
• Не все сценарии внутри RUN гарантированно работают под эмуляцией — известны случаи, когда сложные interpreter-скрипты (например, ansible-galaxy, тяжёлые Python-операции) падают или зависают под QEMU. Каждую стадию Dockerfile стоит проверять индивидуально; если стадия не зависит от целевой архитектуры (генерирует платформонезависимые артефакты вроде сертификатов) — её стоит жёстко закрепить за --platform=linux/amd64, минуя эмуляцию вовсе.
• Скорость сборки под чужую архитектуру заметно ниже нативной — компиляция и тяжёлые операции внутри RUN идут через программную эмуляцию инструкций.
Использование buildx c binfmt
Как работает
Классический подход — регистрация QEMU-эмуляторов в ядре хоста через binfmt_misc (tonistiigi/binfmt --install all, требует --privileged). После регистрации любой процесс на хосте (включая docker run) может прозрачно исполнять бинарники чужой архитектуры.
Плюсы по сравнению с embedded-эмулятором:
• Эмуляция работает не только при сборке, но и при обычном docker run уже собранного образа — можно полноценно тестировать arm64-образ на amd64-машине через testinfra/любые runtime-проверки, не только на этапе сборки.
• Потенциально шире охват edge-кейсов — некоторые операции, падающие под embedded QEMU BuildKit, могут корректно отрабатывать через host-level binfmt_misc (или наоборот — зависит от конкретного случая, требует эмпирической проверки).
Недостатки
• Требует --privileged доступа к хосту — не везде возможно (Kubernetes executor с ограниченным securityContext, managed-кластеры с политиками безопасности).
• Не работает вообще на нодах, где CONFIG_BINFMT_MISC не включён в ядре — подтверждено для Talos Linux (разработчики явно отключили эту опцию по соображениям минимализма/безопасности). Правда, под раннеры можно подрубить экстеншн на конкретную ноду в Талосе, если есть навык/желание.
• В Kubernetes-кластере эмуляция регистрируется на уровне ноды, а не пода — требует либо DaemonSet (кластерный уровень, ставится инфраструктурной командой один раз), либо повторной регистрации в каждом CI job'е (менее эффективно, дублирование).
• Дополнительная точка отказа/обслуживания — нужно поддерживать актуальность DaemonSet, следить за версией tonistiigi/binfmt.
Текущий статус использования
Сейчас binfmt-подход применяется только для локального тестирования образов (task test, задача binfmt-setup) — там --privileged доступен разработчику по умолчанию, и это не создаёт зависимостей для CI-инфраструктуры. Распространение на CI-тестирование (в том числе через DaemonSet на уровне кластера) — предмет дальнейшей проработки.
Post #2749
954
- ❤ 2