Edited at

Anaconda の公式 Docker イメージがアクティベーションしないと使えなくなった件

Anaconda の公式 Docker イメージ をベースにしてイメージをビルドするときにハマった話。


イメージのビルドが失敗するようになった

今まで Anaconda の公式イメージをベースにしてデータ分析用のイメージを作ったりしていたのだけど、Anaconda のバージョンを 2019.07 に上げたところ condapip コマンドが使えなくなった。


Dockerfile

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/shdash のエイリアス) で実行されるため自動で conda activate が実行されず、 condapip コマンドは使えない。

$ 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 配下にある condapip コマンドにパスが通っていたため。 2019.07 ではこれが削除されてしまった。


Anaconda として正しい挙動は何か

Anaconda 公式的には「明示的に conda activate する」が正しく、アクティベートせずに使えていた以前の方が誤りらしい。1

個人的な意見としては、Docker コンテナ内でさらに conda 仮想環境を切り替える用途なんてそうそうないだろうし(そういう使い方をしたければ Docker コンテナを複数作ればよい)、最初から base がアクティベートされた状態にしれくれてもいいのになー、と思う。(ダメ元で Pull Request 投げてみたけどやはりダメだった)

ただまぁ、以前の状態は「sh -c のときもアクティベートされていた」わけではなく「たまたま /opt/conda/bin にパスが通っていたので使えていただけ (アクティベートされていない)」だったので、そのまま使い続けるのがお行儀が悪いというのは理解できる。


対処方法

おそらく最もお行儀の良いのは「 condapip など Anaconda 環境下にあるコマンドを使う場合は毎回アクティベートするコマンドを差し込む」方法だろう。


Dockerfile

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 が読み込まれる。


Dockerfile

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/binPATH に追加する (アクティベートせずに無理やり使う)」方法よりかはだいぶマシだと思う。

懸念点としては 「 sh -c から bash -l -c に変えたことで何か悪影響が出る可能性」だが、今のところ悪影響が出るケースが思いつかない。

何か思い当たる人はコメントしてもらえるとありがたい。


ビルドしたイメージからコンテナを起動するとき

ビルドしたイメージからコンテナを起動するときもまたアクティベートする必要がある。


Dockerfile

RUN echo 'exec "$@"' > /entrypoint.sh

ENTRYPOINT ["/bin/bash", "-l", "/entrypoint.sh"]

今のところ、こんな感じで ENTRYPOINT を用意して bashrc を読み込んでもらう方法くらいしか思いつかない・・・。