問題の概要:Docker内でのcondaとpipの混在による環境破壊
Dockerコンテナ内で機械学習開発環境を構築する際、condaとpipを混在させてパッケージをインストールすると、依存関係の競合が発生し、環境が破壊されることがあります。具体的には、condaでインストールしたパッケージを、後からpipでアップグレードしたり、逆にpipでインストールしたパッケージをcondaで管理しようとすると、以下のようなエラーが発生します。
# 典型的なエラーメッセージの例
ImportError: libcudart.so.11.0: cannot open shared object file: No such file or directory
# または
ConflictError: The installed package 'numpy' conflicts with the existing package 'numpy'
# あるいは、より不可解なエラー
ModuleNotFoundError: No module named 'torch'
# (torchはインストール済みなのに見つからない)
この問題は、condaが独自の依存関係解決エンジンとパッケージキャッシュを持ち、pipがPyPIを参照して別の依存関係解決を行うために発生します。両者が同じ環境(例えば、/opt/conda 配下)のパッケージを上書きし合うことで、整合性が失われてしまうのです。
原因の解説:なぜ競合が起こるのか?
競合の根本原因は、condaとpipの「管理領域」の重複にあります。
1. パッケージ管理のメタデータの不一致
condaは conda-meta/ ディレクトリにインストール済みパッケージのメタデータを保存します。pipは site-packages/ 内の *.dist-info/ または *.egg-info/ ディレクトリに情報を保存します。condaはpipでインストールされたパッケージを認識できず、逆もまた然りです。このため、依存関係の解決が正しく行われなくなります。
2. バイナリ互換性の問題
conda-forgeやanacondaチャネルから配布されるパッケージは、特定のバージョンのライブラリ(例: Intel MKL, CUDA)に対してビルドされています。PyPIからpipでインストールされる同じ名前のパッケージは、異なるバイナリでビルドされている可能性が高く、リンクするシステムライブラリが異なると実行時エラーの原因となります。
3. Dockerレイヤーキャッシュとの相性問題
Dockerfile内で conda install と pip install を別々のRUN命令で行うと、レイヤーが分離されます。後でpip installのレイヤーだけを変更して再ビルドすると、condaのレイヤーはキャッシュから再利用されます。この状態でcondaが管理するパッケージをpipで上書きすると、キャッシュされたcondaレイヤーと新しいpipレイヤーの間で不整合が生じます。
解決方法:Docker内での安全な環境構築手順
原則は「一つの環境では一つのパッケージマネージャに徹する」ことです。conda環境内では、可能な限りcondaだけで全てのパッケージを管理するのが最善策です。
ステップ1: ベースイメージの選択
NVIDIA GPUを使用する場合は、CUDAがプリインストールされた公式のcondaイメージを使うと便利です。
# Dockerfile
FROM nvidia/cuda:11.8.0-cudnn8-runtime-ubuntu22.04
# または、Minicondaがインストール済みのイメージを利用
FROM continuumio/miniconda3:latest
ステップ2: condaのみで環境を構築する(推奨方法)
必要なパッケージがcondaチャネル(conda-forgeを含む)で提供されている場合は、condaのみを使用します。environment.ymlファイルを作成し、依存関係を明記します。
# environment.yml
name: ml-env
channels:
- conda-forge
- defaults
dependencies:
- python=3.10
- numpy=1.24
- pandas=2.0
- scikit-learn=1.3
- pytorch=2.0
- torchvision
- torchaudio
- pytorch-cuda=11.8
- pip # pip自体はcondaでインストール
- pip:
- some-pypi-only-package==1.0.0 # condaにないパッケージのみここに記述
Dockerfileではこのファイルを使って環境を作成します。
# Dockerfile
COPY environment.yml .
RUN conda env create -f environment.yml && conda clean -afy
SHELL ["conda", "run", "-n", "ml-env", "/bin/bash", "-c"]
ENV PATH /opt/conda/envs/ml-env/bin:$PATH
ステップ3: やむを得ずpipを使う場合の安全な手順
condaに存在しないパッケージをインストールする必要がある場合、以下のルールを守ります。
- condaで可能な限り多くの依存パッケージ(特にNumPy, SciPy, TensorFlow/PyTorchなどの基盤ライブラリ)を先にインストールする。
- condaでpipをインストールする(
conda install pip)。これにより、conda環境内のPythonと互換性のあるpipが使われます。 - pipインストールは単一のRUN命令内で、condaインストールの直後に行う。これにより、condaとpipの操作が一つのDockerレイヤーにまとめられ、不整合が起こりにくくなります。
# Dockerfile (安全な例)
RUN conda install -y python=3.10 numpy pandas scikit-learn &&
conda install -y -c pytorch pytorch torchvision torchaudio cudatoolkit=11.8 &&
conda install -y pip &&
pip install --no-cache-dir some-pypi-only-package another-pypi-package &&
conda clean -afy
ステップ4: 競合が発生した場合の修復方法
既に環境が破壊されてしまった場合、Docker内では環境を再構築するのが最も確実です。しかし、調査のために以下のコマンドで状況を確認できます。
# コンテナ内で実行
# condaでインストールされたパッケージをリストアップ
conda list
# pipでインストールされたパッケージをリストアップ
pip list
# 特定のパッケージ(例: numpy)がどちらから来たか確認
conda list | grep numpy
pip show numpy
修復するには、競合しているパッケージを一旦すべてアンインストールし、正しい順序で再インストールします。
# 危険なパッケージを削除(例: pipでインストールされたnumpy)
pip uninstall -y numpy torch
# condaから正しいバージョンを再インストール
conda install -y numpy pytorch
# その後、必要なPyPI専用パッケージをpipでインストール
pip install some-pypi-only-package
コード例・コマンド例:実践的なDockerfile
以下に、PyTorch環境を構築する完全なDockerfileの例を示します。
# Dockerfile
FROM continuumio/miniconda3:latest
WORKDIR /workspace
# 1. environment.ymlをコピー
COPY environment.yml .
# 2. conda環境の作成(pipで入れるべきパッケージもここで定義)
RUN conda env create -f environment.yml &&
conda clean -afy
# 3. シェルをconda環境に設定
SHELL ["conda", "run", "-n", "ml-env", "/bin/bash", "-c"]
# 4. 環境変数でPATHを通す(オプションだが便利)
ENV PATH /opt/conda/envs/ml-env/bin:$PATH
# 5. アプリケーションコードのコピー
COPY src/ ./src/
# 6. デフォルトコマンドはconda環境内で実行される
CMD ["python", "src/app.py"]
まとめ・補足情報
Dockerコンテナ内でのcondaとpipの競合を防ぐための要点をまとめます。
- 基本方針: 一つの仮想環境では一つのパッケージマネージャ(conda)に可能な限り統一する。
- conda優先: 基盤ライブラリ(NumPy, SciPy, PyTorch, TensorFlow, CUDA関連)は必ずcondaでインストールする。
- Dockerレイヤーの考慮: conda installとpip installは可能な限り同じRUN命令内で実行し、レイヤーを分離しない。
- 環境定義のコード化:
environment.ymlファイルを使用して環境を再現可能にする。pip専用パッケージはこのファイルのpip:セクションに記述する。 - イメージの軽量化:
conda clean -afyを実行してキャッシュを削除し、Dockerイメージサイズを小さく保つ。
最後に、どうしてもcondaとpipの混在が必要な場合は、公式ドキュメント「Using pip in an environment」で推奨される手順に従い、常に「conda first, pip last」の順序を守ることを心がけてください。この原則を守ることで、Dockerコンテナ内のPython環境は安定し、再現性の高いAI開発基盤を構築できるでしょう。