我有一个Dockerfile,我正在一起安装一个香草python环境(我将在其中安装一个应用程序,但在晚些时候)。

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

如果我进入该目录(只是为了测试之前的步骤是否已提交),我可以看到文件如预期的那样存在:

$ 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

如果我试着运行源代码命令,我得到相同的'not found'错误如上所示。如果我运行一个交互式shell会话,但是,源确实工作:

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

我可以从这里运行脚本,然后愉快地访问workon, mkvirtualenv等。

我做了一些研究,最初看起来问题可能在于bash作为Ubuntu登录shell,而dash作为Ubuntu系统shell, dash不支持源命令。

然而,这个问题的答案似乎是使用'。'而不是source,但这只会导致Docker运行时爆发一个go panic异常。

从Dockerfile run指令运行shell脚本来解决这个问题的最好方法是什么(我运行Ubuntu 12.04 LTS的默认基本映像)。


当前回答

根据本页上的回答,我想补充一点,您必须意识到,每个RUN语句都是使用/bin/sh -c独立运行的,因此不会获得通常在登录shell中获取的任何环境变量。

到目前为止我发现的最好的方法是将脚本添加到/etc/bash.然后调用每个命令作为bash登录。

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

例如,你可以安装和设置virtualenvwrapper,创建虚拟的env,当你使用bash登录时激活它,然后将你的python模块安装到这个env中:

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启动文件的手册有助于理解在什么时候获取了什么。

其他回答

如果您使用的是Docker 1.12或更新版本,只需使用SHELL !

简短的回答:

一般:

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

对于python vituralenv:

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

长一点的回答:

从https://docs.docker.com/engine/reference/builder/壳

SHELL ["executable", "parameters"] The SHELL instruction allows the default shell used for the shell form of commands to be overridden. The default shell on Linux is ["/bin/sh", "-c"], and on Windows is ["cmd", "/S", "/C"]. The SHELL instruction must be written in JSON form in a Dockerfile. The SHELL instruction is particularly useful on Windows where there are two commonly used and quite different native shells: cmd and powershell, as well as alternate shells available including sh. The SHELL instruction can appear multiple times. Each SHELL instruction overrides all previous SHELL instructions, and affects all subsequent instructions. For example: 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 The following instructions can be affected by the SHELL instruction when the shell form of them is used in a Dockerfile: RUN, CMD and ENTRYPOINT. The following example is a common pattern found on Windows which can be streamlined by using the SHELL instruction: ... RUN powershell -command Execute-MyCmdlet -param1 "c:\foo.txt" ... The command invoked by docker will be: cmd /S /C powershell -command Execute-MyCmdlet -param1 "c:\foo.txt" This is inefficient for two reasons. First, there is an un-necessary cmd.exe command processor (aka shell) being invoked. Second, each RUN instruction in the shell form requires an extra powershell -command prefixing the command. To make this more efficient, one of two mechanisms can be employed. One is to use the JSON form of the RUN command such as: ... RUN ["powershell", "-command", "Execute-MyCmdlet", "-param1 \"c:\\foo.txt\""] ... While the JSON form is unambiguous and does not use the un-necessary cmd.exe, it does require more verbosity through double-quoting and escaping. The alternate mechanism is to use the SHELL instruction and the shell form, making a more natural syntax for Windows users, especially when combined with the escape parser directive: # 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' Resulting in: 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> The SHELL instruction could also be used to modify the way in which a shell operates. For example, using SHELL cmd /S /C /V:ON|OFF on Windows, delayed environment variable expansion semantics could be modified. The SHELL instruction can also be used on Linux should an alternate shell be required such as zsh, csh, tcsh and others. The SHELL feature was added in Docker 1.12.

根据本页上的回答,我想补充一点,您必须意识到,每个RUN语句都是使用/bin/sh -c独立运行的,因此不会获得通常在登录shell中获取的任何环境变量。

到目前为止我发现的最好的方法是将脚本添加到/etc/bash.然后调用每个命令作为bash登录。

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

例如,你可以安装和设置virtualenvwrapper,创建虚拟的env,当你使用bash登录时激活它,然后将你的python模块安装到这个env中:

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启动文件的手册有助于理解在什么时候获取了什么。

根据https://docs.docker.com/engine/reference/builder/#run,运行的默认[Linux] shell是/bin/sh -c。您似乎希望使用bashisms,因此应该使用RUN的“执行表单”来指定您的shell。

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

否则,使用RUN的“shell形式”并指定不同的shell会导致嵌套的shell。

# don't do this...
RUN /bin/bash -c "source /usr/local/bin/virtualenvwrapper.sh"
# because it is the same as this...
RUN ["/bin/sh", "-c", "/bin/bash" "-c" "source /usr/local/bin/virtualenvwrapper.sh"]

如果你有超过一个命令需要不同的shell,你应该阅读https://docs.docker.com/engine/reference/builder/#shell并将其放在RUN命令之前更改默认shell:

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

最后,如果您在根用户的.bashrc文件中放置了您需要的任何东西,您可以在SHELL或RUN命令中添加-l标志,使其成为登录SHELL,并确保它得到源代码。

注意:我故意忽略了这样一个事实,即将脚本作为RUN中的唯一命令是没有意义的。

您可能想要运行bash -v来查看源是什么。

我会做以下事情,而不是玩符号链接:

执行echo "source /usr/local/bin/virtualenvwrapper.sh" >> /etc/bash.bashrc .sh

我也遇到了同样的问题,为了在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"

我希望这能有所帮助。