Новинка Виртуальный VDS сервер в Нидерландах от 390р
Выберите продукт

Docker Buildx для multi-arch: amd64/arm64, QEMU и registry-кэш

Разбираем, как собирать и публиковать multi-arch Docker-образы под amd64 и arm64 с помощью Buildx и QEMU. Покажу настройку builder’а, кэш в registry, практики для Go/Node/Python, проверку манифестов, распределённые билдеры и частые ошибки, из‑за которых ломаются кросс‑сборки на CI.
Docker Buildx для multi-arch: amd64/arm64, QEMU и registry-кэш

Multi-arch образы стали стандартом де-факто. Пользователи ждут, что ваш контейнер запустится и на классическом x86_64 (amd64), и на ARM-платформах (arm64/v8) — от хостингов до IoT и облачных ARM‑серверов. С docker buildx и BuildKit это достижимо без боли: мы сможем собирать универсальные образы локально и на CI, использовать кэш в registry, проверять итоговый манифест и оптимизировать скорость.

Зачем вам multi-arch и когда он критичен

Даже если у вас преимущественно amd64-инфраструктура, мир быстро смещается в сторону ARM. Локальные разработчики на Apple Silicon, ARM‑серверы в облаках, edge‑устройства — это уже не ниша. Универсальный образ:

  • Если запускается на любом железе, снижает поддержку разных тегов.
  • Упрощает CD: один артефакт для всех сред.
  • Сокращает риск «всё работает у меня на x86, но падает на arm».

При этом у multi-arch есть нюансы: сборка может требовать эмуляции (QEMU), а значит — медленнее и капризнее. Поэтому важно понять, когда использовать QEMU, а когда подключать нативные билд‑ноды.

Buildx и BuildKit: что происходит под капотом

docker buildx — надстройка над BuildKit, умеет:

  • Собирать для нескольких платформ с флагом --platform.
  • Публиковать манифест‑листы (multi-arch) в registry.
  • Кэшировать слои локально, в inline‑формате и в registry.
  • Распределять сборки по нескольким билдер‑нодам (в том числе удалённым).

Включите BuildKit по умолчанию, если он не активен:

export DOCKER_BUILDKIT=1
export BUILDKIT_PROGRESS=plain

Подготовка хоста: binfmt_misc и QEMU

Чтобы на amd64‑сервере собирать образы для arm64, BuildKit использует эмуляцию через QEMU. Установить интерпретаторы проще всего контейнером tonistiigi/binfmt:

docker run --privileged --rm tonistiigi/binfmt --install all

Проверить, что эмуляторы появились:

ls -1 /proc/sys/fs/binfmt_misc | grep qemu

Если видите что-то вроде qemu-aarch64, эмуляция для arm64 готова. Важно: QEMU заметно медленнее реального ARM‑ядра, особенно на этапе компиляции. Для продакшн‑пайплайнов подумайте о нативном ARM‑билдере.

Создаём builder для buildx

Рекомендуется использовать драйвер docker-container — он даёт изолированный BuildKit и удобное управление:

docker buildx create --name multiarch --driver docker-container --use
docker buildx inspect --bootstrap
docker buildx ls

--bootstrap подтянет нужные бинарники BuildKit. С этого момента buildx готов к сборке multi-arch.

Схема сборки multi-arch с Buildx: amd64/arm64, QEMU и registry-кэш

Базовый Dockerfile с учётом платформ

BuildKit прокидывает полезные переменные: TARGETPLATFORM, BUILDPLATFORM, TARGETOS, TARGETARCH, TARGETVARIANT. Используйте их, чтобы подстраивать шаги:

# syntax=docker/dockerfile:1.7
FROM --platform=$BUILDPLATFORM golang:1.22-bookworm AS builder
ARG TARGETOS
ARG TARGETARCH
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
ENV CGO_ENABLED=0
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH go build -trimpath -ldflags="-s -w" -o /out/app ./cmd/app

FROM gcr.io/distroless/base-debian12:nonroot
COPY --from=builder /out/app /usr/local/bin/app
USER nonroot
ENTRYPOINT ["/usr/local/bin/app"]

Что важно:

  • Старайтесь делать сборку статичной (CGO_ENABLED=0), чтобы избежать проблем с glibc/musl.
  • Избегайте Alpine, если у вас есть закрытые зависимости на glibc — иначе получите «работает на Debian, падает на Alpine».

Node.js и Python: архитектурные подводные камни

На Node.js нативные модули (node-gyp, prebuild) завязаны на архитектуру. Чтобы корректно пересобрать под целевую платформу, используйте стадии сборки и избегайте переносить node_modules между архитектурами.

# syntax=docker/dockerfile:1.7
FROM --platform=$BUILDPLATFORM node:20-bookworm AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-bookworm-slim
WORKDIR /app
COPY --from=deps /app/dist ./dist
COPY package.json ./
RUN npm ci --omit=dev
CMD ["node", "dist/index.js"]

Для Python заранее продумывайте колёса (wheels). Если требуются бинарные зависимости, убедитесь, что они собираются под целевую архитектуру. Частый кейс — компиляция cryptography, psycopg и т.п. при отсутствии подходящих manylinux wheels для arm64.

Сборка multi-arch и публикация в registry

Самый короткий путь собрать и запушить манифест‑лист:

docker buildx build --platform linux/amd64,linux/arm64 -t registry.example.com/team/app:1.2.3 --push .

Для локального теста одного архитектурного образа вместо --push используйте --load (оно поддерживает только одну платформу за раз):

docker buildx build --platform linux/arm64 -t app:test --load .

Проверка манифеста

Убедиться, что в реестр ушёл именно multi-arch:

docker buildx imagetools inspect registry.example.com/team/app:1.2.3

В выводе увидите список платформ и соответствующие дайджесты. Если видите только один манифест, значит сборка не объединилась в мультиархив.

Кэширование: локально, inline и в registry

Кэш существенно ускоряет повторные сборки, особенно при QEMU. Варианты экспорта:

  • local: на диск билдера.
  • inline: в метаданные слоя образа.
  • registry: отдельные кэш‑артефакты в реестре.

Практичный пресет: кэш в registry c максимальной детальностью и OCI‑медиатипами:

docker buildx build --platform linux/amd64,linux/arm64 -t registry.example.com/team/app:1.2.3 --push --cache-to type=registry,ref=registry.example.com/team/app:cache,mode=max,oci-mediatypes=true --cache-from type=registry,ref=registry.example.com/team/app:cache .

Inline‑кэш удобен для одноузловых сценариев, но registry‑кэш легче шарить между CI‑агентами и разработчиками. Если вам интересны кэш‑маунты и ускорение зависимостей на этапе RUN, загляните в материал как работать с BuildKit cache mounts.

CI со связкой билд-нод amd64 и arm64 для нативных multi-arch сборок

Где QEMU хорош, а где — лучше без него

QEMU позволяет «тащить» сборку arm64 на amd64‑хосте, но помните:

  • Компиляция C/C++ и npm‑модулей с нативными зависимостями под эмуляцией существенно медленнее.
  • Временные аномалии в тестах под эмуляцией не редкость.
  • Долгие пайплайны увеличивают риск таймаутов и затрат на CI.

Золотой стандарт: построить распределённый builder из двух нод — amd64 и arm64. Buildx сам будет гнать соответствующие задачи на нужный узел:

docker buildx create --name fleet --driver docker-container --use
# Добавляем ARM-ноду по SSH (на реальном arm64 сервере уже должен быть установлен Docker)
docker buildx create --append --name fleet ssh://builder@arm64-ci.local
# По желанию — x86-ноду отдельно (если менеджер запущен не там)
docker buildx create --append --name fleet ssh://builder@amd64-ci.local
docker buildx inspect --bootstrap

После этого одна команда docker buildx build --platform linux/amd64,linux/arm64 ... распределит сборку между нодами без QEMU. Это быстрее и надёжнее. Если нужно быстро получить нативные узлы под CI, поднимите их на облачном VDS и подключите по SSH.

Виртуальный хостинг FastFox
Виртуальный хостинг для сайтов
Универсальное решение для создания и размещения сайтов любой сложности в Интернете от 95₽ / мес

Для оценки выгоды от ARM‑инстансов под runtime и сборки можно посмотреть сравнение в статье производительность ARM/VDS для веб‑стеков.

ARG TARGETPLATFORM и условные шаги

В Dockerfile можно менять поведение в зависимости от платформы:

# syntax=docker/dockerfile:1.7
FROM debian:12-slim
ARG TARGETARCH
RUN if [ "$TARGETARCH" = "arm64" ]; then echo arm64 specific; else echo amd64 path; fi

Так проще переключать пакеты, ссылки на артефакты, бинарники релизов и т.п.

APT и архитектуры: частые ошибки

При сборке на Debian/Ubuntu образах для arm64 на amd64‑хосте через QEMU часто ловят:

  • Exec format error — вы запустили бинарник не той архитектуры.
  • E: Unable to locate package или Wrong architecture — вы пытаетесь поставить пакет под другую архитектуру.

Советы:

  • Если нужен мультиархитектурный APT, используйте dpkg --add-architecture и указывайте суффиксы, но лучше избегайте смешивания в Dockerfile.
  • Берите базовый образ целевой архитектуры в каждой стадии и избегайте межархитектурного копирования бинарников.

Go: CGO, OpenSSL и кросс-компиляция

Go отлично компилируется кросс‑платформенно, но CGO ломает сказку: если ваш код тянет C‑библиотеки (например, OpenSSL), вам понадобится соответствующий компилятор и dev‑пакеты под целевую архитектуру. Проще сделать CGO‑free сборку или собирать на нативной ноде arm64.

Node.js: prebuild и node-gyp

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

Python: wheels и manylinux

Следите за колёсами для arm64. Если авторы пакета не выложили сборки, вы попадёте в долгую компиляцию под QEMU или вовсе на ошибки. Выручает нативная arm64‑нода или альтернативные зависимости.

Registry: теги, семвер и неизменяемость

Для предсказуемых деплоев:

  • Публикуйте семвер‑теги и неперезаписываемые дайджесты.
  • Для «latest» делайте атомарное обновление манифеста только после успешной сборки всех платформ.
  • Старайтесь не смешивать разные билды под одним тегом в одно и то же время, чтобы кэш не вводил CI в заблуждение.

Если поднимаете собственный приватный registry, не забудьте про TLS — оформить и подключить SSL-сертификаты проще заранее, чем разбираться с недоверием клиентов Docker.

Секреты, SSH и приватные зависимости

BuildKit поддерживает безопасную передачу секретов и SSH‑ключей в момент сборки. Это позволяет приватно тянуть репозитории или токены, не оставляя их в слоях. Учитывайте, что секреты доступны только во время выполнения шага, где они явно подключены.

# Пример: приватный git-доступ во время RUN
# docker buildx build --ssh default
# В Dockerfile:
# RUN --mount=type=ssh git clone git@github.com:org/private-repo.git

Отладка типичных проблем

  • no matching manifest for linux/arm64/v8. Базовый образ не поддерживает arm64. Выберите другой или переключитесь на совместимую версию.
  • Cannot execute binary file: Exec format error. Вы случайно перенесли бинарник между архитектурами. Собирайте и устанавливайте зависимости внутри стадии той же платформы.
  • node-pre-gyp ERR или gyp ERR. Установите build‑tools и dev‑пакеты для целевой платформы или собирайте на нативной ноде arm64.
  • Медленная сборка под QEMU. Вынесите тяжёлую компиляцию на нативную ноду и включите registry‑кэш.
  • Разные результаты на CI и локально. Стабилизируйте версии базовых образов, используйте --provenance=false при необходимости и фиксируйте версии инструментов сборки.

Миграция существующего проекта на multi-arch: пошагово

  1. Включите BuildKit и создайте builder с драйвером docker-container.
  2. Поднимите QEMU через tonistiigi/binfmt или подключите нативную arm64‑ноду к buildx.
  3. Разделите Dockerfile на чёткие стадии: зависимости, сборка, рантайм.
  4. Внедрите ARG TARGETPLATFORM/TARGETARCH и условные шаги для различий.
  5. Уберите перенос артефактов между архитектурами. Инсталируйте каждый набор зависимостей в своей стадии.
  6. Добавьте registry‑кэш и проверьте выгоду по времени.
  7. Соберите --platform linux/amd64,linux/arm64, опубликуйте --push, проверьте манифест imagetools inspect.
  8. Прогоните smoke‑тесты на обеих архитектурах (по возможности — на нативных нодах).

Практические рекомендации по производительности

  • Базовые образы выбирайте стабильные и минимальные. Distroless или slim‑образы уменьшают слои и риски несовместимости.
  • Максимально отделяйте скачивание зависимостей и сборку — это улучшает кэшируемость.
  • Проксируйте пакетные менеджеры (npm, pip, apt) через локальный прокси‑кэш, если он доступен, чтобы повысить повторяемость и скорость.
  • Для тяжёлых CI‑сценариев используйте связку из двух билд‑нод: amd64 и arm64. Так вы полностью обходите QEMU.

Надёжность и безопасность

  • Не запускайте сборочные контейнеры с лишними привилегиями. --privileged нужен только для установки binfmt.
  • Уменьшайте поверхность атаки: в финальном образе оставляйте только бинарь и необходимые сертификаты/локали.
  • Периодически пересобирайте образы даже без изменения кода, чтобы подтянуть security‑фиксы в базовых слоях.
FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Ключ к успешной multi-arch стратегии: собирать там, где это нативно и быстро, а кэш в registry плюс продуманная структура Dockerfile сгладят остальное.

Чек-лист перед релизом

  • Buildx builder создан и работает с нужными нодами.
  • QEMU установлен, если нет нативной arm64‑ноды.
  • Dockerfile разбит на стадии, нет переносов архитектурно‑зависимых артефактов.
  • Библиотеки с CGO и нативные модули собираются под нужную архитектуру.
  • Включен registry‑кэш и проверена его эффективность.
  • Манифест содержит linux/amd64 и linux/arm64.
  • Смок‑тесты прошли на обеих архитектурах.

Итоги

docker buildx делает multi-arch рутиной: с правильной настройкой builder’а, QEMU и кэша вы получаете универсальные образы, которые одинаково предсказуемо запускаются на amd64 и arm64. Начните с простой публикации в ваш registry, проверьте манифест и постепенно улучшайте Dockerfile, кэш и стратегию сборок. Когда нагрузка вырастет — подключите нативную ARM‑ноду к buildx и забудьте про «медленную эмуляцию».

Поделиться статьей

Вам будет интересно

Debian/Ubuntu: mount: wrong fs type, bad option, bad superblock — как быстро найти и исправить причину OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: mount: wrong fs type, bad option, bad superblock — как быстро найти и исправить причину

Ошибка mount: wrong fs type, bad option, bad superblock в Debian/Ubuntu может означать и простую опечатку в имени раздела, и пробл ...
Debian/Ubuntu: XFS metadata corruption и emergency read-only — пошаговое восстановление OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: XFS metadata corruption и emergency read-only — пошаговое восстановление

Если XFS-раздел внезапно стал доступен только для чтения, а сервер ушёл в emergency mode, главное — не спешить. Разберём безопасны ...
Debian/Ubuntu: как исправить Failed to fetch при apt update OpenAI Статья написана AI (GPT 5)

Debian/Ubuntu: как исправить Failed to fetch при apt update

Ошибка Failed to fetch при apt update в Debian и Ubuntu обычно связана не с самим APT, а с DNS, сетью, зеркалом, прокси, временем ...