Dev ContainersによるVSCode開発環境構築

背景

この記事では、GPU搭載のリモートワークステーションで動くdockerコンテナに手元のVSCodeからSSH接続して、GPUを使うpython開発環境について投稿した。一旦は、自分なりに構築したが、もっと改善点があるのではないかと考え、生成AIを使って改善したことについて述べる。

結論:改善のポイント

生成AIの回答のポイントは次のとおり。

現在の「起動済みDockerコンテナへAttachする方式」から、**プロジェクト自身がDocker+uv+VS Code設定を持つDev Container方式**に変えるのが分かりやすいです。

完成形の構成図

手元のMac
  └─ VS Code
       │ Remote SSH
Ubuntu GPUホスト
  ├─ NVIDIA Driver
  ├─ Docker
  ├─ NVIDIA Container Toolkit
  └─ ~/workspace/pinn/
       ├─ .devcontainer/
       │    ├─ Dockerfile
       │    └─ devcontainer.json
       ├─ pyproject.toml
       ├─ uv.lock
       ├─ .python-version
       ├─ .gitignore
       ├─ .dockerignore
       ├─ src/
       └─ notebooks/
              │ VS Code: Reopen in Container
       Docker Container
         ├─ CUDA Runtime
         ├─ uv(バージョン固定)
         ├─ Python 3.11
         └─ /workspace/.venv

設定ファイル

pyproject.toml

pythonライブラリは、次のとおりdependenciesに集約する。Dockerfileには記述しない。

[project]
name = "pinn"
version = "0.1.0"
description = "PyTorch GPU development environment for PINN"
requires-python = ">=3.11,<3.12"

dependencies = [
    "numpy",
    "pandas",
    "matplotlib",
    "scipy",
    "scikit-learn",
    "seaborn",
    "plotly",
    "tqdm",
    "jupyter",
    "ipykernel",
    "torch",
    "torchvision",
    "torchaudio",
]

[dependency-groups]
dev = [
    "ruff",
    "pytest",
]

Dockerfile

Docker build時には、pythonパッケージは入れない。Dockerコンテナは、CUDA+uv+pythonのみを受け持つ。

FROM nvidia/cuda:12.4.0-runtime-ubuntu22.04

ARG DEBIAN_FRONTEND=noninteractive

# --------------------------------------------------
# OS packages
# --------------------------------------------------
RUN apt-get update && apt-get install -y \
    ca-certificates \
    curl \
    git \
    build-essential \
    sudo \
    && rm -rf /var/lib/apt/lists/*

# --------------------------------------------------
# uv
# バージョンを固定
# --------------------------------------------------
COPY --from=ghcr.io/astral-sh/uv:0.11.32 \
    /uv /uvx /bin/

# --------------------------------------------------
# Python
# --------------------------------------------------
RUN uv python install 3.11

# --------------------------------------------------
# Workspace
# --------------------------------------------------
WORKDIR /workspace

# uvのキャッシュと.venvが別FSの場合の警告回避にも有効
ENV UV_LINK_MODE=copy

# .venvをPATHの先頭にする
ENV PATH="/workspace/.venv/bin:${PATH}"

CMD ["sleep", "infinity"]

devcontainer.json

{
    "name": "PyTorch GPU Development for PINN",

    "build": {
        "dockerfile": "Dockerfile",
        "context": ".."
    },

    "runArgs": [
        "--gpus=all"
    ],

    "workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind",
    "workspaceFolder": "/workspace",

    "postCreateCommand": "uv sync --locked",

    "customizations": {
        "vscode": {
            "extensions": [
                "ms-python.python",
                "ms-toolsai.jupyter"
            ],
            "settings": {
                "python.defaultInterpreterPath": "/workspace/.venv/bin/python"
            }
        }
    },

    "remoteEnv": {
        "PATH": "/workspace/.venv/bin:${containerEnv:PATH}"
    }
}

ここでのポイントは、次の部分。

"postCreateCommand": "uv sync --locked",

上記の設定で、Dev Containerを開こうとすると、次のようなメッセージとなる。(ubuntu・GPUホストにuvがインストールされてない場合。Dockerコンテナにはuvはインストールされている)

Running the postCreateCommand from devcontainer.json...

[88364 ms] Start: Run in container: /bin/sh -c uv sync --locked
Using CPython 3.11.15
Creating virtual environment at: .venv
error: Unable to find lockfile at `uv.lock`, but `--locked` was provided. To
       create a lockfile, run `uv lock` or `uv sync` without the flag.
[89785 ms] postCreateCommand from devcontainer.json failed with exit code 2. Skipping any further user-provided commands.
任意のキーを押してターミナルを終了します。

こうなった場合は、「“postCreateCommand”: “uv sync –locked”,」の部分を一時的に次のようにする。

"postCreateCommand": "uv sync"

上記変更後、VS Codeから「Dev Containers: Rebuild and Reopen in Container」を実行する。そうすると、「uv sync」が実行され、コンテナ内に以下が作成される。

/workspace/uv.lock
/workapace/.env/

結果として、Ubuntuホスト側には、以下が作成される。

~/workspace/pinn/uv.lock
~/workapace/pinn/.env/

この後、先程変更したdevcontainer.jsonの該当箇所を元に戻す("–locked"を付ける)。

接続手順

リモート(ubuntuホスト)に接続する

先ずは、MacのVS Codeで、「Command + Shift + p」によりコマンドパレットを開き以下を実行する。

Remote-SSH: Connect to Host...

SSHホストを選んで、ubuntuホストでの該当ユーザのパスワードを入力する。すると、新たなVS Codeが開かれubuntuホストに接続される。

「ssh接続の設定」については、冒頭リンクした記事のここを参照のこと。

リモート(ubuntu ホスト)のプロジェクトを開く

ubuntuホストに接続された状態のVS Codeで、「ファイル」→「フォルダを開く…」によって、ubuntuホストの以下を開く。

~/workspace/pinn

Dev Containerを起動

ubuntuホスト側のフォルダが開いた状態で、コマンドパレット(Command+Shift+p)を開き以下を実行する。

Dev Containers: Reopen in Container

ここで、Dockerfileがbuildされたりして暫く時間がかかる。

この間、次のようなことがVS Codeから行われている。

1. .devcontainer/devcontainer.json を読む
2. Dockerfileをbuild
3. --gpus=all でContainer起動
4. Ubuntu側projectを/workspaceへmount
5. uv sync --locked
6. /workspace/.venv作成
7. Python/Jupyter extensionsをContainer内へinstall
8. VS CodeをContainerへ接続

上記は生成AIの解説で、これまで手動で起動中のコンテナに接続していた操作が不要になるとのこと。

2回目以降の接続手順

  1. MacでVS Codeを起動
  2. コマンドパレット(command+Shift+p)から「Remote-SSH: Connect to Host…」実行
  3. ホストを選択して、パスワード入力すると、VS Codeの新たなウィンドウが開く
  4. VS Codeで、プロジェクトディレクトリ(pyproject.tomlを配置したディレクトリ)を開く
  5. VS Codeの右下に「フォルダーに開発コンテナーの構成ファイルが含まれています。コンテナで開発するフォルダをもう一度開きます。」がポップアップされるので、「コンテナーで再度開く」をクリック。
  6. コンテナが立ち上がり、GPUが使えるpythonの開発環境(プロジェクト)が使えるようになる。

まとめ

今回改善したpython開発環境は、VS Codeで目的の開発環境(プロジェクト)に行くだけで、自動的にコンテナの起動、接続が行われるので、とても便利。

また、責務の分離がされており、一度理解すると分かりやすい。


も参照してください