じゃあ、おうちで学べる

本能を呼び覚ますこのコードに、君は抗えるか

RustのDockerfile、2025年はこれでいこう

はじめに

「Dockerでビルドすると遅いんだよね」「イメージが2GB超えちゃって…」

そんな会話はもう過去の話です。2025年、コンテナ化は劇的に進化しました。Rustも例外ではありません。cargo-chefBuildKitキャッシュマウントの組み合わせでビルド時間を5-10倍短縮、2.63GBのイメージをdistrolessイメージで約50MBmusl静的リンクならわずか1.7MBという値を達成できます。

この記事では、実践的なDockerfileパターンとベンチマーク結果を詳しく解説します。

実際に検証したAxum Webアプリケーションでは、distroless版で50.3MBmusl+scratch版で1.71MBを達成しました。中規模プロジェクト(約500の依存関係)での初回ビルドは10分コード変更後の再ビルドはわずか40秒です。

信じられないかもですが、これが2025年の現実です。ちゃんとやれって話です。あと皆さんのDockerfileも教えて欲しいです。

このブログが良ければ読者になったりnwiizoXGithubをフォローしてくれると嬉しいです。では、早速はじめていきます。

2025年の重要なアップデート

Rust 2024 Edition(2025年2月20日リリース)

Rust 1.85.0でRust 2024 Editionが安定版になりました。Docker環境でRust 1.85以降を使えば、Edition 2024の機能が使えます。

doc.rust-lang.org

blog.rust-lang.org

Docker関連の進化

  • Docker Engine v28:コンテナネットワーキングのセキュリティ強化、AMD GPUサポート docs.docker.com
  • docker init GA:Rustプロジェクト用の最適化されたDockerfile自動生成 docs.docker.com
  • Docker Bake GA:複雑なビルド設定の宣言的管理 docs.docker.com
  • BuildKit 0.25.1:Git URLクエリパラメータ、シークレットの環境変数化など新機能 github.com

基本的な考え方

マルチステージビルドは前提条件

2025年でマルチステージビルドを使わないのは、正直あり得ません。

まずメンテナンス性が格段に向上します。最終的な成果物以外ではサイズを意識したトリッキーな記述が不要になるため、Dockerfileの可読性が劇的に良くなります。次にビルド速度のアップ。並列化、キャッシュマウント、tmpfsなど最適化オプションが豊富に使えるようになり、ビルドパイプライン全体が高速化します。そして何よりセキュリティの向上。シークレット管理の仕組みが標準化され、機密情報の取り扱いが安全になりました。

docs.docker.com

COPYは最小限に、--mountを活用

COPYが登場するのは、実質的に2つの場面だけです。マルチステージビルドで別ステージから成果物を持ってくる場合と、最終ステージでアプリケーションバイナリをコピーする場合。それ以外、特にソースコードのビルド時には--mount=type=bindを使用します。

docs.docker.com

必ず記述すべきおまじない

# syntax=docker/dockerfile:1

この1行を必ず先頭に記述します。最新のDockerfile構文が自動的に利用され、新機能が使えるようになります。

docs.docker.com

2025年のDockerfileはこれでやります

前置きはこれくらいにして、実際のコードを見ていきましょう。

これが2025年のRust標準Dockerfileです。cargo-chefによる依存関係の分離、BuildKitキャッシュマウント、distrolessイメージ、非rootユーザー実行。この記事で解説してきたベストプラクティスのすべてが、この1つのテンプレートに詰め込まれています。

# syntax=docker/dockerfile:1

ARG RUST_VERSION=1.85
ARG APP_NAME=myapp

# cargo-chefを使った依存関係キャッシング
FROM lukemathwalker/cargo-chef:latest-rust-${RUST_VERSION} AS chef
WORKDIR /app

FROM chef AS planner
COPY . .
RUN cargo chef prepare --recipe-path recipe.json

FROM chef AS builder
# 依存関係のビルド(キャッシュ可能)
COPY --from=planner /app/recipe.json recipe.json
RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked \
    --mount=type=cache,target=/usr/local/cargo/git,sharing=locked \
    cargo chef cook --release --recipe-path recipe.json

# アプリケーションのビルド
COPY . .
RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked \
    --mount=type=cache,target=/usr/local/cargo/git,sharing=locked \
    --mount=type=cache,target=/app/target,sharing=locked \
    cargo build --release --bin ${APP_NAME} && \
    cp ./target/release/${APP_NAME} /bin/server

# テストステージ(オプション)
FROM chef AS test
COPY . .
RUN --mount=type=cache,target=/usr/local/cargo/registry \
    --mount=type=cache,target=/usr/local/cargo/git \
    cargo test

# 本番ステージ:distroless
FROM gcr.io/distroless/cc-debian12:nonroot AS runtime
COPY --from=builder /bin/server /app/
WORKDIR /app
EXPOSE 8000
ENTRYPOINT ["/app/server"]

このDockerfileの主な特徴

  • cargo-chefによる依存関係の分離とキャッシング
  • BuildKitキャッシュマウントでレイヤーを跨いだキャッシュ
  • distrolessによる最小サイズと高セキュリティ
  • 非rootユーザー:nonrootタグ)での実行
  • オプショナルなテストステージ

ビルド最適化の3つの柱

1. cargo-chef

cargo-chefは、Rustの依存関係管理をDockerレイヤーキャッシュに適合させる画期的なツールです。依存関係のコンパイルソースコードコンパイルを完全に分離します。

動作メカニズム(2段階):

  1. cargo chef prepare:Cargo.tomlとCargo.lockを解析してrecipe.jsonを作成
  2. cargo chef cook:最小限のプロジェクト構造を再構築して依存関係のみをビルド

重要:同じRustバージョンと作業ディレクトリを全ステージで使用すること。異なるバージョンを使うとキャッシュが無効化されます。

実測データ:

  • cargo-chefのみで55%の改善
  • cargo-chef + sccacheで79%の改善(34秒→7秒)
  • 商用プロジェクト(14,000行、500依存関係)で10分→2分

github.com

2. BuildKitキャッシュマウント

BuildKitのキャッシュマウント(Docker 18.09以降)を使うと、レイヤー無効化を超えて永続化するキャッシュボリュームが利用できます。

3つの重要なキャッシュポイント:

RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked \
    --mount=type=cache,target=/usr/local/cargo/git,sharing=locked \
    --mount=type=cache,target=/app/target,sharing=locked \
    cargo build --release
  • /usr/local/cargo/registry:crates.ioからのダウンロード
  • /usr/local/cargo/git:Git依存関係
  • /app/target:ビルド成果物

sharing=lockedパラメータは排他アクセスを保証し、パッケージマネージャーの破損を防ぎます。

CI環境でのキャッシュ共有:

# GitHub Actions
- uses: docker/build-push-action@v6
  with:
    cache-from: type=gha
    cache-to: type=gha,mode=max

パフォーマンスベンチマーク

  • ベースライン:90.60秒
  • BuildKitキャッシュマウント:15.63秒(5.8倍高速)
  • cargo-chef:18.81秒(4.8倍高速)
  • 三位一体(chef + BuildKit + sccache):7-12秒(7.5-13倍高速)

docs.docker.com

3. sccache

sccache(v0.7.x)はMozilla製のccache風コンパイララッパーで、個々のコンパイル成果物を細粒度でキャッシュします。

github.com

FROM rust:1.85 AS builder

# sccacheのインストールと設定
RUN cargo install sccache --version ^0.7
ENV RUSTC_WRAPPER=sccache \
    SCCACHE_DIR=/sccache \
    CARGO_INCREMENTAL=0

WORKDIR /app

RUN --mount=type=cache,target=/usr/local/cargo/registry \
    --mount=type=cache,target=$SCCACHE_DIR,sharing=locked \
    --mount=type=bind,target=. \
    cargo build --release

重要:CARGO_INCREMENTAL=0は必須。インクリメンタルコンパイルとsccacheは競合します。

キャッシュヒット率:

  • 初回ビルド:0%
  • ソースコード変更のみ:85-95%
  • 依存関係を更新した時:60-75%

注意点: sccacheの効果は環境によって大きく異なります。一部の環境では効果が薄く、逆にオーバーヘッドとなる場合があります。自環境でのベンチマークが必須です。

イメージサイズの最適化:ベースイメージ選択戦略

ビルドステージ:rust:slim推奨

2025年はrust:slim(Debian系)でよいと思っています。

console.cloud.google.com

FROM rust:1.85-slim-bookworm AS builder

理由はシンプルです。Debian stable(bookworm)ベースでglibcを使用しているため、広範な互換性とマルチスレッドワークロードでの優れたパフォーマンスを発揮します。完全版のrust:latestが624MBもあるのに対し、rust:slimはコンパイルに必要な最小限のパッケージだけを含んでいます。無駄がありません。

rust:alpineは避けてください。 muslの互換性問題に加えて、マルチスレッドアプリケーションで最大30倍のパフォーマンス劣化が報告されています。イメージサイズの小ささに惹かれる気持ちはわかりますが、本番環境でこの劣化は致命的です。

https://hub.docker.com/_/rust

最終ステージ:distroless推奨

gcr.io/distroless/cc-debian12が2025年の標準です。

FROM gcr.io/distroless/cc-debian12:nonroot

distrolessの特徴:

  • サイズ:21-29MB
  • glibcSSL証明書タイムゾーンデータ、/etc/passwdを含む
  • パッケージマネージャー、シェル不要なバイナリを完全排除
  • SLSA 2準拠、cosign署名検証が可能
  • CVEスキャンで従来イメージより50-80%少ない脆弱性
  • :nonrootタグでUID 65534(nobody)として非rootで実行

github.com

イメージサイズ比較(実測値)

イメージ構成 サイズ 用途 特徴
scratch + musl(実測) 1.71MB CLIツール最小化 完全静的リンク
distroless/static 2-3MB 静的リンクバイナリ 最小限のファイル
distroless/cc-debian12(実測) 50.3MB Webアプリ推奨 glibc
debian-slim 80-120MB フル互換性 デバッグツールあり
rust:latest(未最適化) 2.63GB 開発専用 ビルドツール込み

実測削減率:

  • rust:latest(2.63GB)→ distroless(50.3MB):98.1%削減
  • rust:latest(2.63GB)→ musl+scratch(1.71MB):99.9%削減

静的リンク vs 動的リンク

musl(x86_64-unknown-linux-musl)での静的リンク:

FROM rust:1.85-alpine AS builder
RUN apk add --no-cache musl-dev
WORKDIR /app

# 依存関係のキャッシュ
COPY Cargo.toml Cargo.lock ./
RUN --mount=type=cache,target=/usr/local/cargo/registry \
    mkdir src && echo "fn main() {}" > src/main.rs && \
    cargo build --release --target x86_64-unknown-linux-musl && \
    rm -rf src

# アプリケーションのビルド
COPY src ./src
RUN --mount=type=cache,target=/usr/local/cargo/registry \
    cargo build --release --target x86_64-unknown-linux-musl

FROM scratch
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/myapp /myapp
ENTRYPOINT ["/myapp"]

利点:

  • 依存関係ゼロで完全にポータブル
  • scratchコンテナで実行可能
  • イメージサイズ5-10MB

欠点:

  • シングルスレッドで0.9-1.0倍、マルチスレッドで0.03-0.5倍のパフォーマンス
  • 一部依存関係でsegfaultのリスク

本番環境の推奨:

  • 複雑なアプリケーション(Webサーバー、DB接続):glibc + distroless/cc-debian12
  • シンプルなCLIツール:musl + scratchを検討
  • パフォーマンスが重要:必ずglibcを使用

マルチアーキテクチャビルド

linux/amd64linux/arm64の両対応が2025年の標準要件です。

cargo-zigbuild:セットアップゼロのクロスコンパイル

cargo-zigbuild(v0.20.1)はZigツールチェインを使い、セットアップ不要でクロスコンパイルできます。

github.com

# syntax=docker/dockerfile:1

ARG RUST_VERSION=1.85

FROM --platform=$BUILDPLATFORM rust:${RUST_VERSION}-alpine AS builder
WORKDIR /app

# Zigとcargo-zigbuildのインストール
RUN apk add --no-cache musl-dev openssl-dev zig
RUN cargo install --locked cargo-zigbuild

# ターゲットの設定
ARG TARGETPLATFORM
RUN case ${TARGETPLATFORM} in \
    "linux/amd64") echo x86_64-unknown-linux-musl > /rust_target ;; \
    "linux/arm64") echo aarch64-unknown-linux-musl > /rust_target ;; \
    esac && \
    rustup target add $(cat /rust_target)

# 依存関係とビルド
COPY Cargo.toml Cargo.lock ./
RUN --mount=type=cache,target=/usr/local/cargo/registry \
    mkdir src && echo "fn main() {}" > src/main.rs && \
    cargo zigbuild --release --target $(cat /rust_target) && \
    rm -rf src

COPY src ./src
RUN --mount=type=cache,target=/usr/local/cargo/registry \
    cargo zigbuild --release --target $(cat /rust_target)

FROM alpine:latest
ARG TARGETPLATFORM
COPY --from=builder /app/target/*/release/app /app
CMD ["/app"]

重要:--platform=$BUILDPLATFORMを使うと、ビルド自体はネイティブアーキテクチャで実行できるので、QEMUエミュレーションより圧倒的に速いです(QEMUエミュレーションは16-25倍遅い)。

実測データ:

  • ネイティブビルド:2-3分
  • QEMUエミュレーション:50分(16-25倍遅い
  • cargo-zigbuildクロスコンパイル:13分

Docker buildxでのマルチプラットフォームビルド

# ビルダーの作成
docker buildx create --name container-builder \
    --driver docker-container --bootstrap --use

# マルチプラットフォームビルド
docker buildx build \
    --platform linux/amd64,linux/arm64 \
    -t myimage:latest \
    --push .

https://docs.docker.com/build/buildx/docs.docker.com

セキュリティベストプラクティス

1. 非rootユーザーで実行

distroless :nonrootタグが最も簡単:

FROM gcr.io/distroless/cc-debian12:nonroot
COPY --from=builder /app/target/release/myapp /usr/local/bin/
CMD ["/usr/local/bin/myapp"]

自動的にUID 65534(nobody)として実行されます。

カスタムユーザー作成:

FROM debian:bookworm-slim

ARG UID=10001
RUN adduser \
    --disabled-password \
    --gecos "" \
    --home "/nonexistent" \
    --shell "/sbin/nologin" \
    --no-create-home \
    --uid "${UID}" \
    appuser

USER appuser
COPY --from=builder /app/target/release/myapp /app/
CMD ["/app/myapp"]

2. 脆弱性スキャン

Trivy(推奨):

# イメージスキャン
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
    aquasec/trivy image myapp:latest

# CI/CD統合
- name: Run Trivy scan
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: 'myapp:${{ github.sha }}'
    severity: 'CRITICAL,HIGH'
    exit-code: '1'

github.com

distrolessのセキュリティ優位性:

  • Alpine(musl)からChiseled Ubuntuglibc)への移行で30+ CVEが0 CVEに
  • distrolessイメージはAlpineより50-80%少ないCVE
  • パッケージマネージャー不在により攻撃ベクトル削減
  • SLSA 2準拠、cosign署名認証

3. シークレット管理

絶対に避けるべき:環境変数へのシークレット設定

イメージに焼き込まれてしまいます。

正しい方法:

# ビルド時シークレット
RUN --mount=type=secret,id=api_token,env=API_TOKEN \
    cargo build --release
# 実行時
docker build --secret id=api_token,env=API_TOKEN .

4. イメージバージョンのピン留め

# ❌ 避けるべき
FROM rust:latest

# ✅ 推奨
FROM rust:1.85-slim-bookworm

ユースケース別Dockerfile

Webアプリケーション(Axum / Actix-web)

上記の「標準Dockerfile」パターンをそのまま使用できます。

CLIツール(完全静的リンク)

# syntax=docker/dockerfile:1

FROM rust:1.85-alpine AS builder
WORKDIR /app

RUN apk add --no-cache musl-dev openssl-dev openssl-libs-static

# 依存関係のキャッシュ
COPY Cargo.toml Cargo.lock ./
RUN --mount=type=cache,target=/usr/local/cargo/registry \
    mkdir src && echo "fn main() {}" > src/main.rs && \
    cargo build --release --target x86_64-unknown-linux-musl && \
    rm -rf src

COPY src ./src
RUN --mount=type=cache,target=/usr/local/cargo/registry \
    cargo build --release --target x86_64-unknown-linux-musl

FROM scratch
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/cli-tool /app/
ENTRYPOINT ["/app/cli-tool"]

シェルエイリアスは以下のように設定できます。

alias my-cli='docker run --rm -v $(pwd):/data my-cli-image'

ワークスペース(モノレポ)対応

# syntax=docker/dockerfile:1

ARG SERVICE_NAME=api-gateway
ARG RUST_VERSION=1.85

FROM lukemathwalker/cargo-chef:latest-rust-${RUST_VERSION} AS chef
WORKDIR /app

FROM chef AS planner
COPY . .
RUN cargo chef prepare --recipe-path recipe.json

FROM chef AS builder
ARG SERVICE_NAME
COPY --from=planner /app/recipe.json recipe.json
RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked \
    --mount=type=cache,target=/usr/local/cargo/git,sharing=locked \
    cargo chef cook --release --bin ${SERVICE_NAME} --recipe-path recipe.json

COPY . .
RUN --mount=type=cache,target=/usr/local/cargo/registry,sharing=locked \
    --mount=type=cache,target=/app/target,sharing=locked \
    cargo build --release --bin ${SERVICE_NAME} && \
    cp ./target/release/${SERVICE_NAME} /bin/service

FROM gcr.io/distroless/cc-debian12:nonroot
COPY --from=builder /bin/service /app/
ENTRYPOINT ["/app/service"]

異なるサービスを同じDockerfileから生成できます。

docker build --build-arg SERVICE_NAME=api-gateway -t gateway .
docker build --build-arg SERVICE_NAME=user-service -t users .

実践的な検証結果

実際のAxum Webアプリケーション(依存関係82個)で3つの戦略を検証しました。

検証環境:

  • CPU: Apple M-series (ARM64)
  • Docker: Colima on macOS
  • Rust: 1.85 (Edition 2024)

3つのパターン比較

パターン1: Naive(最適化なし)- デフォルトの実態

Dockerfile.naive は何も工夫しないシンプルなビルドです。これが「デフォルトの何もしていない状態」です。

⚠️ デフォルト状態のビルド結果

  • 初回ビルド時間: 約10-15分(依存関係82個を全てコンパイル
  • ソースコード変更後の再ビルド: 約10-15分(依存関係も毎回再コンパイル
  • 最終イメージサイズ: 2.63GB
  • セキュリティ: rootユーザー、開発ツール込み(脆弱性大)

問題点:

  • ソースコード1行変更するだけで10-15分のビルドが毎回走る
  • イメージに不要なRustコンパイラ(500MB)、ビルドツール、ドキュメントが全て含まれる
  • マルチステージビルドがないため、最終イメージが巨大
  • cargo-chefがないため、依存関係とソースコードが分離されていない

パターン2: Baseline(cargo-chef + distroless)

Dockerfile は2025年の推奨パターンです。

ビルド結果

  • ビルド時間: 38秒(依存関係キャッシュ済み)
  • 最終イメージサイズ: 50.3MB
  • セキュリティ: 非rootユーザー(UID 65534)、最小限のファイル
  • Trivy脆弱性: 0 HIGH/CRITICAL

パターン3: Ultra-minimal(musl + scratch)

Dockerfile.musl は最小サイズを優先したパターンです。

ビルド結果

  • ビルド時間: 46秒(依存関係キャッシュ済み)
  • 最終イメージサイズ: 1.71MB
  • セキュリティ: rootユーザー(scratchに制限あり)

比較結果まとめ

項目 Naive (未最適化) Baseline (distroless) Ultra-minimal (musl)
イメージサイズ 2.63GB 50.3MB 1.71MB
削減率 - (100%) 98.1%削減 99.9%削減
ビルド時間 30秒 38秒 46秒
マルチステージ ❌ なし ✅ あり (4段階) ✅ あり (2段階)
キャッシュ最適化 ❌ なし ✅ cargo-chef + BuildKit ✅ BuildKit
ベースイメージ rust:1.85 (full) distroless/cc-debian12 scratch
リンク方式 動的(glibc 動的(glibc 静的(musl)
開発ツール ❌ 含まれる ✅ 除去済み ✅ 除去済み
セキュリティ ❌ 低 ✅ 高 ⚠️ 中
デバッグ ✅ 可能 ❌ 困難 ❌ 不可能

パフォーマンスベンチマーク

商用プロジェクト(14,000行、500依存関係):

  • 最適化なし:10分
  • cargo-chef使用:2分(5倍高速化)

大規模ワークスペース(400 crate、1500依存関係):

  • 未最適化:約65分
  • 最適化後:約2分(30倍以上の改善)

検証の再現方法

このリポジトリで実際に試せます。

# Baseline版のビルド
docker build -t rust-demo:baseline .

# Ultra-minimal版のビルド
docker build -f Dockerfile.musl -t rust-demo:musl .

# サイズ比較
docker images | grep rust-demo

# 動作確認
docker run -p 8000:8000 rust-demo:baseline
docker run -p 8001:8000 rust-demo:musl

よくある問題と解決策

OpenSSLリンクエラー

エラー: "Could not find directory of OpenSSL installation"

解決策1:vendored OpenSSL(最も簡単)

[dependencies]
openssl = { version = "0.10", features = ["vendored"] }

解決策2:Alpine適切パッケージ

FROM rust:1.85-alpine
RUN apk add --no-cache openssl-dev openssl-libs-static musl-dev

解決策3:Debianベース使用

FROM rust:1.85-slim-bookworm
RUN apt-get update && apt-get install -y pkg-config libssl-dev

解決策4:rustls(Rust-native TLS

[dependencies]
reqwest = { version = "0.11", features = ["rustls-tls"], default-features = false }

muslリンクエラー

Alpine向け:

FROM rust:1.85-alpine
RUN apk add musl-dev openssl-dev openssl-libs-static
RUN rustup target add x86_64-unknown-linux-musl
ENV PKG_CONFIG_ALLOW_CROSS=1
RUN cargo build --release --target x86_64-unknown-linux-musl

必要な環境変数

  • RUSTFLAGS='-C target-feature=+crt-static'
  • PKG_CONFIG_ALLOW_CROSS=1
  • OPENSSL_STATIC=1(システムOpenSSL使用時)

DNS解決エラー(scratchイメージ)

解決策1:distroless/static使用

FROM gcr.io/distroless/static-debian12

解決策2:Pure Rust DNSゾルバー

[dependencies]
reqwest = { version = "0.11", features = ["trust-dns"] }

解決策3:必要ファイルコピー

FROM alpine:latest AS ca-certificates
RUN apk add -U --no-cache ca-certificates

FROM scratch
COPY --from=ca-certificates /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/

.dockerignoreの重要性

.dockerignoreがないと、target/ディレクトリ(数GB)がビルドコンテキストに含まれ、ビルドが遅くなります

# .dockerignore
target/
.git/
.env
*.log

効果: ビルドコンテキストのサイズを数GBから数MBに削減 → ビルド開始が高速化。

イメージサイズ肥大化

一般的原因と解決策:

  1. 最終イメージにビルドツール含む → マルチステージビルドで93%削減
  2. 本番環境でfull rustイメージを使用 → slimランタイムベースで95%削減
  3. バイナリにデバッグシンボルが含まれるstrip target/release/myappで30-40%削減
  4. 開発依存関係 → プロファイル設定
[profile.release]
strip = true
lto = true
codegen-units = 1

2025年の新ツール活用

docker init - プロジェクトの素早い立ち上げ

# プロジェクトディレクトリで実行
docker init

# Rustを選択すると自動生成:
# - Dockerfile
# - compose.yaml
# - .dockerignore
# - README.Docker.md

docs.docker.com

Docker Bake - 複雑なビルドの管理

docker-bake.hcl:

group "default" {
  targets = ["app"]
}

variable "TAG" {
  default = "latest"
}

target "app" {
  context = "."
  dockerfile = "Dockerfile"
  tags = ["myapp:${TAG}"]
  platforms = ["linux/amd64", "linux/arm64"]
  cache-from = ["type=registry,ref=myapp:cache"]
  cache-to = ["type=registry,ref=myapp:cache,mode=max"]
}
# 実行
docker buildx bake

# 変数をオーバーライド
docker buildx bake --set TAG=v1.0.0

docs.docker.com

おわりに

この記事では、2025年時点でのRust Dockerのベストプラクティスを包括的に解説しました。cargo-chefによる依存関係の分離キャッシングBuildKitの永続キャッシュマウントdistrolessイメージによるセキュリティ強化という3つの柱を中心に、実践的なDockerfileパターンと実測データを提供しています。

Rustのコンテナ化は長い間「ビルドが遅い」「イメージが大きい」という課題を抱えていました。コンパイル時間の長さは諦めるしかなく、数GBのイメージサイズは「Rustだから仕方ない」と言われてきました。

しかし、2025年現在、その課題は完全に解決しました。適切な最適化で、ビルド時間を5-10倍短縮イメージサイズを98-99%削減できます。これは単なる理論ではなく、実際のプロダクション環境で日々使われている技術です。

2025年のゴールデンルール

この記事で紹介した技術を実践する際は、以下の10のポイントを押さえておくといいでしょう。

  1. # syntax=docker/dockerfile:1を必ず記述 - 最新のDockerfile構文を自動利用
  2. cargo-chefで依存関係を分離 - 5-10倍のビルド高速化を実現
  3. BuildKitキャッシュマウントを活用 - レイヤーを超える永続的なキャッシュ
  4. distroless/cc-debian12:nonrootを使用 - 50MB、非root、高セキュリティ
  5. rust:slim-bookwormでビルド - Alpineは避ける(マルチスレッド性能問題)
  6. RUN --mount=type=bindソースコードをマウント - COPYの最小化
  7. マルチステージビルドは必須 - 2025年の前提条件
  8. 非rootユーザーで実行 - セキュリティの基本原則
  9. TrivyまたはGrypeでスキャン - 継続的なセキュリティ検証
  10. イメージバージョンをピン留め - :latestは避ける

大半の本番ワークロードには、glibc + distroless/cc-debian12 + cargo-chefの組み合わせが最適解です。この構成により、50MBの小サイズ2分の高速ビルドフルパフォーマンス優れたセキュリティプロファイルを実現できます。

マルチスレッドアプリケーションでmuslを使う場合、1点だけ注意が必要です。最大30倍のパフォーマンス劣化リスクがあるので、本番環境への導入前に必ずベンチマークで検証しましょう。イメージサイズだけで判断すると、後で後悔します。

2025年のRust Dockerは、従来の課題を完全に克服しました。高速、小サイズ、セキュア、マルチアーキテクチャ対応の成熟した技術スタックになっています。この記事で紹介した標準Dockerfileパターンは、そのままプロダクション環境で使える構成です。まずは標準パターンから始めて、必要なら sccachecargo-zigbuild などの高度な最適化を追加するといいでしょう。

Rustエコシステムの進化とDockerの機能強化で、今後もさらに改善していくはずです。この記事が、あなたのRustアプリケーションのコンテナ化に役立てば嬉しいです。