DockerでGo製のアプリケーションを動かすならdistrolessを使おうというお話
はじめに
タイトルがあれですが,要は distroless と Go 製のアプリケーションは相性がいいっていうだけのお話です.
distroless とは
distroless は,Google が開発している Debian ベースのイメージで最小限のファイルしか含まれていない[1]ためとても軽量という特徴があります.
最小限のファイルしか含まれていないというのは伊達でなくイメージにもよりますがglibc
・libssl
・openssl
すら含まれていないです.
シェルやパッケージマネージャーも含まれていないため,外部ライブラリを導入しないといけないようなアプリケーションには向きませんがシングルバイナリで動作するようなアプリケーションには向いています.
また,最小限のフォイルしか含まれていないためイメージサイズも小さくなります.イメージが小さくなって不都合が起こることは基本的には無いと思います.そういった意味でも,distroless を採用するのはありではないでしょうか.
distroless × Go
Go 製アプリケーションはシングルバイナリで動作するためglibc
などのパッケージに依存しません.そのため distroless でも問題なく動作させることができます.
そのため distroless はシングルバイナリで動作する Go アプリケーションと,とても相性が良いです.
distroless vs Alphine
軽量なベースイメージと言うと Alphine も有ると思います.ですが,最近は Alphine を使うメリットよりもデメリットのほうが大きくなる場合があります[2].
また,distroless のイメージサイズは Alphine の 50%程度らしいのでイメージの軽量化だけが目的なら distroless を使ったほうが良いでしょう.
参考
Distroless images are very small. The smallest distroless image, gcr.io/distroless/static-debian11, is around 2 MiB. That's about 50% of the size of alpine (~5 MiB), and less than 2% of the size of debian (124 MiB).
distroless を使うときの注意
まずシェルが含まれていないため実行バイナリを直接指定する必要があります.
ENTRYPOINT ["myapp"]
また,コンテナランタイムがアプリケーションをシェルから起動するのを防ぐために exec 形式で指定する必要があります[3].
exec 形式は,以下のようにENTRYPOINT []
のカッコ内に起動したい実行ファイルの名前を書く方式です.Docker も,この書き方を推奨しています[4].
# Bad : ENTRYPOINT "myapp"
# Good
ENTRYPOINT ["myapp"]
また,distroless を使用する際はマルチステージビルドを活用してイメージをビルドする必要があります.
Docker の場合は以下のようになります.
#=============== Build ===============
FROM golang:1.20 as build
WORKDIR /app
COPY . .
RUN go mod download
RUN CGO_ENABLED=0 go build -o /app/bin
#============ Application ============
FROM gcr.io/distroless/static-debian11
COPY /app/bin /
CMD ["/app"]
まとめ
Docker で Go 製のアプリケーションを動かすときには distroless も考えてみるといいよっていう話でした.
もちろん,distroless ではなく debian-slim などのイメージのほうが適切な場合もありますが選択肢の一つとして distroless をおすすめできたならと思います.
おまけ
glibc
を含んでいるイメージも有るため Rust などで作成されたアプリケーションでも使用することができると思います.また,一部の言語に関しては実行環境が含められたイメージも有るので公式リポジトリを覗いてみると良いと思います.
Discussion