きり丸の技術日記

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

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 buildxのoutputオプションに必要なオプションを付与すると、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するということはできませんでした。そのため、-tとpush=trueでビルドしながらpushしています。

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

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

ソースコード

なし

終わりに

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

参考情報