distroless
コンテナイメージから「Linuxディストリビューションらしさ」を取り除き、アプリケーションとその実行に必要なランタイム依存だけを残す設計。パッケージマネージャもシェルもコアユーティリティも入れない。 #container #security
何が入っていないのか
典型的な distroless イメージには以下がない。
- シェル(
/bin/sh) - パッケージマネージャ(
apt,apk,yum) ls,cat,curlなどのコアユーティリティ- ドキュメント、manページ、ロケールデータ
残るのは、アプリのバイナリと、それが動くのに必要な共有ライブラリ・CA証明書・タイムゾーンデータ・/etc/passwd 程度。
狙い
攻撃面の縮小: シェルがないコンテナでは、RCEを取られてもそこから対話的に侵入を広げるのが難しい。パッケージマネージャがなければ追加ツールを引き込まれることもない。
CVEノイズの削減: スキャナが報告するCVEの大半は、アプリと無関係なOSコンポーネント由来のものになりがち。そもそも入れないことで、スキャン結果のS/N比が上がる。
サイズ: gcr.io/distroless/static-debian13 は約2MiB。Alpine(約5MiB)の半分以下、Debian(124MiB)の2%未満。
起源
Googleの GoogleContainerTools/distroless が概念を広めた。Bazelでビルドされ、static/base/cc/java/nodejs/python3 のように言語ランタイム単位でイメージが用意されている。
その後、Chainguardのコンテナイメージや、多くのベンダーの「最小イメージ」路線がこの発想を引き継いでいる。
使い方はマルチステージビルドが前提
パッケージマネージャがないので、distrolessイメージの中でビルドはできない。ビルド用ステージで成果物を作り、それをdistrolessなランタイムステージへCOPYする形になる。
FROM golang:1.24 AS build
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -o /app ./cmd/server
FROM gcr.io/distroless/static-debian13
COPY --from=build /app /app
ENTRYPOINT ["/app"]
運用上の痛み
シェルがないので docker exec -it ... sh でのデバッグができない。対処としては以下がある。
- デバッグ用のバリアントを使う。Googleのdistrolessは
:debugタグにBusyBoxが入っており、Chainguardのイメージは-devサフィックス付きタグにシェルとapkが入っている kubectl debugの ephemeral container で、デバッグ用イメージを同じプロセス名前空間にアタッチする- 静的リンクしたバイナリを一時的に
COPYする