distrolessとは?
Googleが公開している、ランタイム用のDockerイメージです。
なぜ使うか?
Dockerはプロダクション環境にも導入されるようになりました。
プロダクション環境では脆弱性対応などをする必要があります。
デプロイされるDockerイメージをアプリケーション実行に必要なものだけに制限することで、このような間接的な作業を減らすことが可能です。
distroless
イメージはこのような要望に対応した、アプリケーションランタイムのみに特化したDockerイメージです。
実際にイメージに入っているのは、gcr.io/distroless/base
の場合、
- glibc
- libssl
- openssl
- ca-certificates
- A /etc/passwd entry for a root user
- A /tmp directory
のみです。
nodejsイメージなどは、上記に追加で各ランタイム用のバイナリが含まれるようです。
使ってみる
distrolessイメージを使ったランタイムイメージを作成する
https://github.com/GoogleContainerTools/distroless
のexampleを元にnodejsで試してみました。
以下のような Dockerfile
が格納されています。
FROM node:8.9.1 AS build-env
ADD . /app
WORKDIR /app
FROM gcr.io/distroless/nodejs
COPY --from=build-env /app /app
WORKDIR /app
CMD ["hello.js"]
ビルドステージ(最初3行)とそのアウトプットを distroless
イメージにコピーするステージ(最後の4行)に分かれています。
※distroless
イメージの話というよりマルチステージビルドの話になりますが、この記述の意図は、「アプリのビルド時だけに必要な依存パッケージは最終イメージに持ち込ませない」だと思います。
このDockerfile
を使って、
distroless
イメージをベースとしたDockerイメージをビルドしてみます。
$ docker build . -t nodejs_distroless 水 7/18 22:09:30 2018
Sending build context to Docker daemon 7.168kB
Step 1/7 : FROM node:8.9.1 AS build-env
8.9.1: Pulling from library/node
85b1f47fba49: Pull complete
ba6bd283713a: Pull complete
817c8cd48a09: Pull complete
47cc0ed96dc3: Pull complete
8888adcbd08b: Pull complete
6f2de60646b9: Pull complete
51fa8867e10f: Pull complete
3de546fb9d8f: Pull complete
Digest: sha256:552348163f074034ae75643c01e0ba301af936a898d778bb4fc16062917d0430
Status: Downloaded newer image for node:8.9.1
---> 1934b0b038d1
Step 2/7 : ADD . /app
---> edaf827024d3
Step 3/7 : WORKDIR /app
Removing intermediate container 1712ca1226a1
---> 26d537ccf985
Step 4/7 : FROM gcr.io/distroless/nodejs
latest: Pulling from distroless/nodejs
57752e7f9593: Pull complete
ba7c544469e5: Pull complete
fe1a8dd285c0: Pull complete
Digest: sha256:ed997b713028c133799c41c2401e5d77ba1973876e92c55d1d67b6a0b68e9c29
Status: Downloaded newer image for gcr.io/distroless/nodejs:latest
---> a853ae078c7b
Step 5/7 : COPY --from=build-env /app /app
---> b3c68eb49cc5
Step 6/7 : WORKDIR /app
Removing intermediate container 79aa5baa396d
---> b6f4f9fd66d3
Step 7/7 : CMD ["hello.js"]
---> Running in 4dfbc310729f
Removing intermediate container 4dfbc310729f
---> ccb158736e44
Successfully built ccb158736e44
Successfully tagged nodejs_distroless:latest
実際どうなのか
イメージサイズ
node
Dockerイメージを使ってビルドした結果と比較します。
ベースのイメージ | イメージサイズ |
---|---|
node:8.9.1 | 676MB |
node:8.9.1-alpine | 67.7MB |
gcr.io/distroless/nodejs:latest | 75.1MB |
結果的に、alpineの方が容量が少なくなりましたが、
ほとんど同レベルとなっています。
ログインしようとしてみる
$ docker run --rm -it --entrypoint /bin/bash nodejs_distroless
docker: Error response from daemon: OCI runtime create failed: container_linux.go:348: starting container process caused "exec: \"/bin/bash\": stat /bin/bash: no such file or directory": unknown.
$ docker run --rm -it --entrypoint /bin/sh nodejs_distroless
docker: Error response from daemon: OCI runtime create failed: container_linux.go:348: starting container process caused "exec: \"/bin/sh\": stat /bin/sh: no such file or directory": unknown.
できませんでした。
まとめ
最初タイトルを「最小のランタイム用」としようと思っていましたが、
nodejsはalpineよりもサイズが大きくなってしまいました笑
(Golangでやってみればよかった)
現状は以下のイメージが公開されています。
- gcr.io/distroless/base (Golangなどバイナリでコンパイルされたものの実行環境)
- gcr.io/distroless/java (openjdk8ベース)
- gcr.io/distroless/cc (Rust, D言語用)
- gcr.io/distroless/python2.7
- gcr.io/distroless/python3
- gcr.io/distroless/nodejs
- gcr.io/distroless/java/jetty
- gcr.io/distroless/dotnet
このうち、base以外はまだ、プロダクションレディのようです。
※java, ccについては、GitレポジトリのReadmeからはどちらかわかりませんでした。。