きり丸の技術日記

技術検証したり、資格等をここに残していきます。

Gitにコミットした中身を検索する (目的行の変更ハッシュを探したいときに)

始めに

小ネタ。ライブラリを管理しているときに、いつから依存関係に含まれているか、削除されているかを調べるために使用したコマンドを残します。

ユースケース

  • 特定の記載が入ったコミットを探す

環境

  • git
    • 2.43.0

実装

次のコマンドで、uv.lockに含まれているgreenletライブラリが追加・変更・削除されたコミットを調べます。

git log -S "greenlet" -- uv.lock

終わりに

実際はI18nファイルに記載されていた行が削除されており、そのエラーを調査するために使用しました。

I18nファイルは意識的に整理しないとドンドン膨らんでいくので、定期的に棚卸していたのですがレアなジョブに引っかかって1年越しにエラーとして引っかかってしまいました。静的なキーならわかりやすいのですが、動的にキーを発行すると途端に追えなくなるので、静的なキーも動的なキーも含めて使用・未使用を調べるライブラリが欲しいです…。

Dockerをzstdで圧縮する。少しでも軽くしたい

始めに

先日の記事にて、FastAPIのイメージが1GBになってしまった件を書きました。何か対策できないかと調査していく中で、gzipではなく、zstdで圧縮することで高い圧縮率と高い解凍速度を達成できることを知りました。

今回の記事ではzstdでDockerイメージの圧縮をします。

環境

  • Docker Engine
    • 27.4.1
  • Docker Buildx
    • v0.19.3

zstd圧縮を使用するためには、次のバージョンが必要です。

  • Docker Engine
    • v24.0.0以降
  • Docker Buildx
    • 0.8.0以降
# バージョン確認用コマンド
docker version
docker buildx version

実装

確実に目的のバージョン(最新バージョン)でビルドするために、ビルダーインスタンスを作成します。

次に、docker buildxoutputオプションに必要なオプションを付与すると、zstdで圧縮できます。

docker buildx create --name builder --use --platform linux/amd64
docker buildx build --builder builder -t kirimaru/xxx:zstd --output type=image,oci-mediatypes=true,compression=zstd,compression-level=3,force-compression=true,push=true .
# 実際に使用しているコマンド
# docker buildx build --builder builder --target dev_runtime -t kirimaru/fastapi-practice_dev-runtime:$RUNTIME_TAG --output type=image,oci-mediatypes=true,compression=zstd,compression-level=3,force-compression=true,push=true .

次のコマンドでgzipで圧縮されていないことを確認しました。

# gzipで圧縮していた時のイメージ
docker buildx imagetools inspect --raw kirimaru/fastapi-practice_dev-runtime:python-1c86b7b0d3465cb68c6c3c6e1b256137ef6af039
# 該当箇所の抜粋
# "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip"

# zstdで圧縮したときのイメージ
# ※ マルチステージビルドだと、マニフェストのdigest値まで指定する必要があります
docker buildx imagetools inspect --raw kirimaru/fastapi-practice_dev-runtime:python-97df63af48201968a20c804cdb84e10765e68ee7@sha256:00b9a175bfb82fcd9f3b018e1d726d1a118c7d7357fe0649df1ad73d372222e4
# "mediaType": "application/vnd.oci.image.layer.v1.tar+zstd"

各オプション解説

--builder builder

特定のビルダーインスタンス(今回はbuilder)を使用するという指定です。


-t kirimaru/xxx:zstd

ビルド時のイメージにタグを付与します。詳細は後述しますが、zstdで圧縮する際には必須級です。


--output オプション
  • type=image
    • 出力タイプをイメージとして指定します。指定は必須です
  • oci-mediatypes=true
    • OCI(Open Container Initiative)メディアタイプを使用します
  • compression=zstd
    • Zstandard(zstd)圧縮を使用します
  • compression-level=3
    • zstd圧縮のレベルを3に設定します。(1-22の範囲、低いほど高速で圧縮率は低下しますが、3がバランスが取れているようです)
  • force-compression=true
    • 既に圧縮されているレイヤーも再圧縮します
  • push=true
    • ビルド後にイメージをレジストリにプッシュします

ハマったポイント

リモートリポジトリにイメージをpushする際にzstdで圧縮するようで、ローカルで一度ビルドしてからタグを付与しなおしてpushするということはできませんでした。そのため、-tpush=trueでビルドしながらpushしています。

他の細かい失敗は次に記載しています。

  • bakeではうまくzstdで圧縮できなかった
    • おそらく、pushオプションを付与すれば大丈夫だろうが未検証
  • --type=imagesだとビルドキャッシュだけに残るようで、イメージ化されなかった。かといって、--loadを付与して明示的にimage化したらgzipになった
  • --type=dockerにすれば問題なさそうだったが、こちらはgzipで圧縮された

ソースコード

なし

終わりに

ローカルでzstdの最大圧縮レベルの22を検証しようとしたらまったくイメージサイズが変わらなくてずっとハマってました。他のブログも参考にしたのですが、push=trueにしている理由等も特に記載がなかったので、完全に見落としでしたね。

参考情報

Rust製のPythonライブラリは軽いと思ってた

始めに

pydantic等のPythonのライブラリはコア部分がRustで書かれていて、Pythonでも十分な処理速度が確保されている、という表現をされることがあります。

そのため、基本的にはRustで作られたライブラリに移行しようとして、テンプレートエンジンのjinja2からminijinjaに移行しようとしました。その過程で気付いたことをメモするだけの記事です。

※ Pythonのminijinjaについては、試験的な試みでminijinja単体の機能が完全に移行されているわけではないし、処理速度も早いとかは明記されていないので、現時点で移行は勧めないです。

minijinja-py is an experimental binding of MiniJinja to Python

環境

  • Python
    • 3.13
  • jinja2
    • 3.15
  • minijinja
    • 1.63

まとめ

  • Rust製ライブラリは処理速度面で優位なことが多い
  • Rust単体でビルドしたときはバイナリファイルにされるので軽い
  • Pythonとの互換性を持たせたサイズが大きいので、総合的にはサイズが大きくなりがち

気付いたこと

jinja2からminijinjaのサイズを確認するときに気付きました。

uv pip list --format freeze | awk -F = {'print $1'} | grep -E 'jinja2|minijinja' | xargs uv pip show | grep -E 'Location:|Name:' | cut -d ' ' -f 2 | paste -d ' ' - - | awk '{print $2 "/" tolower($1)}' | xargs du -sh 2> /dev/null | sort -hr
2.1M    /products/fastapi-practice/.venv/lib/python3.13/site-packages/minijinja
536K    /products/fastapi-practice/.venv/lib/python3.13/site-packages/jinja2

コマンドの実行結果を見て分かるとおり、jinja2は536k程度にもかかわらず、minijinjaは2.1Mもサイズがあります。具体的にどこのファイルが大きいかというと、Rust製ライブラリのコア部分です。

# ls -ltrah
-rw-r--r--   2 hiroto hiroto  238 Nov 17 23:38 _lowlevel.pyi
-rw-r--r--   2 hiroto hiroto  558 Nov 17 23:38 _internal.py
-rw-r--r--   2 hiroto hiroto 5.1K Nov 17 23:38 __init__.pyi
-rw-r--r--   2 hiroto hiroto 5.3K Nov 17 23:38 __init__.py
-rwxr-xr-x   2 hiroto hiroto 2.1M Nov 17 23:38 _lowlevel.abi3.so

ちなみに、Rustのcrates.ioを見ると分かるのですがRust上で使用する場合は156KB程度です。

また、会社で使用しているFastAPIプロジェクトをPython3.13にアップデートしようとしたら、Docker Imageのサイズが1GBくらいまで膨らんでしまいました。Python3.12でも800MBくらいになっています。

終わりに

Rustの高速な処理能力とPythonの豊富なライブラリエコシステムを使用できることは喜ばしいのですが、Dockerのイメージサイズがどんどん大きくなっていくのが困りますね。正直、感覚的にサイズ大きいと感じているだけではあるので、1GBとかが当たり前であるならそれはそれでよいのですが…。

もしPythonのプロジェクトをDockerのイメージ化する際に、イメージサイズをダイエットさせる方法があれば教えていただきたいです。実運用上はECRを使用しているので、実はオプションで圧縮可能だったりしたらもっと嬉しいです。

参考情報