web-dev-qa-db-ja.com

Dockerfileで 'source'を指定してRUN命令を使用しても機能しません

私は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'エラーが発生します。対話型のシェルセッションを実行した場合、ソースは機能します。

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

ここからスクリプトを実行して、workonmkvirtualenvなどにうまくアクセスできます。

私は少し掘り下げましたが、最初は問題がUbuntu login Shellのようにbashの違いにあるかのように見えました。 _、およびダッシュ、Ubuntu システムシェルダッシュsourceコマンドをサポートしていません。

しかし、これに対する答えはsourceの代わりに '。'を使用することであるように見えますが、これは単にDockerランタイムをゴーパニックで爆発させます例外。

これを回避するためにDockerfile RUN命令からシェルスクリプトを実行するための最善の方法は何ですか(Ubuntu 12.04 LTSのデフォルトのベースイメージから実行しています)。

209

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

260
chobo

元の答え

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

これはすべてのUbuntu Dockerベースイメージに対して機能するはずです。私は一般的に私が書くすべてのDockerfileに対してこの行を追加します。

関係者による編集

alteringを使わずに、「このDockerfile全体でbashの代わりにshを使う」という効果を得たい場合は*コンテナの中のOS、あなたは Dockerにあなたの意図を伝えることができます 。それはこんな感じです:

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

*考えられる被害は、Linuxのmanyscripts(新鮮なUbuntuインストールでgrep -rHInE '/bin/sh' /が2700以上の結果を返す)が/bin/shで完全にPOSIXシェルを期待することです。 bashシェルはPOSIXと追加の組み込み関数だけではありません。 POSIXのものとは完全に異なる振る舞いをする(そしてそれ以上の)組み込み関数があります。私は完全にPOSIXを避け(そしてあなたが他のシェルでテストしなかったどんなスクリプトもあなたが基本主義を避けたと思うのでうまくいくだろうという誤謬)そして単にバシズムを使うことを支持する。しかし、あなたはそれをあなたのスクリプトの中の適切なShebangで行います。 OS全体の下からPOSIXシェルを引き出すのではありません。 (Linuxに付属している2700以上のスクリプトすべてと、インストールしたパッケージ内のすべてのスクリプトを確認する時間がない限り)

この回答の詳細は下記をご覧ください。 https://stackoverflow.com/a/45087082/117471

130
Anubhav Sinha

私は同じ問題を抱えていて、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"

助けになれば幸いです。

43
Andrea Grandi

最も簡単な方法は、sourceの代わりにドット演算子を使用することです。これは、bash sourceコマンドと同じshです。

の代わりに:

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

つかいます:

RUN . /usr/local/bin/virtualenvwrapper.sh
39
mixja

RUN命令のデフォルトのシェルは["/bin/sh", "-c"]です。

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

シェル命令 を使用して、Dockerfileの後続のRUN命令のデフォルトシェルを変更できます。

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

これで、デフォルトのシェルが変更され、すべてのRUN命令で明示的に定義する必要がなくなりました。

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

追加の注意事項:ログインシェルを開始する--loginオプションを追加することもできます。これは例えば~/.bachrcが読まれることを意味します、そしてあなたはあなたのコマンドの前にそれを明示的に供給する必要はありません。

37

このページの答えを基にして、各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起動ファイル のマニュアルを読むと、何がいつ提供されるのかを理解するのに役立ちます。

20
TomDotTom

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 ["executable", "parameters"]

Shell命令は、Shell形式のコマンドに使用されるデフォルトのShellをオーバーライドすることを可能にします。 Linuxのデフォルトのシェルは["/ bin/sh"、 "-c"]で、Windowsのデフォルトのシェルは["cmd"、 "/ S"、 "/ C"]です。シェル命令はDockerfileにJSON形式で記述する必要があります。

Shell命令は、cmdとpowershellの2つの一般的に使用されているまったく異なるネイティブシェル、およびshを含む利用可能な代替シェルがあるWindowsで特に便利です。

シェル命令は複数回現れることがあります。各シェル命令は、以前のすべてのシェル命令をオーバーライドし、後続のすべての命令に影響を与えます。例えば:

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

シェル形式のDockerfileをRock、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"

これは2つの理由で非効率的です。まず、不要なcmd.exeコマンドプロセッサ(別名シェル)が呼び出されています。次に、シェル形式の各RUN命令には、コマンドの先頭に追加のpowershellコマンドを追加する必要があります。

これをより効率的にするために、2つのメカニズムのうちの1つを使用することができる。 1つは、JSON形式のRUNコマンドを使用することです。

...
RUN ["powershell", "-command", "Execute-MyCmdlet", "-param1 \"c:\\foo.txt\""]
...

JSON形式はあいまいさがなく、不要なcmd.exeを使用していませんが、二重引用符とエスケープによる冗長性が必要です。代替のメカニズムは、特にエスケープパーサーディレクティブと組み合わせたときに、シェル命令とシェルフォームを使用して、Windowsユーザーにとってより自然な構文を作成することです。

# 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>

シェル命令はまた、シェルが動作する方法を修正するためにも使用され得る。たとえば、WindowsでShell cmd/S/C/V:ON | OFFを使用すると、遅延環境変数展開セマンティクスを変更できます。

Zsh、csh、tcshなどの代替シェルが必要な場合は、シェル命令をLinuxでも使用できます。

シェル機能はDocker 1.12で追加されました。

18
Mithril

https://docs.docker.com/engine/reference/builder/#run によると、RUNのデフォルトの[Linux]シェルは/bin/sh -cです。あなたはバシズムを期待しているように見えるので、あなたのシェルを指定するためにRUNの "exec形式"を使うべきです。

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

それ以外の場合は、RUNの "シェル形式"を使用して別のシェルを指定すると、シェルがネストされます。

# 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"]

別のシェルが必要なコマンドが複数ある場合は、 https://docs.docker.com/engine/reference/builder/#Shell そしてRUNコマンドの前にこれを配置してデフォルトのシェルを変更します。

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

最後に、必要なものをrootユーザーの.bashrcファイルに配置した場合は、ShellまたはRUNコマンドに-lフラグを追加して、それをログインシェルにして、確実にソースにすることができます。

注:スクリプトをRUN内の唯一のコマンドとして使用するのは無意味であるという事実を意図的に無視しました。

11
Bruno Bronosky

Dockerのドキュメントによると

/ bin/sh以外の別のシェルを使用するには、目的のシェルを渡したexecフォームを使用します。例えば、

RUN ["/bin/bash", "-c", "echo hello"]

https://docs.docker.com/engine/reference/builder/#run を参照してください。

8
Gianluca Casati

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
3
vikas027

これは、sourceがファイルシステム上のどこかにあるバイナリではなく、bashに組み込まれているために起こります。あなたがソースとなっているスクリプトが後でコンテナを変更することを意図していますか?

2
Paul Morie

何が供給されているのかを見るためにbash -vを実行したいと思うかもしれません。

シンボリックリンクで遊ぶ代わりに、次のようにします。

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

2
vimdude

Pipを使ってvirtualenvに何かをインストールしようとしているのであれば、まずPATH envを変更してvirtualenvのbinフォルダーを調べます。

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

それから、Dockerfileに続くpip installコマンドは、最初に/ path/to/venv/bin/pipを見つけてそれを使用します。これはシステムpythonではなくそのvirtualenvにインストールされます。

0
shadfc

Shellが利用可能な場合は、 この回答 -do n'tを使用してください。 このコメント ごとに1つのコマンドでdockerfileの残りを配置する必要があります。

古いDockerバージョンを使用しており、Shellにアクセスできない場合、.bashrc(Dockerfilesではまれなケースです)から何も必要ない限り、これは機能します。

ENTRYPOINT ["bash", "--rcfile", "/usr/local/bin/virtualenvwrapper.sh", "-ci"]

-iは、bashにrcfileを読み込ませるために必要です。

0
Mohan

私は自分の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
mattexx