Go ModulesなプロジェクトのDockerビルドを高速化する

Go Modulesに対応させたプロジェクトをDocker内でビルドして実行するとします。
単純にDockerfileを書いてしまうとソースコードに変更が入るたびにModulesのダウンロードが走ってしまい、とても時間がかかってしまいます。
そこでDockerのイメージレイヤのキャッシュ機能を使って高速化する方法を紹介します。

Go Modulesのキャッシュ

キャッシュが効かずに毎回Modulesのダウンロードが行われる原因としては、Docker内でGoのビルドをするためにソースコードをコピーしている COPY コマンド部分が、前回の状態と異なるためキャッシュを使うことができないところにあります。
そのため、ソースコードをコピーする前にModulesのダウンロードを行えば、ソースコードの変更を行っても前回のキャッシュが使え、大幅な短縮になります。

以下が一例です。

FROM golang:1.12.4 as build

ENV GO111MODULE=on
WORKDIR /go/src/path/to/proj

COPY go.mod .
COPY go.sum .

RUN set -x \
  && go mod download

COPY . .

RUN set -x \
  && go build -o /go/bin/app

FROM ubuntu:18.04

COPY --from=build /go/bin/app /go/bin/app

CMD ["/go/bin/app"]

ソースコード全体をコピーする前にModules関連だけをコピーして、 go mod download を実行してダウンロードしています。
これでModulesに変更が入らない限り COPY . . から実行されるため、大幅な短縮になります。