昔書いた記事: yamlで保存してあるconda仮想環境をDocker上で構築をふと見返してみると、色々と微妙というか「個人的には今ならこうは書かないな。。。」と思ったのでアップデート版を書いてみる。
概要
前提知識:
-
conda cliの基本と概念
- 特にAnacondaやminicondaで仮想環境を作ることができ、yamlファイルの形でexport出来ること
- 逆に、yamlファイルを取り込むことでAnacondaやminicondaなどで仮想環境を作成出来ること
- docker周りの基本
- linux周りの基本
やること:
-
conda env export
コマンドなどから生成したyamlファイルや、それと同じ形式で手作業などで作成したyamlファイルからdockerで動作するconda環境を作る- せっかくDockerでやるので、なるべく無駄なものは入れずミニマルに作る方針
前回からの主な改善点
-
multistage-buildを使う
- ツールのビルドなど下準備用のLayerを実際に動かすイメージから分離出来るため、余計なものをインストールしなくて良くなったり、キャッシュファイルの削除を頑張らなくても良くなったりする。すなわち、イメージサイズの削減につながるし、余計なものが無い方がセキュリティ的にも良いと思われる。
- conda環境の生成にmambaを使っている
-
conda
のC++による再実装(githubのREADMEより)であり、conda互換でかつ高速(ビルド時間の削減)
-
- conda環境の生成にmambaforgeイメージを利用している
- Anacondaリポジトリの商用利用が有償化されており、minicondaを使っているとdefault channel(=Anaconda公式リポジトリ)からパッケージを取得してしまう可能性があった気がするので、商用利用にあたる場合、使うyamlファイルで全パッケージリポジトリレベルまで指定されていない場合はminicondaを使わないほうが無難と思われる
-
miniforgeはやや不正確ながらざっくりいうとdefault channelがconda-forgeになっている
miniconda
のようなもの。mambaforgeはそれのmamba
コマンド(先述)が使えるバージョン - あと、前回はminicondaのインストールから始めていたので、今回は最初から
mamba
が使えるイメージを使うことで記述内容をかなり簡略化できた
- (本質では無いが)docker内で使うユーザーの権限見直し
- 以前の記事ではrootへの昇格を許していたが、通常はやらない方が良いので改めている
改善後の環境生成方法
今回は以下のyamlファイル:
name: example_env
channels:
- conda-forge
dependencies:
- python==3.10.*
- pandas
- jupyterlab
- pip
- pip:
- python-language-server[all]
を使ってconda環境を作る。
内容は割とナンセンスで本質的では無いので注意。
使うDockerfileは例えば以下のような感じにする。
FROM condaforge/mambaforge:latest as builder
COPY conda_env.yaml .
RUN mkdir -p /usr/local/conda && \
mamba env create -f ./conda_env.yaml -p /usr/local/conda
# 今回の例ではベースイメージにubuntuを使う。(mambaforgeイメージのベースも執筆時点でubuntuだったりする)
FROM ubuntu:latest as main
# NOTE: 環境によって設定すべき値は変わるので、必要があれば編集する
# または、ARGとしているのでbuild時に値を与えて変更する
ARG USERNAME=user
ARG GROUPNAME=user
ARG UIDVALUE=1000
ARG GIDVALUE=1000
# NOTE: 使いたいパッケージがあれば適宜追加する
RUN apt-get update && \
apt-get install --no-install-recommends --yes \
tini && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# rootユーザーで動かすのは避けたいのでユーザーを作成する
# 設定するユーザー名やUIDなどは必要に応じて上記のARGの値で変更する
RUN groupadd --gid ${GIDVALUE} ${GROUPNAME} && \
useradd --uid ${UIDVALUE} --gid ${GROUPNAME} --shell /bin/bash --create-home ${USERNAME}
# この例では/usr/local/condaディレクトリ上にconda環境を格納する
RUN mkdir -p /usr/local/conda
# builderイメージからそっくりそのままコピーする(所有権は変えておく)
COPY --from=builder --chown=${UIDVALUE}:${UIDVALUE} /usr/local/conda/ /usr/local/conda
# 実行ユーザーをrootから先程作成したものに切り替え
USER ${UIDVALUE}
# 作成したconda環境にPATHを追加する
ENV PATH=/usr/local/conda/bin:${PATH}
# ----------------------------------------------------------------------------------------------- #
# ここから下は特にユースケース次第で完全に変わる部分なので適当に編集すること。
# 今回はそこそこ実用的な例?としてjupyterlabを起動している
# なお、例なのでjupyterの起動オプションでtoken=''(ログイン認証無し)としているが、むやみにやるのは良くない
WORKDIR /home/${USERNAME}
EXPOSE 8888
ENTRYPOINT ["tini", "--"]
CMD ["jupyter", "lab", "--ip=0.0.0.0", "--NotebookApp.token=''"]
multi-stage buildを使ったことなどにより、前回記事よりも簡潔に記述できている。(トリッキーな部分を大幅に少なくできている)
なお、作成するconda環境内のbin
ディレクトリだが、一部のコマンドは絶対パスの参照をもつシンボリックがある?ようで、
builderとmainとでconda環境の置き場所の絶対パスを同じにしないと動かなかったので、編集して使う際は注意。
これで、上記のDockerfile
とconda_env.yaml
を置いてあるディレクトリで
docker build . -t <タグ名>
などとすればイメージが作れる。
なお、厳密に条件を揃えた訳では無いが、同一のconda_env.yaml
を使って作成したDockerイメージのサイズを比較したところ、執筆時点(2022/11/4)では前回記事の方法で850MB、今回の方法では753MBとなった。
思ったよりもイメージサイズを削減できていることがわかった。ちゃんと調べていないが、前回記事はminicondaをインストールして動かしたりするためにapt-get
などで入れないといけないパッケージが若干あったため、主にその分で差が出ている?と思われる。(正直どちらも割と大きいが)
(↑今回の例ではなんとなくtiniを入れているが必須のものではなく、aptなどでマストで追加しないといけないパッケージは実質存在しない)
感想
- multi-stage buildは便利
- (正直ここ最近ほぼ全くPythonとかcondaとか触っていないので、知識のアップデート漏れがあったら申し訳ないです)