Using the RUN instruction in a Dockerfile with 'source' does not work
我有一个dockerfile,我正在组装它来安装一个普通的python环境(我将在其中安装一个应用程序,但在稍后的日期)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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 |
生成运行正常,直到最后一行,在此我得到以下异常:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | [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 |
如果我将cx1〔0〕导入该目录(只是为了测试前面的步骤是否已提交),我可以看到文件按预期存在:
1 2 3 4 5 6 7 8 9 10 | $ 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 |
如果我尝试运行
1 2 3 4 | $ docker run 3a87 bash source bash: line 1: source: filename argument required source: usage: source filename [arguments] |
我可以从这里运行脚本,然后愉快地访问
我做了一些挖掘,最初看起来问题可能在于bash作为Ubuntu登录shell和dash作为Ubuntu系统shell的区别,dash不支持
然而,对此的答案似乎是使用"."而不是
从dockerfile运行指令中运行shell脚本的最佳方法是什么(运行Ubuntu12.04LTS的默认基本映像)。
原始答案
1 2 | FROM ubuntu:14.04 RUN rm /bin/sh && ln -s /bin/bash /bin/sh |
这应该适用于每个Ubuntu Docker基本映像。我通常会为我写的每一个文档添加这一行。
由相关旁观者编辑如果你想在整个dockerfile中使用
1 | SHELL ["/bin/bash","-c"] |
* The possible damage is that many scripts in Linux (on a fresh Ubuntu install
grep -rHInE '/bin/sh' / returns over 2700 results) expect a fully POSIX shell at/bin/sh . The bash shell isn't just POSIX plus extra builtins. There are builtins (and more) that behave entirely different than those in POSIX. I FULLY support avoiding POSIX (and the fallacy that any script that you didn't test on another shell is going to work because you think you avoided basmisms) and just using bashism. But you do that with a proper shebang in your script. Not by pulling the POSIX shell out from under the entire OS. (Unless you have time to verify all 2700 plus scripts that come with Linux plus all those in any packages you install.)
在下面的答案中有更多的细节。https://stackoverflow.com/a/45087082/117471
我有同样的问题,为了在virtualenv内执行pip安装,我必须使用以下命令:
1 2 3 4 5 6 7 | 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" |
希望有帮助。
最简单的方法是使用点运算符代替source,它相当于bash
而不是:
1 | RUN source /usr/local/bin/virtualenvwrapper.sh |
用途:
1 | RUN . /usr/local/bin/virtualenvwrapper.sh |
1 | RUN"source file" # translates to: RUN /bin/sh -c"source file" |
使用shell指令,可以更改dockerfile中后续
1 | SHELL ["/bin/bash","-c"] |
现在,默认shell已经更改,您不需要在每个运行指令中显式定义它。
1 | RUN"source file" # now translates to: RUN /bin/bash -c"source file" |
附加说明:您还可以添加
基于本页的答案,我将补充说,您必须注意,每个run语句都独立于使用
到目前为止,我发现的最好方法是将脚本添加到
1 2 | RUN echo"source /usr/local/bin/virtualenvwrapper.sh">> /etc/bash.bashrc RUN /bin/bash --login -c"your command" |
例如,可以安装和设置virtualenvwrapper,创建虚拟env,在使用bash登录时激活它,然后将python模块安装到该env中:
1 2 3 4 5 6 7 | 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或更高版本,只需使用
一般:
1 | SHELL ["/bin/bash","-c"] |
对于python vituralenv:
1 | SHELL ["/bin/bash","-c","source /usr/local/bin/virtualenvwrapper.sh"] |
长回答:
来自https://docs.docker.com/engine/reference/builder//shell
1 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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 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 helloThe 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:
1
2
3 ...
RUN powershell -command Execute-MyCmdlet -param1"c:\foo.txt"
...The command invoked by docker will be:
1 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:
1
2
3 ...
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:
1
2
3
4
5
6
7 # 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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 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.
根据http:/ / / / / docs.docker.com引擎参考/ # Builder运行默认的[ ]是
1 | RUN ["/bin/bash","-c","source /usr/local/bin/virtualenvwrapper.sh"] |
否则,使用"壳形式和指定一个不同的'运行在嵌套壳壳的结果。
1 2 3 4 | # 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"] |
如果你有超过1个命令需要一个不同的壳,你应该读docs.docker.com http://////# Builder引擎参考壳壳放在改变你的默认运行在您的命令:
1 | SHELL ["/bin/bash","-c"] |
最后,如果你有什么经验,在根用户的文件,你需要
注:我有故意忽视这样的事实,它是一个开源的脚本不运行在一个唯一的指挥。
根据Docker文件
To use a different shell, other than ‘/bin/sh’, use the exec form passing in the desired shell. For example,
1 RUN ["/bin/bash","-c","echo hello"]
请参见https://docs.docker.com/engine/reference/builder/运行
我在运行dockerfile中的EDOCX1[0]时也遇到了问题。
这对于构建Centos6.6 Docker容器来说非常好,但在Debian容器中给出了问题。
1 | RUN cd ansible && source ./hacking/env-setup |
这就是我解决问题的方式,也许不是一种优雅的方式,但这对我来说是有用的。
1 2 3 | RUN echo"source /ansible/hacking/env-setup">> /tmp/setup RUN /bin/bash -C"/tmp/setup" RUN rm -f /tmp/setup |
您可能需要运行
我将执行以下操作,而不是使用符号链接:
这可能是因为
我来把我的东西在
1 2 3 4 5 6 7 8 9 | SHELL ["/bin/bash","-c","-l"] # Install ruby version specified in .ruby-version RUN rvm install $(<.ruby-version) # Install deps RUN rvm use $(<.ruby-version) && gem install bundler && bundle install CMD rvm use $(<.ruby-version) && ./myscript.rb |
如果你只是想用pip在virtualenv中安装一些东西,你可以先修改路径env来查找virtualenv的bin文件夹。
然后,dockerfile后面的任何