通過多階段構建減小Golang鏡像的大小
我們如何通過引入具有多階段構建過程的Dockerfiles來減小Golang鏡像的大小?
讓我們從一個通用的Dockerfile開始,它負責處理基本的事務,如依賴項、構建二進制文件、暴露必要的端口等,以便為Go中的一個非常基礎的REST API提供服務。
FROM golang:1.16-alpine
ENV GO111MODULE=on
WORKDIR /app
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build
ENV HTTP_PORT=8080
EXPOSE 8080
ENTRYPOINT ["/app/reduce-docker-size"]
那將無縫地構建您項目的二進制文件,并創建Docker鏡像。
這樣做真的足夠好嗎? 我會說不,因為生成的鏡像大小超過300MB(確切地說是322MB),因為它包含了所有的Golang工具,這對我們來說是不必要的,因為我們指示編譯器禁用cgo(CGO_ENABLED=0)并靜態鏈接任何將為我們提供自包含可執行文件的C綁定(其大小僅為6.05MB!),無需任何外部框架或運行時依賴。
圖片
CGO_ENABLED=0 是至關重要的,如果我們不構建自包含的可執行文件,多階段構建過程將無法工作。
我們可以做得更好的是,采用所謂的多階段構建。多階段構建允許多個不同的構建過程,這些構建可以完全從不同的基礎鏡像構建,選擇性地將文件從一個階段傳遞到下一個階段,從而剝離最終鏡像中所有不必要的文件。例如,我們可以將前一個階段稱為BUILD,然后引入第二個階段,我們稱之為BINARIES,該階段使用alpine:latest作為基礎鏡像,并從BUILD階段復制我們構建的應用程序的二進制文件。
# BUILD
FROM golang:1.16-alpine as BUILD
ENV GO111MODULE=on
WORKDIR /app
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build
ENV HTTP_PORT=8080
EXPOSE 8080
# BINARIES
FROM alpine:latest
COPY --from=BUILD /app/reduce-docker-size /app/reduce-docker-size
ENTRYPOINT ["/app/reduce-docker-size"]
由于不再需要,配備了golang工具包的 已被清理。現在鏡像大小已降至11.7MB。
圖片
這個好到足夠了嗎? 我會說是的,但是為了實驗的緣故,我們還是盡量挑戰一下極限。我們繼續沿著多階段構建的道路前進,但這次在我們的第二階段,我們將不再使用alpine:latest,而是轉向一個非常特殊的名為scratch的鏡像,這是一個完全空白的鏡像,實際上什么都沒有。
# BUILD
FROM golang:1.16-alpine as BUILD
ENV GO111MODULE=on
WORKDIR /app
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build
ENV HTTP_PORT=8080
EXPOSE 8080
# MINIATURE
FROM scratch
COPY --from=BUILD /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=BUILD /app/reduce-docker-size /app/reduce-docker-size
ENTRYPOINT ["/app/reduce-docker-size"]
新創建的鏡像現在已經降至6.34MB!
圖片
因為我們預先告知的scratch鏡像實際上是空的,所以找不到任何根SSL證書。以下指令將在最終鏡像中復制證書,絕對不應被省略:
COPY — from=BUILD /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
請問使用scratch作為最終階段的基礎鏡像值得嗎?我會說既值得又不值得。如果你排除一些特殊情況——那些在alpine:latest和scratch構建的最終鏡像之間的5.36MB差異可能會產生巨大的影響——在其余的情況下,你最終會在生產中得到一個完全沒有任何工具的容器,我完全不推薦這樣做。這些特殊情況很少見,所以在為了僅僅5.36M。