由于Golang編譯之后的文件是二進制,而scratch是docker最基礎的空image,所以可以使用scratch來構建Go程序的docker image,使得最終構建的image最小化.
構建image過程分為兩步:
- 在Go基礎image中build.
- 將build好的二進制文件拷貝到scratch image中。
無需cgo的程序
對于無需cgo交叉編譯的程序,使用scratch來作為最終運行的基礎image非常合適。
首先,選擇合適版本的golang基礎image來build,這里沒有必要選擇更小的golang alpine,build過程中pull一般會有緩存所以pull速度差別不大,此外alpine中沒有git和ssl,我們在構建image過程中都有可能用到,況且alpine也不會影響最終image大小。
FROM golang:1.13 AS builder
禁掉cgo交叉編譯,我們服務器一般為linux amd64,build二進制文件。
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o /bin/appmain main.go
對于絕大多數go程序而言,是無需root來運行,根據docker best practice,使用non-root來運行程序能夠帶來更好的安全性,所以我們使用non-root用戶來運行,創(chuàng)建一個appuser,之后再拷貝到scratch運行image中。(scratch是空image,所以在builder中創(chuàng)建user,再拷貝。)
# 創(chuàng)建appuser
RUN groupadd -r appuser && useradd --no-log-init -r -g appuser appuser
...
# 拷貝appuser到scratch
COPY --from=builder /etc/passwd /etc/passwd
...
# 選擇appuser為默認程序運行用戶
USER appuser
多數程序可能會用到ssl,我們將builder中的crt拷貝一下即可。(如果builder是alpine,不能拷貝,需要在alpine中apk先預裝一下。)
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
完整版Dockerfile
FROM golang:1.13 AS builder
COPY . /app
WORKDIR /app
RUN groupadd -r appuser && useradd --no-log-init -r -g appuser appuser
RUN go mod download
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o /bin/appmain main.go
FROM scratch
COPY --from=builder /etc/passwd /etc/passwd
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /bin/appmain /bin/appmain
USER appuser
CMD [ "/bin/appmain" ]
需要cgo的程序
有些Go程序是需要cgo交叉編譯的,例如ethereum. 對于需要cgo的程序,相對于scratch,更推薦使用alpine來作為基礎image,原因是alpine中帶有l(wèi)ibc,并且體積也才2MB多。而scratch中沒有,當然也可以在builder中l(wèi)dd依賴并拷貝到scratch中。只是用alpine會更方便一些。
在alpine中只要軟鏈接一下就可以使用。
RUN mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2
此外,創(chuàng)建non-root用戶的步驟也沒有必要在builder中進行了,可以直接在alpine中創(chuàng)建。
RUN addgroup -S appuser && adduser -S -G appuser appuser
完整版Dockerfile
FROM golang:1.13 AS builder
COPY . /app
WORKDIR /app
RUN go mod download
RUN CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o /bin/appmain main.go
FROM alpine:3.10
RUN mkdir /lib64 && ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /bin/appmain /bin/appmain
RUN addgroup -S appuser && adduser -S -G appuser appuser
USER appuser
CMD [ "/bin/appmain" ]