Docker ARM 映像构建失败
Docker build fails for ARM images
我尝试在 Travis-CI 上为多种架构构建一个 docker 图像。这对 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 构建已经失败:
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
的问题,但不知道如何处理。
首先,让我们了解一下,您究竟想通过以下技巧来做什么:
before_script:
- docker run --rm --privileged multiarch/qemu-user-static:register --reset
当你向Linux内核询问运行某个可执行文件时,它需要知道如何加载这个特定文件,以及这个文件是否与当前机器兼容。默认情况下,为 arm64v8
编译的 ELF 二进制文件被内核拒绝,运行ning 在 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
图像以确认这一点)!
根据 execve(2)
的联机帮助页,当内核无法加载解释器时,它 returns "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
文件,找到此类二进制文件的 QEMU 处理程序,然后查找解释器,但没有找到并失败,因此“ 没有这样的文件或目录”错误。
要解决这个问题,您必须使用安装了 QEMU 的基础映像(因此,内部有 /usr/bin/qemu-aarch64-static
),这将允许您执行 RUN
s.
我尝试在 Travis-CI 上为多种架构构建一个 docker 图像。这对 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 构建已经失败:
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
的问题,但不知道如何处理。
首先,让我们了解一下,您究竟想通过以下技巧来做什么:
before_script:
- docker run --rm --privileged multiarch/qemu-user-static:register --reset
当你向Linux内核询问运行某个可执行文件时,它需要知道如何加载这个特定文件,以及这个文件是否与当前机器兼容。默认情况下,为 arm64v8
编译的 ELF 二进制文件被内核拒绝,运行ning 在 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
图像以确认这一点)!
根据 execve(2)
的联机帮助页,当内核无法加载解释器时,它 returns "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
文件,找到此类二进制文件的 QEMU 处理程序,然后查找解释器,但没有找到并失败,因此“ 没有这样的文件或目录”错误。
要解决这个问题,您必须使用安装了 QEMU 的基础映像(因此,内部有 /usr/bin/qemu-aarch64-static
),这将允许您执行 RUN
s.