Docker构建ARM镜像失败

9

我尝试在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版本的构建已经在第一个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个回答

15

首先,让我们理解一下,通过使用以下技巧,你是想要做什么:{{exactly}}

before_script:
  - docker run --rm --privileged multiarch/qemu-user-static:register --reset

当您要求Linux内核运行某个可执行文件时,它需要知道如何加载该特定文件,以及该文件是否与当前机器兼容。默认情况下,针对例如arm64v8编译的ELF二进制文件将被内核拒绝,在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

当这个处理程序被注册时,内核知道如果遇到以指定在字段中的魔数字节开头的二进制文件(还考虑了

1
感谢您提供这个全面的答案。如果我理解正确,aarch64二进制文件必须在镜像中使用qemu-*-static而不是在主机系统中使用。因为容器架构的原因,对吗?所以实际上它正在执行: 容器二进制文件 --> 主机binfmt --> 容器qemu好的,当我想到它时,我看到Dockerfiles将qemu-*-static二进制文件复制到实际镜像中。我看到了它,但没有理解。现在清楚了。再次感谢您的解释。 - user3775041
正确的做法是将 QEMU 二进制文件放在容器中,因为内核会从调用 execve() 的进程所在的 mount namespace 中查找二进制文件,即从构建容器文件系统中查找。 - Danila Kiver
顺便提一下,看起来你可以让内核使用主机上的QEMU二进制文件,但是你需要以不同的方式声明处理程序(即不要依赖于你使用的容器):请参见内核文档中的“修复二进制文件”选项。 - Danila Kiver

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接