Dockerfile、いつも脳死で書いている方なので、今回少しでも改良しつつ色々学べないかなと思い、色々直してみました。
既存のDockerfile
既存のDockerfileは下のようになっていました。
FROM golang:1.24.4-bookworm
WORKDIR /go/src/app
COPY . .
RUN go mod download
RUN CGO_ENABLED=0 go build -v -o binary main.go
EXPOSE 3000
CMD ["./binary"]
このDockerfileのあまり良くないかなと思うところは全ファイルをコピーしているため、動作には必要ないツールの設定ファイル等の余分なファイルが含まれてしまい、イメージが大きくなってしまっています。
イメージが大きいと
- build時間の増加
- ecr等へのpush時間の増加
- ローカルストーレージの圧迫
といったデメリットがあります。
実際にイメージサイズは1.48GBとなっていました。
また、go mod download
を行なっているため、毎回イメージ作成時にダウンロードを行なってしまっています。
そのためビルド時間は49sとかなり長いです。
[+] Building 49.8s (12/12) FINISHED
直してみる
上記の点を踏まえてDockerfileを改良してみました。
FROM golang:1.24.4-bookworm
ENV APP_DIR=/go/src/app
COPY go.mod go.sum $APP_DIR/
COPY vendor/ $APP_DIR/vendor/
COPY main.go $APP_DIR/
COPY common $APP_DIR/common/
COPY application/ $APP_DIR/application/
COPY domain/ $APP_DIR/domain/
COPY infrastructure/ $APP_DIR/infrastructure/
COPY interfaces/ $APP_DIR/interfaces/
COPY migrations/ $APP_DIR/migrations/
COPY .env $APP_DIR/
WORKDIR $APP_DIR
RUN CGO_ENABLED=0 go build -mod=vendor -v -o binary main.go
EXPOSE 3000
CMD ["./binary"]
まず、全ファイルコピーをせず、動作に必要なファイルだけコピーするようにしました。
こうすることによって必要なファイルだけを取り入れるようになり、イメージサイズも1.18GBと300MBほど小さくなりました。
また、vendorディレクトリをコピーするようになったのでその分ビルド時間が短縮されました。
結果30sほど短くなりました。
[+] Building 16.5s (19/19) FINISHED
マルチステージングビルドを行ってみる
これでもイメージサイズがまだ大きいなと感じたのでマルチステージングビルドを行ってみます。
以下のように書いてみました。
FROM golang:1.24.4-bookworm AS builder
ENV APP_DIR=/go/src/app
COPY go.mod go.sum $APP_DIR/
COPY vendor/ $APP_DIR/vendor/
COPY main.go $APP_DIR/
COPY common $APP_DIR/common/
COPY application/ $APP_DIR/application/
COPY domain/ $APP_DIR/domain/
COPY infrastructure/ $APP_DIR/infrastructure/
COPY interfaces/ $APP_DIR/interfaces/
WORKDIR $APP_DIR
RUN CGO_ENABLED=0 go build -mod=vendor -v -o binary main.go
FROM gcr.io/distroless/base
WORKDIR /app
COPY --from=builder /go/src/app/binary ./
EXPOSE 3000
ENTRYPOINT ["./binary"]
下の方に数行だけ追加されていると思うのですが、これが2段階目のビルドです。
こちらではdistrolessというイメージを使って、そこにバイナリファイルだけ移動させるということを行なっています。
これを行うことでイメージサイズがなんと58.20MBになりました。
ビルド時間も16.5sとあまり変わらない結果になったので、マルチステージングビルドがなぜよく使われているのか知れた気がします。
まとめ
今回、Dockerfileの改良のために
- COPYするファイルを限定化
- goのモジュールをvendorに移行
- マルチステージングビルドの採用
を行なってみました。
これ以外にも良くするコツがあると思うので、また知ったら記事にしようかなと思います。