[bash] ‘source’와 함께 Dockerfile에서 RUN 명령어를 사용할 수 없습니다

나는 바닐라 파이썬 환경을 설치하기 위해 함께 모은 Dockerfile을 가지고 있습니다 (나중에 앱을 설치할 것입니다).

FROM ubuntu:12.04

# required to build certain python libraries
RUN apt-get install python-dev -y

# install pip - canonical installation instructions from pip-installer.org
# http://www.pip-installer.org/en/latest/installing.html
ADD https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py /tmp/ez_setup.py
ADD https://raw.github.com/pypa/pip/master/contrib/get-pip.py /tmp/get-pip.py
RUN python /tmp/ez_setup.py
RUN python /tmp/get-pip.py
RUN pip install --upgrade pip

# install and configure virtualenv
RUN pip install virtualenv
RUN pip install virtualenvwrapper
ENV WORKON_HOME ~/.virtualenvs
RUN mkdir -p $WORKON_HOME
RUN source /usr/local/bin/virtualenvwrapper.sh

빌드는 마지막 줄까지 정상적으로 실행되며 다음 예외가 발생합니다.

[previous steps 1-9 removed for clarity]
...
Successfully installed virtualenvwrapper virtualenv-clone stevedore
Cleaning up...
 ---> 1fc253a8f860
Step 10 : ENV WORKON_HOME ~/.virtualenvs
 ---> Running in 8b0145d2c80d
 ---> 0f91a5d96013
Step 11 : RUN mkdir -p $WORKON_HOME
 ---> Running in 9d2552712ddf
 ---> 3a87364c7b45
Step 12 : RUN source /usr/local/bin/virtualenvwrapper.sh
 ---> Running in c13a187261ec
/bin/sh: 1: source: not found

만약 내가 ls그 디렉토리에 (바로 이전 단계가 최선을 다하고 있다고 테스트) 나는 파일이 예상대로 존재하는 것을 볼 수있다 :

$ docker run 3a87 ls /usr/local/bin
easy_install
easy_install-2.7
pip
pip-2.7
virtualenv
virtualenv-2.7
virtualenv-clone
virtualenvwrapper.sh
virtualenvwrapper_lazy.sh

source명령을 실행하려고하면 위와 같은 ‘찾을 수 없음’오류가 발생합니다. 그러나 대화 형 셸 세션을 실행하면 소스가 작동합니다.

$ docker run 3a87 bash
source
bash: line 1: source: filename argument required
source: usage: source filename [arguments]

그때 행복하게 접근을 여기에서 스크립트를 실행 할 수 있습니다 workon, mkvirtualenv

내가 뒷조사를 해봤 그리고 처음에 문제가 사이의 차이에 거짓말을 할 수 것처럼 보였다 bash는 우분투로 로그인 쉘대시 우분투 같은 시스템 쉘 , 대시 지원하지 source명령을 사용합니다.

그러나 이에 대한 대답은 ‘.’ 을 사용하는 것으로 보입니다 . 대신에 sourceDocker 런타임이 go panic 예외와 함께 폭발합니다.

이 문제를 해결하기 위해 Dockerfile RUN 명령에서 쉘 스크립트를 실행하는 가장 좋은 방법은 무엇입니까?



답변

RUN /bin/bash -c "source /usr/local/bin/virtualenvwrapper.sh"


답변

원래 답변

FROM ubuntu:14.04
RUN rm /bin/sh && ln -s /bin/bash /bin/sh

이것은 모든 우분투 도커 기본 이미지에서 작동합니다. 나는 일반적으로 내가 쓰는 모든 Dockerfile에 대해이 줄을 추가합니다.

관련 방관자가 편집

당신이 “사용의 효과를 얻고 싶다면 bash대신 sh하지 않고,이 전체 Dockerfile에 걸쳐”를 변질가능성이 손상 * 용기 내부의 OS를, 당신은 할 수 도커에게 당신의 의도를 말해 . 그것은 다음과 같이 수행됩니다.

SHELL ["/bin/bash", "-c"]

* 가능한 피해는 Linux의 많은 스크립트 (새로운 Ubuntu 설치 grep -rHInE '/bin/sh' /에서 2700 이상의 결과를 반환 함)가에서 POSIX 셸을 완전히 기대한다는 것 /bin/sh입니다. bash 쉘은 POSIX와 추가 내장 기능이 아닙니다. POSIX와 완전히 다르게 작동하는 내장 (및 그 이상)이 있습니다. 나는 POSIX (그리고 다른 쉘에서 테스트하지 않은 스크립트가 당신이 기저귀를 피했다고 생각하기 때문에 작동한다는 오해)를 피하고 bashism을 사용하는 것을 완전히 지원합니다. 그러나 당신은 스크립트에서 적절한 shebang으로 그렇게합니다. 전체 OS에서 POSIX 셸을 잡아 당기는 것이 아닙니다. (리눅스와 함께 제공되는 모든 2700 플러스 스크립트와 설치 한 패키지의 모든 스크립트를 확인할 시간이 없다면)

이 답변에 대한 자세한 내용은 아래를 참조하십시오. https://stackoverflow.com/a/45087082/117471


답변

RUN명령 의 기본 쉘 은 ["/bin/sh", "-c"]입니다.

RUN "source file"      # translates to: RUN /bin/sh -c "source file"

SHELL instruction을 사용하면 RUNDockerfile에서 후속 명령어에 대한 기본 쉘을 변경할 수 있습니다 .

SHELL ["/bin/bash", "-c"] 

이제 기본 쉘이 변경되었으며 모든 RUN 명령에서 명시 적으로 정의 할 필요가 없습니다.

RUN "source file"    # now translates to: RUN /bin/bash -c "source file"

추가 사항 : --login로그인 쉘을 시작하는 옵션을 추가 할 수도 있습니다 . 즉 ~/.bachrc, 예를 들어 명령을 읽기 전에 명시 적으로 소싱 할 필요가 없습니다.


답변

나는 같은 문제가 있었고 virtualenv 내에서 pip install을 실행하려면이 명령을 사용해야했습니다.

RUN pip install virtualenv virtualenvwrapper
RUN mkdir -p /opt/virtualenvs
ENV WORKON_HOME /opt/virtualenvs
RUN /bin/bash -c "source /usr/local/bin/virtualenvwrapper.sh \
    && mkvirtualenv myapp \
    && workon myapp \
    && pip install -r /mycode/myapp/requirements.txt"

도움이 되길 바랍니다.


답변

가장 간단한 방법은 소스 대신 도트 연산자를 사용하는 것입니다. 이는 bash source명령 과 동일합니다 .

대신에:

RUN source /usr/local/bin/virtualenvwrapper.sh

사용하다:

RUN . /usr/local/bin/virtualenvwrapper.sh


답변

Docker 1.12 이상을 사용하는 경우 SHELL!

짧은 답변:

일반:

SHELL ["/bin/bash", "-c"] 

파이썬 vituralenv의 경우 :

SHELL ["/bin/bash", "-c", "source /usr/local/bin/virtualenvwrapper.sh"]

긴 답변 :

에서 https://docs.docker.com/engine/reference/builder/#/shell

SHELL ["executable", "parameters"]

SHELL 명령어를 사용하면 셸 형식의 명령에 사용 된 기본 셸을 재정의 할 수 있습니다. Linux의 기본 쉘은 [ “/ bin / sh”, “-c”]이고 Windows의 경우 [ “cmd”, “/ S”, “/ C”]입니다. SHELL 명령어는 Dockerfile에 JSON 형식으로 작성해야합니다.

SHELL 명령어는 cmd와 powershell, sh를 포함하여 사용 가능한 대체 쉘과 같이 일반적으로 사용되는 두 가지 고유 쉘이있는 Windows에서 특히 유용합니다.

SHELL 명령은 여러 번 나타날 수 있습니다. 각 SHELL 명령은 모든 이전 SHELL 명령을 무시하고 모든 후속 명령에 영향을줍니다. 예를 들면 다음과 같습니다.

FROM microsoft/windowsservercore

# Executed as cmd /S /C echo default
RUN echo default

# Executed as cmd /S /C powershell -command Write-Host default
RUN powershell -command Write-Host default

# Executed as powershell -command Write-Host hello
SHELL ["powershell", "-command"]
RUN Write-Host hello

# Executed as cmd /S /C echo hello
SHELL ["cmd", "/S"", "/C"]
RUN echo hello

쉘 명령의 쉘 형식이 Dockerfile에서 사용될 때 SHELL 명령의 영향을받을 수있는 명령은 RUN, CMD 및 ENTRYPOINT입니다.

다음 예제는 SHELL 명령어를 사용하여 간소화 할 수있는 Windows에서 발견되는 일반적인 패턴입니다.

...
RUN powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"
...

docker가 호출 한 명령은 다음과 같습니다.

cmd /S /C powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"

이것은 두 가지 이유로 비효율적입니다. 먼저 불필요한 cmd.exe 명령 프로세서 (일명 셸)가 호출됩니다. 둘째, 쉘 형식의 각 RUN 명령에는 명령 앞에 접두사를 추가로 추가해야합니다.

이를보다 효율적으로 만들기 위해 두 가지 메커니즘 중 하나를 사용할 수 있습니다. 하나는 다음과 같은 JSON 형식의 RUN 명령을 사용하는 것입니다.

...
RUN ["powershell", "-command", "Execute-MyCmdlet", "-param1 \"c:\\foo.txt\""]
...

JSON 형식은 분명하고 불필요한 cmd.exe를 사용하지 않지만 큰 따옴표와 이스케이프를 통해 더 자세한 정보가 필요합니다. 대체 메커니즘은 SHELL 명령어와 셸 형식을 사용하여 Windows 사용자를 위해 특히 이스케이프 파서 지시문과 결합 할 때보다 자연스러운 구문을 만드는 것입니다.

# escape=`

FROM microsoft/nanoserver
SHELL ["powershell","-command"]
RUN New-Item -ItemType Directory C:\Example
ADD Execute-MyCmdlet.ps1 c:\example\
RUN c:\example\Execute-MyCmdlet -sample 'hello world'

를 야기하는:

PS E:\docker\build\shell> docker build -t shell .
Sending build context to Docker daemon 4.096 kB
Step 1/5 : FROM microsoft/nanoserver
 ---> 22738ff49c6d
Step 2/5 : SHELL powershell -command
 ---> Running in 6fcdb6855ae2
 ---> 6331462d4300
Removing intermediate container 6fcdb6855ae2
Step 3/5 : RUN New-Item -ItemType Directory C:\Example
 ---> Running in d0eef8386e97


    Directory: C:\


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       10/28/2016  11:26 AM                Example


 ---> 3f2fbf1395d9
Removing intermediate container d0eef8386e97
Step 4/5 : ADD Execute-MyCmdlet.ps1 c:\example\
 ---> a955b2621c31
Removing intermediate container b825593d39fc
Step 5/5 : RUN c:\example\Execute-MyCmdlet 'hello world'
 ---> Running in be6d8e63fe75
hello world
 ---> 8e559e9bf424
Removing intermediate container be6d8e63fe75
Successfully built 8e559e9bf424
PS E:\docker\build\shell>

SHELL 명령어는 쉘 작동 방식을 수정하는 데에도 사용될 수 있습니다. 예를 들어, Windows에서 SHELL cmd / S / C / V : ON | OFF를 사용하면 지연된 환경 변수 확장 시맨틱을 수정할 수 있습니다.

SHELL 명령어는 zsh, csh, tcsh 등과 같은 대체 쉘이 필요한 경우 Linux에서도 사용할 수 있습니다.

SHELL 기능은 Docker 1.12에 추가되었습니다.


답변

이 페이지의 답변을 바탕으로 각 RUN 문이 다른 RUN 문과 독립적으로 실행된다는 것을 알아야합니다. /bin/sh -c 되므로 일반적으로 로그인 쉘에서 제공되는 환경 변수를 얻지 .

지금까지 찾은 가장 좋은 방법은 스크립트를 추가하고 /etc/bash.bashrc각 명령을 bash 로그인으로 호출하는 것입니다.

RUN echo "source /usr/local/bin/virtualenvwrapper.sh" >> /etc/bash.bashrc
RUN /bin/bash --login -c "your command"

예를 들어 virtualenvwrapper를 설치 및 설정하고 가상 환경을 생성하고 bash 로그인을 사용할 때 활성화 한 다음 파이썬 모듈을 다음 환경에 설치할 수 있습니다.

RUN pip install virtualenv virtualenvwrapper
RUN mkdir -p /opt/virtualenvs
ENV WORKON_HOME /opt/virtualenvs
RUN echo "source /usr/local/bin/virtualenvwrapper.sh" >> /etc/bash.bashrc
RUN /bin/bash --login -c "mkvirtualenv myapp"
RUN echo "workon mpyapp" >> /etc/bash.bashrc
RUN /bin/bash --login -c "pip install ..."

bash 시작 파일 에 대한 매뉴얼을 읽으면 소스가 무엇인지 이해하는 데 도움이됩니다.