🐙

alpineよりもdistrolessと言われて早何年経ち、今更ながら調べてみた

に公開

1. 導入

前提

筆者は数年前のalpine全盛期で知識が止まっていた。
alpineよりもdistrolessだよね!という話だけは知っていた。

イメージサイズが大きいことのデメリット

  • デプロイの遅延: CI/CDパイプラインでのpush/pullに時間がかかる。
  • ストレージコストの増大: イメージレジストリの容量を圧迫する。

alpineでも小ささだけならかなりのもの(イメージサイズは8MB)だけど、distrolessは下記の大きなメリットがある。

distrolessとは

  • Googleが提供する、アプリケーションの実行に必要な最小限のランタイムだけを含むDockerベースイメージ。
  • シェルすら含まないが、アプリケーションを動かす分にはあまり不自由しない。
  • セキュリティリスクの低減: 不要なライブラリやツール(シェル、パッケージマネージャ等)が含まれないので、攻撃対象領域(Attack Surface)が狭まる。

ツール類が入っていないため、もちろんalpineと同等かそれ以上にイメージは小さくなる。

2. Go + distrolessでイメージを作成してみる。

  • Go言語の特徴:

  • シングルバイナリを生成できるため、Dockerと非常に相性が良い。

  • 劇的に小さなイメージサイズ(例:元のイメージはなんと2MB程度なので 10MB以下 のイメージも目指せる)により、Goのビルドの速さも合わさってCI/CDが瞬時に終わる。

  • マルチステージビルドの実践 (Dockerfile)

    • ステージ1(ビルド環境): golang:1.xx などでアプリケーションをビルドする。
    • ステージ2(実行環境): gcr.io/distroless/static にビルドしたバイナリだけをコピーする。
  • 成果物:

# Builder
# runnerがdebian12なので合わせる debian12=bookworm
FROM golang:1.25-bookworm AS go_builder

ARG BUILD_VERSION="dev"

# ビルドツールのDL
RUN apt-get update && apt-get install -y build-essential

WORKDIR /build

# 依存関係のDL
COPY go.mod go.sum ./
RUN go mod download

# ソースコードのCOPY
COPY . .

# ビルド
# CGOを無効化
RUN CGO_ENABLED=0 go build -ldflags "-s -w -X main.version=${BUILD_VERSION}" -o app main.go

# Runner
# https://github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/static-debian12
COPY --from=go_builder /build/app /
USER nonroot
CMD ["/app"]

3. CGO依存ライブラリ の罠

  • 問題の発生: staticイメージを使えるのは、Goの標準ライブラリだけで完結している(CGOを使っていない)場合。
  • : gcr.io/distroless/static-debianXX は、Cの共有ライブラリ(libc等)を含まない。
    • CGOを有効( CGO_ENABLED=1 )にしてビルドしたバイナリは static イメージでは動作しない(実行時エラーになる)。

4. 解決策と現実的な落とし所

  • 解決策: gcr.io/distroless/base-debianXX もしくは gcr.io/distroless/cc-debianXX を使用する。

    • base イメージには、Cライブラリ、cc イメージにはc++ライブラリが含まれているため、CGOでビルドしたバイナリも動作する。
  • 最終成果物:

# Builder
# runnerがdebian12なので合わせる debian12=bookworm
FROM golang:1.25-bookworm AS go_builder

ARG BUILD_VERSION="dev"

# ビルドツールのDL
RUN apt-get update && apt-get install -y build-essential

WORKDIR /build

# 依存関係のDL
COPY go.mod go.sum ./
RUN go mod download

# ソースコードのCOPY
COPY . .

# ビルド
# CGOを有効化
RUN CGO_ENABLED=1 go build -ldflags "-s -w -X main.version=${BUILD_VERSION}" -o app main.go

# Runner
# https://github.com/GoogleContainerTools/distroless
# イメージを変更
FROM gcr.io/distroless/cc-debian12
COPY --from=go_builder /build/app /
USER nonroot
CMD ["/app"]

  • トレードオフ:
    • イメージサイズは static よりも増加する。( 元のイメージが34MBなので、50MB〜 くらい)
    • Goならシングルバイナリにできるとドヤ顔で言ったが、できなかったパターン。
  • 考察:
    • それでも、debianubuntu のフルイメージ(数百MB)に比べれば依然として非常に小さい。

5. まとめ

  • GoアプリケーションのDockerイメージ化において、distroless はデプロイ速度とセキュリティを劇的に向上させる第一選択肢になる。
  • CGO不要なアプリ(Pure Go)なら static を使い、最小(10MB以下)を目指せる(何より気分的にすごく気持ちがいい)。
  • CGOが必要なアプリでも base ( cc )を使うことで、十分なメリット(省サイズ + 高セキュリティ)が得られる。

6. 感想

  • これまでぼんやりalpineよりもdistrolessなんだ〜と知っているだけだったが、alpine使わなくていいんじゃないか?という言説が腹落ちした(開発環境用のツール入れるならリッチなイメージでいいし、本番環境はdistrolessでいい)

7. 参考

https://zenn.dev/aqyuki/articles/87be6171daf6b5

https://qiita.com/matoruru/items/27b478c311370106e6e6

https://blog.inductor.me/entry/alpine-not-recommended

https://qiita.com/Yuuki557/items/3d088de91ab86bc71600

株式会社アクティブコア

Discussion