MAATRIX / Блог / Docker на ARM: особенности

Docker на ARM: особенности

Docker на ARM: особенности

MAATRIX

ARM-серверы дешевле и экономичнее по энергии, чем классические x86, и всё чаще попадаются в списке тарифов у хостеров. Соблазн переехать понятен — но перед переездом стоит понять, что на ARM работает без вопросов, а что потребует лишних телодвижений. Разбираемся по порядку: от установки Docker до сборки собственных multi-arch образов.

Установка Docker на ARM ничем не отличается от x86

Это первое, что стоит знать: сам движок Docker одинаково ставится что на amd64, что на arm64. Официальный скрипт и репозитории пакетов автоматически подхватывают архитектуру вашей системы.

На Ubuntu или Debian ARM-сервере установка выглядит так же, как на обычном x86-сервере:

sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Обратите внимание на arch=$(dpkg --print-architecture) — на ARM-сервере эта команда вернёт arm64, и apt сам подтянет нужный пакет из репозитория. Никаких дополнительных флагов не нужно. Проверить, что всё встало правильно:

docker version
uname -m

Второй командой полезно свериться, с чем вы имеете дело: aarch64 — это 64-битный ARM (тот самый arm64), armv7l — 32-битный ARM, который на современных облачных серверах почти не встречается, но иногда попадается на одноплатных компьютерах вроде Raspberry Pi. Разница важна: подавляющее большинство образов на Docker Hub собирается под arm64, а под armv7 — уже не всегда.

Если вы переносите инфраструктуру с x86-сервера на ARM, установка — не то место, где стоит ждать сюрпризов. Сюрпризы начинаются на следующем шаге.

Multi-arch образы: как устроены и как проверить, что образ вам подходит

Docker-образ, который вы тянете командой docker pull nginx, на самом деле часто не один файл, а манифест-список (manifest list) — ссылка на несколько образов, каждый под свою архитектуру. Когда вы делаете docker pull на arm64-сервере, Docker сам выбирает из списка вариант под arm64. Это и называется multi-arch образом, и именно так собирает свои официальные образы Docker Hub для большинства популярных проектов: nginx, postgres, redis, python и так далее.

Проблема в том, что далеко не все образы на Docker Hub — multi-arch. Многие сторонние образы (особенно нишевые, старые или собранные разработчиками "для себя") существуют только под amd64. Если вы попытаетесь их запустить на ARM, будет либо явная ошибка при пуле, либо — что хуже — Docker найдёт совместимый по имени тег, но под чужую архитектуру, и контейнер не запустится (подробнее в следующем разделе).

Проверить архитектуры образа до того, как тянуть его на прод-сервер, можно без скачивания:

docker manifest inspect nginx:latest

В выводе ищите блок platform — там будет список из нескольких architecture (amd64, arm64, arm/v7 и так далее). Если в выводе только один блок с "architecture": "amd64" — образ не multi-arch, и на ARM он не пойдёт как есть.

Более наглядный вариант — через buildx:

docker buildx imagetools inspect postgres:16

Эта команда выводит таблицу платформ прямо построчно, что удобнее читать глазами. Пример того, что вы увидите для типового официального образа:

ПлатформаПоддерживается
linux/amd64да
linux/arm64да
linux/arm/v7иногда
linux/386редко

Правило простое: перед тем как разворачивать что-то новое на ARM-сервере — особенно образ от небольшого стороннего разработчика, — потратьте 10 секунд на docker manifest inspect. Это дешевле, чем потом разбираться, почему контейнер не стартует.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать VPS

Ошибка exec format error: что она означает на практике

Если всё же запустить x86-образ на ARM-сервере (например, явно указав amd64-тег или если Docker подобрал не тот вариант), при попытке запуска процесса внутри контейнера вы получите что-то вроде:

standard_init_linux.go:228: exec user process caused: exec format error

Это не ошибка конфигурации и не проблема с правами — это буквально то, что написано: ядро процессора физически не умеет исполнять бинарник, скомпилированный под другой набор инструкций. x86-64 и ARM64 — разные архитектуры процессора на уровне машинного кода, и обычное ядро Linux не может просто "перевести" одни инструкции в другие на лету.

Здесь важно понимать разницу между двумя похожими, но разными ситуациями:

  • Контейнер вообще не стартует с exec format error — значит, внутри лежит бинарник под чужую архитектуру, и без эмуляции (см. следующий раздел) или пересборки образа это не запустится.
  • Контейнер стартует, но работает мучительно медленно — вероятно, он всё-таки исполняется через QEMU-эмуляцию (если она у вас настроена), и это уже вопрос производительности, а не работоспособности.

Первый шаг диагностики — посмотреть, какую архитектуру реально тянет Docker:

docker inspect --format='{{.Architecture}}' <image_name>

Если там amd64, а сервер — arm64, вопрос закрыт: образ не подходит, нужен либо multi-arch аналог, либо собственная сборка под ARM.

Сборка собственных multi-arch образов через buildx

Если готового ARM-образа для вашего приложения нет, а исходники (Dockerfile) есть — вы можете собрать multi-arch образ самостоятельно. За это отвечает docker buildx, который в современных версиях Docker уже идёт встроенным плагином (пакет docker-buildx-plugin, который мы поставили в первом разделе).

Логика buildx: он поднимает отдельный билдер, который умеет собирать под несколько платформ за один проход и пушить готовый manifest list в реестр.

# создать билдер с поддержкой multi-platform
docker buildx create --name multiarch --use
docker buildx inspect --bootstrap

# собрать и запушить образ сразу под amd64 и arm64
docker buildx build \
  --platform linux/amd64,linux/arm64 \
  -t your-registry/your-app:latest \
  --push .

Важные нюансы:

  • Флаг --push обязателен для multi-arch сборки — Docker не умеет держать multi-platform образ локально в обычном docker images, ему нужен реестр, куда сложить manifest list. Локально можно собрать только под одну платформу с флагом --load.
  • Если ваш Dockerfile тянет бинарные зависимости (компилирует C-расширения, качает бинарники под конкретную архитектуру), сборка под arm64 на amd64-хосте будет идти через эмуляцию QEMU и может быть заметно медленнее обычной. Ориентируйтесь на то, что сборка multi-arch образа с нуля способна занять в разы больше времени, чем сборка под одну архитектуру — точных цифр не даю, слишком зависит от проекта.
  • Собирать сам образ на ARM-сервере под arm64 — быстрее и без эмуляции. Если у вас уже есть ARM-VPS под CI, логично собирать arm64-часть образа именно там, а amd64-часть — на обычном x86-раннере, и сводить всё через docker buildx imagetools create в единый манифест.

Если вы ещё не разворачивали Docker с нуля, есть смысл сначала пройти базовую установку на Ubuntu 24.04 — шаги там совпадают что для x86, что для ARM, разница появляется только начиная с этой статьи.

QEMU-эмуляция: работает, но не для продакшена

Отдельно стоит сказать про механизм, который иногда путают с "нормальной поддержкой ARM" — binfmt-эмуляцию через QEMU. Она позволяет запускать amd64-контейнер на arm64-хосте (и наоборот) без пересборки, транслируя инструкции на лету:

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

После этого docker сможет запустить, например, --platform linux/amd64 образ на arm64-сервере, и вместо exec format error вы увидите, что контейнер действительно стартует. Звучит как решение всех проблем, но на практике это костыль:

  • Производительность падает заметно — эмуляция инструкций процессора недёшева по определению. Для CI-джобы, которая запускается раз в день, это терпимо. Для прод-сервиса под нагрузкой — нет.
  • Не все программы внутри контейнера вообще корректно работают под эмуляцией — особенно если они делают низкоуровневые системные вызовы или зависят от конкретных возможностей процессора.

Используйте QEMU-эмуляцию как временную затычку — например, чтобы быстро проверить, стартует ли вообще приложение, пока готовите нормальную arm64-сборку через buildx. Как постоянное решение для боевого окружения — не рекомендую.

Производительность на ARM: честно про ожидания

Для типовых веб-задач — nginx, PHP-FPM, Node.js, обычная база данных под умеренной нагрузкой — производительность ARM-серверов на практике сопоставима с аналогичными по характеристикам x86-инстансами. Это касается большинства задач, для которых люди вообще берут VPS: сайты, API, боты, очереди задач, кеши.

Но есть категория задач, где стоит проверять совместимость заранее, а не постфактум:

  • AI-инференс с конкретными библиотеками. Не все сборки PyTorch, TensorFlow и особенно узкоспециализированных библиотек (CUDA-зависимые, оптимизированные под AVX-инструкции x86) имеют официальные ARM-колёса (wheels) в PyPI. Иногда единственный вариант — собирать из исходников на месте, что не всегда тривиально и не всегда быстро.
  • Проприетарные бинарные зависимости. Если ваше приложение тянет закрытый бинарник от вендора (антивирус, лицензионный модуль, какая-то legacy-библиотека) — есть шанс, что под arm64 его просто не существует.
  • Специфичные системные утилиты, завязанные на инструкции конкретного процессора — здесь тоже возможны сюрпризы.

Практический совет: прежде чем переносить прод-нагрузку на ARM, разверните тестовый ARM-сервер и прогоните на нём реальный набор зависимостей — pip install -r requirements.txt или сборку Docker-образа целиком. Это займёт полчаса, но избавит от неприятностей после переезда. Если что-то пойдёт не так с образом уже в проде, пригодится и материал про то, когда контейнер не запускается — часть тех же диагностических шагов применима и здесь.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать VPS

Частые вопросы

Все ли официальные образы Docker Hub поддерживают ARM?

Нет. Большинство самых популярных (nginx, postgres, redis, python, node) — да, но далеко не все сторонние образы. Проверяйте через docker manifest inspect перед использованием.

Можно ли просто запустить x86-образ на ARM без пересборки?

Только через QEMU-эмуляцию (tonistiigi/binfmt), и то с потерей производительности. Без эмуляции получите exec format error.

Нужно ли что-то менять в docker-compose.yml для ARM?

Обычно нет, если все используемые образы multi-arch. Если нет — можно явно прописать нужную платформу через platform: linux/arm64 в сервисе, но это сработает только если такой вариант образа реально существует.

Стоит ли переезжать на ARM ради экономии, если приложение — обычный сайт на PHP или Node.js?

Да, для типовых веб-задач разница в поведении почти не ощущается, а экономия на тарифе может быть заметной.

Как понять архитектуру сервера, если доступ только по SSH и неизвестно, что арендовали?

Команда uname -m покажет aarch64 (это ARM64) или x86_64 (это обычный x86).