【问题标题】:Docker build fails for ARM imagesARM 映像的 Docker 构建失败
【发布时间】:2019-12-02 08:43:47
【问题描述】:

我尝试在 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 的构建工作没有问题。第一个 RUN 命令导致 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 是问题,但不知道如何处理。

【问题讨论】:

    标签: linux docker travis-ci multiarch


    【解决方案1】:

    首先,让我们了解一下,究竟你试图通过以下技巧来做什么:

    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
    

    当这个处理程序被注册时,内核知道如果它面对以魔术字节开头的二进制文件,在magic字段中指定(也考虑到mask),它必须运行/usr/bin/qemu-aarch64-static二进制文件(解释器)并让它负责加载和运行请求的二进制文件。

    你的问题是:/usr/bin/qemu-aarch64-static 解释器在哪里,它知道如何在amd64 上运行aarch64 二进制文件? 没有人,因为您使用的基础映像不包含它(我手动提取并自省 arm64v8/nextcloud:apache 映像以确认这一点)!

    根据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 文件,找到此类二进制文件的QEMU 处理程序,然后寻找解释器,没有找到它并失败,因此“no这样的文件或目录”错误。

    要解决此问题,您必须使用安装了 QEMU 的基础映像(因此内部有 /usr/bin/qemu-aarch64-static),这将允许您执行 RUNs。

    【讨论】:

    • 感谢您提供全面的答复。如果我理解正确,aarch64 二进制文件必须在图像中使用 qemu-*-static 而不是在主机系统中。因为容器架构,对吧?所以实际上它正在做:container binary --&gt; host binfmt --&gt; container qemu 好的,当我想到它时,我已经看到 Dockerfiles 将 qemu-*-static 二进制文件复制到实际图像中。我看到了,但没看懂。现在很清楚了。再次感谢您的解释。
    • 正确,QEMU 二进制文件必须在容器中,因为内核从调用execve() 的进程的挂载命名空间中查找二进制文件,即从构建容器文件系统中查找二进制文件。
    • 顺便说一下,看起来你可以让内核使用来自主机的 QEMU 二进制文件,但是你需要以不同的方式声明处理程序(即不要依赖你使用的容器):请参阅“ kernel documentation 中的修复二进制选项
    猜你喜欢
    • 2019-12-13
    • 2020-10-16
    • 2018-06-02
    • 2022-09-28
    • 2020-06-09
    • 2022-01-02
    • 1970-01-01
    • 2021-12-29
    • 2017-11-26
    相关资源
    最近更新 更多