Golang環境を作る際にgoenvでゴニョゴニョしてたけど、なんかしっくりこなくて、もうDockerで構成してしまおうと思ってDockerfile書いた際に、Multi Stage Buildという機能でイメージサイズがかなり削減出来るようなのでやってみた

前提

  • Multi Stage Buildは、Docker 17.05以降で利用出来ます。
  • composeでMulti Stage Buildなコンテナを扱うには、Version 2.3以降のformatでymlを書く必要があります。

ソース

nobiki/docker-base:1.0

結果から

以下がビルドを終えた後のイメージサイズです。developreleaseを比べてみると、821MBぐらい差があり、かなりのイメージサイズ削減効果がある事がうかがえます

1
2
3
4
5
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
base-container latest 9c061107e862 7 seconds ago 3.07MB # release
<none> <none> cf4c820b6ec7 8 seconds ago 778MB # build
<none> <none> 598747837216 10 seconds ago 824MB # develop

Dockerfile(github

Dockerfileを「develop」→「build」→「release」の3ステージに分ける想定で書きました

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
FROM golang:1.11.1-stretch as develop
WORKDIR /apps
RUN go get -u github.com/golang/dep/cmd/dep
ENV GOROOT /usr/local/go/
ENV GOPATH /apps/go
ENV PATH $PATH:$GOROOT/bin:$GOPATH/bin


FROM golang:1.11.1-stretch as build
WORKDIR /apps
COPY ./apps /apps
ADD . /apps
RUN go build -o hello hello.go


FROM busybox as release
WORKDIR /apps
COPY --from=build /apps/hello /usr/local/bin/hello
ENTRYPOINT ["/usr/local/bin/hello"]

FROMのあとに、asで名前をつけれます

developには、開発時に必要な物を、buildgo buildした後は実行ファイルだけあればいいので、release--from=buildをつけて、ステージをまたいだ実行ファイルのコピーをしています

docker-compose.yml(github

「version」と「build」の箇所が勘所になります

  • 「version」は、Version 2.3以降のフォーマットで記述する必要があります
  • 「build」は、targetに、ビルドしたいステージを指定します

hello.go

特になんの変哲もないHello Worldです

1
2
3
4
5
6
7
package main

import "fmt"

func main() {
fmt.Printf("Hello World\n")
}

コンテナ実行

composeのtargetをreleaseにしてビルドすると、こんな感じになります(builddevelopを指定すると、それ以降のタスクは走りません)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
$ docker-compose -f ../docker-network.yml -f docker-compose.yml up -d
Building base-container

## ---- developのタスク [ここから]
Step 1/15 : FROM golang:1.11.1-stretch as develop
---> 45e48f60e268
Step 2/15 : WORKDIR /apps
---> Running in a83a750399dc
Removing intermediate container a83a750399dc
---> d32416728583
Step 3/15 : RUN go get -u github.com/golang/dep/cmd/dep
---> Running in 8ff0266375b9
Removing intermediate container 8ff0266375b9
---> 8b518f314647
Step 4/15 : ENV GOROOT /usr/local/go/
---> Running in 4423b4c299d7
Removing intermediate container 4423b4c299d7
---> b45e6a75ff4d
Step 5/15 : ENV GOPATH /apps/go
---> Running in 98979a838224
Removing intermediate container 98979a838224
---> a0abeb79e97d
Step 6/15 : ENV PATH $PATH:$GOROOT/bin:$GOPATH/bin
---> Running in 18250683a73b
Removing intermediate container 18250683a73b
---> 598747837216

## ---- buildのタスク [ここから]
Step 7/15 : FROM golang:1.11.1-stretch as build
---> 45e48f60e268
Step 8/15 : WORKDIR /apps
---> Using cache
---> d32416728583
Step 9/15 : COPY ./apps /apps
---> df0a804518af
Step 10/15 : ADD . /apps
---> db532946d608
Step 11/15 : RUN go build -o hello hello.go
---> Running in 882df4b353cc
Removing intermediate container 882df4b353cc
---> cf4c820b6ec7

## ---- releaseのタスク [ここから]
Step 12/15 : FROM busybox as release
---> 59788edf1f3e
Step 13/15 : WORKDIR /apps
---> Running in 3bf041edb67f
Removing intermediate container 3bf041edb67f
---> efe83c82ab36
Step 14/15 : COPY --from=build /apps/hello /usr/local/bin/hello
---> 8e930dc3d426
Step 15/15 : ENTRYPOINT ["/usr/local/bin/hello"]
---> Running in 91e41a9f29c5
Removing intermediate container 91e41a9f29c5
---> 9c061107e862

Successfully built 9c061107e862
Creating base-volume ... done
Creating base-container ... done

結果として、3つイメージが出来上がります

1
2
3
4
5
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
base-container latest 9c061107e862 7 seconds ago 3.07MB # release
<none> <none> cf4c820b6ec7 8 seconds ago 778MB # build
<none> <none> 598747837216 10 seconds ago 824MB # develop

buildとdevelopは、今後コンテナを作成する際のキャッシュとして利用されますが、普通に削除してしまっても大丈夫です

Dockerでなimageとか未使用のnetworkを一括削除