106. Dockerfile通过多阶段构建减小Golang镜像的大小

我们如何通过引入具有多阶段构建过程的Dockerfiles来减小Golang镜像的大小?

让我们从一个通用的Dockerfile开始,它负责处理基本的事务,如依赖项、构建二进制文件、声明暴露的端口等,以便为Go中的一个非常基础的REST API提供服务。

假如在reduce-docker-size项目下有一个dockerfile文件。

reduce-docker-size/dockerfile

bash 复制代码
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阶段复制我们构建的应用程序的二进制文件。

bash 复制代码
# 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的镜像,这是一个完全空白的镜像,实际上什么都没有。

bash 复制代码
# 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证书。以下指令将在最终镜像中复制证书,绝对不应被省略:

go 复制代码
COPY --- from=BUILD /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/

请问使用scratch作为最终阶段的基础镜像值得吗?我会说既值得又不值得。如果你排除那些在alpine:latest和scratch构建的最终镜像之间的5.36MB差异可能会产生巨大的影响,在其余的情况下,你最终会在生产中得到一个完全没有任何工具的容器,我完全不推荐这样做。这些特殊情况很少见,所以在为了仅仅5.36MB(实际上是alpine:latest的大小)而给自己找麻烦之前,要三思。

相关推荐
豆角焖肉27 分钟前
SpringBoot 集成腾讯 AI 人脸识别
人工智能·spring boot·后端·人脸识别·腾讯 ai·base64 图片
付威202338 分钟前
如果你想拥有自己的 Agent,为什么值得从 rpi 开始
后端
可乐ea1 小时前
2000 个智能体把 Agent 重写成 Rust:编排流水线拆解
开发语言·后端·rust·多智能体协作·ai编程助手·智能体编排·rust重写
用户72757483196501 小时前
02-变量、常量和字符串:len("Hello, 世界") 为什么不是 9?
后端
SamDeepThinking1 小时前
CyclicBarrier的底层实现
java·后端·面试
god_cvz1 小时前
第二章:6、对话记忆与引用标注
后端·ai编程
拖孩1 小时前
全程 AI 写的小程序,刚接上会员虚拟支付,上线第二天就有人真金白银买单了|双签名、定价、iOS 的坑我全踩了一遍
前端·后端·微信小程序
我是人✓1 小时前
SpringMVC异常处理与拦截器实战揭秘
java·开发语言·后端
仟里码1 小时前
九:goroutine(1.什么是协程)
后端
不是万能的豆学长1 小时前
排查Go服务40天后的内存泄漏问题
开发语言·后端·golang·go