いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

マルチステージビルドとDistrolessイメージで小さなイメージを作る

概要

f:id:gsf_zero1:20211029114204p:plain

よく忘れるので、ここにメモメモ。。。

マルチステージビルドを利用すると、ビルド時のステージと実行時のステージを分けることが出来るので

余計なものが入っていないイメージをつくることが出来ます。

さらに、Googleが管理しているDistrolessイメージをベースにすることでさらに小さなサイズにすることが出来ます。

Distrolessイメージにはシェルすらも入っていないのでセキュリティ的にも良しですね。

DistrolessイメージはGoと相性がいいので、今回はGoのプログラムをコンテナの中で実行するようにします。

サンプルとなるプログラム

helloworldだけを普通に出力するプログラムだとimportされるパッケージが少ないので、少しだけ非同期入れて出力するようにしたプログラムです。

処理の内容に意味はありません。

package main

import (
    "context"
    "fmt"
    "time"

    "github.com/devlights/gomy/ctxs"
    "github.com/devlights/gomy/iter"
)

func main() {
    var (
        rootCtx          = context.Background()
        mainCtx, mainCxl = context.WithCancel(rootCtx)
        procCtx, procCxl = context.WithTimeout(mainCtx, 1*time.Second)
    )
    defer mainCxl()
    defer procCxl()

    <-exec(procCtx).Done()
}

func exec(pCtx context.Context) context.Context {
    var (
        tasks = make([]context.Context, 0, 5)
    )

    for i := range iter.Range(5) {
        tasks = append(tasks, func(pCtx context.Context, i int) context.Context {
            ctx, cancel := context.WithCancel(pCtx)
            go func() {
                defer cancel()

                select {
                case <-ctx.Done():
                    break
                default:
                    fmt.Printf("[%d] hello-go\n", i+1)
                }
            }()
            return ctx
        }(pCtx, i))
    }

    return ctxs.WhenAll(pCtx, tasks...)
}

マルチステージなしのDockerfile

alpineベースのイメージを使いました。

# syntax=docker/dockerfile:1-labs
FROM golang:1.17-alpine

WORKDIR /workspace

ENV CGO_ENABLED=0

COPY go.* .
RUN go mod download

COPY . .
RUN go build -o app main.go

CMD [ "/workspace/app" ]

マルチステージありのDockerfile

ビルドステージは alpineベースのものを使って、実行時はDistrolessを使っています。

# syntax=docker/dockerfile:1-labs
# -----------------------------------------------------
# base image
# -----------------------------------------------------
FROM golang:1.17-alpine as base

WORKDIR /workspace
ENV CGO_ENABLED=0

COPY go.* .
RUN go mod download

COPY . .
RUN go build -o app main.go

# -----------------------------------------------------
# runner image
# -----------------------------------------------------
FROM gcr.io/distroless/static:latest

WORKDIR /app
COPY --from=base /workspace/app app

CMD [ "/app/app" ]

Makefile

何回もコマンド打つのが面倒なので make できるようにしました。

build: \
   copy-gomod \
   build-nostage \
   build-multistage \
   rm-gomod

copy-gomod:
  cp ../go.* .

rm-gomod:
  rm go.*

build-nostage:
  docker buildx build -t multistage-ex:nostage -f Dockerfile.nostage ${PWD}

build-multistage:
  docker buildx build -t multistage-ex:multistage -f Dockerfile.multistage ${PWD}

run:
  time -p docker container run --rm multistage-ex:nostage
  time -p docker container run --rm multistage-ex:multistage

clean:
  docker image rm multistage-ex:nostage multistage-ex:multistage
  docker image prune -f

実行

ビルド

$ make build
docker buildx build -t multistage-ex:nostage -f Dockerfile.nostage /workspace/try-docker/multistage_build
docker buildx build -t multistage-ex:multistage -f Dockerfile.multistage /workspace/try-docker/multistage_build

イメージサイズの確認

$ docker image list multistage-ex
REPOSITORY      TAG          IMAGE ID       CREATED          SIZE
multistage-ex   multistage   bde4552a21c8   41 seconds ago   4.33MB
multistage-ex   nostage      edc5dfebefbf   52 seconds ago   376MB

マルチステージなしの場合は 376MB で、マルチステージありの場合は 4.33MB となっていますね。

参考情報


過去の記事については、以下のページからご参照下さい。

  • いろいろ備忘録日記まとめ

devlights.github.io

サンプルコードは、以下の場所で公開しています。

  • いろいろ備忘録日記サンプルソース置き場

github.com

github.com

github.com