我有一个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指令的默认shell是["/bin/sh", "-c"]。

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

使用SHELL指令,你可以改变Dockerfile中后续RUN指令的默认SHELL:

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

现在,默认shell已经更改,您不需要在每个RUN指令中显式地定义它

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

附加提示:您还可以添加——login选项,该选项将启动登录shell。这意味着~/。例如Bashrc会被读取,你不需要在你的命令之前显式地来源它

其他回答

这可能是因为源代码是bash内置的,而不是文件系统上的二进制文件。您的意图是让您正在获取的脚本在之后更改容器吗?

我也有同样的问题。如果你也使用python基础映像,你可以将shell脚本中的shebang行更改为#!/bin/bash。 例如,请参阅Manuel Lazo的container_entrypoint.sh。

这是我在Ubuntu 20.04上的解决方案

RUN apt -y update
RUN apt -y install curl
SHELL ["/bin/bash", "-c"]
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash
RUN source /root/.bashrc
RUN bash -c ". /root/.nvm/nvm.sh && nvm install v16 && nvm alias default v16 && nvm use default"

下面是一个示例Dockerfile,它利用了一些聪明的技术,让你为每个run节运行一个完整的conda环境。您可以使用类似的方法来执行脚本文件中的任意准备。

注意:当涉及到登录/交互式vs非登录/非交互式shell,信号,exec,多个参数的处理方式,引用,CMD和ENTRYPOINT如何交互,以及其他一百万件事情时,有很多细微差别,所以如果在使用这些东西时,事情会发生变化,不要气馁。我花了很多令人沮丧的时间在各种文学作品中挖掘,但我仍然不太明白这一切是如何奏效的。

## Conda with custom entrypoint from base ubuntu image
## Build with e.g. `docker build -t monoconda .`
## Run with `docker run --rm -it monoconda bash` to drop right into
## the environment `foo` !
FROM ubuntu:18.04

## Install things we need to install more things
RUN apt-get update -qq &&\
    apt-get install -qq curl wget git &&\
    apt-get install -qq --no-install-recommends \
        libssl-dev \
        software-properties-common \
    && rm -rf /var/lib/apt/lists/*

## Install miniconda
RUN wget -nv https://repo.anaconda.com/miniconda/Miniconda3-4.7.12-Linux-x86_64.sh -O ~/miniconda.sh && \
    /bin/bash ~/miniconda.sh -b -p /opt/conda && \
    rm ~/miniconda.sh && \
    /opt/conda/bin/conda clean -tipsy && \
    ln -s /opt/conda/etc/profile.d/conda.sh /etc/profile.d/conda.sh

## add conda to the path so we can execute it by name
ENV PATH=/opt/conda/bin:$PATH

## Create /entry.sh which will be our new shell entry point. This performs actions to configure the environment
## before starting a new shell (which inherits the env).
## The exec is important! This allows signals to pass
RUN     (echo '#!/bin/bash' \
    &&   echo '__conda_setup="$(/opt/conda/bin/conda shell.bash hook 2> /dev/null)"' \
    &&   echo 'eval "$__conda_setup"' \
    &&   echo 'conda activate "${CONDA_TARGET_ENV:-base}"' \
    &&   echo '>&2 echo "ENTRYPOINT: CONDA_DEFAULT_ENV=${CONDA_DEFAULT_ENV}"' \
    &&   echo 'exec "$@"'\
        ) >> /entry.sh && chmod +x /entry.sh

## Tell the docker build process to use this for RUN.
## The default shell on Linux is ["/bin/sh", "-c"], and on Windows is ["cmd", "/S", "/C"]
SHELL ["/entry.sh", "/bin/bash", "-c"]
## Now, every following invocation of RUN will start with the entry script
RUN     conda update conda -y

## Create a dummy env
RUN     conda create --name foo

## I added this variable such that I have the entry script activate a specific env
ENV CONDA_TARGET_ENV=foo

## This will get installed in the env foo since it gets activated at the start of the RUN stanza
RUN  conda install pip

## Configure .bashrc to drop into a conda env and immediately activate our TARGET env
RUN conda init && echo 'conda activate "${CONDA_TARGET_ENV:-base}"' >>  ~/.bashrc
ENTRYPOINT ["/entry.sh"]

如果您使用的是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.