🙌

Windows11同士でDockerを使ってPythonの開発環境を別のPCに移行する

2025/03/01に公開

概要

学習用にDockerを使用して異なるパソコンに環境を移行する手順を試したので、その忘備録。
数年後に同じことをやる時に思い出すための手がかりとして書いています。

読者層

・Docker?podman?コンテナ?イメージファイルって何なのよ?ってレベルの方。
・開発環境をDockerで初めて移行してみたいという方

共通の注意点

・PowerShellで動かないときはコマンドプロンプト、またはWSLでコマンドを叩く必要があります。
・本記事においては、基本的にはコマンドプロンプトでコマンドを叩いてるとお考えください。

Dockerよりもっと簡単な方法(Windows同士での環境連携)

Windows同士で環境をコピーするだけなら仮想環境(venv)からrequirements.txtを出力させて、

.\venv\Scripts\Activate #仮想環境のアクティベート
pip freeze > requirements.txt # 再実行時には上書きされる
deactivate #仮想環境のディアクティベート

メモ:pipreqsを使用すると、importされているライブラリのみをリストアップしてくれるらしい。今回は使っていない。

その後移行先でpip installを叩くだけ

pip install -r requirements.txt

で良いのですが、

もう少し現場に応用の効くDockerを試したい

と考えました。
Podmanを使ってリリースすることは現場で何度か経験したものの、イメージファイルを使用してコンテナ自体を扱うことが何なのか、一度理解しておきたい。

ざっくり用語

Docker
コンテナを管理実行するプログラム。

Podman
コンテナを管理実行するプログラム。ではあるが、デーモンを使用せずrootless(root権限が存在しない)であるため、ホストOSへの影響を限定的にできることで、よりセキュアな実行環境の構築が可能

これら2ツールの違い
コマンド上の操作感に大きな違いはないので、どちらかを学習すれば応用が効く。今回はDockerを扱う。

イメージ
実行可能なアプリケーションのテンプレ。(OS, ミドルウェア, ライブラリ, 設定などを含む)
pyやjarを含める場合も含めない場合もある。

コンテナ
イメージを実行して生成した実行環境インスタンス。

Dockerfile
イメージの中身に何を含めるか指定したファイル。今回はソースコードを含めない。

Docker使用準備

Docker導入

・Docker for Windows
以下からダウンロードしてインストーラを実行するだけ。
https://www.docker.com/ja-jp/get-started/

WSL導入(省略可・Linuxに環境移行する場合は使う)

・WSL2
以下コマンドプロンプトかPowerShellでコマンドを叩けばインストールされる。
https://learn.microsoft.com/ja-jp/windows/wsl/install

wsl --install
実行結果
ダウンロード中: Linux 用 Windows サブシステム 2.4.11
インストール中: Linux 用 Windows サブシステム 2.4.11
Linux 用 Windows サブシステム 2.4.11 はインストールされました。
この操作を正しく終了しました。 
ダウンロード中: Ubuntu
インストール中: Ubuntu
ディストリビューションが正常にインストールされました。'wsl.exe -d Ubuntu' を使用して起動できます

Dockerファイル作成

.dockerignore

.dockerignore
venv
__pycache__
*.pyc
*.pyo
*.pyd
.git

# 開発環境関連
.env
.venv
env/
.idea/
.vscode/
*.swp
*.swo
.DS_Store

# テスト関連
__pycache__/
.pytest_cache/
.coverage
htmlcov/
.tox/

# ビルド関連
*.egg-info/
dist/
build/
*.egg

# ログ関連
*.log
logs/

# その他
.dockerignore
Dockerfile
README.md

Dockerfile

Dockerfile
# ベースとなるPythonの公式イメージ(Pythonのバージョンは適宜変更)
FROM python:3.13-slim
# FROM python:3.13 #こっちはイメージサイズ削減のため使わない

# 作業ディレクトリを設定
WORKDIR /app

# OSレベルのライブラリをインストール_これらはPyQt/PySideを使うために必要
RUN apt-get update && apt-get install -y \
    libgl1-mesa-glx \
    libxkbcommon0 \
    libxkbcommon-x11-0 \
    libdbus-1-3 \
    libxcb-icccm4 \
    libxcb-image0 \
    libxcb-keysyms1 \
    libxcb-randr0 \
    libxcb-render-util0 \
    libxcb-xinerama0 \
    libxcb-shape0 \
    libegl1 \
    libopengl0 \
    libgles2 \
    libfontconfig1 \
    libglib2.0-0 \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# 必要なファイルをコンテナにコピー
COPY requirements.txt .

# ライブラリをインストール
RUN pip install --no-cache-dir -r requirements.txt

# アプリケーションのコードをコピー。GitHubからソースを取得する場合はコメントアウトする
# COPY . .

# PYTHONPATHを設定して、アプリケーションのディレクトリをPythonのimportパスに追加
ENV PYTHONPATH=/app/src

# エントリーポイント(変更が必要なら適宜修正)
# CMD ["python", "main.py"]

requirements.txt

後述のエラー2ににて記述。

Dockerビルドとトラブルシューティング

Dockerfile
docker build -t app_name . 

・app_nameの部分は任意のわかりやすい値にする。
・ビルドするタイミングでは仮想環境をアクティベートする必要はない。

エラー1
ERROR: error during connect: Head "http://%2F%2F.%2Fpipe%2FdockerDesktopLinuxEngine/_ping": open //./pipe/dockerDesktopLinuxEngine: The system cannot find the file specified.

【解決方法】
・単純にDockerを起動してない時に起きる。
・Docker Desktopを起動後に再度コマンドを叩けば解消される。

エラー2。Gitからコードの取得に失敗
> [4/5] RUN pip install --no-cache-dir -r requirements.txt:
1.356 Obtaining my_project from git+https://github.com/username/repository.git@<commit_hash>#egg=my_project (from -r requirements.txt (line 5))
1.357   Cloning https://github.com/username/repository.git (to revision <commit_hash>) to ./src/my_project
1.359   Running command git clone --filter=blob:none --quiet https://github.com/username/repository.git /app/src/my_project
1.627   fatal: could not read Username for 'https://github.com': No such device or address
1.650   error: subprocess-exited-with-error

【解決方法】
pip freezeで自動生成したrequirements.txtを、以下の通り変更するだけ。
-eの記述は状況や好みに応じて変更が必要。今回はローカルのソースを使用するため、-eオプションはコメントアウトしてOK。

requirements.txt
#-e git+https://github.com/username/repository.git@<commit_hash>#egg=my_project # GitHubから取得する場合のみ記述する

【備考】
・DockerFileに「COPY . .」が記載されているため、ローカルのソースを利用される。これにより-eオプションは省略して良い。

【Gitからソースを取得する場合(詳細は今回は触れない)】
・プライベートレポジトリからソースを取得する処理のために、トークンを設定しておく必要がある
・この方法は最新のコードをDockerビルドに含めることができるメリットがあるが、アクセストークンを管理する手間が生じる。
・CI/CDパイプラインに適している。トレードオフ。

Dockerイメージの出力

[+] Building 92.7s (11/11) FINISHED

と出ていればイメージ作成は成功。

# これでアクティブなディレクトリにイメージファイルが出力される。
docker save app_name > app_name.tar

出直されたファイルを、環境移行先のパソコンにコピーして皇族の処理を実行すれば良い。

Dockerイメージのテスト

このまま環境移行先のパソコンにデータをコピーしたとて、例外が発生して結局利用できないなんてことがまあまあある。
ファイルサイズもそれなりに大きくなりがちなこともあり、こうした手間と時間のロスを避けるためにも、環境移行前のテスト必須と言える。

docker run -it -v D:/projects/app_name:/app docker_image_name /bin/bash
Docker コンテナを作成し実行するコマンド。以下の要素に分解できる。

docker run -it:
-i : 標準入力を開いたままにする
-t : ターミナルを割り当てる

-v D:/projects/app_name:/app:
D:/projects/app_name (ホスト側、つまり環境移行先のパス)
/app (コンテナ内のマウント先)
ホストのディレクトリをコンテナにマウントするオプション
D:/projects/app_name (ホスト側のパス)
これは Dockerfile の WORKDIR /app で設定したディレクトリと一致させると便利

docker_image_name:
実行する Docker イメージ名で、「docker image」コマンドで表示されるREPOSITORY名。
docker build -t docker_image_name . コマンドでビルドした際のタグ名でもある

/bin/bash:
コンテナ内で実行するコマンド
これは Dockerfile の CMD 命令をオーバーライドする。

Dockerイメージのロードと実行

・出力されたDockerイメージを含んだtarは展開せず、そのまま移行先のディレクトリにコピーする
・コピーした先で以下コマンドを実行する。ただし状況により使い分ける

【共通_Dockerにイメージをロードする】

移行先の環境で実行
> docker load < invoicerenamer.tar

【共通_Dockerのイメージを表示する】

移行先の環境で実行
> docker images
REPOSITORY       TAG       IMAGE ID       CREATED       SIZE
app_name   latest    4bd209b7c3b6   8 hours ago   1.8GB

パターン1_Dockerイメージ内のAppを実行する

移行先のIDEで実行
> docker run invoicerenamer   
REPOSITORY       TAG       IMAGE ID       CREATED       SIZE
app_name   latest    4bd209b7c3b6   8 hours ago   1.8GB
Traceback (most recent call last):
 File "/app/main.py", line 2, in <module>
   from PySide6.QtWidgets import QApplication
ImportError: libGL.so.1: cannot open shared object file: No such file or directory

ImportError: libGL.so.1ライブラリが不足しているという例外
ここでは言及しないが、このような例外が発生した場合はDockerfileの修正が必要。
今回の場合、以下を追記したら改善された。
RUN apt-get update && apt-get install -y libgl1-mesa-glx

パターン2_DockerイメージをホストPC内にボリュームマウントする(恐らくこちらが一般的)

ホストPCとはイメージをマウントするパソコンのことで、ボリュームマウントを実行するだけでは、ホスト側の既存ソースコードが上書きされることはない。言い換えるとホストPCのソースコードが優先される。

このため、最低限のソースコードはDockerイメージからボリュームマウントにより参照し、最新のコードはGitなどから取得して開発やテストを行えるようになる。

移行先の環境で実行
> docker run -it -v D:/projects/app_name:/app docker_image_name /bin/bash

・"C:\path\to\your\code":/appの太字部分にはプロジェクトフォルダを指定
your_image_nameにはそのままイメージ名を指定
/bin/bashエントリーポイントの指定(CMD ["python", "main.py"])を無視してこちらを実行する。Dockerfile次第では省略可。

ボリュームマウントした後起動したコマンドで、main.pyを実行すると、GUIプログラムが起動する。

移行先環境で実行
root@abbcccdddd:/app# python main.py

本件での結果は、この通りGUIライブラリが不足してるという問題が発生するが、とりあえず起動命令までは試せたことが確認できる。

qt.qpa.plugin: From 6.5.0, xcb-cursor0 or libxcb-cursor0 is needed to load the Qt xcb platform plugin.
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.

Available platform plugins are: minimal, offscreen, eglfs, wayland-egl, vkkhrdisplay, xcb, wayland, minimalegl, linuxfb, vnc.

Aborted (core dumped)

とりあえず知恵熱でそうw

Discussion