簡単にできるDockerイメージのダウンサイジング

# 概要

Dockerイメージのサイズが大きくなるとレジストリにPushやPullする時間が増します
運用環境や開発環境でイメージのやり取りを繰り返される場合は、回数を重ねるごとにバカにならないほどの待ち時間が発生することになる
また、ネットワーク帯域も無駄に消費します
なので、簡単にできるイメージサイズのダウンサイジングをご紹介します
下記のサンプルでは**1.04GBのイメージ ⇒ 415MB**までにダウンサイジングしています
もし、下記以外にもダウンサイジングの方法がありましたら、ご教示ください！

以下のサンプルコードでは、**差を分かりやすくするためにあえてjdkをダウンロードし、所有権を変更しています**
なお、イメージのリビルド時にキャッシュの有効利用は考慮していません

# おさらい

Dockerイメージはビルド時にDockerfileの各コマンドごとに中間イメージが作成される
**各中間イメージにはそのコマンドで実行された変更履歴が含まれます**
各コマンドが実行され、生成された中間イメージは以下のようになります
![docker image layer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30522/f5db7765-0e3f-ea71-fd11-76dfc1b73943.png)


# 最適化していない状態

まず、なにも最適化していないDockerfileでビルドされたイメージサイズは**1.04GB**もあります！

```Dockerfile:Dockerfile

FROM node:12-slim

WORKDIR /tmp

RUN wget https://download.java.net/java/GA/jdk12.0.2/e482c34c86bd4bf8b56c0b35558996b9/10/GPL/openjdk-12.0.2_linux-x64_bin.tar.gz
RUN tar -zxvf ./openjdk-12.0.2_linux-x64_bin.tar.gz
RUN chown node:node -R /tmp/jdk-12.0.2/

RUN apt-get update -y
RUN apt-get upgrade -y
```

historyコマンドで中間イメージの容量を確認していきましょう
jdkの取得やファイル解凍で容量を消費しているのはわかりやすいのですが、
**chownのようにファイルのメタデータ（所有権）の変更分も容量を消費します**
ここは案外見落としがちなポイントです

```shell

> docker build -t downsize:1 ./

-----------------------------------------------------------------------------------------------------------
REPOSITORY      TAG                       IMAGE ID            CREATED              SIZE
downsize        1                         e7d7b8b217ba        About a minute ago   1.04GB


> docker history downsize:1

-----------------------------------------------------------------------------------------------------------
IMAGE               CREATED             CREATED BY                                      SIZE
e7d7b8b217ba        2 minutes ago       /bin/sh -c apt-get upgrade -y                   3.85MB
f70cff27b81a        2 minutes ago       /bin/sh -c apt-get update -y                    16.4MB
ce460fc686bc        2 minutes ago       /bin/sh -c chown node:node -R /tmp/jdk-12.0.…   335MB
29b8fb0905ac        2 minutes ago       /bin/sh -c tar -zxvf ./openjdk-12.0.2_linux-…   335MB
39545720b094        3 minutes ago       /bin/sh -c wget https://download.java.net/ja…   198MB
48b8f4c4641a        3 minutes ago       /bin/sh -c #(nop) WORKDIR /tmp                  0B
710340b3b82e        2 months ago        /bin/sh -c #(nop)  CMD ["node"]                 0B
<missing>           2 months ago        /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B
<missing>           2 months ago        /bin/sh -c #(nop) COPY file:238737301d473041…   116B
<missing>           2 months ago        /bin/sh -c set -ex   && for key in     6A010…   5.08MB
<missing>           2 months ago        /bin/sh -c #(nop)  ENV YARN_VERSION=1.16.0      0B
<missing>           2 months ago        /bin/sh -c buildDeps='xz-utils'     && ARCH=…   90.4MB
<missing>           2 months ago        /bin/sh -c #(nop)  ENV NODE_VERSION=12.6.0      0B
<missing>           2 months ago        /bin/sh -c groupadd --gid 1000 node   && use…   333kB
<missing>           2 months ago        /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>           2 months ago        /bin/sh -c #(nop) ADD file:5ffb798d64089418e…   55.3MB
```

# コマンドをまとめる

各コマンドをすべてひとつの`RUN`にまとめることで一連のコマンドで発生した履歴も１つになる
中間イメージが減る分容量消費も抑えられる
結果的に**300MB減の704MB**になりました

```Dockerfile:Dockerfile

FROM node:12-slim

WORKDIR /tmp

RUN wget https://download.java.net/java/GA/jdk12.0.2/e482c34c86bd4bf8b56c0b35558996b9/10/GPL/openjdk-12.0.2_linux-x64_bin.tar.gz \
    && tar -zxvf ./openjdk-12.0.2_linux-x64_bin.tar.gz \
    && chown node:node -R /tmp/jdk-12.0.2/ \
    && apt-get update -y \
    && apt-get upgrade -y
```

```shell

> docker build -t downsize:2 ./

-----------------------------------------------------------------------------------------------------------
REPOSITORY      TAG                       IMAGE ID            CREATED              SIZE
downsize        2                         30a607b1a778        About a minute ago   704MB

> docker history downsize:2

-----------------------------------------------------------------------------------------------------------
IMAGE               CREATED             CREATED BY                                      SIZE    
30a607b1a778        2 minutes ago       /bin/sh -c wget https://download.java.net/ja…   553MB
48b8f4c4641a        7 minutes ago       /bin/sh -c #(nop) WORKDIR /tmp                  0B
710340b3b82e        2 months ago        /bin/sh -c #(nop)  CMD ["node"]                 0B
<missing>           2 months ago        /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B
<missing>           2 months ago        /bin/sh -c #(nop) COPY file:238737301d473041…   116B
<missing>           2 months ago        /bin/sh -c set -ex   && for key in     6A010…   5.08MB
<missing>           2 months ago        /bin/sh -c #(nop)  ENV YARN_VERSION=1.16.0      0B
<missing>           2 months ago        /bin/sh -c buildDeps='xz-utils'     && ARCH=…   90.4MB
<missing>           2 months ago        /bin/sh -c #(nop)  ENV NODE_VERSION=12.6.0      0B
<missing>           2 months ago        /bin/sh -c groupadd --gid 1000 node   && use…   333kB
<missing>           2 months ago        /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>           2 months ago        /bin/sh -c #(nop) ADD file:5ffb798d64089418e…   55.3MB
```

# 一時ファイルを削除する

これも忘れがちですが、一時ファイルを削除するだけでサイズ減に繋がる
上記のコマンドに `rm -f ./openjdk-12.0.2_linux-x64_bin.tar.gz` を追加しただけで、
さらに**200MB減の506MB**になりました

```Dockerfile:Dockerfile
FROM alpine

WORKDIR /tmp

RUN wget https://download.java.net/java/GA/jdk12.0.2/e482c34c86bd4bf8b56c0b35558996b9/10/GPL/openjdk-12.0.2_linux-x64_bin.tar.gz \
    && tar -zxvf ./openjdk-12.0.2_linux-x64_bin.tar.gz \
    && chown postgres:postgres -R /tmp/jdk-12.0.2/ \
    && rm -f ./openjdk-12.0.2_linux-x64_bin.tar.gz

```

openjdk-12.0.2_linux-x64_bin.tar.gz分のサイズが減った！

```shell

> docker build -t downsize:3 ./

-----------------------------------------------------------------------------------------------------------
REPOSITORY      TAG                       IMAGE ID            CREATED              SIZE
downsize        2                         92986480403b        About a minute ago   506MB

> docker history downsize:3
-----------------------------------------------------------------------------------------------------------
IMAGE               CREATED             CREATED BY                                      SIZE
92986480403b        4 seconds ago       /bin/sh -c wget https://download.java.net/ja…   355MB
48b8f4c4641a        9 minutes ago       /bin/sh -c #(nop) WORKDIR /tmp                  0B
710340b3b82e        2 months ago        /bin/sh -c #(nop)  CMD ["node"]                 0B
<missing>           2 months ago        /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B
<missing>           2 months ago        /bin/sh -c #(nop) COPY file:238737301d473041…   116B
<missing>           2 months ago        /bin/sh -c set -ex   && for key in     6A010…   5.08MB
<missing>           2 months ago        /bin/sh -c #(nop)  ENV YARN_VERSION=1.16.0      0B
<missing>           2 months ago        /bin/sh -c buildDeps='xz-utils'     && ARCH=…   90.4MB
<missing>           2 months ago        /bin/sh -c #(nop)  ENV NODE_VERSION=12.6.0      0B
<missing>           2 months ago        /bin/sh -c groupadd --gid 1000 node   && use…   333kB
<missing>           2 months ago        /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>           2 months ago        /bin/sh -c #(nop) ADD file:5ffb798d64089418e…   55.3MB
```

## これだと減らないので注意！

コマンドを分けることで`IMAGE ID:30a607b1a778`の結果は553MBのままなので、そのまま中間イメージとして加算される

```Dockerfile:Dockerfile
FROM node:12-slim

WORKDIR /tmp

RUN wget https://download.java.net/java/GA/jdk12.0.2/e482c34c86bd4bf8b56c0b35558996b9/10/GPL/openjdk-12.0.2_linux-x64_bin.tar.gz \
    && tar -zxvf ./openjdk-12.0.2_linux-x64_bin.tar.gz \
    && chown node:node -R /tmp/jdk-12.0.2/ \
    && apt-get update -y \
    && apt-get upgrade -y

RUN rm -f ./openjdk-12.0.2_linux-x64_bin.tar.gz
```

```shell

> docker build -t downsize:3.1 ./

-----------------------------------------------------------------------------------------------------------
REPOSITORY      TAG                       IMAGE ID            CREATED              SIZE
downsize        3.1                       4ee2a610be24        About a minute ago   704MB

> docker history downsize:3.1
-----------------------------------------------------------------------------------------------------------
IMAGE               CREATED             CREATED BY                                      SIZE
4ee2a610be24        About a minute ago   /bin/sh -c rm -f ./openjdk-12.0.2_linux-x64_…   0B
30a607b1a778        8 minutes ago        /bin/sh -c wget https://download.java.net/ja…   553MB
48b8f4c4641a        13 minutes ago       /bin/sh -c #(nop) WORKDIR /tmp                  0B
710340b3b82e        2 months ago         /bin/sh -c #(nop)  CMD ["node"]                 0B
<missing>           2 months ago         /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B
<missing>           2 months ago         /bin/sh -c #(nop) COPY file:238737301d473041…   116B
<missing>           2 months ago         /bin/sh -c set -ex   && for key in     6A010…   5.08MB
<missing>           2 months ago         /bin/sh -c #(nop)  ENV YARN_VERSION=1.16.0      0B
<missing>           2 months ago         /bin/sh -c buildDeps='xz-utils'     && ARCH=…   90.4MB
<missing>           2 months ago         /bin/sh -c #(nop)  ENV NODE_VERSION=12.6.0      0B
<missing>           2 months ago         /bin/sh -c groupadd --gid 1000 node   && use…   333kB
<missing>           2 months ago         /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>           2 months ago         /bin/sh -c #(nop) ADD file:5ffb798d64089418e…   55.3MB
```

# マルチステージング化

今回のサンプルでは残念ながらサイズ減にはならなかったが、
中間イメージサイズの起算が `COPY --from=base /tmp/jdk-12.0.2 /tmp/jdk-12.0.2`になりますので、
事前処理であれこれ(ソースのコンパイルなど)した場合は、マルチステージング化することで簡単に容量減に繋がります

```Dockerfile:Dockerfile

FROM node:12-slim as base

WORKDIR /tmp

RUN wget https://download.java.net/java/GA/jdk12.0.2/e482c34c86bd4bf8b56c0b35558996b9/10/GPL/openjdk-12.0.2_linux-x64_bin.tar.gz \
    && tar -zxvf ./openjdk-12.0.2_linux-x64_bin.tar.gz \
    && chown node:node -R /tmp/jdk-12.0.2/

FROM node:12-slim

COPY --from=base /tmp/jdk-12.0.2 /tmp/jdk-12.0.2

RUN apt-get update -y \
    && apt-get upgrade -y
```

```shell
> docker build -t downsize:4 ./
-----------------------------------------------------------------------------------------------------------
REPOSITORY      TAG                       IMAGE ID            CREATED              SIZE
downsize        4                       593b49b2ed5e        About a minute ago     506MB

> docker history downsize:4
-----------------------------------------------------------------------------------------------------------
IMAGE               CREATED             CREATED BY                                      SIZE
593b49b2ed5e        12 minutes ago      /bin/sh -c apt-get update -y     && apt-get …   20.3MB
f5659402be7a        12 minutes ago      /bin/sh -c #(nop) COPY dir:523c1f51e78e02aa1…   335MB
710340b3b82e        2 months ago        /bin/sh -c #(nop)  CMD ["node"]                 0B
<missing>           2 months ago        /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B
<missing>           2 months ago        /bin/sh -c #(nop) COPY file:238737301d473041…   116B
<missing>           2 months ago        /bin/sh -c set -ex   && for key in     6A010…   5.08MB
<missing>           2 months ago        /bin/sh -c #(nop)  ENV YARN_VERSION=1.16.0      0B
<missing>           2 months ago        /bin/sh -c buildDeps='xz-utils'     && ARCH=…   90.4MB
<missing>           2 months ago        /bin/sh -c #(nop)  ENV NODE_VERSION=12.6.0      0B
<missing>           2 months ago        /bin/sh -c groupadd --gid 1000 node   && use…   333kB
<missing>           2 months ago        /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>           2 months ago        /bin/sh -c #(nop) ADD file:5ffb798d64089418e…   55.3MB
```

# パッケージマネージャのキャッシュを削除

これも忘れがちなのですが、パッケージマネージャのキャッシュを削除することで容量減に繋がります
**さらに20MB減の490MBになりました**

```Dockerfile:Dockerfile

FROM node:12-slim as base

WORKDIR /tmp

RUN wget https://download.java.net/java/GA/jdk12.0.2/e482c34c86bd4bf8b56c0b35558996b9/10/GPL/openjdk-12.0.2_linux-x64_bin.tar.gz \
    && tar -zxvf ./openjdk-12.0.2_linux-x64_bin.tar.gz \
    && chown node:node -R /tmp/jdk-12.0.2/

FROM node:12-slim

COPY --from=base /tmp/jdk-12.0.2 /tmp/jdk-12.0.2

RUN apt-get update -y \
    && apt-get upgrade -y \
    && apt-get autoremove -y \
    && apt-get clean -y \
    && rm -rf /var/lib/apt/lists/*
```


```shell
> docker build -t downsize:5 ./
-----------------------------------------------------------------------------------------------------------
REPOSITORY      TAG                       IMAGE ID            CREATED              SIZE
downsize        5                       6de3a6ddead6          About a minute ago   490MB

> docker history downsize:5
-----------------------------------------------------------------------------------------------------------
IMAGE               CREATED              CREATED BY                                      SIZE
6de3a6ddead6        About a minute ago   /bin/sh -c apt-get update -y     && apt-get …   3.85MB
f5659402be7a        12 minutes ago       /bin/sh -c #(nop) COPY dir:523c1f51e78e02aa1…   335MB
710340b3b82e        2 months ago         /bin/sh -c #(nop)  CMD ["node"]                 0B
<missing>           2 months ago         /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B
<missing>           2 months ago         /bin/sh -c #(nop) COPY file:238737301d473041…   116B
<missing>           2 months ago         /bin/sh -c set -ex   && for key in     6A010…   5.08MB
<missing>           2 months ago         /bin/sh -c #(nop)  ENV YARN_VERSION=1.16.0      0B
<missing>           2 months ago         /bin/sh -c buildDeps='xz-utils'     && ARCH=…   90.4MB
<missing>           2 months ago         /bin/sh -c #(nop)  ENV NODE_VERSION=12.6.0      0B
<missing>           2 months ago         /bin/sh -c groupadd --gid 1000 node   && use…   333kB
<missing>           2 months ago         /bin/sh -c #(nop)  CMD ["bash"]                 0B
<missing>           2 months ago         /bin/sh -c #(nop) ADD file:5ffb798d64089418e…   55.3MB
```

# スリムなベースイメージを使う

上記のサンプルだとベースイメージに`Debian`系を使用していますが、超軽量な`Alpine Linux`を使用することで劇的にサイズ減が図れます
ただし下記の`node:12-alpine`はNode.js込みのため、さほどサイズ減につながりませんが、素のAlpine Linuxだと**5.53MB !**

```Dockerfile:Dockerfile

FROM node:12-alpine as base

WORKDIR /tmp

RUN wget https://download.java.net/java/GA/jdk12.0.2/e482c34c86bd4bf8b56c0b35558996b9/10/GPL/openjdk-12.0.2_linux-x64_bin.tar.gz \
    && tar -zxvf ./openjdk-12.0.2_linux-x64_bin.tar.gz \
    && chown node:node -R /tmp/jdk-12.0.2/

FROM node:12-alpine

COPY --from=base /tmp/jdk-12.0.2 /tmp/jdk-12.0.2

RUN apk update \
    && apk upgrade \
    && rm -rf /var/cache/apk/*
```

```shell
> docker build -t downsize:6 ./
-----------------------------------------------------------------------------------------------------------
REPOSITORY      TAG                       IMAGE ID            CREATED              SIZE
downsize        6                       2389f4627226          About a minute ago   415MB

> docker history downsize:5
-----------------------------------------------------------------------------------------------------------
IMAGE               CREATED              CREATED BY                                      SIZE
2389f4627226        About a minute ago   RUN /bin/sh -c apk update     && apk upgrade…   22.1kB
<missing>           About a minute ago   COPY /tmp/jdk-12.0.2 /tmp/jdk-12.0.2 # build…   335MB
<missing>           3 days ago           /bin/sh -c #(nop)  CMD ["node"]                 0B
<missing>           3 days ago           /bin/sh -c #(nop)  ENTRYPOINT ["docker-entry…   0B
<missing>           3 days ago           /bin/sh -c #(nop) COPY file:238737301d473041…   116B
<missing>           3 days ago           /bin/sh -c apk add --no-cache --virtual .bui…   5.5MB
<missing>           3 days ago           /bin/sh -c #(nop)  ENV YARN_VERSION=1.17.3      0B
<missing>           3 days ago           /bin/sh -c addgroup -g 1000 node     && addu…   69.3MB
<missing>           3 days ago           /bin/sh -c #(nop)  ENV NODE_VERSION=12.10.0     0B
<missing>           4 months ago         /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B
<missing>           4 months ago         /bin/sh -c #(nop) ADD file:a86aea1f3a7d68f6a…   5.53MB
```