🦖

dockerのマルチステージビルドとホットリロード環境@Golang

2022/08/20に公開

dockerのマルチステージビルドとは

dockerにはマルチステージビルドというイメージ容量を簡単に小さくできる構文が用意されている。

マルチステージビルドでは、Dockerfileの中で複数のFROM文を使用し、各FROMでは異なるベースイメージを用いて新しいステージを構築できる。任意のステージから別のステージにコピーする対象を選べるので、最終的なイメージでは必要なものだけでビルドされる。

With multi-stage builds, you use multiple FROM statements in your Dockerfile. Each FROM instruction can use a different base, and each of them begins a new stage of the build. You can selectively copy artifacts from one stage to another, leaving behind everything you don’t want in the final image.

https://docs.docker.com/develop/develop-images/multistage-build/

必要なファイルのみをビルドすることができるため、軽量なAlpine-Linuxなどをベースイメージとしてバイナリファイルだけをコピーすることでイメージサイズを小さくすることができる、という具合。

Airによるホットリロード環境

dockerで開発を進める際に、ローカルのソースコードに変更があるたびにコンテナに反映する必要がある。変更毎にコンテナを再起動するのは面倒だ。Airを使うことでローカルの変更をコンテナを再起動せず、コンテナへの即時反映が可能になる。詳細は以下を確認。
https://github.com/cosmtrek/air

上記を踏まえて、以下の要件を満たすGolangの環境構築を行いたい。

  • dockerのマルチステージビルド
  • Airを用いたホットリロード

実装

最終的なディレクトリ構成は以下。簡単にwebサーバーを立て、レスポンスを返す実装のみしている。

.
├── Dockerfile
├── docker-compose.yml
├── go.mod
└── main.go
docker-compose.yml
version: "3"
services:
  go-env:
    container_name: go-env
    build:
      args:
        - target=dev # デフォルトはローカル用ホットリロード環境
    volumes:
      - .:/app
    ports:
      - "9090:8080"
Dockerfile
# Build Stage for deploy --------------------
FROM golang:1.18-alpine AS deploy-builder

WORKDIR /app

COPY go.mod ./ # 必要があればgo.sumもコピーする
RUN go mod download

COPY . .

RUN go build -ldflags "-w -s" -o main



# Deploy Stage ------------------------------
FROM alpine:latest AS deploy

RUN apk update

COPY --from=deploy-builder /app/main . # プログラム実行に必要なバイナリファイルのみコピー

CMD ["./main"]



# Local -------------------------------------
FROM golang:1.18 AS devlop

WORKDIR /app

RUN go install github.com/cosmtrek/air@latest
CMD ["air"]
.air.toml
# 他はほぼデフォルト
[build]
  full_bin = "APP_ENV=dev APP_USER=air ./tmp/main 8080" # コンテナ側ポート

デプロイ用のコンテナとローカル開発用コンテナの出しわけは、docker build--targetオプションで出しわけをすることができる。今回はローカル開発環境がデフォルトでビルドされるようにdocker-compose.ymlに記述している。



ローカルの環境構築するときの手順は以下で行なった。

# ビルド(ローカルは--targetを指定しないでok)
$ docker compose build --no-cache

# .air.tomlファイルの生成("go-env"は各自docker-compose.ymlで設定したサービス名に変換のこと)
$ docker compose run go-env --rm air init

# コンテナ起動
# docker compose up -d

デプロイ環境のビルドはこんな感じ。

 $ docker build -t awonosuke/go-env:latest --target deploy ./

一応、GitHubに残しておいた。
https://github.com/awonosuke/build-small-hot-go-environment

検証

用意したホットリロードがうまくいっているか、デプロイ用のコンテナはどの程度軽量になっているのかをせっかくなので検証したい。

Under construction...

おまけ

ホットリロードとは?

アプリケーションのコード ファイルを編集し、そのコード変更を実行中のアプリケーションに対して直ちに適用できるようにすることで、これを実現します ("ホット リロード" とも呼ばれます)。

https://learn.microsoft.com/ja-jp/visualstudio/debugger/hot-reload

参考書籍

https://budougumi0617.github.io/2022/07/22/release_go_web_application_book/

参考ページ

https://qiita.com/po3rin/items/8b57e6c22f2b34751333
https://zenn.dev/farstep/articles/a7819ec705dcc0
https://zenn.dev/ajapa/articles/bc399c7e4c0def
https://zenn.dev/hrs/articles/go-gin-air-docker

Discussion