🌾

[Python] Rye を Docker で動かす時に .venv を Volume Trick するとエラーになる問題

2024/04/22に公開

まず Docker に Rye いらんだろというツッコミは置いといてください。

はじめに

開発環境を Docker 化する際は依存関係を同期すると重くなるし無意味なので Volume Trick で除外するのが定番です。

    volumes:
      - .:/app
      - /app/node_modules
      - /app/.venv

こういうやつですね。

しかし、 Rye は .venv が存在するとエラーになってしまうため、以下のようになります。

root@4b701b6919f3:/app# rye sync
error: virtualenv is not managed by rye. Run `rye sync -f` to force.

書いてあるとおり -f オプションをつけると以下のようになります。

root@1821b1bc27d4:/app# rye sync -f
Forcing re-creation of non-rye managed virtualenv
error: failed to delete existing virtualenv (at '/app/.venv')

Caused by:
    Resource busy (os error 16)

これは何が起きているかと言うと、 -f オプションは .venv フォルダを丸ごと削除する挙動になっていて、一方で Volume Trick を使うと削除不可(当たり前)のボリュームがマウントされている状態なので、その2つの動作が競合してエラーになっています。
https://github.com/astral-sh/rye/issues/868 同様の問題らしき issue も立っていますが、1年近く放置されているので、修正されることはないと思いますし、Pull Requestを投げてもどうなんだろうという感じがします。修正自体は難しくなさそうです。

強い意志で Docker 以外では開発しないというのであれば Volume Trick を使わないという方法でも切り抜けられます。しかし Mac 環境では、前よりは格段にましになりましたがファイル同期があまり速くないですし、ホスト側に Docker 環境のネイティブモジュールが混ざってしまって、ホスト側のアーキテクチャで動かないバイナリが生成されてしまうので、あまり良いとは言えません。

解決法

Rye は .venv/rye-venv.json が存在するとプロジェクトが存在すると認識するので(2024年4月24日現在)、 Docker 環境下にこのファイルを常に作られるようにしましょう。 Volume Mount は Docker のビルド時ではなく起動時(コンテナ作成時)に行われるので、起動後に手動で叩くのが良いでしょう。 Rye が入っている前提なので、 pyproject.toml にコマンドを作っておくのが良さそうです。

#!/bin/bash
# init-venv-docker.sh

echo '{ "python": "cpython@3.12.0" } ' > $PWD/.venv/rye-venv.json
# pyproject.toml

[tool.rye.scripts]
init-venv-docker = "/bin/bash ./init-venv-docker.sh"

tool.rye.scripts は何でも書けるわけではないので、シェルスクリプトに実行内容を分離しています。

root@1f10616bd389:/app# ls .venv
rye-venv.json
root@1f10616bd389:/app# rye sync
Python version mismatch (found cpython@3.12.0, expected cpython@3.12.2), recreating.
Initializing new virtualenv in /app/.venv
Python version: cpython@3.12.2
Generating production lockfile: /app/requirements.lock
Generating dev lockfile: /app/requirements-dev.lock
...

これで動いてくれるようになりました。

devcontainer を使う想定になると思うので、 postStartCommand に仕込むのが良いでしょう。

もちろん公式の挙動ではないので、突然動かなくなる可能性があります。 Docker 環境を併用する場合は Rye 以外を使うことも検討しましょう。

Docker も Rye も環境分離という点ではほぼ一致しているので、「 Docker 環境下で Rye を使う」というのは環境分離を二重に行うことになり、構成が複雑になるだけであまり良いことはないと思います。通常は Docker を使う必要はなく、 Rye だけで十分でしょう。

ネイティブ拡張モジュールを多用していたり、それらのビルドに特殊な環境が要求されるとか、外部プログラムを要求されるとか、そういう場面で Docker でないとほぼ開発環境を作れないというような状態であれば、その Docker 環境で Rye も使う、というのはありかと思います。

開発環境ではない通常の実行環境として Docker Image を生成する場合は https://rye-up.com/guide/docker/ を読んでください。

Discussion