🐳

Dockerを使ったWindows上の実験環境構築

2023/09/27に公開

TL;DR

イメージからgpuコンテナをビルドして起動

docker image build -t <container name> .
docker run --name <container name> --gpus all --shm-size 8gb -v <e.g. /C/project/dir>:/workspace -i -t <image name>

これまではVSCodeのDockerとReopen Containerでbuild imageからrunまで全部を扱っていたのですが、エラーでコンテナにアクセスできない事件が起こると解決が難しいため、素でDockerを使えるように覚書を残すことにしました。

よくこういうエラーに出くわす
Command failed: 
C:\Users\USER\AppData\Local\Programs\Microsoft VS Code\Code.exe 
  --ms-enable-electron-run-as-node c:\Users\USER\.vscode\extensions\ms-vscode-remote.remote-containers-0.309.0\dist\spec-node\devContainersSpecCLI.js up 
  --user-data-folder c:\Users\USER\AppData\Roaming\Code\User\globalStorage\ms-vscode-remote.remote-containers\data 
  --container-session-data-folder /tmp/devcontainers-32d3095e-6f39-41e8-a350-ccb11b3f17631695564713488 
  --workspace-folder e:\EXPERIMENT\project_1 
  --workspace-mount-consistency cached 
  --id-label devcontainer.local_folder=e:\EXPERIMENT\project_1
  --id-label devcontainer.config_file=e:\EXPERIMENT\project_1\.devcontainer\devcontainer.json 
  --log-level debug 
  --log-format json 
  --config e:\EXPERIMENT\project_1\.devcontainer\devcontainer.json 
  --default-user-env-probe loginInteractiveShell 
  --mount type=volume, source=vscode, target=/vscode, external=true 
  --skip-post-create 
  --update-remote-user-uid-default on 
  --mount-workspace-git-root true

他にもGPUとメモリのアクセスなどでRemote Explorerがアクセスできないことがある。
これが起こるともうどうしようもない。

Dockerfile用意

実験はE:\EXPERIMENT\project_1\の上で行う。全体の流れとしては、まずDocker上のコンテナを作り、GPUアクセスが可能な状態にしてコンテナへログインする。たったこれだけなのだが、Dockerへの謎の忌避感があるため、精神的に重労働である。なぜ...?

まずDockerfileを書く。最小構成は次のようなimageを使うことが多い。

Dockerfile
ARG PYTORCH="2.1.1"
ARG CUDA="12.1"
ARG CUDNN="8"

FROM pytorch/pytorch:${PYTORCH}-cuda${CUDA}-cudnn${CUDNN}-devel

ENV TORCH_CUDA_ARCH_LIST="6.0 6.1 7.0 7.5 8.0 8.6+PTX" \
    TORCH_NVCC_FLAGS="-Xfatbin -compress-all" \
    CMAKE_PREFIX_PATH="$(dirname $(which conda))/../" \
    FORCE_CUDA="1" \
    DEBIAN_FRONTEND=noninteractive

# Install the required packages
RUN apt-get update
RUN apt-get install -y ffmpeg git graphviz \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# torch torchvision
RUN pip install ipython jupyter ruff graphviz \
    && pip install numpy scikit-learn scikit-image pillow pandas polars matplotlib seaborn openpyxl \ 
    && pip install torchinfo torchmetrics torchview lightning timm transformers pymc
CMD ["/bin/bash"]

Containerの作成・実行・停止

箇条書きで行いたい操作をノートしていく。

imageをビルド

E:\EXPERIMENT\project_1 $ ls
    dockerfile
    dataset/
    code.py

# path上にあるDockerfileをビルドしてimageを作成
# docker image build [オプション] PATH
# option: `--tag` (`-t`) ... 'name:tag' により名前とタグを指定
E:\EXPERIMENT\project_1 $ docker image build -t experiment:v1 .

# imageを確認
E:\EXPERIMENT\project_1 $ docker image ls
    REPOSITORY      TAG       IMAGE ID       CREATED        SIZE
    experiment      v1        762dce37c70c   now            13.2GB

containerを作成して実行

docker runで作成と実行を同時に行う。作成だけのときはdocker createを使う。

# 新たなコンテナー内でコマンドを実行
# docker run [オプション] IMAGE [COMMAND] [ARG...]
# option: `--gpu` ... コンテナに追加する GPU デバイスを指定(API 1.40 以上で対応)
# option: `--shm-size` ... /dev/shm (Dockerに占有を許可するメモリ?)のサイズを指定
# option: `-v` (`--volume`) ... ボリュームをバインドマウントする 
# option: `-i` (`--interactive`) ... コンテナにアタッチしていない状態でも標準出力を開放し続ける
# option: `-t` (`--tty`) ... 疑似TTY(標準出力先のターミナル)を割り当てる
E:\EXPERIMENT\project_1 $ docker run \
    --name phi_brain
    --gpus all \
    --shm-size 8gb \
    -v /E/EXPERIMENT/project_1:/workspace \
    -i \
    -t \
    experiment:v1
    
# コンテナが作成された後自動的に実行されてログイン状態になる
# E:\EXPERIMENT\project_1直下の内容が/workspace内へバインドマウントされている
root@3c81667e09f0:/workspace $ ls
    dockerfile
    dataset/
    code.py

この-vが曲者で、"<コンテナへ継げたいホストのディレクトリ>:<コンテナ内でマウント先を展開したいディレクトリ>"の形式でabsolute pathを渡さなければ行けないのだが、Windowsの特徴により最初ここでつまずいていた。
Windows側での素の表記はE:\EXPERIMENT\project_1\なのだが、Dockerへ渡す場合は/E/EXPERIMENT/project_1のようにUNIX的な表記にする必要がある。区切り文字などはMS-DOS時代の名残だと思うが、この仕様を考えた人は外出の度にちょっと長めの赤信号に掛かる呪いにかかってほしい。

containerから退出

# コンテナを起動したまま退出し、もとのターミナルの標準出力へ戻る
root@3c81667e09f0:/workspace $ # (ここで CTRL + P + Q)

# コンテナの確認
E:\EXPERIMENT\project_1 $ docker ps
    CONTAINER ID   IMAGE           COMMAND        CREATED       STATUS    PORTS   NAMES
    3c81667e09f0   experiment:v1   "/opt/nvid…"   1 hours ago   Up 1 hours        phi_brain

または

# コンテナを停止
root@3c81667e09f0:/workspace $ # exit

E:\EXPERIMENT\project_1 $ docker ps
    CONTAINER ID   IMAGE           COMMAND        CREATED       STATUS    PORTS   NAMES

containerへ再接続

# 再接続する
# docker attach [オプション] コンテナID
E:\EXPERIMENT\project_1 $ docker attach 3c81667e09f0

root@3c81667e09f0:/workspace $ 

これでいつでも実験を再開できる。

またはコンテナが停止している場合は次のように起動する。

E:\EXPERIMENT\project_1 $ docker start 3c81667e09f0
E:\EXPERIMENT\project_1 $ docker attach 3c81667e09f0

root@3c81667e09f0:/workspace $ 

DockerへVSCodeを接続

環境自体はこれで整うためCLIから全ての操作が可能なのだが、GUI上での操作に慣れた軟弱者なので標準入出力だけでは不便である。
VSCodeのDockerプラグインとRemote Explorerで快適な実験ライフを送ろう。

ms-azuretools.vscode-docker 1.26.1 Microsoft
https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker

ms-vscode.remote-explorer 0.4.1 Microsoft
https://marketplace.visualstudio.com/items?itemName=ms-vscode.remote-explorer

インストールしたら、左のタブからDockerのアイコンを開き、接続するコンテナを選択する。


新しいwindowが開き、プロジェクトが開かれる。

これによりVSCodeのReopen Containerでエラー地獄になっても実験が滞りません。やったね。

おまけ: Dcoker Engineの保存場所変更

実験に使っているとDockerエンジンが肥大化しがちなので、OSが格納されているストレージから隔離して何が起こっても大丈夫にしておくと安心になる。

内容は https://e-penguiner.com/change-location-of-docker-on-windows/ の通りなので割愛するが、実行することだけをを端的にまとめると次になる。

# C:\Users\USER\AppData\Local\Docker\wsl\distro\ext4.vhdx ← docker engine
# C:\Users\USER\AppData\Local\Docker\wsl\data\ext4.vhdx ← docker image & container
# move ↑ to E:\DOCKER\wsl...

# export to new dir
$ wsl --export docker-desktop "E:\DOCKER\wsl\data\docker-desktop.tar"
$ wsl --export docker-desktop-data "E:\DOCKER\wsl\distro\docker-desktop-data.tar"

# delete old setting
$ wsl --unregister docker-desktop
$ wsl --unregister docker-desktop-data

# install distribution to <InstallLocation> from <InstallTarFile> as version 2
$ wsl --import docker-desktop "E:\DOCKER\wsl\data" "E:\DOCKER\wsl\data\docker-desktop.tar"
$ wsl --import docker-desktop-data "E:\DOCKER\wsl\distro" "E:\DOCKER\wsl\distro\docker-desktop-data.tar"

Discussion