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
結果から 以下がビルドを終えた後のイメージサイズです。develop
とrelease
を比べてみると、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を「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
には、開発時に必要な物を、build
でgo 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
にしてビルドすると、こんな感じになります(build
かdevelop
を指定すると、それ以降のタスクは走りません)
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を一括削除