dockerfile中的RUN命令产生的结果与在容器中手动运行相同命令的结果不同 [英] RUN command in dockerfile produces different result than manually running same commands inside container

查看:129
本文介绍了dockerfile中的RUN命令产生的结果与在容器中手动运行相同命令的结果不同的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在创建具有gcc 4.8.5的Ubuntu 12.04 docker映像.我正在获取gcc 4.8.5源并自己构建.该容器将在Ubuntu 18.04主机上运行.

I am creating a Ubuntu 12.04 docker image that has gcc 4.8.5. I am getting gcc 4.8.5 source and building it myself. This container will be running on a Ubuntu 18.04 host.

参考底部的代码,如果我没有将其放在dockerfile中并在启动容器后运行相同的命令,则该构建工作正常,但是,如果我在dockerfile中使用RUN代替,则会得到以下构建错误

Referencing the code at the bottom, if I don't put this in the dockerfile and run the same commands after starting the container, the build works fine, however if I use RUN instead in the dockerfile, I get the following build error

In file included from /usr/include/stdio.h:28:0,
             from ../../../gcc-4.8.5/libgcc/../gcc/tsystem.h:87,
             from ../../../gcc-4.8.5/libgcc/libgcc2.c:27:
/usr/include/features.h:324:26: fatal error: bits/predefs.h: No such 
file or directory
#include <bits/predefs.h>
                      ^

问题似乎源于./gcc-4.8.5/configure调用. 在容器内运行时,我得到:

The problem seems to stem from the ./gcc-4.8.5/configure call. When run inside the container i get:

checking build system type... i686-pc-linux-gnu

当放入dockerfile时,我得到:

When put in the dockerfile i get:

checking build system type... x86_64-unknown-linux-gnu

有人可以在dockerfiles中填写我对RUN的理解吗,因为我感觉我缺少有关其运行方式的信息.我的印象是这些命令将在上一层运行?但似乎它们正在我的主机上运行.

Can someone fill in my understanding of the RUN in dockerfiles because i feel like i am missing something about how its working. I was under the impression that those commands would run in the previous layer? But it seems like they are running on my host.

## Get gcc 4.8.5 and build it
RUN wget ftp://gcc.gnu.org/pub/gcc/releases/gcc-4.8.5/gcc-4.8.5.tar.gz \
&& tar xzf gcc-4.8.5.tar.gz && \
cd gcc-4.8.5 && \
./contrib/download_prerequisites && \
cd .. && mkdir gccbuild && cd gccbuild && \
../gcc-4.8.5/configure \
--prefix="/opt/gcc" \
--enable-shared --with-system-zlib --enable-threads=posix \
--enable-__cxa_atexit --enable-checking --enable-gnu-indirect-function \
--enable-languages="c,c++" --disable-bootstrap \
&& make all && make install 

docker build -t 12.04_builder - < dockerfile
docker run -i -t 12.04_builder

完成dockerfile:

Complete dockerfile:

FROM jnickborys/i386-ubuntu:12.04

RUN apt-get update && \ 
    apt-get install -y \ 
      wget \
      build-essential \
      libssl-dev \
      git \
      asciidoc \
      libpulse-dev \
      libasound2-dev \
      libpcsclite-dev 

## Get latest cmake that has a 32-bit version
RUN wget https://github.com/Kitware/CMake/releases/download/v3.6.3/cmake-3.6.3-Linux-i386.sh && \ 
    chmod +x cmake-3.6.3-Linux-i386.sh && \ 
    ./cmake-3.6.3-Linux-i386.sh --skip-license --prefix=/usr

## Get gcc 4.8.5 and build it
RUN wget ftp://gcc.gnu.org/pub/gcc/releases/gcc-4.8.5/gcc-4.8.5.tar.gz \
    && tar xzf gcc-4.8.5.tar.gz && \
    cd gcc-4.8.5 && \
    ./contrib/download_prerequisites && \
    cd .. && mkdir gccbuild && cd gccbuild && \
    ../gcc-4.8.5/configure \
    --prefix="/opt/gcc" \
    --enable-shared --with-system-zlib --enable-threads=posix \
    --enable-__cxa_atexit --enable-checking --enable-gnu-indirect-function \
    --enable-languages="c,c++" --disable-bootstrap 
    && make all && make install

推荐答案

首先,要了解一些背景知识:在构建过程中运行的平台检测脚本使用uname(1)实用程序(因此是uname(2)系统调用)来确定运行它的硬件:

First of all, a little bit of background: the platform detection script which runs during the build uses uname(1) utility (thus uname(2) system call) to identify the hardware it runs on:

root@6e4b69adfd4c:/gcc-4.8.5# grep 'uname -m' config.guess 
UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown

在64位计算机上,uname -m返回x86_64.但是,有一个系统调用可以覆盖此结果:personality(2).当进程调用personality(2)时,在调用uname(2)时,它及其后续的派生分支(子进程)开始看到虚假结果.因此,有可能要求内核在uname(2)中提供伪造的硬件信息.

On your 64-bit machine uname -m returns x86_64. However, there is a system call which allows overriding this result: personality(2). When the process calls personality(2), it and its subsequent forks (children) start seeing the fake results when calling uname(2). So, there is the possibility to ask the kernel to provide the fake hardware information in uname(2).

您使用的基本映像(jnickborys/i386-ubuntu:12.04)包含32位二进制文​​件并定义了入口点/usr/bin/linux32,该入口点调用personality(PER_LINUX32)要求内核假装它在32位硬件上运行并返回<uname(2)中的c11>(可以分别使用docker inspectstrace进行检查).这样可以假装容器化进程在32位环境中运行.

The base image you use (jnickborys/i386-ubuntu:12.04) contains the 32-bit binaries and defines the entrypoint /usr/bin/linux32, which calls personality(PER_LINUX32) to ask the kernel to pretend that it runs on 32-bit hardware and to return i686 in uname(2) (this may be checked using docker inspect and strace respectively). This makes possible to pretend that the containerized process runs in 32-bit environment.

执行RUN指令与在容器中手动执行之间有什么区别?

What is the difference between executing the build in RUN directive and manually in the container?

RUN中执行构建时,Docker不会使用入口点来运行命令.它使用SHELL指令中指定的内容(默认为/bin/sh -c).这意味着运行构建的外壳的个性不会改变,并且它(和子进程)会看到真实的硬件信息-x86_64,因此,您在32位环境中获得了x86_64-unknown-linux-gnu构建系统类型,并且构建失败.

When you execute the build in RUN, Docker does not use the entrypoint to run the commands. It uses what is specified in the SHELL directive instead (default is /bin/sh -c). This means that the personality of the shell running the build is not altered, and it (and the child processes) sees the real hardware information - x86_64, thus, you get x86_64-unknown-linux-gnu build system type in 32-bit environment and the build fails.

当您在容器中手动运行构建时(例如,使用docker run -it jnickborys/i386-ubuntu:12.04启动构建,然后执行与Dockerfile中相同的步骤之后),将调用入口点,因此个性被更改,内核开始报告它可以在32位硬件(i686)上运行,因此您获得i686-pc-linux-gnu构建系统类型,并且构建可以正确运行.

When you run the build manually in the container (e.g. after starting it using docker run -it jnickborys/i386-ubuntu:12.04 and then performing the same steps as in the Dockerfile), the entrypoint is called, thus, the personality is altered, and the kernel starts reporting that it runs on 32-bit hardware (i686), so you get i686-pc-linux-gnu build system type, and the build runs correctly.

如何解决此问题?取决于您想要什么.如果您的目标是为64位环境构建gcc,则只需使用64位基本映像即可.如果要针对32位环境进行构建,您的选择之一是在这些RUN之前更改用于RUNSHELL:

How to fix this? Depends on what do you want. If your goal is to build gcc for 64-bit environment, just use the 64-bit base image. If you want to build for 32-bit environment, one of your options is to alter the SHELL being used for RUNs before these RUNs:

SHELL ["/usr/bin/linux32", "/bin/sh", "-c"]

这将使Docker以更改的个性执行RUN,因此,将正确检测到构建系统类型(i686-pc-linux-gnu),并且构建将成功.如果需要,可以在构建后将SHELL改回/bin/sh -c.

This will make Docker execute RUNs with altered personality, so, the build system type will be detected correctly (i686-pc-linux-gnu) and the build will succeed. If required, you may change the SHELL back to /bin/sh -c after the build.

这篇关于dockerfile中的RUN命令产生的结果与在容器中手动运行相同命令的结果不同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆