🧑🏻‍💻

VS Code + Docker Desktop で Python の開発を行うための設定と要点解説

に公開

VS Code の Developing inside a Container で Python の開発を行うための
各種設定の要点を解説します

なぜプログラム開発に仮想環境を使うべきなのかについては次を参照お願いします:

https://futureys.tokyo/lets-use-docker-when-create-virtual-environment-for-development/
https://futureys.tokyo/lets-use-virtual-environment-to-prepare-program-development/

VS Code と Docker の基礎的なことについても解説を省いているので、
難解に感じる場合は次の記事を参照お願いします:

https://futureys.tokyo/lets-develop-program-without-installing-language-by-docker-and-vscode/
https://futureys.tokyo/table-of-contents-vscode-lesson-for-remote-development/
https://futureys.tokyo/table-of-contents-docker-lesson-start-from-practical-use/

前提

この記事では次の前提に基づいた設定を解説します:

  • Docker イメージとしてどこかにデプロイすることは考慮しないことを前提とし、
    複雑さを減らしたり開発速度を重視した設定とします
  • 依存関係 Python パッケージの管理には uv を使うこととします
  • できる限り再現性を重視して、
    セマンティックバージョニングではパッチバージョンを固定します

デプロイを考慮しないことのメリット

Docker イメージとしてデプロイしないのであれば、
開発環境の構築専用の Dockerfile を作成することをお奨めします

開発環境として使いつつ、Docker イメージとしてデプロイすることもできるような
Dockerfile の書き方もできますが、
その場合は次のようなデメリットがあります:

  • Dockerfile の書き方が複雑になります
  • 開発時の Docker イメージのビルドが遅くなります

なお、Docker イメージとしてデプロイするための Dockerfile の書き方は、
公式ドキュメントの次の記事に一例が掲載されています:

https://docs.astral.sh/uv/guides/integration/docker/#intermediate-layers

また、次の記事で公式ドキュメントを踏まえた日本語解説がされています:

https://zenn.dev/shundeveloper/articles/5602a8a0131bd9

ファイルの配置

この後解説する各ファイルを次のように配置します:

your-project/
├── .devcontainer/
│   ├── compose.yml
│   └── devcontainer.json
├── compose.yml
├── Dockerfile
├── pyproject.toml
└── uv.lock

そして VS Code で上記 your-project ディレクトリーを開き、
コマンドパレットから Remote-Containers: Reopen in Container を実行すれば
VS Code が開き直されます
開いた VS Code は次のような状態になります:

  • uv が使えるターミナルが開けます
  • Python 開発を効率的に行うための設定が一通り行われています

Dockerfile の設定

Dockerfile
# uv の公式イメージはパッチバージョンを固定できないので使用しません
# また、Alpine ではなく Debian ベースのイメージを使用します
# Python で Alpine イメージを使うと次のデメリットがあるため:
# - ビルドがとても遅くなります
# - イメージが大きくなります
# - 時間を浪費することになります
# - 場合によってはよくわからないランタイムのバグが発生します
# 詳細は次の記事を参照お願いします:
# - Using Alpine can make Python Docker builds 50× slower
#   https://pythonspeed.com/articles/alpine-docker-python/
FROM python:3.13.5-slim-bookworm
# .devcontainer/devcontainer.json の workspaceFolder で指定したディレクトリーと
# 同じにします
# 上記のディレクトリーに開発用の Python 仮想環境を作成する必要があるため
WORKDIR /workspace
# uv 公式が推奨するインストール方法です
# メリットはおそらく次の通りです:
# - Docker によってキャッシュされるので、Docker イメージの再ビルドが速くなります
# - uv が pip の依存関係にならなくなります
# Using uv in Docker | uv
# https://docs.astral.sh/uv/guides/integration/docker/#installing-uv
COPY --from=ghcr.io/astral-sh/uv:0.7.15 /uv /uvx /bin/
COPY pyproject.toml uv.lock /workspace/
# インストール時にダウンロードする依存関係 Python パッケージを
# Docker Volume にキャッシュします
# これにより、Docker イメージの再ビルドが速くなります
# uv 公式が推奨する Python プロジェクト依存関係 Python パッケージインストール方法です:
# - Using uv in Docker | uv
#.  https://docs.astral.sh/uv/guides/integration/docker/#caching
ENV UV_LINK_MODE=copy
RUN --mount=type=cache,target=/root/.cache/uv \
    uv sync

compose.yaml の設定

compose.yml
services:
  # .devcontainer/devcontainer.json の service で指定した名前と同じにする必要があります
  your-service-name-here:
    # 開発環境として起動するコンテナーを Dockerfile をビルドして作成するようにするため
    build: .
    volumes:
      # .devcontainer/devcontainer.json の workspaceFolder で
      # 設定するディレクトリーと同じディレクトリーに
      # compose.yml が配置されているディレクトリーをマウントします
      # これにより、Developing inside a Container で開かられる VS Code で
      # compose.yml が配置されているディレクトリーのファイルが開けるようになります
      # 公式のテンプレートではホスト側の更新を優先するよう
      # .:/workspace:cached となっていますが、意図は不明です:
      # - Use cached consistency in bind mounts · microsoft/vscode-dev-containers@5b6371c
      #   https://github.com/microsoft/vscode-dev-containers/commit/5b6371c0daf9e8e1e263eee45ef1fbbbf5aa66b8
      - .:/workspace
      # Docker イメージのビルド時に作成される仮想環境をマウントします
      # この設定がないと、Docker イメージのビルド時に作成される仮想環境が
      # 上で設定されているマウントによって消えてしまい、次の問題が発生します:
      # - VS Code で Reopen in Container した後に再度 uv sync する必要があります
      # - 仮想環境のディレクトリー: .venv がホスト側に作成され残ってしまいます
      # - パフォーマンスが低下します
      # 参考:
      # - node_modules/ and Docker volume mount 問題と対策 - castaneaiのブログ
      #   https://castaneai.hatenablog.com/entry/2019/01/29/151257
      # - Answer: node.js - Docker-compose: node_modules not present in a volume after npm install succeeds - Stack Overflow
      #   https://stackoverflow.com/a/32785014/12721873
      # - Improve disk performance
      #   https://code.visualstudio.com/remote/advancedcontainers/improve-performance#_use-a-targeted-named-volume
      - /workspace/.venv
      # uv が依存関係 Python パッケージをインストールするための
      # キャッシュディレクトリーを Docker の volume にキャッシュしておきます
      # これにより、uv sync の実行時に依存関係 Python パッケージのダウンロードが
      # Docker の volume にキャッシュされるので、uv sync の高速化が期待できます
      # uv 公式が推奨する方法です:
      # - Using uv in Docker | uv
      #   https://docs.astral.sh/uv/guides/integration/docker/#caching
      - uv-cache:/root/.cache/uv
volumes:
  uv-cache:

.devcontainer/compose.yml の設定

このファイルでは Developing inside a Container のための enrtypoint と command を設定します
1 つめの compose.yml に設定してこのファイルを省いてもよいのですが、
ファイルを分けておくと、開発した Python コードを開発者以外にも使ってもらうことができます (後述)

.devcontainer/compose.yml
services:
  # .devcontainer/devcontainer.json の service で指定した名前と同じにする必要があります
  your-service-name-here:
    # VS Code の Developing inside a Container を使うためには、
    # デフォルトのコンテナコマンドが失敗したり終了したりして
    # コンテナがシャットダウンしないようにする必要があります
    # そのため、コンテナーの起動時に実行される ENTRYPOINT を消去して、デフォルトのコマンドを上書きします
    # 参考:
    # - Create a Dev Container
    #   https://code.visualstudio.com/docs/devcontainers/create-dev-container#_use-docker-compose
    entrypoint: []
    command: sleep infinity

.devcontainer/devcontainer.json の設定

このファイルには customizations.vscode の設定を記述する必要がありますが、
設定内容のベストプラクティスの変化が激しいので、
ここでは Developing inside a Container の観点から必要な設定のみを記述して解説します

customizations.vscode を含めた完全な設定内容は次の記事を参照お願いします:
https://zenn.dev/y_shinoda/articles/python-development-settings-devcontainer-json

.devcontainer/devcontainer.json
{
  // name は何でも構いません
  "name": "Existing Docker Compose (Extend)",
  // root ディレクトリーの compose.yml から読み込むように、次の順に設定します
  // - compose.yml 内の設定で使われている相対パスは
  //   最初に読み込んだ compose.yml が基準となるため
  // - 後に読み込んだ compose.yml の設定が先に読み込んだ設定を上書きするため
  "dockerComposeFile": [
    "../compose.yml",
    "./compose.yml"
  ],
  "service": "your-service-name-here",
  // [Add dev container configuration files...] を選択すると追加される
  // devcontainer.json のテンプレートでは
  // `/workspaces/${localWorkspaceFolderBasename}` となっていますが、
  // clone repository in container volume 方式の
  // Developing inside a Container を行わない限り
  // 次の設定で問題ありません:
  // - VS Code + Docker Desktop デフォルトが親ディレクトリーをマウントする設定の理由
  //   https://zenn.dev/y_shinoda/articles/reasons-why-parent-folder-is-mounted-by-default
  "workspaceFolder": "/workspace",
  "customizations": {
    "vscode": {
      "extensions": [
        // ベストプラクティスの変化が激しいため省略
      ],
      "settings": {
        // ベストプラクティスの変化が激しいため省略
      }
    }
  }
}

最低限の pyproject.toml

すでに pyproject.toml が存在する場合は不要ですが、
まだ存在しない場合は、用意しておかないと Docker イメージのビルド時にエラーになるので、
最低限の内容を記述した
次のような pyproject.toml を用意しておきます (内容は環境構築後に更新してください):

pyproject.toml
[project]
name = "workspace"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = []

最低限の uv.lock

同様に、すでに uv.lock が存在する場合は不要ですが、
まだ存在しない場合は、用意しておかないと Docker イメージのビルド時にエラーになるので、
最低限の内容を記述した次のような uv.lock を用意しておきます:

uv.lock
version = 1
revision = 2
requires-python = ">=3.13"

[[package]]
name = "workspace"
version = "0.1.0"
source = { virtual = "." }

開発した Python コードを開発者以外にも使ってもらう方法

前述の設定ファイルをすべて Git 管理しておけば、
Developing inside a Container を使わない開発者にも
開発した Python コードを使ってもらうことができます
その場合は、次のいずれかの設定を行うことで実現できます

  • Dockerfile の ENTRYPOINT
  • compose.yml の entrypoint

例えば main.py というファイルを開発したとすると、
次のような Dockerfile の設定を行うことで、
Developing inside a Container を使わない開発者にも
開発した Python コードを使ってもらうことができます:

Dockerfile
ENTRYPOINT ["uv", "run", "main.py"]

そして、Developing inside a Container を使わない開発者は、
プロジェクトを Git clone し、次のようにして開発した Python コードを実行できます:

docker compose run --rm your-service-name-here <main.py に渡す引数>

Discussion