go 項目怎么讓 docker 鏡像體積減小?本文做了詳細介紹。
1. 直接編譯得到運行文件 22M
使用的項目源碼地址 (https://github.com/scoful/kingProject)
本地直接編譯打一個linux運行包
set GOOS=linux
set GOARCH=amd64
go build main.go
結果是22M
2. 不編譯直接運行的鏡像 941M
Dockerfile文件內容
# 基礎鏡像,基于golang最新鏡像構建
FROM golang
# 作者
MAINTAINER scoful
# 全局工作目錄
WORKDIR $GOPATH/kingProject
# 把運行Dockerfile文件的當前目錄所有文件復制到目標目錄
COPY . $GOPATH/kingProject
# 環境變量
# 用于代理下載go項目依賴的包
ENV GOPROXY https://goproxy.cn,direct
# 需暴露的端口
EXPOSE 8888
# 可外掛的目錄
VOLUME ["/go/kingProject/config","/go/kingProject/log"]
# docker run命令觸發的真實命令(相當于不編譯打包,源代碼直接運行)
ENTRYPOINT ["go","run","main.go"]
編譯鏡像后查詢結果如下:
結果是941M,基本跟基礎鏡像golang的大小一致,而且因為沒有預先編譯,等到運行的時候再編譯并拉取依賴包,run起來很慢
3. 編譯后的鏡像 1.14G
Dockerfile文件內容
# 基礎鏡像,基于golang最新鏡像構建
FROM golang
# 作者
MAINTAINER scoful
# 全局工作目錄
WORKDIR $GOPATH/kingProject
# 把運行Dockerfile文件的當前目錄所有文件復制到目標目錄
COPY . $GOPATH/kingProject
# 環境變量
# 用于代理下載go項目依賴的包
ENV GOPROXY https://goproxy.cn,direct
# 編譯
RUN GOOS=linux GOARCH=amd64 go build main.go
# 需暴露的端口
EXPOSE 8888
# 可外掛的目錄
VOLUME ["/go/kingProject/config","/go/kingProject/log"]
# docker run命令觸發的真實命令(相當于直接運行編譯后的可運行文件)
ENTRYPOINT ["./main"]
結果是1.14G,更大了,因為加上了編譯過程中拉取的包,但是預先編譯,所以直接run,速度很快
4. 優化:使用alpine版本的基礎鏡像 517M
優化的方向:如果一個鏡像在https://hub.docker.com/里能搜到有alpine版本,盡量用alpine版本,相當于是官方提供的最小化可用版本
Dockerfile文件內容
# 基礎鏡像,基于golang的alpine版本鏡像構建
FROM golang:alpine
# 作者
MAINTAINER scoful
# 全局工作目錄
WORKDIR $GOPATH/kingProject
# 把運行Dockerfile文件的當前目錄所有文件復制到目標目錄
COPY . $GOPATH/kingProject
# 環境變量
# 用于代理下載go項目依賴的包
ENV GOPROXY https://goproxy.cn,direct
# 編譯
RUN GOOS=linux GOARCH=amd64 go build main.go
# 需暴露的端口
EXPOSE 8888
# 可外掛的目錄
VOLUME ["/go/kingProject/config","/go/kingProject/log"]
# docker run命令觸發的真實命令(相當于直接運行編譯后的可運行文件)
ENTRYPOINT ["./main"]
結果是517M,比1.14G減少了650.36M,直接降了56%,而且run一樣很快
5. 再優化:使用多級構建的鏡像 28.4M
再優化的方向:go項目其實只是在build的階段需要go環境,run的時候是不需要的,那build完后go環境用個alpine版本就行
Dockerfile文件內容
# 基礎鏡像,基于golang的alpine鏡像構建--編譯階段
FROM golang:alpine AS builder
# 作者
MAINTAINER scoful
# 全局工作目錄
WORKDIR /go/kingProject
# 把運行Dockerfile文件的當前目錄所有文件復制到目標目錄
COPY . /go/kingProject
# 環境變量
# 用于代理下載go項目依賴的包
ENV GOPROXY https://goproxy.cn,direct
# 編譯,關閉CGO,防止編譯后的文件有動態鏈接,而alpine鏡像里有些c庫沒有,直接沒有文件的錯誤
RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build main.go
# 使用alpine這個輕量級鏡像為基礎鏡像--運行階段
FROM alpine AS runner
# 全局工作目錄
WORKDIR /go/kingProject
# 復制編譯階段編譯出來的運行文件到目標目錄
COPY --from=builder /go/kingProject/main .
# 復制編譯階段里的config文件夾到目標目錄
COPY --from=builder /go/kingProject/config ./config
# 需暴露的端口
EXPOSE 8888
# 可外掛的目錄
VOLUME ["/go/kingProject/config","/go/kingProject/log"]
# docker run命令觸發的真實命令(相當于直接運行編譯后的可運行文件)
ENTRYPOINT ["./main"]
結果是28.4M,比517M減少了488.6M,再降95%,那個533M是第一級構建生成的
6. 再再優化:使用多級構建+scratch基礎鏡像 22.8M
再再優化的方向:再極端一點,第二級構建的時候用個空鏡像來當基礎鏡像
Dockerfile文件內容
# 基礎鏡像,基于golang的alpine鏡像構建--編譯階段
FROM golang:alpine AS builder
# 作者
MAINTAINER scoful
# 全局工作目錄
WORKDIR /go/kingProject
# 把運行Dockerfile文件的當前目錄所有文件復制到目標目錄
COPY . /go/kingProject
# 環境變量
# 用于代理下載go項目依賴的包
ENV GOPROXY https://goproxy.cn,direct
# 編譯,關閉CGO,防止編譯后的文件有動態鏈接,而alpine鏡像里有些c庫沒有,直接沒有文件的錯誤
RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build main.go
# 使用scratch這個空鏡像為基礎鏡像--運行階段
FROM scratch AS runner
# 全局工作目錄
WORKDIR /go/kingProject
# 復制編譯階段編譯出來的運行文件到目標目錄
COPY --from=builder /go/kingProject/main .
# 復制編譯階段里的config文件夾到目標目錄
COPY --from=builder /go/kingProject/config ./config
# 需暴露的端口
EXPOSE 8888
# 可外掛的目錄
VOLUME ["/go/kingProject/config","/go/kingProject/log"]
# docker run命令觸發的真實命令(相當于直接運行編譯后的可運行文件)
ENTRYPOINT ["./main"]
結果是22.8M,已經約等于不用鏡像直接編譯出的可執行文件大小了,比28.4M減少了5.6M,再降20%,但用scratch當基礎鏡像的壞處是沒法exec進入容器內部,因為真的就是空鏡像,啥都沒有,啥都不支持
7. 再再再優化:go編譯命令去掉冗余輸出 16.3M
再再再優化的方向:再再極端一點,go編譯的時候,加上參數 -ldflags="-w -s",直接去掉一些冗余輸出內容
Dockerfile文件內容
# 基礎鏡像,基于golang的alpine鏡像構建--編譯階段
FROM golang:alpine AS builder
# 作者
MAINTAINER scoful
# 全局工作目錄
WORKDIR /go/kingProject
# 把運行Dockerfile文件的當前目錄所有文件復制到目標目錄
COPY . /go/kingProject
# 環境變量
# 用于代理下載go項目依賴的包
ENV GOPROXY https://goproxy.cn,direct
# 編譯,關閉CGO,防止編譯后的文件有動態鏈接,而alpine鏡像里有些c庫沒有,直接沒有文件的錯誤
RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-w -s" main.go
# 使用scratch這個空鏡像為基礎鏡像--運行階段
FROM scratch AS runner
# 全局工作目錄
WORKDIR /go/kingProject
# 復制編譯階段編譯出來的運行文件到目標目錄
COPY --from=builder /go/kingProject/main .
# 復制編譯階段里的config文件夾到目標目錄
COPY --from=builder /go/kingProject/config ./config
# 需暴露的端口
EXPOSE 8888
# 可外掛的目錄
VOLUME ["/go/kingProject/config","/go/kingProject/log"]
# docker run命令觸發的真實命令(相當于直接運行編譯后的可運行文件)
ENTRYPOINT ["./main"]
結果是16.3M,看似比不用鏡像直接編譯出的可執行文件還小,那是因為直接編譯沒有加上這個參數,如果加上大小還是差不多的,比22.8M減少了6.5M,再降29%,好了,降無可降了,用1.14G來對比的話,減少了1.12G,足足降了99%,鵝妹子嚶!
8. 最終版:順便解決時區問題 16.3M
Dockerfile文件內容
# 基礎鏡像,基于golang的alpine鏡像構建--編譯階段
FROM golang:alpine AS builder
# 作者
MAINTAINER scoful
# 全局工作目錄
WORKDIR /go/kingProject
# 把運行Dockerfile文件的當前目錄所有文件復制到目標目錄
COPY . /go/kingProject
# 環境變量
# 用于代理下載go項目依賴的包
ENV GOPROXY https://goproxy.cn,direct
# 編譯,關閉CGO,防止編譯后的文件有動態鏈接,而alpine鏡像里有些c庫沒有,直接沒有文件的錯誤
RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-w -s" main.go
RUN echo "https://mirrors.aliyun.com/alpine/v3.8/main/" > /etc/apk/repositories
&& echo "https://mirrors.aliyun.com/alpine/v3.8/community/" >> /etc/apk/repositories
&& apk add --no-cache tzdata
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
&& echo Asia/Shanghai > /etc/timezone
&& apk del tzdata
# 使用scratch這個空鏡像為基礎鏡像--運行階段
FROM scratch AS runner
# 全局工作目錄
WORKDIR /go/kingProject
# 復制編譯階段編譯出來的運行文件到目標目錄
COPY --from=builder /go/kingProject/main .
# 復制編譯階段里的config文件夾到目標目錄
COPY --from=builder /go/kingProject/config ./config
# 復制編譯階段里的時區文件到目標目錄
COPY --from=builder /etc/localtime /etc/localtime
COPY --from=builder /etc/timezone /etc/timezone
# 需暴露的端口
EXPOSE 8888
# 可外掛的目錄
VOLUME ["/go/kingProject/config","/go/kingProject/log"]
# docker run命令觸發的真實命令(相當于直接運行編譯后的可運行文件)
ENTRYPOINT ["./main"]
9. 最最推薦使用版:多級+alpine 21.9M
綜上所述,scratch鏡像有它的缺陷,是一個真的空鏡像,不支持很多命令,比如cp,sh等,如果要進入容器內部查東西,都進不去,不適合真實情況,所以還是推薦alpine鏡像,很小5M多,可以接受。
Dockerfile文件內容
# 基礎鏡像,基于golang的alpine鏡像構建--編譯階段
FROM golang:alpine AS builder
# 作者
MAINTAINER scoful
# 全局工作目錄
WORKDIR /go/kingProject
# 把運行Dockerfile文件的當前目錄所有文件復制到目標目錄
COPY . /go/kingProject
# 環境變量
# 用于代理下載go項目依賴的包
ENV GOPROXY https://goproxy.cn,direct
# 編譯,關閉CGO,防止編譯后的文件有動態鏈接,而alpine鏡像里有些c庫沒有,直接沒有文件的錯誤
RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-w -s" main.go
# 使用alpine這個輕量級鏡像為基礎鏡像--運行階段
FROM alpine AS runner
# 全局工作目錄
WORKDIR /go/kingProject
# 復制編譯階段編譯出來的運行文件到目標目錄
COPY --from=builder /go/kingProject/main .
# 復制編譯階段里的config文件夾到目標目錄
COPY --from=builder /go/kingProject/config ./config
# 將時區設置為東八區
RUN echo "https://mirrors.aliyun.com/alpine/v3.8/main/" > /etc/apk/repositories
&& echo "https://mirrors.aliyun.com/alpine/v3.8/community/" >> /etc/apk/repositories
&& apk add --no-cache tzdata
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
&& echo Asia/Shanghai > /etc/timezone
&& apk del tzdata
# 需暴露的端口
EXPOSE 8888
# 可外掛的目錄
VOLUME ["/go/kingProject/config","/go/kingProject/log"]
# docker run命令觸發的真實命令(相當于直接運行編譯后的可運行文件)
ENTRYPOINT ["./main"]
over,Enjoy!!!
原文標題:給go項目打最小docker鏡像,足足降低99%
文章出處:【微信公眾號:馬哥Linux運維】歡迎添加關注!文章轉載請注明出處。
-
Linux
+關注
關注
87文章
11345瀏覽量
210392 -
鏡像
+關注
關注
0文章
170瀏覽量
10802 -
Docker
+關注
關注
0文章
492瀏覽量
11965
原文標題:給go項目打最小docker鏡像,足足降低99%
文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論