Distrolessで、運用コストの低いDockerイメージを作る

Googleが公開しているDistrolessは、アプリケーションの実行に特化したDockerイメージで、パッケージマネージャやシェル、その他不要なプログラムを含まないのが特徴です。

余分なものが含まれないため、セキュリティスキャン結果のノイズが少なくなる、運用コストが減るなどのメリットが考えられます。

本記事では、このDistorolessをベースに、Goで書かれたHello Worldアプリの実行用イメージを作ってみます。

2018年11月現在、PythonやNode.jsといった言語用のイメージは実験的なステータスとなっています。実験的とされていないのは、GoやRust、Javaなどの言語用イメージのみです。

hello.go

まずは、Hello Worldアプリのソースです。よくある内容ですね。

$ cat hello.go
package main
import "fmt"
func main() {
fmt.Println("Hello, world")
}

Dockerfile

そして、Dockerfileです。まずアプリビルド用のイメージで go install し、その結果をDistrolessベースのイメージにコピーしています。

$ cat Dockerfile
FROM golang:1.8 as build
WORKDIR /go/src/hello
COPY hello.go .
RUN go install
FROM gcr.io/distroless/base
COPY --from=build /go/bin/hello /
CMD ["/hello"]

ビルドして実行

上記のDockerfileを使い、イメージをビルドして実行してみます。

$ docker build -t distroless-hello .
$ docker run -t distroless-hello
Hello, world

はい、意図通り Hello, world と出力されました。

コンテナのシェルに入るには

Distrolessにはシェルが含まれないため、コンテナのシェルに入るには、デバッグ用のイメージ (:debug) を使う必要があります。

$ cat Dockerfile-debug
FROM golang:1.8 as build
WORKDIR /go/src/hello
COPY hello.go .
RUN go install
FROM gcr.io/distroless/base:debug
COPY --from=build /go/bin/hello /
CMD ["/hello"]

シェルに入ってみる

シェルに入るには --entrypoint=sh を指定します。

$ docker build -t distroless-hello-debug -f Dockerfile-debug .
$ docker run --entrypoint=sh -ti distroless-hello-debug
/ # ls -l
total 1532
drwxr-xr-x 2 root root 6 Jan 1 1970 bin
drwxr-xr-x 2 root root 6 Jan 1 1970 boot
drwxr-xr-x 2 root root 12288 Nov 28 05:17 busybox
drwxr-xr-x 5 root root 360 Nov 28 05:36 dev
drwxr-xr-x 1 root root 66 Nov 28 05:36 etc
-rwxr-xr-x 1 root root 1551621 Nov 28 05:15 hello
drwx------ 1 root root 26 Nov 28 05:36 home
drwxr-xr-x 3 root root 30 Nov 28 05:17 lib
drwxr-xr-x 2 root root 34 Nov 28 05:17 lib64
dr-xr-xr-x 113 root root 0 Nov 28 05:36 proc
drwx------ 2 root root 6 Jan 1 1970 root
drwxr-xr-x 2 root root 6 Jan 1 1970 run
drwxr-xr-x 2 root root 6 Jan 1 1970 sbin
dr-xr-xr-x 13 root root 0 Nov 28 05:36 sys
drwxrwxrwt 2 root root 6 Jan 1 1970 tmp
drwxr-xr-x 9 root root 92 Nov 28 05:17 usr
drwxr-xr-x 11 root root 116 Nov 28 05:17 var

BusyBox経由で、ls などの各種コマンドが使えます。

Alpine Linuxイメージとのサイズ比較

参考のため、Alpineベースのイメージとのサイズを比較してみます。

Distroless: 18.4MB

$ docker images distroless-hello --format "{{.Size}}"
18.4MB

Alpine: 5.96MB

$ cat Dockerfile-alpine
FROM golang:1.8 as build
WORKDIR /go/src/hello
COPY hello.go .
RUN go install
FROM alpine
COPY --from=build /go/bin/hello /
CMD ["/hello"]
$ docker run -t distroless-hello-alpine
Hello, world
$ docker images distroless-hello-alpine --format "{{.Size}}"
5.96MB

意外にも、Alpineベースのほうが小さいですね。サイズが重要な場合、現状ではAlpineが無難だといえます。

まとめ

本記事では、Distrolessを使い、アプリケーションの実行に特化したイメージを作ってみました。運用コストが抑えられるので、マッチするユースケースがあれば、ぜひプロダクション環境で使ってみたいところです。

なお、下記のGitHubリポジトリに本記事で使ったファイルをまとめています。よろしければご利用ください。