なぜマルチステージビルド?
AWS学習中、DockerをECRやECSにデプロイする機会があるが、Dockerのイメージサイズを小さくすることで、
- ECRのようなレジストリへの保管費用を削減
- pull時の通信費用を抑えることができる
- ローカルのストレージの圧迫を抑える
などマルチステージビルドの利点が大きかったのでそこで学んだことを公開します。
マルチステージビルドとは?
マルチステージビルドとは「複数のステージを用いたビルド」となります。
マルチステージビルドの利点とは、通常 Docker イメージには、アプリケーションおよび実行に必要なものの他に、イメージのビルドに関わるライブラリ等も含まれています。しかし、本番環境では アプリケーションおよびその実行に必要なもののみ をビルドしたいですよね。
そこで複数のステージを用いると、この悩みが解消されます。
ここで、ステージを二つ用意するとします。
一つ目のステージでは、アプリケーションのビルドを行い Docker イメージを作成します。
二つ目のステージでは、一つ目のステージで作成したイメージの中から必要なものだけをコピーしてきます。
このようにステージを二つ用意することで、最終的な Docker イメージには必要なものだけが含まれるようになるのです。
その結果、イメージサイズが小さくなり、本番環境の運用のパフォーマンスが向上します。
具体的な記述
Before
FROM golang:1.17.1
WORKDIR /go/src/api/
COPY ./ /go/src/app
RUN apt-get update && apt-get install -y vim
RUN go get github.com/rubenv/sql-migrate/... && \
go get github.com/pilu/fresh
CMD go run main.go
EXPOSE 8080
After
// 中間イメージに当たる
FROM golang:1.17.1-alpine as builder
RUN apk add --update --no-cache ca-certificates git && \
apk add vim && \
apk add --no-cache gcc && \
apk add --no-cache musl-dev
WORKDIR /go/src/api/
COPY ./ /go/src/api
RUN go install github.com/pilu/fresh && \
go install github.com/rubenv/sql-migrate/...
// バイナリファイルを作成する (これが`as dev`にコピーされることでイメージサイズを小さくすることができる)
RUN GOOS=linux \
GOARCH=amd64 \
go build -a -installsuffix cgo -o main main.go
FROM alpine as dev
WORKDIR /go/src/api/
// ここでbuilder(中間イメージ)からバイナリファイルをCOPYする
COPY --from=builder /go/src/api/main /go/src/api/main
CMD [ "/go/src/app/main" ]
EXPOSE 8080