我有一个Dockerfile,我正在整理它来安装一个vanilla 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
如果我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
命令,我会得到与上面相同的“未找到”错误。但是,如果我运行交互式shell会话,则源确实有效:
$ docker run 3a87 bash
source
bash: line 1: source: filename argument required
source: usage: source filename [arguments]
我可以从这里运行脚本,然后愉快地访问workon
,mkvirtualenv
等。
我已经做了一些挖掘,最初看起来问题可能在于 bash 与Ubuntu 登录shell 和破折号之间的区别作为Ubuntu 系统shell ,破折号不支持source
命令。
然而,对此的答案似乎是使用'。'而不是source
,但这只会导致Docker运行时出现乱码异常。
从Dockerfile RUN指令运行shell脚本以解决此问题的最佳方法是什么(运行Ubuntu 12.04 LTS的默认基本映像)。
答案 0 :(得分:259)
RUN /bin/bash -c "source /usr/local/bin/virtualenvwrapper.sh"
答案 1 :(得分:128)
FROM ubuntu:14.04
RUN rm /bin/sh && ln -s /bin/bash /bin/sh
这适用于每个Ubuntu docker基础映像。我通常为我写的每个Dockerfile添加这一行。
如果您希望在整个Dockerfile中使用“bash
而不是sh
”,则不会更改并且可能会损坏 *容器内的操作系统,您只需tell Docker your intention。这是这样做的:
SHELL ["/bin/bash", "-c"]
*可能的损害是Linux中的许多脚本(在新的Ubuntu安装
grep -rHInE '/bin/sh' /
上返回超过2700个结果)期望/bin/sh
处的完整POSIX shell。 bash shell不只是POSIX加上额外的内置函数。内置(和更多)与POSIX中的内容完全不同。我完全支持避免POSIX(以及任何你没有在另一个shell上测试的脚本因为你认为你避免使用basmisms而工作的谬论)并且只是使用了bashism。但是你在脚本中使用适当的shebang来做到这一点。不是从整个操作系统下拉出POSIX shell。 (除非您有时间验证Linux附带的所有2700多个脚本以及您安装的任何软件包中的所有脚本。)
以下答案中的更多细节。 https://stackoverflow.com/a/45087082/117471
答案 2 :(得分:43)
我有同样的问题,为了在virtualenv中执行pip安装,我不得不使用这个命令:
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"
我希望它有所帮助。
答案 3 :(得分:39)
最简单的方法是使用点运算符代替source,它是bash source
命令的等价物:
而不是:
RUN source /usr/local/bin/virtualenvwrapper.sh
使用:
RUN . /usr/local/bin/virtualenvwrapper.sh
答案 4 :(得分:37)
RUN
指令的默认shell为["/bin/sh", "-c"]
。
RUN "source file" # translates to: RUN /bin/sh -c "source file"
使用SHELL instruction,您可以更改Dockerfile中后续RUN
指令的默认shell:
SHELL ["/bin/bash", "-c"]
现在,默认shell已更改,您无需在每个RUN指令中明确定义
RUN "source file" # now translates to: RUN /bin/bash -c "source file"
附加说明:您还可以添加--login
选项以启动登录shell。这意味着例如~/.bachrc
将被读取,您无需在命令
答案 5 :(得分:20)
在此页面上的答案的基础上,我想补充一点,你必须知道每个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登录时激活它,然后将python模块安装到此环境中:
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 startup files上的手册有助于了解何时采购。
答案 6 :(得分:17)
如果您使用的是Docker 1.12或更高版本,请使用SHELL
!
一般:
SHELL ["/bin/bash", "-c"]
for python vituralenv:
SHELL ["/bin/bash", "-c", "source /usr/local/bin/virtualenvwrapper.sh"]
来自https://docs.docker.com/engine/reference/builder/#/shell
SHELL ["executable", "parameters"]
SHELL指令允许使用shell表单的默认shell 要覆盖的命令。 Linux上的默认shell是 [" / bin / sh"," -c"],在Windows上是[" cmd"," / S",&# 34 / C&#34]。贝壳 必须在Dockerfile中以JSON格式编写指令。
SHELL指令在Windows上特别有用 是两种常用且完全不同的原生shell:cmd和 powershell,以及备用shell,包括sh。
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
SHELL指令可能会影响以下指令 当它们的shell形式在Dockerfile中使用时:RUN,CMD和 入口点。
以下示例是在Windows上找到的常见模式 通过使用SHELL指令简化:
... RUN powershell -command Execute-MyCmdlet -param1 "c:\foo.txt" ...
docker调用的命令为:
cmd /S /C powershell -command Execute-MyCmdlet -param1 "c:\foo.txt"
由于两个原因,效率很低。首先,没有必要 正在调用cmd.exe命令处理器(也称为shell)。第二,每次运行 shell形式的指令需要额外的powershell -command 为命令添加前缀。
为了提高效率,可以采用两种机制中的一种。 一种是使用RUN命令的JSON形式,例如:
... RUN ["powershell", "-command", "Execute-MyCmdlet", "-param1 \"c:\\foo.txt\""] ...
虽然JSON表单是明确的,但不使用不必要的表单 cmd.exe,它确实需要通过双引号和更多的详细程度 逃跑。替代机制是使用SHELL指令和 shell表单,为Windows用户提供更自然的语法, 特别是与escape parser指令结合使用时:
# 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指令也可用于修改a的方式 shell操作。例如,使用SHELL cmd / S / C / V:ON | OFF Windows,延迟环境变量扩展语义可以 修改。
如果备用,SHELL指令也可以在Linux上使用 需要shell,如zsh,csh,tcsh等。
在Docker 1.12中添加了SHELL功能。
答案 7 :(得分:11)
根据https://docs.docker.com/engine/reference/builder/#run,RUN
的默认[Linux] shell为/bin/sh -c
。你似乎期待bashisms,所以你应该使用" exec形式" RUN
指定你的shell。
RUN ["/bin/bash", "-c", "source /usr/local/bin/virtualenvwrapper.sh"]
否则,使用" shell表单" RUN并指定不同的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"]
最后,如果您在root用户的.bashrc
文件中放置了所需内容,则可以将-l
标记添加到SHELL
或{{1}命令使其成为登录shell并确保它获取源。
注意:我故意忽略了这样一个事实:将脚本作为RUN中唯一的命令来源是没有意义的。
答案 8 :(得分:8)
根据Docker文档
要使用除'/ bin / sh'之外的其他shell,请使用传入所需shell的exec表单。例如,
RUN ["/bin/bash", "-c", "echo hello"]
答案 9 :(得分:3)
我在Dockerfile中运行source
时遇到了问题
这对于构建CentOS 6.6 Docker容器非常好,但在Debian容器中存在问题
RUN cd ansible && source ./hacking/env-setup
这是我解决它的方式,可能不是一种优雅的方式,但这对我有用
RUN echo "source /ansible/hacking/env-setup" >> /tmp/setup
RUN /bin/bash -C "/tmp/setup"
RUN rm -f /tmp/setup
答案 10 :(得分:2)
您可能希望运行bash -v
以查看来源。
我会执行以下操作,而不是使用符号链接:
RUN echo "source /usr/local/bin/virtualenvwrapper.sh" >> /etc/bash.bashrc
答案 11 :(得分:2)
这可能会发生,因为source
是bash的内置内容,而不是文件系统上某处的二进制文件。你打算使用脚本来改变容器吗?
答案 12 :(得分:0)
如果您只是尝试使用pip在virtualenv中安装某些内容,则可以修改PATH环境以首先查看virtualenv的bin文件夹
ENV PATH="/path/to/venv/bin:${PATH}"
然后Dockerfile中的任何pip install
命令将首先找到/ path / to / venv / bin / pip并使用它,它将安装到virtualenv而不是系统python中。
答案 13 :(得分:0)
我最终把我的env内容放在.profile
中并且突变SHELL
类似
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
答案 14 :(得分:0)
如果您有SHELL
可用,则应使用this answer-不要使用已接受的一个,这将迫使您将其余的dockerfile放入一个this comment中的命令。
如果您使用的是较旧的Docker版本,但无权访问SHELL
,那么只要您不需要.bashrc
中的任何内容,这便会起作用(在Dockerfiles中很少见):
ENTRYPOINT ["bash", "--rcfile", "/usr/local/bin/virtualenvwrapper.sh", "-ci"]
请注意,-i
是使bash完全读取rcfile所必需的。
答案 15 :(得分:0)
这是一个示例Dockerfile,它利用几种巧妙的技术为您提供了每个$audit->getMetadata()
节的完整conda环境。您可以使用类似的方法在脚本文件中执行任意准备。
注意:关于登录/交互式与非登录/非交互式外壳,信号RUN
,处理多个arg的方式,引用how CMD and ENTRYPOINT interact和一百万,有很多细微差别其他事物,因此,如果在使用这些事物进行黑客入侵时发现东西横摆,不要气be。我花了许多令人沮丧的时间来研究各种文学作品,但我仍然不太了解所有点击的方式。
exec
答案 16 :(得分:0)
对于使用Django Web网络框架开发的应用程序,我正在处理类似的情况,而这些步骤对我来说是完美的:
[mlazo@srvjenkins project_textile]$ cat docker/Dockerfile.debug
FROM malazo/project_textile_ubuntu:latest
ENV PROJECT_DIR=/proyectos/project_textile PROJECT_NAME=project_textile WRAPPER_PATH=/usr/share/virtualenvwrapper/virtualenvwrapper.sh
COPY . ${PROJECT_DIR}/
WORKDIR ${PROJECT_DIR}
RUN echo "source ${WRAPPER_PATH}" > ~/.bashrc
SHELL ["/bin/bash","-c","-l"]
RUN mkvirtualenv -p $(which python3) ${PROJECT_NAME} && \
workon ${PROJECT_NAME} && \
pip3 install -r requirements.txt
EXPOSE 8000
ENTRYPOINT ["tests/container_entrypoint.sh"]
CMD ["public/manage.py","runserver","0:8000"]
[mlazo@srvjenkins project_textile]$ cat tests/container_entrypoint.sh
#!/bin/bash
# *-* encoding : UTF-8 *-*
sh tests/deliver_env.sh
source ~/.virtualenvs/project_textile/bin/activate
exec python "$@"
[mlazo@srvjenkins project_textile]$ cat ./tests/container_deployment.sh
#!/bin/bash
CONT_NAME="cont_app_server"
IMG_NAME="malazo/project_textile_app"
[ $(docker ps -a |grep -i ${CONT_NAME} |wc -l) -gt 0 ] && docker rm -f ${CONT_NAME}
docker run --name ${CONT_NAME} -p 8000:8000 -e DEBUG=${DEBUG} -e MYSQL_USER=${MYSQL_USER} -e MYSQL_PASSWORD=${MYSQL_PASSWORD} -e MYSQL_HOST=${MYSQL_HOST} -e MYSQL_DATABASE=${MYSQL_DATABASE} -e MYSQL_PORT=${MYSQL_PORT} -d ${IMG_NAME}
我真的希望这对其他人有帮助。
问候,
答案 17 :(得分:0)
我遇到了同样的问题。如果您还使用 Python 基础映像,则可以将 shell 脚本中的 shebang 行更改为 #!/bin/bash
。
例如,参见 Manuel Lazo 中的 container_entrypoint.sh
。