0
1
お題は不問!Qiita Engineer Festa 2024で記事投稿!
Qiita Engineer Festa20242024年7月17日まで開催中!

マルチステージビルドとdistrolessイメージを活用してGoイメージを極限まで軽量化する

Posted at

マルチステージビルド は,Dockerイメージのビルドを複数のステージに分割する機能です.Goのソースコードをビルドするステージと実行時のステージを分離することで,実行時のイメージサイズを軽減できます

実行時のイメージには Alpine のような軽量Linuxが使われることが多いですが,distrolessイメージも良く知られています.distrolessイメージは,Debianイメージに含まれるファイルを極限まで削減したもので,シェルすら含まれていません.

Distroless images are very small. The smallest distroless image, gcr.io/distroless/static-debian11, is around 2 MiB. That's about 50% of the size of alpine (~5 MiB), and less than 2% of the size of debian (124 MiB).
https://github.com/GoogleContainerTools/distroless

今回は実行時のイメージサイズを alpine / distrolessとした場合で比較してみます

ソースコード (Dockerfile)

スターを頂けると励みになります

builderイメージ

Goのソースコードをビルドするステージです.次の3つのステージで構成しています

  • baseステージ: 各ステージ間で共通する処理を実行
  • depsステージ: パッケージの依存関係を解決
  • builderステージ: Goのソースコードをビルド

Goのビルド成果物 (実行ファイル) はserverという名前にしています

FROM golang:1.19-alpine as base

# ワークディレクトリの指定
WORKDIR /app

# ----------------------------------------------------------------
# 依存関係の解決
# ----------------------------------------------------------------
FROM base as deps

# モジュールのダウンロード
COPY go.mod go.sum ./
RUN go mod download

# ----------------------------------------------------------------
# ビルド
# ----------------------------------------------------------------
FROM base as builder

COPY --from=deps /go/pkg /go/pkg
COPY . .

# 外部依存の無い実行ファイルを作る
ARG CGO_ENABLED=0
# 64bit linux用にビルドする
ARG GOOS=linux
ARG GOARCH=amd64
# ビルド成果物にデバック情報が含まれないようにする
RUN go build -ldflags '-s -w' -o ./server

実行時のイメージ (runner)

alpineイメージを利用する場合

builderステージから実行ファイルserverをコピーし,ENTRYPOINTで実行します

# イメージのタグを指定した方が良いが,面倒なので今回は省略
FROM alpine as runner

WORKDIR /app

RUN addgroup --system --gid 10001 nonroot
RUN adduser --system --uid 10001 nonroot

COPY --from=builder --chown=nonroot:nonroot /app/server .
ENTRYPOINT ["./server"]

USER nonroot

EXPOSE 8080

distrolessイメージを利用する場合

こちらのリポジトリから利用可能なイメージを選びます.今回は最も軽量なgcr.io/distroless/static-debian12を選びました.python, java, nodejsのランタイムが含まれるイメージも利用できます

alpineの場合と同様にserverをコピーし,ENTRYPOINTで実行します

FROM gcr.io/distroless/static-debian12:nonroot as runner-distroless

WORKDIR /app

USER nonroot

COPY --from=builder /app/server .

ENTRYPOINT ["./server"]

EXPOSE 8080

ENTRYPOINT ./serverとしてはいけません.この書き方ではシェルを介して./serverを実行しますが,distrolessイメージにはシェルが含まれないので実行できません
https://kinsta.com/jp/blog/dockerfile-entrypoint/

イメージサイズの比較

それぞれのイメージを適当な名前でビルドします

    docker build . --target runner-alpine -t go-alpine
    docker build . --target runner-distroless -t go-distroless

イメージサイズを確認します

docker images | grep go-

go-alpine       latest     7034c38b2fcf   48 seconds ago      13.3MB
go-distroless   latest     e9c3cc892aff   About an hour ago   6.46MB

alpineイメージが 13.3MB でdistrolessイメージが 6.46MB となりました.およそ半分にまでイメージサイズを削減できます

開発環境 (おまけ)

おまけで開発環境も載せておきます.こちらの記事を参考にしました

Goのホットリロードツールであるairを利用します

FROM golang:1.19-alpine as dev

WORKDIR /app

RUN go install github.com/cosmtrek/air@v1.40.0

ENTRYPOINT ["air"]

airの最新バージョンはgithub.com/air-verse/airにリポジトリが変更されているので注意です

開発環境ではdocker-composeでワークスペースをvolumeマウントしています.docker-composeではビルドステージを指定するためにbuild > args- target=devを指定します

# docker-composeの例
version: "3.3"

services:
  go:
    container_name: go
    build:
      context: .
      dockerfile: Dockerfile
      args:
        - target=dev
    volumes:
      - .:/app
    ports:
      - 8080:8080

以上です

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1