なぜ Docker イメージを軽くするのか
- Docker イメージのダウンロードが早くなる
- Docker イメージのアップロードが早くなる
- CI やプロダクションなど各環境へのイメージの配布が効率的に行える
- ホストのディスク容量を圧迫しない
どう Docker イメージを軽くするのか
- 軽いベースイメージを使う
- 不要なファイルの削除する
- レイヤの結合してレイヤ数を減らす
- マルチステージビルドする
やってみた
以下の記事で Dockerfile を作成したが、パフォーマンスについては考慮されていなかった。 この Dockerfile を減量する。 減量することにより、Docker Hub からのイメージのダウンロード時間が短くなりユーザがより素早くブログ移行をすることができる。
初期状態(869MB)
FROM golang:latest RUN go get -u github.com/tenntenn/qiitaexporter RUN go get -u github.com/x-motemen/blogsync RUN apt-get update RUN apt-get install gettext-base -y WORKDIR /Documents COPY blogsync.template /Documents COPY setup.sh /Documents RUN mkdir -p ~/.config/blogsync COPY config.yaml.tmp /root/.config/blogsync RUN chmod +x setup.sh ENTRYPOINT ./setup.sh
軽いベースイメージ、不要ファイル削除、レイヤ結合(410MB)
FROM golang:1.14-alpine RUN apk --no-cache add libintl && \ apk --no-cache add git && \ apk --no-cache add --virtual .gettext gettext && \ cp /usr/bin/envsubst /usr/local/bin/envsubst && \ apk del .gettext && \ go get github.com/tenntenn/qiitaexporter \ github.com/x-motemen/blogsync && \ rm -rf $GOPATH/src $GOPATH/pkg WORKDIR /Documents COPY blogsync.template /Documents COPY setup.sh /Documents RUN mkdir -p ~/.config/blogsync COPY config.yaml.tmp /root/.config/blogsync RUN chmod +x setup.sh ENTRYPOINT ./setup.sh
Alpine Linux を使う
golang:latest
が 803MB に対して、golang:alpine
は 370MB である。
軽いベースイメージを使うだけで 400MB 以上の軽量になる。
frolvlad/alpine-go
の方がイメージサイズが少し小さかったが、安心と安全の公式イメージを使う。
alpine はパッケージ管理がapk
になる。
git
も入ってないので、go get
するためにはgit
をインストールする必要はある。
不要ファイルの削除
go get
で取得したソースやapk
関連で不要になったファイルを削除を削除することでイメージが軽量になる。
RUN を&でまとめる
Docker イメージではレイヤーが構成されている。
そのため、層を減らすことで軽量化できる。
RUN
を分けて書くとその分レイヤーが生成されるため重くなってしまう。
例
以下が一番軽量。
RUN go get github.com/tenntenn/qiitaexporter \ github.com/x-motemen/blogsync && \ rm -rf $GOPATH/src $GOPATH/pkg
以下は不要なファイルを削除していない分だけ重たくなる。
RUN go get github.com/tenntenn/qiitaexporter \ github.com/x-motemen/blogsync && \
以下はファイルを削除していないものに、さらにレイヤーを増やしただけになり一番重い。 たとえ、不要なファイルを削除しようともその前段のレイヤーは変わらず存在しているため軽くなることはない。
RUN go get github.com/tenntenn/qiitaexporter \ github.com/x-motemen/blogsync RUN rm -rf $GOPATH/src $GOPATH/pkg
マルチステージビルド(26.3MB)
上の作業をしている際に、これはわざわざgolang
イメージをつかわなくとも、事前にgo get
しておいてバイナリをローカルからイメージへコピーすればいいのではと思った。
ただ、そうすると Dockerfile を読むだけではなおさら理解が難しくなり、汎用性は下がるだろうと思った。
ところが、すでにその機能は公式にサポートされていた。 それが MultiStageBuild である。 ビルド用と実行用にイメージを分けることができる。 事前にローカルでバイナリを生成して、イメージにコピーするよりも Dockerfile 内でバイナリを生成して、渡すことで可読性も担保される。
FROM golang as builder RUN CGO_ENABLED=0 go get github.com/tenntenn/qiitaexporter github.com/x-motemen/blogsync FROM alpine COPY --from=builder /go/bin/qiitaexporter /bin/qiitaexporter COPY --from=builder /go/bin/blogsync /bin/blogsync RUN apk --no-cache add libintl && \ apk --no-cache add --virtual .gettext gettext && \ cp /usr/bin/envsubst /usr/local/bin/envsubst && \ apk del .gettext WORKDIR /Documents COPY blogsync.template /Documents COPY setup.sh /Documents RUN mkdir -p ~/.config/blogsync COPY config.yaml.tmp /root/.config/blogsync RUN chmod +x setup.sh ENTRYPOINT ./setup.sh
ビルドごとにRUN CGO_ENABLED=0 go get github.com/tenntenn/qiitaexporter github.com/x-motemen/blogsync
が走ってしまうことが課題にある。
docker history
することで、レイヤごとの重さをみることができる。
alpine と go の CLI ツールのサイズが大半を占めているのでこれ以上の減量は難しい。
[qiitatohatena] docker history adachikun/qiitatohatena IMAGE CREATED CREATED BY SIZE COMMENT 517f8dd94816 10 minutes ago /bin/sh -c #(nop) ENTRYPOINT ["/bin/sh" "-c… 0B <missing> 10 minutes ago /bin/sh -c chmod +x setup.sh 248B <missing> 10 minutes ago /bin/sh -c #(nop) COPY file:654810ae2d9e4894… 119B <missing> 10 minutes ago /bin/sh -c mkdir -p ~/.config/blogsync 0B <missing> 10 minutes ago /bin/sh -c #(nop) COPY file:554e23819df70ab7… 248B <missing> 10 minutes ago /bin/sh -c #(nop) COPY file:8d2614cf1943aa71… 214B <missing> 10 minutes ago /bin/sh -c #(nop) WORKDIR /Documents 0B <missing> 10 minutes ago /bin/sh -c apk --no-cache add libintl && … 101kB <missing> 10 minutes ago /bin/sh -c #(nop) COPY file:006d3047ed766467… 11.1MB <missing> 10 minutes ago /bin/sh -c #(nop) COPY file:a2c93431654cceb7… 9.51MB <missing> 8 days ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B <missing> 8 days ago /bin/sh -c #(nop) ADD file:c92c248239f8c7b9b… 5.57MB
おわりに
一般的な軽量化手法を試すだけで、869MBから26MBまで減量することができた。 これにより、Qiitaからはてなブログへ移行したい人は30倍高速にDockerイメージをダウンロードすることができるようになった。
今回は Docker イメージの軽量化のみに注目した。 しかし、Docker イメージにはビルド高速化のためにキャッシュや buildkit など関連する要素があり今後記事にしたい。 また、docker-slim という Docker イメージを自動で減量するツールもあり試してみたいと思った。