はじめに
ミリシタ新イベ、転天革命じゃん...😇 nikkieです。
今回はRyeに慣れるために素振りしたクソライブラリをクソDockerイメージにしていきます!
目次
- はじめに
- 目次
- 前提:Ryeに慣れるために素振りしたクソライブラリ
- 結論
- Dockerイメージビルドのための戦略を練る
- requirements.lockにある依存をDockerイメージにインストール
- ソースコードを(site-packages以下に)インストール
- 動作環境
- 終わりに
前提:Ryeに慣れるために素振りしたクソライブラリ
からあげ皇帝のunkoの系譜を汲み、先日Ryeバージョンを爆誕させました
このクソライブラリunkoをインストール済みのDockerイメージを作るのがこの記事のゴールとなります。
結論
- requirements.lockからeditable installの行を除き、依存ライブラリを先にインストール
- ソースコードをCOPYして、インストール
- マルチステージビルドで、site-packages以下をCOPY!
% docker build -f docker/Dockerfile -t unko-by-rye:example . % docker run --rm -it unko-by-rye:example hello puripuri % docker run --rm -it unko-by-rye:example python -q >>> from unko import deru >>> deru() puripuri
Dockerイメージビルドのための戦略を練る
まずRyeが生成したファイルのうち、Dockerイメージのビルドに関連するものを洗い出します(太字で示します)
- requirements.lock
- 現状のunkoは依存がないトイプロジェクトですが、実プロジェクトで使える方法を知りたいので、適当に行を追加することにします
- requirements-dev.lock
- こちらは開発環境をsyncさせるのにとても役立つファイルです。開発で使う依存は今回作るDockerイメージには不要なので扱いません
- src
- ここにクソライブラリの実体があります
- pyproject.tomlにプロジェクトのメタデータがありますね
requirements.lockにある依存をDockerイメージにインストール
Ryeはrequirements.lockやrequirements-dev.lockを元に、魔法のように開発環境を再現させます(rye sync
)。
魔法の仕掛けは以下
-e file:. kojo-fan-art==0.1.1
-e
の行はeditable installです(--editable
とも指定できます)- 依存ライブラリについてはバージョン固定して表されています1
editable install
https://pip.pypa.io/en/stable/topics/local-project-installs/#editable-installs
Editable installs allow you to install your project without copying any files.
通常のpip install
はPyPIからライブラリの実装をダウンロードし、site-packages以下に保存します。
例えば、仮想環境を使っていたら、.venv/lib/python3.12/site-packages
2のようになります
開発中のライブラリも同様にsite-packages以下にコピーする運用だと、ソースコードをいじった後毎回pip install
が必要です。
これは面倒ですし、たまたまpip install
を忘れてしまって期待通り動かず、開発中に混乱してしまうかもしれません。
そこでeditable installの出番となります!
開発中のライブラリのソースコードは一度だけeditable installしておけば、いくら編集してもそれが反映されます。
文字通り、ソースコードを編集可能なインストールです!
Dockerイメージのマルチステージビルドとeditable install
私はDockerイメージを作る以上はサイズが小さいものを作りたいので、マルチステージビルドしていきます(取り回しやすいので、小さいは、正義!)。
マルチステージビルドではDockerイメージ間でsite-packages以下をコピーします。
ですが、requirements.lock
の記載(editable install)とは噛み合いません。
そこで、Dockerイメージのビルドの中では、通常のinstallをしてソースコードをsite-packages以下に置くように工夫します。
Ryeのコミュニティに見つけた工夫
Rye + Docker · mitsuhiko rye · Discussion #239 · GitHub
WORKDIR /backend COPY ./requirements.lock /backend/ RUN sed '/-e/d' requirements.lock > requirements.txt RUN pip install -r requirements.txt
- requirements.lockだけDockerイメージの中にコピー
- これは理にかなっていて、requirements.lockの中身を変えない限り、ビルド中のレイヤーはキャッシュされ、ビルド時間短縮に寄与します3
- sedコマンドで-eを含む行を削除
- 依存ライブラリのバージョン指定だけが残りますね
- 依存ライブラリをインストール
% cat requirements.lock -e file:. kojo-fan-art==0.1.1 % sed '/-e/d' requirements.lock kojo-fan-art==0.1.1
nikkieによる改善案
WORKDIR /backend COPY ./requirements.lock /backend/ RUN <<EOF sed '/^-e/d' requirements.lock > requirements.txt pip install -r requirements.txt EOF
- sedで削除するパターンを「-eで始まる」行に変えました
- これにより
hoge-eee==0.0.1
のような-eを含む依存指定の行が削除されなくなります - Ryeのworkspaceを使うと-eで始まる行が複数できます。sedの削除はパターンにマッチした全行を消すので対応できています
- sedの
-i
オプションでrequirements.lockをインプレースに変更することもできます
- これにより
% cat requirements.lock -e file:. -e file:subpkg kojo-fan-art==0.1.1 hoge-eee==0.0.1 % sed '/-e/d' requirements.lock kojo-fan-art==0.1.1 % sed '/^-e/d' requirements.lock kojo-fan-art==0.1.1 hoge-eee==0.0.1
- イメージビルド中のレイヤーの数を減らすために、RUNコマンドをまとめたいです
- heredocが書けるので使いました
- 私はEOFの代わりに、まとめた処理を表す「関数名」を使っています
- ちなみに好みで、python -m pip install推しです
ソースコードを(site-packages以下に)インストール
先に述べた通常のinstallを進めるだけです。
COPY . . RUN python -m pip install --no-cache-dir .
srcディレクトリを含む一式をDockerイメージ側にコピーし、editableではない(=通常の)インストールをしました。
これでsite-packages以下にコピーされます。
ソースコードの一部(例:printする文字列)を変えた後のDockerイメージのビルドは、COPY命令以降が都度実行される形になります。
requirements.lockは変更頻度が小さいので、キャッシュされたレイヤーが使われやすいのです。
動作環境
% docker --version Docker version 24.0.2, build cb74dfc % rye --version rye 0.16.0 commit: 0.16.0 (c003223d5 2023-12-16) platform: macos (aarch64) self-python: cpython@3.11 symlink support: true
終わりに
Ryeで環境構築したクソライブラリをインストールしたクソDockerイメージの作り方でした。
おもちゃな例で取り上げていますが、以下の点は実用的ではないかと思います
- Ryeが生成したrequirements.lockの-eで始まる行はDockerイメージのビルドにおいては除く
- site-packages以下にインストールするため、ソースコードのeditable installはしない(通常のインストールをする)
実プロジェクトでも試してみて学びがあれば更新します
ちなみに-e
の行をRyeが書き込むという挙動4は、音楽性が違うなと感じるポイントの1つです。
Ryeがラップしているpip-compileでは-e
の行は書き込みません。
rye sync
のeasyさのために、Dockerイメージビルドで複雑さが持ち込まれているように見えており、それをネガティブに評価しています。
(Ryeはmitsuhikoさんの個人的な課題解決のツールだから、mitshuhikoさんのユースケースではソースコードを含むDockerイメージをあんまりビルドしないのかなーって。あ、もしかすると、buildしてからPyPIに上げちゃうから、それをDockerイメージでインストールする想定なのかも!)
- 依存をなにか追加したかったので自作ライブラリを指定しました ↩
- モジュール検索パスとも関係するようです。このあたり理解を深めたいところ https://docs.python.org/ja/3/using/cmdline.html#envvar-PYTHONPATH↩
- 例えば「Instruction order matters for leveraging build cache」にあります ↩
- このあたり https://github.com/mitsuhiko/rye/blob/0.16.0/rye/src/lock.rs#L369↩