本記事は、TUNA-JP Advent Calendar 2023 の2日目になります。
はじめに
みなさん、Rye を使っていますか? Rye とは、Rust で書かれた Python 用パッケージ管理ツールです。これまでは、pyenv で Python のバージョンの管理をして、poetry などでパッケージの管理をするといった使い分けを行う必要がありました。しかし、Rye を使うことで Python のバージョンもパッケージも rye コマンドひとつで管理することができます。
その利便性から、Rye をおすすめする記事はたくさんあるのですが、Buildpack を使って Rye で管理された Python プロジェクトから Docker イメージを作成する方法を見つけることはできませんでした。
Rye も便利ですが、Buildpack も便利なので、Rye x Buildpack で Docker イメージを作りたいと思い、Buildpack を使って Rye で管理された Python プロジェクトから Docker イメージを作成できないか試してみました。
準備
まずは準備として、以下のソフトウェアをインストールしておきます。
Rye プロジェクトの作成
今回はサンプルとして、Flask で Hello World を返すサーバを作成したいと思います。
最初に rye init
コマンドでプロジェクトを作成します。すると、以下のように、Python プロジェクトの雛形が作成されます。
$ rye init rye-buildpack-sample
success: Initialized project in /Users/ttanigaki/workspace/local/rye-buildpack-sample
Run `rye sync` to get started
$ ls -al rye-buildpack-sample
total 32
drwxr-xr-x@ 9 ttanigaki staff 288 12 2 02:46 .
drwxr-xr-x@ 6 ttanigaki staff 192 12 2 02:46 ..
drwxr-xr-x@ 9 ttanigaki staff 288 12 2 02:46 .git
-rw-r--r--@ 1 ttanigaki staff 93 12 2 02:46 .gitignore
-rw-r--r--@ 1 ttanigaki staff 29 12 2 02:46 .python-version
drwxr-xr-x@ 7 ttanigaki staff 224 12 2 02:46 .venv
-rw-r--r--@ 1 ttanigaki staff 52 12 2 02:46 README.md
-rw-r--r--@ 1 ttanigaki staff 344 12 2 02:46 pyproject.toml
drwxr-xr-x@ 3 ttanigaki staff 96 12 2 02:46 src
rye pin <python-version>
コマンドを実行すると、プロジェクトにおける Python のバージョンを固定することができます。
$ cd rye-buildpack-sample
$ rye pin 3.11
pinned 3.11.6 in /Users/ttanigaki/workspace/local/rye-buildpack-sample/.python-version
そして、rye sync
コマンドを実行すると、指定したバージョンの設定を反映させることができます。
$ rye sync
~~ 省略 ~~
Successfully installed rye-buildpack-sample-0.1.0
Done!
rye run
コマンドで、プロジェクト内の virtualenv 環境で後続のコマンドを実行することができます。ここでは、3.11.6 が使用されていることがわかります。
$ rye run python -V
Python 3.11.6
flask
パッケージをインストールするために、rye add
コマンドを使います。設定を反映させるために、追加で rye sync
コマンドも実行します。
$ rye add flask
Added flask>=3.0.0 as regular dependency
$ rye sync
~~ 省略 ~~
Successfully installed blinker-1.7.0 click-8.1.7 flask-3.0.0 itsdangerous-2.1.2 jinja2-3.1.2 markupsafe-2.1.3 rye-buildpack-sample-0.1.0 werkzeug-3.0.1
Done!
Flask を使って、以下のような Hello World を返すコードを作成します。
$ cat src/rye_buildpack_sample/main.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return "Hello World!!"
if __name__ == '__main__':
app.run(host="0.0.0.0", port=8080)
rye run
コマンドを使って、上記コードが動作することを確認します。
$ rye run python src/rye_buildpack_sample/main.py
* Serving Flask app 'main'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://0.0.0.0:8080
Press CTRL+C to quit
# 別ターミナル
$ curl http://localhost:8080
Hello World!!
ここまでで、Rye で管理された Python プロジェクトを作成することができました。
Buildpack を使って Docker イメージを作成する
それでは、Buildpack を使って Docker イメージを作成したいと思いますが、pack build
コマンドを実行する前に、2つ準備が必要です。
1つ目は、requirements.txt
を作成することです。ビルド時にパッケージをインストールする必要がありますが、Paketo Buildpack には Rye で管理された Python プロジェクト内のパッケージを検知する仕組みがありません。
そこで、以下のように requirements.lock
から -e
を含む行を削除することで、requirements.txt
を作成して、Python プロジェクト内のパッケージを検知できるようにします。
cat requirements.lock | sed '/-e/d' > requirements.txt
2つ目は、アプリケーションを起動するコマンドを Procfile
に指定しておくことです。今回は、以下のように python
コマンドで main.py
を実行するコマンドを指定します。
$ cat Procfile
web: python src/rye_buildpack_sample/main.py
以上の準備ができたら、pack build
コマンドで Docker イメージを作成します。
$ pack build flask-hello --builder paketobuildpacks/builder:base
base: Pulling from paketobuildpacks/builder
~~ 省略 ~~
Successfully built image flask-hello
最後に、docker run
コマンドで、作成したイメージが動作することを確認します。
$ docker run --platform linux/x86_64 -d -p 8080:8080 -e PORT=8080 flask-hello
# 別ターミナルで実行
$ curl http://localhost:8080
Hello World!!
さいごに
思ったより、簡単に Docker イメージを作成することができました。ただ、パッケージを追加・削除するたびに requirements.txt
を更新するのはかなりの手間で、パッケージが不足するようなミスを引き起こす可能性があります。なので、requirements.txt
なしに Docker イメージを作成する方法がないか探したいと思います。