13 Oct 2019, 20:53

Multi Stage Docker Build With Go Module

Share

為什麼要使用Multi-stage來編譯Golang?

假設我們有一個簡單的Golang程式如下

main.go

package main

import "github.com/gin-gonic/gin"

func main() {
	r := gin.Default()
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "pong",
		})
	})
	r.Run() // listen and serve on 0.0.0.0:8080
}

然後我們爲了這支程式寫了個Dockerfile

Dockerfile

FROM golang:1.13.1-stretch 

COPY . /api 
WORKDIR /api

ENV GO111MODULE=on

RUN CGO_ENABLED=0 GOOS=linux go build -o api main.go

CMD ["./api"]

編譯他吧!

docker build . -t api

編譯完我們來看一下Image有多大?

docker images
#REPOSITORY                                   TAG                 IMAGE ID            CREATED              SIZE
#api                                          latest              1f3921830d5a        39 seconds ago       842MB

有沒有搞錯?要842MB? 怎麼不去搶?

瘦身

為什麼會這麼大呢?因為在編譯之前,go會把相依的go module都抓下來,這當然大啊!所以可透過multi-stage來避免這個問題。看一下

Dockerfile

#first stage - builder
FROM golang:1.13.1-stretch as builder
COPY . /api
WORKDIR /api
ENV GO111MODULE=on
RUN CGO_ENABLED=0 GOOS=linux go build -o api main.go

#second stage
FROM alpine:latest
WORKDIR /root/
COPY --from=builder /api .
CMD ["./api"]

一樣給他跑一下

docker build . -t multi-stage-api
#REPOSITORY                                   TAG                 IMAGE ID            CREATED             SIZE
#multi-stage-api                              latest              27fd709e4c9d        7 seconds ago       41.3MB

差了快800MB!

寫到這裏,不知道大家有沒有注意到每次編譯go都會去抓新的module, 這好浪費時間啊!來 繼續往下看 參考

alpine是精簡的fs, 缺少許多程式需要的library例如tzdata等等,如何寫出完整的Dockerfile呢?請看此

comments powered by Disqus