Anaconda の公式 Docker イメージ をベースにしてイメージをビルドするときにハマった話。
[追記]
現在では PATH が戻っているようですね・・・。
https://github.com/ContinuumIO/docker-images/pull/148
イメージのビルドが失敗するようになった
今まで Anaconda の公式イメージをベースにしてデータ分析用のイメージを作ったりしていたのだけど、Anaconda のバージョンを 2019.07 に上げたところ conda や pip コマンドが使えなくなった。
FROM continuumio/anaconda3:2019.07
RUN conda install -y cudatoolkit
$ docker build .
/bin/sh: 1: conda: not found
ちなみに bash でログインした場合は問題なく使える。(Anaconda イメージのデフォルト起動コマンドは bash)
$ docker run --rm -it continuumio/anaconda3:2019.07
(base) # which conda
/opt/conda/bin/conda
これはログインシェルとして bash を起動した場合は conda activate が実行されるように設定されているため。
$ docker run --rm -it continuumio/anaconda3:2019.07 cat /root/.bashrc
(略)
. /opt/conda/etc/profile.d/conda.sh
conda activate base
しかし Dockerfile のビルド時は /bin/sh -c (debian の場合 /bin/sh は dash のエイリアス) で実行されるため自動で conda activate が実行されず、 conda や pip コマンドは使えない。
$ docker run --rm -it continuumio/anaconda3:2019.07 sh -c 'conda info -e'
sh: 1: conda: not found
ここで気になるのは「 2019.07 以前は使えていたじゃん」という点だが、これは何故かというと、以前は Anaconda イメージの Dockerfile に ENV PATH /opt/conda/bin:$PATH がベタ書きされていたので conda activate しなくても /opt/conda/bin 配下にある conda や pip コマンドにパスが通っていたため。 2019.07 ではこれが削除されてしまった。
Anaconda として正しい挙動は何か
Anaconda 公式的には「明示的に conda activate する」が正しく、アクティベートせずに使えていた以前の方が誤りらしい。1
個人的な意見としては、Docker コンテナ内でさらに conda 仮想環境を切り替える用途なんてそうそうないだろうし(そういう使い方をしたければ Docker コンテナを複数作ればよい)、最初から base がアクティベートされた状態にしれくれてもいいのになー、と思う。(ダメ元で Pull Request 投げてみたけどやはりダメだった)
ただまぁ、以前の状態は「sh -c のときもアクティベートされていた」わけではなく「たまたま /opt/conda/bin にパスが通っていたので使えていただけ (アクティベートされていない)」だったので、そのまま使い続けるのがお行儀が悪いというのは理解できる。
対処方法
おそらく最もお行儀の良いのは「 conda や pip など Anaconda 環境下にあるコマンドを使う場合は毎回アクティベートするコマンドを差し込む」方法だろう。
FROM continuumio/anaconda3:2019.07
RUN . /opt/conda/bin/activate \
&& conda install -y cudatoolkit
ADD . /app
WORKDIR /app
RUN . /opt/conda/bin/activate \
&& pip install -r requirements.txt
しかしこの方法では RUN が終了するたびにリセットされてしまう為 RUN のたびにアクティベートする必要があってとても面倒臭い。
もう一つの方法としては「ビルド時の RUN の実行コマンドを bash -l -c に変更する」が考えられる。 -l オプションをつけることでログインシェルとして扱われるので毎回 ~/.bashrc が読み込まれる。
FROM continuumio/anaconda3:2019.07
SHELL ["/bin/bash", "-l", "-c"]
RUN conda install -y cudatoolkit
ADD . /app
WORKDIR /app
RUN pip install -r requirements.txt
これなら RUN のたびに ~/.bashrc を読み込んで自動でアクティベートしてくれるので、毎回アクティベートをするコマンドを差し込む必要がなくなり、Dockerfile がスッキリする。
この方法は多少行儀は悪いものの「/opt/conda/bin を PATH に追加する (アクティベートせずに無理やり使う)」方法よりかはだいぶマシだと思う。
懸念点としては 「 sh -c から bash -l -c に変えたことで何か悪影響が出る可能性」だが、今のところ悪影響が出るケースが思いつかない。
何か思い当たる人はコメントしてもらえるとありがたい。
ビルドしたイメージからコンテナを起動するとき
ビルドしたイメージからコンテナを起動するときもまたアクティベートする必要がある。
RUN echo 'exec "$@"' > /entrypoint.sh
ENTRYPOINT ["/bin/bash", "-l", "/entrypoint.sh"]
今のところ、こんな感じで ENTRYPOINT を用意して bashrc を読み込んでもらう方法くらいしか思いつかない・・・。