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:latestscratch构建的最终镜像之间的5.36MB差异可能会产生巨大的影响,在其余的情况下,你最终会在生产中得到一个完全没有任何工具的容器,我完全不推荐这样做。这些特殊情况很少见,所以在为了仅仅5.36MB(实际上是alpine:latest的大小)而给自己找麻烦之前,要三思。

相关推荐
SmartBrain36 分钟前
Spring Boot的高性能技术栈的工程实践
spring boot·后端·架构
AMoon丶1 小时前
Golang--内存管理
开发语言·后端·算法·缓存·golang·os
zabr1 小时前
花了 100+ 篇笔记,我整理出 了一套 AI Agent 工程完全指南
前端·后端·agent
神奇小汤圆1 小时前
Java面试题及答案整理(2026年金三银四最新版,持续更新)
后端
uzong1 小时前
“腾讯QClaw全面开放”,不花 Token 钱、真正体验一把小龙虾的快乐,最低成本全面了解龙虾
人工智能·后端
楼田莉子1 小时前
C++高并发内存池:内存池调优与测试
c++·后端·哈希算法·visual studio
短剑重铸之日1 小时前
《ShardingSphere解读》16 改写引擎:如何理解装饰器模式下的 SQL 改写实现机制?
java·数据库·后端·sql·shardingsphere·分库分表·装饰器模式
q5431470872 小时前
VScode 开发 Springboot 程序
java·spring boot·后端
学习要积极2 小时前
Springboot图片验证码-EasyCaptcha
java·spring boot·后端
Nyarlathotep01132 小时前
可重入锁ReentrantLock基础和原理
后端