🌾

【Rye + uv + Ruff】Docker で VS Code の Dev Container 上に快適な Python 環境を構築する

2024/04/11に公開

0. はじめに

株式会社ディー・エヌ・エーに入社し,MLOps エンジニアをやっている @a5chin です.

本記事では,図 1 の様に VS Code の Dev Container 上に爆速で快適な Python 環境を構築することを目指します.

Ruff
図 1: Dev Container 上で開発をすると Ruff による自動フォーマット[1]と pre-commit が走る

https://github.com/a5chin/python-rye/tree/main

本記事の内容は全て上記リポジトリで簡単に試すことができるので,ぜひ clone して試して頂けたらと思います↑
Dockerfile 内で,Rye, uv, そして Ruff をインストールする手順を記述することで開発環境を標準化し,異なる環境間での一貫性を担保することができます.

0.1. 事前準備

本記事で作成したリポジトリを動かすためには,Docker Desktop と VS Code のダウンロード,VS Code 上で Dev Container のインストールが必要です.
また,以下コマンドでリポジトリを clone すると,今後進めやすくなるので clone することをおすすめします.

git clone https://github.com/a5chin/python-rye

0.1.1. Docker Desktop のダウンロード

https://www.docker.com/ja-jp/products/docker-desktop/

上記リンクからお使いの OS に合った Docker Desktop をダウンロードしてください↑

Docker Desktop は Mac や Windows に簡単にインストールできるアプリケーションです.
これにより,コンテナ化アプリケーションやマイクロサービスを構築し共有することができます.

0.1.2. VS Code のダウンロード

https://code.visualstudio.com/download

上記リンクからお使いの OS に合った VS Code をダウンロードしてください↑

VS Code は Microsoft 社から無償で提供されており,Windows, macOS, および Linux で使用できる,軽量ながら強力なソースコードエディターです.
拡張機能によって,全体的な機能に加えてさらに機能を追加することでより快適に開発することができます.

0.1.3. Dev Container のインストール

https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers

VS Code 上で VS Code の拡張機能である,Dev Container をインストールしてください.
VS Code 上で⇧⌘xと入力すると,サイドに Extensions が開くのでms-vscode-remote.remote-containersと入力して,出てきた拡張機能をインストールしてください.

Dev Container は Microsoft が開発している VS Code の拡張機能です.
Dev Container を使うことで,VS Code から Docker コンテナを開発環境として使用できます.

1. 結論

https://github.com/a5chin/python-rye/tree/main

Step Save commit GitHub Actions
Python Formatter (Ruff)
Python Linter (Ruff)
Docker Linter (Hadolint)
pytest
docker build

上記対応表の様に,私が設定した Dev Countainer 上で開発をすることによって,Format[1:1], Lint[2], Test[3], Build[4] を自動化しています.

1.1. 実際の操作画面

Python プログラムを保存して,git で commit した時の操作画面です.
高速でとても快適です!

Ruff
図 2: Dev Container 上で開発をすると Ruff による自動フォーマット[1:2]と pre-commit が走る

2. Docker で開発環境を整える

以下では,主に Docker について解説していきます.
Dev Container は Docker コンテナを開発環境として作成するからです.

2.1. Rye を使える様にする

https://github.com/astral-sh/rye/tree/main
Rye は,上記 GitHub リポジトリで Astral 社が開発している Python のプロジェクト管理ツールです.
Rust[5] で開発されているため,とても高速に動作します.

以下のフローで Docker コンテナ内で Rye を使える様にします.

  1. Rye を curl でダウンロードする
  2. Rye を使える様に Path を通す
  3. ダウンロードした rye の所有者を変更する

Dev Container では vscode ユーザーで作業するので,ダウンロードした rye フォルダの所有者を vscode ユーザーに変更することをおすすめします.
所有者を root から変更しない場合は,VS Code 上で Rye の設定ファイルを書き換えることができないからです.

.vscode/Dockerfile
  FROM mcr.microsoft.com/vscode/devcontainers/base:bullseye

  WORKDIR /opt

  # hadolint ignore=DL3008
  RUN apt-get update && \
      apt-get install -y --no-install-recommends \
          ca-certificates \
          curl

  # curl を使って Rye をダウンロードする
+ SHELL [ "/bin/bash", "-o", "pipefail", "-c" ]
+ RUN curl -sSf https://rye-up.com/get | RYE_INSTALL_OPTION="--yes" bash && \
+     rye config --set-bool behavior.global-python=true

  # Rye を使える様に Path を通す
+ ENV RYE_HOME="/opt/rye"
+ ENV PATH="$RYE_HOME/shims:$PATH"

+ RUN chown -R vscode $RYE_HOME

2.2. uv を使える様にする

https://github.com/astral-sh/uv/tree/main

uv は,上記 GitHub リポジトリで Astral 社が開発している Python のパッケージマネージャーです.

Rye のコマンドで uv を使用するかどうかを設定できるので,uv を使いたい場合はrye config --set-bool behavior.use-uv=trueを実行してください.
デフォルトはfalseなので,uv を使うには明示的にtrueにする必要があります.

.vscode/Dockerfile
  FROM mcr.microsoft.com/vscode/devcontainers/base:bullseye

  WORKDIR /opt

  # hadolint ignore=DL3008
  RUN apt-get update && \
      apt-get install -y --no-install-recommends \
          ca-certificates \
          curl

  SHELL [ "/bin/bash", "-o", "pipefail", "-c" ]
  RUN curl -sSf https://rye-up.com/get | RYE_INSTALL_OPTION="--yes" bash && \
       rye config --set-bool behavior.global-python=true && \
+      rye config --set-bool behavior.use-uv=true

  ENV RYE_HOME="/opt/rye"
  ENV PATH="$RYE_HOME/shims:$PATH"

  RUN chown -R vscode $RYE_HOME

2.3. Ruff を使える様にする

https://github.com/astral-sh/ruff/tree/main

ruff
図 3: Lint[2:1] する際のスピード比較

Ruff は,上記 GitHub リポジトリで Astral 社が開発している Python 用の Formatter[1:3], Linter[2:2] です.
Rust[5:1] で書かれているため高速で,なんと Pylint[6] の 200 倍以上のスピードです!

.python-versionで指定した Python と pyproject.toml, requirements.lock, requirements-dev.lockで指定したパッケージを Rye の uv を使ってインストールします.

.vscode/Dockerfile
  FROM mcr.microsoft.com/vscode/devcontainers/base:bullseye

  WORKDIR /opt

  # hadolint ignore=DL3008
  RUN apt-get update && \
      apt-get install -y --no-install-recommends \
          ca-certificates \
          curl

  SHELL [ "/bin/bash", "-o", "pipefail", "-c" ]
  RUN curl -sSf https://rye-up.com/get | RYE_INSTALL_OPTION="--yes" bash && \
      rye config --set-bool behavior.global-python=true && \
      rye config --set-bool behavior.use-uv=true

  ENV RYE_HOME="/opt/rye"
  ENV PATH="$RYE_HOME/shims:$PATH"

+ COPY ./.python-version ./pyproject.toml ./requirements* ./
+ RUN rye pin "$(cat .python-version)" && \
+     rye sync

  RUN chown -R vscode $RYE_HOME

2.3.1. Ruff の設定

https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules

Ruff の設定はruff.tomlファイルで設定を書くことができ,基本的に公式から流用しています.
しかし,上記ドキュメントには Conflict してしまう設定は ignore することを推奨すると書かれていたので,追記しています.

ruff.toml
  # Exclude a variety of commonly ignored directories.
  exclude = [
      ".bzr",
      ".direnv",
      ".eggs",
      ".git",
      ".git-rewrite",
      ".hg",
      ".ipynb_checkpoints",
      ".mypy_cache",
      ".nox",
      ".pants.d",
      ".pyenv",
      ".pytest_cache",
      ".pytype",
      ".ruff_cache",
      ".svn",
      ".tox",
      ".venv",
      ".vscode",
      "__pypackages__",
      "_build",
      "buck-out",
      "build",
      "dist",
      "node_modules",
      "site-packages",
      "venv",
  ]

  # Same as Black.
  line-length = 88
  indent-width = 4

  # Assume Python 3.12
  target-version = "py312"

  [lint]
  # Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`)  codes by default.
  select = ["ALL"]
+ ignore = [
+     "COM812", "COM819",
+     "D100", "D203", "D213", "D300",
+     "E111", "E114", "E117",
+     "ISC001", "ISC002",
+     "Q000", "Q001", "Q002", "Q003",
+     "W191",
+ ]

  # Allow fix for all enabled rules (when `--fix`) is provided.
  fixable = ["ALL"]
  unfixable = []

  # Allow unused variables when underscore-prefixed.
  dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"

  [format]
  # Like Black, use double quotes for strings.
  quote-style = "double"

  # Like Black, indent with spaces, rather than tabs.
  indent-style = "space"

  # Like Black, respect magic trailing commas.
  skip-magic-trailing-comma = false

  # Like Black, automatically detect the appropriate line ending.
  line-ending = "auto"

2.3.2. Dev Container の設定

.devcontainer/devcontainer.jsonの様に Dev Container の設定をすることで,保存時に自動フォーマット[1:4]されます.

.devcontainer/devcontainer.json
{
    "name": "Rye",
    "build": {
        "context": "..",
        "dockerfile": "Dockerfile"
    },
    "customizations": {
        "vscode": {
            "extensions": [
                "charliermarsh.ruff"
            ],
            "settings": {
                "python.defaultInterpreterPath": "/opt/rye/shims/python",
                "[python]": {
                    "editor.defaultFormatter": "charliermarsh.ruff",
                    "editor.codeActionsOnSave": {
                        "source.fixAll.ruff": "explicit",
                        "source.organizeImports.ruff": "explicit"
                    },
                    "editor.formatOnSave": true
                },
                "files.insertFinalNewline": true,
                "terminal.integrated.defaultProfile.linux": "zsh",
                "terminal.integrated.profiles.linux": {
                    "zsh": {
                        "path": "/bin/zsh"
                    }
                }
            }
        }
    },
    "remoteUser": "vscode"
}

2.4. Multi-stage builds

Docker の Multi-stage builds(マルチステージビルド)を使うことで Docker イメージを削減することができます.
1 段階目のビルドで必要なパッケージをダウンロードし,2 段階目のビルドに必要なファイルだけを渡しています.

.devcontainer/Dockerfile
+ FROM debian:bullseye-slim as builder

  WORKDIR /opt

  ENV RYE_HOME="/opt/rye"
  ENV PATH="$RYE_HOME/shims:$PATH"

  # hadolint ignore=DL3008
  RUN apt-get update && \
      apt-get install -y --no-install-recommends \
          ca-certificates \
          curl

  SHELL [ "/bin/bash", "-o", "pipefail", "-c" ]
  RUN curl -sSf https://rye-up.com/get | RYE_INSTALL_OPTION="--yes" bash && \
      rye config --set-bool behavior.global-python=true && \
      rye config --set-bool behavior.use-uv=true

  COPY ./.python-version ./pyproject.toml ./requirements* ./
  RUN rye pin "$(cat .python-version)" && \
      rye sync


+ FROM mcr.microsoft.com/vscode/devcontainers/base:bullseye
+ COPY --from=builder /opt/rye /opt/rye
+
+ ENV RYE_HOME="/opt/rye"
+ ENV PATH="$RYE_HOME/shims:$PATH"
+ ENV PYTHONUNBUFFERED True
+
+ RUN rye config --set-bool behavior.global-python=true && \
+     rye config --set-bool behavior.use-uv=true

  RUN chown -R vscode $RYE_HOME

3. おまけ

ブランチ毎に機能を分けていく予定なので,機能を追加次第更新していきます.

3.1. jupyter ブランチ

Jupyter Notebook もフォーマットできるようにしました.
主に Jupyter Notebook を使用する時に切り替えるブランチです.

https://github.com/a5chin/python-rye/tree/jupyter

jupyter

4. まとめ

本記事では,VS Code の Dev Container 上に Rye, uv, Ruff を使って爆速で快適な Python 環境を構築する方法について解説しました.
皆さんが快適な Python ライフを送れることを願います💫

最後になりますが,弊社では MLOps エンジニアを募集しているのでご応募お待ちしています!
https://herp.careers/v1/denacareer/pvaMZdKXMr78

脚注
  1. 一貫性のあるコーディングスタイルを強制させることです. ↩︎ ↩︎ ↩︎ ↩︎ ↩︎

  2. 潜在的なバグや不適切なコーディングスタイルを検出し,それらを修正することでコードの品質を向上できます. ↩︎ ↩︎ ↩︎

  3. コードの機能をテストし,期待通りに動作することを確認することです. ↩︎

  4. Docker イメージを構築することを指します.イメージからコンテナを作成することができます. ↩︎

  5. 性能,メモリ安全性,安全な並行性を目指して設計されたマルチパラダイムのプログラミング言語です.C, C++ 言語に代わるシステムプログラミング言語を目指しています. ↩︎ ↩︎

  6. Python における Linter の一種です. ↩︎

GitHubで編集を提案
DeNA Engineers

Discussion