首页 文章

使用带有'source'的Dockerfile中的RUN指令不起作用

提问于
浏览
174

我有一个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 命令,我会得到与上面相同的'not found'错误 . 但是,如果我运行交互式shell会话,则源确实有效:

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

我可以从这里运行脚本,然后愉快地访问 workonmkvirtualenv 等 .

我做了一些挖掘,最初看起来问题可能在于 bash 作为Ubuntu登录shell和 dash 作为Ubuntu系统shell之间的区别, dash 不支持 source 命令 .

但是,对此的答案似乎是使用 '.' 而不是 source ,但这只会导致Docker运行时爆炸,出现恐慌异常 .

从Dockerfile RUN指令运行shell脚本以解决此问题的最佳方法是什么(运行Ubuntu 12.04 LTS的默认基本映像) .

14 回答

  • 2

    原始答案

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

    这适用于每个Ubuntu docker基础映像 . 我通常为我写的每个Dockerfile添加这一行 .

    由有关旁观者编辑

    如果你想获得“在整个Dockerfile中使用 bash 而不是 sh ”的效果,没有 alteringpossibly damaging *容器内的操作系统,你可以只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

  • 114

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

    我希望它有所帮助 .

  • 7

    这可能会发生,因为 source 是一个内置的bash而不是文件系统上的某个二进制文件 . 你打算使用脚本来改变容器吗?

  • 7

    如果您只是尝试使用pip在virtualenv中安装某些东西,可以修改PATH环境以首先查看virtualenv的bin文件夹

    ENV PATH="/path/to/venv/bin:${PATH}"

    然后,Dockerfile中的任何 pip install 命令将首先找到/ path / to / venv / bin / pip并使用它,它将安装到virtualenv而不是系统python中 .

  • 15

    我最终把我的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
    
  • 0

    您可能希望运行 bash -v 以查看所获取的内容 .

    我会做以下而不是使用符号链接:

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

  • 215

    检查SHELL Command . Linux上的默认shell是["/bin/sh","-c"]

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

    您可以使用 SHELL 更改默认shell,这会更改用于Dockerfile中后续 RUN 指令的shell

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

    现在,默认shell已更改,您无需在每个RUN指令中明确定义它

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

    Additional Note :您还可以添加启动登录shell的 --login 选项 . 这意味着 ~/.bachrc 例如将被读取,您不需要在命令之前显式地获取它

  • 29

    最简单的方法是使用点运算符代替source,它是bash source 命令的sh等价物:

    代替:

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

    使用:

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

    基于此页面上的答案,我要补充一点,您必须知道每个RUN语句都使用 /bin/sh -c 独立于其他语句运行,因此不会获得通常在登录shell中获取的任何环境变量 .

    到目前为止,我发现的最好的方法是将脚本添加到 /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上的手册有助于了解何时采购 .

  • 19

    根据https://docs.docker.com/engine/reference/builder/#run RUN 的默认[Linux] shell是 /bin/sh -c . 您似乎期待bashisms,因此您应该使用"exec form" 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 文件中放置了所需的任何内容,则可以将 -l 标志添加到 SHELLRUN 命令以使其成为登录shell并确保其获取来源 .

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

  • 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
    
  • 36

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

    SHELL [“可执行文件”,“参数”]
    SHELL指令允许覆盖用于shell命令形式的默认shell . Linux上的默认shell是[“/ bin / sh”,“ - c”],在Windows上是[“cmd”,“/ S”,“/ C”] . SHELL指令必须以JSON格式写入Dockerfile . SHELL指令在Windows上特别有用,其中有两个常用且完全不同的本机shell:cmd和powershell,以及包括sh的备用shell . SHELL指令可以多次出现 . 每个SHELL指令都会覆盖所有先前的SHELL指令,并影响所有后续指令 . 例如:FROM microsoft / windowsservercore

    #执行为cmd / S / C echo默认值
    RUN echo默认

    #执行为cmd / S / C powershell -command Write-Host default
    RUN powershell -command Write-Host默认值

    #执行powershell -command Write-Host hello
    SHELL [“powershell”,“ - command”]
    RUN写主机问候

    #执行为cmd / S / C echo hello
    SHELL [“cmd”,“/ S”“,”/ C“]
    RUN echo你好
    当在Dockerfile中使用它们的shell形式时,SHELL指令可能会影响以下指令:RUN,CMD和ENTRYPOINT . 以下示例是在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形式的每个RUN指令都需要额外的powershell -command前缀命令 . 为了提高效率,可以采用两种机制中的一种 . 一种是使用RUN命令的JSON形式,例如:...
    RUN [“powershell”,“ - command”,“Execute-MyCmdlet”,“ - param1 \”c:\ foo.txt \“”]
    ...
    虽然JSON表单是明确的,并且不使用不必要的cmd.exe,但它确实需要通过双引号和转义更加详细 . 另一种机制是使用SHELL指令和shell表单,为Windows用户提供更自然的语法,特别是与转义解析器指令结合使用时:#escape =`

    来自microsoft / nanoserver
    SHELL [“powershell”,“ - command”]
    RUN New-Item -ItemType目录C:\ Example
    添加Execute-MyCmdlet.ps1 c:\ example
    运行c:\ example \ Execute-MyCmdlet -sample'hello world'
    导致:PS E:\ docker \ build \ shell> docker build -t shell .
    将构建上下文发送到Docker守护程序4.096 kB
    第1/5步:从microsoft / nanoserver
    ---> 22738ff49c6d
    第2/5步:SHELL powershell -command
    --->在6fcdb6855ae2中运行
    ---> 6331462d4300
    拆除中间容器6fcdb6855ae2
    步骤3/5:运行New-Item -ItemType目录C:\ Example
    --->在d0eef8386e97中运行

    目录:C:\

    模式LastWriteTime长度名称


    d ----- 10/28/2016 11:26 AM示例

    ---> 3f2fbf1395d9
    删除中间容器d0eef8386e97
    步骤4/5:添加Execute-MyCmdlet.ps1 c:\ example
    ---> a955b2621c31
    拆除中间容器b825593d39fc
    步骤5/5:运行c:\ example \ Execute-MyCmdlet'hello world'
    --->在be6d8e63fe75中运行
    你好,世界
    ---> 8e559e9bf424
    拆除中间容器be6d8e63fe75
    成功建成8e559e9bf424
    PS E:\ docker \ build \ shell>
    SHELL指令也可用于修改shell的运行方式 . 例如,在Windows上使用SHELL cmd / S / C / V:ON | OFF,可以修改延迟的环境变量扩展语义 . 如果需要备用shell,例如zsh,csh,tcsh等,也可以在Linux上使用SHELL指令 . 在Docker 1.12中添加了SHELL功能 .

  • 0

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

  • 1

    根据Docker文档

    要使用除'/ bin / sh'之外的其他shell,请使用exec表单传递在所需的外壳中 . 例如,RUN [“/ bin / bash”,“ - c”,“echo hello”]

    https://docs.docker.com/engine/reference/builder/#run

相关问题