Dockerfile のマルチステージビルド

Dockerfile のマルチステージビルドを使い、Go アプリケーションの本番環境用の Dockerfile を作る。

リファレンス

本文

Dockerfile のマルチステージビルドを使い、Go アプリケーションの本番環境用の Dockerfile を作る。

前提として Docker Compose を使用する。

完成品はこちら

まずディレクトリ構成は下記とする。

<root>
|
|-app
| |-go.mod
| |-go.sum
| |-server.go
| |-etc...
|
|-docker
| |-go
|   |-Dockerfile.prod
|
|-compose.prod.yaml

app ディレクトリにソースコードが入っており、app/server.go がビルド対象である。

docker/go/Dockerfile.prod が今回作成する本番環境用 Dockerfile。

compose.prod.yaml が本番環境用 Compose ファイル。

基本的なところはリファレンスを見ればわかるので、私が詰まったところを記載する。

ポイント1. Compose ファイルの build context

compose.prod.yaml について、build を以下のようにする。

build:
  context: .
  dockerfile: docker/go/Dockerfile.prod

今回、docker/go/Dockerfile.prod には app ディレクトリ内のファイルを一度コピーするので、context をルートディレクトリにすることによって Dockerfile.prod が存在するディレクトリの外にあるファイルも参照可能とする。

ポイント2. Dockerfile でのベースイメージ

完成した Dockerfile.prod は以下の通り。

FROM golang:1.19 AS build-env
WORKDIR /app
COPY app ./
RUN go mod tidy && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o server server.go
RUN touch /empty

FROM gcr.io/distroless/static-debian11

WORKDIR /app

COPY --from=build-env /app/server ./
COPY --from=build-env /app/.env.prod ./.env
COPY --from=build-env /app/private/prod/*.json ./private/prod/
COPY --from=build-env /empty ./storage/log/.emptyfile

ENTRYPOINT ["./server"]

このファイル内におけるホスト側のファイルパスはルートから見たファイルパスを記載する。
一段階目、COPY app ./ でソースコードを全部コピーできる。

なお COPY app/* ./ とするとローカルパッケージの import に失敗するので注意。

RUN touch /empty は、二段階目に使うベースイメージで mkdir を使用できないので /empty をコピーするという方法でディレクトリを作成するためのもの。

次に二段階目のベースイメージは gcr.io/distroless/static-debian11 を使う。
最初は scratch を使おうとしたのだが、CA Certificates が入っていないせいで Firebase Authentication が使えなかった。

一から自力で必要なものを入れることのメリットとデメリットを検討し、Google が用意してくれているイメージを使うほうがいいと思ったのでこのベースイメージを使うことにした。

起動

私の Compose ファイルは他のコンテナが .env を使うので、起動はルートディレクトリで

docker compose -f compose.prod.yaml --env-file .env.prod up

となる。

以上