Сбой сборки Docker для образов ARM
Я пытаюсь создать образ докера для нескольких архитектур на Travis-CI. Это хорошо работает для amd64 и i386, но не для ARM.
Dockerfile построить поверх {ARCH}/nextcloud:apache
который построен на вершине php:7.3-apache-stretch
который снова использует debian:stretch-slim
, Таким образом, все изображения используют один и тот же стек и должны реагировать одинаково.
.travis.yml
env:
- TAG=i386 ARCH=i386
- TAG=amd64 ARCH=amd64
- TAG=armhf ARCH=arm32v7
- TAG=aarch64 ARCH=arm64v8
before_script:
- docker run --rm --privileged multiarch/qemu-user-static:register --reset
script:
- docker build --pull --build-arg ARCH=$ARCH -t escoand/nextcloud:$TAG nextcloud
Dockerfile
ARG ARCH
FROM ${ARCH}/nextcloud:apache
RUN apt-get update && apt-get install -y supervisor && \
rm -rf /var/lib/apt/lists/* && \
mkdir /var/log/supervisord /var/run/supervisord
Как уже упоминалось, сборка для i386 и amd64 работает без проблем. Сборки ARM терпят неудачу уже с первой командой RUN:
standard_init_linux.go:185: exec user process caused "no such file or directory"
The command '/bin/sh -c apt-get update && apt-get install -y supervisor && rm -rf /var/lib/apt/lists/* && mkdir /var/log/supervisord /var/run/supervisord' returned a non-zero code: 1
https://travis-ci.org/escoand/dockerfiles/jobs/562967055
Для меня это звучит как /bin/sh
это проблема, но не знаю, как справиться с этим.
1 ответ
Прежде всего, давайте разберемся, что именно вы пытаетесь сделать, используя следующий прием:
before_script:
- docker run --rm --privileged multiarch/qemu-user-static:register --reset
Когда вы просите ядро Linux запустить какой-нибудь исполняемый файл, ему нужно знать, как загрузить этот конкретный файл и совместим ли этот файл с текущей машиной или нет. По умолчанию двоичный файл ELF скомпилирован, скажем, для arm64v8
отклоняется ядром, работающим на amd64
аппаратное обеспечение.
Однако функция ядра binfmt_misc позволяет вам указать, как обрабатывать исполняемые файлы, которые он обычно не может обрабатывать самостоятельно - это включает в себя случаи, когда ядро не знает двоичный формат или считает его несовместимым с текущей машиной.
С чего начался контейнер с картинки multiarch/qemu-user-static:register
делает? Он регистрирует новые обработчики для двоичных файлов ELF, созданных для альтернативных архитектур, например:
$ cat /proc/sys/fs/binfmt_misc/qemu-aarch64
enabled
interpreter /usr/bin/qemu-aarch64-static
flags:
offset 0
magic 7f454c460201010000000000000000000200b700
mask ffffffffffffff00fffffffffffffffffeffffff
Когда этот обработчик зарегистрирован, ядро знает, что если оно сталкивается с двоичным файлом, начинающимся с магических байтов, указанных в magic
поле (также с учетом mask
), это должно бежать /usr/bin/qemu-aarch64-static
двоичный (интерпретатор), и пусть он позаботится о загрузке и запуске запрошенного двоичного файла.
Проблема в вашем вопросе: где /usr/bin/qemu-aarch64-static
переводчик, который умеет бегать aarch64
двоичные файлы на amd64
? Там нет никого, так как базовое изображение, которое вы используете, не включает его (я вытащил и arm64v8/nextcloud:apache
изображение вручную, чтобы подтвердить это)!
Согласно manpage для execve(2)
, когда ядро не может загрузить интерпретатор, оно возвращает ошибку "ENOENT" (такой файл или каталог отсутствует):
ERRORS
<...>
ENOENT The file pathname or a script or ELF interpreter does not exist,
or a shared library needed for the file or interpreter cannot be found.
Итак, вот что происходит: вы строите образ и указываете базовый образ, созданный для машин ARM. Во-первых RUN
, ядро пытается выполнить /bin/sh
file from the image, находит обработчик QEMU для этого типа двоичных файлов, затем ищет интерпретатор, не находит его и выдает ошибку, следовательно, возникает ошибка "нет такого файла или каталога".
Чтобы решить эту проблему, вы должны использовать базовый образ, на котором установлен QEMU (и, таким образом, /usr/bin/qemu-aarch64-static
внутри), что позволит вам выполнить RUN
s.