🐙
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にビルドしたバイナリだけをコピーする。
- ステージ1(ビルド環境):
-
成果物:
# 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イメージでは動作しない(実行時エラーになる)。
- CGOを有効(
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ならシングルバイナリにできるとドヤ顔で言ったが、できなかったパターン。
- イメージサイズは
-
考察:
- それでも、
debianやubuntuのフルイメージ(数百MB)に比べれば依然として非常に小さい。
- それでも、
5. まとめ
- GoアプリケーションのDockerイメージ化において、
distrolessはデプロイ速度とセキュリティを劇的に向上させる第一選択肢になる。 - CGO不要なアプリ(Pure Go)なら
staticを使い、最小(10MB以下)を目指せる(何より気分的にすごく気持ちがいい)。 - CGOが必要なアプリでも
base(cc)を使うことで、十分なメリット(省サイズ + 高セキュリティ)が得られる。
6. 感想
- これまでぼんやりalpineよりもdistrolessなんだ〜と知っているだけだったが、alpine使わなくていいんじゃないか?という言説が腹落ちした(開発環境用のツール入れるならリッチなイメージでいいし、本番環境はdistrolessでいい)
7. 参考
Discussion