CLOVER🍀

That was when it all began.

Distroless Dockerイメージ(OpenJDK)を試す

これは、なにをしたくて書いたもの?

前々から、DistrolessというDockerイメージが気になっていて、ちょっと試して見ることにしました。

GitHub - GoogleContainerTools/distroless: 🥑 Language focused docker images, minus the operating system.

Base Image Journey 2018 - Speaker Deck

Distroless

Distroless Dockerイメージとは、アプリケーション(というか言語)と、その依存関係のみが含まれるDockerイメージです。

"Distroless" images contain only your application and its runtime dependencies. They do not contain package managers, shells or any other programs you would expect to find in a standard Linux distribution.

その他のプログラムは入っておらず、シェルすらありません。よって、イメージのサイズも小さめです。

どうしてこういったイメージが存在するのかというと、不要なものを極力省くことによって、セキュリティリスクを抑えることが
できるとされています。

Why should I use distroless images?

各言語のベースとなるDockerイメージに、どんなものが含まれているかはこちらに記載があります。

https://github.com/GoogleContainerTools/distroless/tree/master/base

  • ca-certificates
  • /etc/passwd (root only)
  • /tmp directory
  • tzdata
  • glibc
  • libssl
  • openssl

このDockerイメージの使い方としては、シェルが含まれないので、実行対象となるファイルやスクリプトを入れたDockerイメージを作り、
ENTRYPOINTやCMDで実行対象のファイルを指定するようです。

How do I use distroless images?

例えば、Javaであれば以下のように書きます。

CMD ["main.jar"]

https://github.com/GoogleContainerTools/distroless/blob/master/examples/java/Dockerfile

ベースイメージの方では、「java -jar」が指定されています。

Distroless Dockerイメージではアプリケーションのビルドはできないので、ビルドが必要な言語の場合はマルチステージビルドを使って
別のDockerイメージでビルドした結果を使用します。

ベースとなるOSイメージは、Debianのようです。現時点では、Debian 10です。

Base Operating System

各言語のイメージとしては、以下があります。

distroless/examples at master · GoogleContainerTools/distroless · GitHub

今回は、Java(OpenJDK)のイメージを使用してみます。

Distroless(OpenJDK)のDockerイメージ

以下のようにして、まずはDockerイメージを取得してみます。

$  docker image pull gcr.io/distroless/java:latest
$  docker image pull gcr.io/distroless/java:8
$  docker image pull gcr.io/distroless/java:11

確認。

$ docker container run -it --rm gcr.io/distroless/java:latest -version
openjdk version "1.8.0_232"
OpenJDK Runtime Environment (build 1.8.0_232-8u232-b09-1~deb9u1-b09)
OpenJDK 64-Bit Server VM (build 25.232-b09, mixed mode)


$ docker container run -it --rm gcr.io/distroless/java:8 -version
openjdk version "1.8.0_232"
OpenJDK Runtime Environment (build 1.8.0_232-8u232-b09-1~deb9u1-b09)
OpenJDK 64-Bit Server VM (build 25.232-b09, mixed mode)


$ docker container run -it --rm gcr.io/distroless/java:11 -version
openjdk version "11.0.5" 2019-10-15
OpenJDK Runtime Environment (build 11.0.5+10-post-Debian-1bpo91)
OpenJDK 64-Bit Server VM (build 11.0.5+10-post-Debian-1bpo91, mixed mode)

latestは、JDK 8を指しているようです。細かいバージョンを指定できるタグは…なさそうです。

起動時には「java -jar」を実行しようとするので、シェルを実行することはできません。

$ docker container run -it --rm gcr.io/distroless/java:8 bash
Error: Unable to access jarfile bash

イメージのサイズですが、こんな感じでした。

$ docker image ls | grep distroless
gcr.io/distroless/java             8                   0ca884705e30        50 years ago         125MB
gcr.io/distroless/java             latest              0ca884705e30        50 years ago         125MB
gcr.io/distroless/java             11                  032fa27b8c17        50 years ago         195MB

比較として、OpenJDKのイメージを見てみます。

$ docker image ls | grep openjdk | grep -v none | sort
openjdk                            11-jdk              193af7392c2e        4 days ago          606MB
openjdk                            11-jdk-slim         724512274dbb        4 days ago          401MB
openjdk                            11-jre              67edbda57bd6        4 days ago          267MB
openjdk                            11-jre-slim         fa68260be6bf        4 days ago          204MB
openjdk                            8-jdk               0bfcee65c8ca        4 days ago          488MB
openjdk                            8-jdk-alpine        a3562aa0b991        9 months ago        105MB
openjdk                            8-jdk-slim          4229f5fb33f9        4 days ago          284MB
openjdk                            8-jre               5557c40af992        4 days ago          246MB
openjdk                            8-jre-alpine        f7a292bbb70c        9 months ago        84.9MB
openjdk                            8-jre-slim          9c82c74fbc96        4 days ago          184MB

Alpineほどではないですが、JREに比べるとスリムになりますね。できる限りイメージは小さくしたいけれど、Alpineを避けたい人には
Distroless Dockerイメージは有力な選択肢になるんでしょうね。

インストールされているOpenJDKは、Debianでパッケージ管理されているものみたいです。

https://github.com/GoogleContainerTools/distroless/blob/master/java/BUILD

Quarkusで試す

せっかくなので、Distroless Dockerイメージを使って、自前のイメージを作ってみましょう。

お題は、Quarkusの最小構成でいきます。1.2.0.Finalを使用します。

プロジェクトの作成。

$ curl -o app.zip 'https://code.quarkus.io/d?g=org.example&a=app&v=1.0.0&b=MAVEN&c=org.example.ExampleResource&s=&cn=code.quarkus.io'
$ unzip app.zip

Dockerfileは、こんな感じで作成。
Dockerfile

FROM openjdk:8-jdk-slim AS build-env
ADD ./app /app
WORKDIR /app
RUN ./mvnw package -DskipTests=true

FROM gcr.io/distroless/java:8
COPY --from=build-env /app/target/app-1.0.0-runner.jar /app/app-1.0.0-runner.jar
COPY --from=build-env /app/target/lib /app/lib
WORKDIR /app
CMD ["app-1.0.0-runner.jar"]

こちらを参考にしています。

https://github.com/GoogleContainerTools/distroless/blob/master/examples/java/Dockerfile

ビルド。

$ docker image build -t kazuhira/quarkus-app:latest .

起動。

$ docker container run -it --rm -p 8080:8080 kazuhira/quarkus-app:latest

確認。

$ curl localhost:8080/hello
hello

OKそうですね。

くどいようですが、このイメージを使っても「docker container exec」でそのままではコンテナ内に入ることはできません。

$ docker container exec dazzling_ritchie -it bash
OCI runtime exec failed: exec failed: container_linux.go:346: starting container process caused "exec: \"-it\": executable file not found in $PATH": unknown

これをやりたかったら、BusyBoxを使ったりすることになるでしょう。

とりあえず、こんな感じで。