Команда RUN в dockerfile дает другой результат, чем выполнение вручную тех же команд внутри контейнера - PullRequest
0 голосов
/ 29 января 2019

Я создаю образ докера Ubuntu 12.04 с gcc 4.8.5.Я получаю исходный код gcc 4.8.5 и собираю его сам.Этот контейнер будет работать на хосте Ubuntu 18.04.

Ссылка на код внизу, если я не помещу это в файл docker и не запусту те же команды после запуска контейнера, сборка работает нормально, однакоесли я использую RUN вместо этого в файле docker, я получаю следующую ошибку сборки

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.При запуске внутри контейнера я получаю:

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

Когда помещается в Dockerfile, я получаю:

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

Может ли кто-нибудь заполнить мое понимание RUN в файлах Docker, потому что я чувствую, что яЯ что-то упускаю из-за того, как это работает.У меня сложилось впечатление, что эти команды будут выполняться в предыдущем слое?Но похоже, что они работают на моем хосте.

## 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

Полный файл docker:

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

1 Ответ

0 голосов
/ 31 января 2019

Прежде всего, немного предыстории: скрипт определения платформы, который запускается во время сборки, использует утилиту uname(1) (таким образом, uname(2) системный вызов) для идентификации оборудования, на котором он работает:

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).

Используемый вами базовый образ (jnickborys/i386-ubuntu:12.04) содержит 32-разрядные двоичные файлы и определяет точку входа /usr/bin/linux32, который вызывает personality(PER_LINUX32), чтобы попросить ядро ​​сделать вид, что оно работает на 32-разрядном оборудовании, и вернуть i686 в uname(2) (это можно проверить, используя docker inspect и strace соответственно).Это позволяет сделать вид, что контейнерный процесс выполняется в 32-разрядной среде.

В чем разница между выполнением директивы build в RUN и вручную в контейнере?

Когда вы выполняетесборка в RUN, Docker не использует точку входа для запуска команд.Вместо этого он использует то, что указано в директиве SHELL (по умолчанию /bin/sh -c).Это означает, что индивидуальность оболочки, выполняющей сборку, не изменяется, и она (и дочерние процессы) видит реальную аппаратную информацию - x86_64, таким образом, вы получаете x86_64-unknown-linux-gnu тип системы сборки в 32-битной среде исборка завершается неудачей.

Когда вы запускаете сборку вручную в контейнере (например, после ее запуска с использованием docker run -it jnickborys/i386-ubuntu:12.04 и выполнения тех же шагов, что и в Dockerfile), точка входа вызывается, таким образом, изменяется личностьи ядро ​​начинает сообщать, что оно работает на 32-разрядном оборудовании (i686), поэтому вы получаете i686-pc-linux-gnu тип системы сборки, и сборка выполняется правильно.

Как это исправить?Зависит от того, что вы хотите.Если ваша цель - создать gcc для 64-битной среды, просто используйте 64-битный базовый образ.Если вы хотите построить для 32-битной среды, один из ваших вариантов - изменить SHELL, используемый для RUN с до этих RUN с:

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

Это заставит Docker выполнитьRUN с измененной индивидуальностью, поэтому тип системы сборки будет определен правильно (i686-pc-linux-gnu), и сборка будет выполнена успешно.При необходимости вы можете изменить SHELL обратно на /bin/sh -c после сборки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...