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的大小)而给自己找麻烦之前,要三思。

相关推荐
骆晨学长16 分钟前
基于springboot的智慧社区微信小程序
java·数据库·spring boot·后端·微信小程序·小程序
AskHarries21 分钟前
利用反射实现动态代理
java·后端·reflect
Flying_Fish_roe1 小时前
Spring Boot-Session管理问题
java·spring boot·后端
hai405872 小时前
Spring Boot中的响应与分层解耦架构
spring boot·后端·架构
liupenglove3 小时前
golang操作mysql利器-gorm
mysql·golang
Adolf_19933 小时前
Flask-JWT-Extended登录验证, 不用自定义
后端·python·flask
叫我:松哥3 小时前
基于Python flask的医院管理学院,医生能够增加/删除/修改/删除病人的数据信息,有可视化分析
javascript·后端·python·mysql·信息可视化·flask·bootstrap
海里真的有鱼3 小时前
Spring Boot 项目中整合 RabbitMQ,使用死信队列(Dead Letter Exchange, DLX)实现延迟队列功能
开发语言·后端·rabbitmq
工业甲酰苯胺3 小时前
Spring Boot 整合 MyBatis 的详细步骤(两种方式)
spring boot·后端·mybatis
Rookie_explorers4 小时前
Linux下go环境安装、环境配置并执行第一个go程序
linux·运维·golang