dockerのmulti stage buildを理解する
今回は、dockerのmulti stage buildとやらを紹介したいと思います。
(※この機能は、Docker 17.05以降のversionで使用できます。)
multi stage buildとは
そもそもmulti stage buildとはなんぞやという感じだったので、そこから説明したいと思います。
以下のように、1つのDockerfileでFROM
を複数使用してbuildを行うことをmulti stage buildと言います。
(詳しいコードの中身については、「使い方」の章で説明したいと思います。)
FROM golang:1.12 as builder WORKDIR /app COPY ./sample.go . RUN GOOS=linux go build -o sample sample.go FROM alpine:latest WORKDIR /root/ COPY --from=builder /app/sample .
いつ使うのか
golangなどのコンパイルしてコードを実行するような言語の環境を作成する時に使います。
1つのDockerfileで、コンパイルする環境と実行環境を分けて作成することができます。
なぜ使うのか
最終的に出力イメージのサイズを軽くすることができるからです。
イメージサイズを軽くすることで、配布する側もダウンロードして使用する側も負担が減ります。
なぜ軽くなるかというと、コンパイルで使用した一時的な環境を捨てて、実行環境のみをイメージとして作成することができるためです。
具体的にどれくらい軽くなるかについてですが、「使い方」の章で説明するサンプルコードだとイメージサイズが100倍近く軽くなっています。
使い方
今回はdockerの公式docでも紹介されていたように、golangの環境を作成するコードを書いていきたいと思います。
Dockerfileでは、以下の簡単な.go
ファイルをコンパイルして実行ファイルを作成し、配置するところまでやっていきたいと思います。
package main import "fmt" func main() { a := 1 b := 2 fmt.Println(a + b) }
single stage build
まずは、single stage buildの復習からです。
Dockerfileは以下のように書きました。
FROM golang:1.12 WORKDIR /app COPY ./sample.go . RUN go build -o sample sample.go
イメージを作成し、コンテナを起動し入り込んでみます。
$docker build -t single_stage_build:latest . $docker run -it single_stage_build /bin/sh # ls sample sample.go # ./sample 3
無事、実行できました!
multi stage build
続いて、multi stage buildです。
先ほど書いたDockerfileの再掲です。
FROM golang:1.12 as builder WORKDIR /app COPY ./sample.go . RUN GOOS=linux go build -o sample sample.go FROM alpine:latest WORKDIR /root/ COPY --from=builder /app/sample .
上記のコードについて、抜粋して説明していきます。
FROM golang:1.12 as builder
の部分では、as <name>
をすることで、ステージに名前を指定することができます。ステージ名は後で使用します。
RUN GOOS=linux go build -o sample sample.go
の部分について、GOOS=linux
で、実行環境をLinuxに指定しています。コンパイルを行う環境と実行環境が違うため指定しています。(クロスコンパイル)
(もしかしたら指定しなくても良いかもしれません。なくても動作することは確認しました)
COPY --from=builder /app/sample .
の部分では、最初のステージからファイルをコピーしています。
どのステージから持ってくるかについては、--from=<stage name>
で指定しています。
イメージの作成方法は、通常のDockerfileと同様に行います。
$docker build -t multi_stage_build:latest .
イメージからコンテナを起動して入り込んでみると、コンパイル済みの実行ファイルのみが置かれていることを確認できます。(もちろん実行もできます)
$docker run -it multi_stage_build /bin/sh ~ # ls sample ~ # ./sample 3
イメージサイズの比較
$docker images REPOSITORY TAG IMAGE ID CREATED SIZE multi_stage_build latest 28c0ead58d63 4 minutes ago 7.58MB <none> <none> 35ed8be9c145 4 minutes ago 824MB single_stage_build latest 5438467d2c15 45 minutes ago 816MB
single stage buildのイメージは816MB、multi stage buildのイメージは7.58MBと100倍近く軽いイメージを作成することができました!!
実は中間イメージ(<none>
のやつ)が残ってしまっていますがこれについては、以下のサイトでdockerのメンテナーの方がバグではなく仕様だと説明しています。
簡単に説明すると、「buildするたびに毎回0からイメージを作成するなんて無駄だから、キャッシュとして残してるんだよ」みたいな感じで説明しています。
詳しく見たい方は、以下のサイトを参考にしてもらえればと思います。
おまけ
dockerの公式docにはRUN
の部分で、
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o sample sample.go
みたいな感じで書いてあったのですが、以下のサイトによるとgolangのver1.10以降では
CGO_ENABLED=0 go build -a -installsuffix cgo
のオプションはいらないとのことです。