如何减少 golang 项目 docker 镜像的大小?
2024-05-07 by dongnan
环境描述
- OS: Ubuntu Server 20.04 LTS
- Docker: v20.10.12
- App: 使用
golang
开发的项目,运行在docker
环境。
问题描述
下载的 golang
镜像 大小为 860MB
$ docker images | grep golang
golang 1.21.5 e11dfa244376 3 weeks ago 815MB
编译的应用镜像 大小为 1.2GB
$ docker images | grep ade-server
ade_ade-server latest b25caeb154d6 3 days ago 1.2GB
每次编译程序都要下载依赖包,例如:
$ docker-compose up -d --build
.
Building ade-server
Step 1/9 : FROM golang:1.21.5
---> e11dfa244376
Step 2/9 : MAINTAINER "dongnan"
---> Using cache
---> bc73e12f45e2
Step 3/9 : ENV GO111MODULE=on
---> Using cache
---> c81552632a8d
Step 4/9 : ENV GOPROXY=https://goproxy.cn,direct
---> Using cache
---> 8560be56a430
Step 5/9 : WORKDIR /opt
---> Using cache
---> c29f05adfe00
Step 6/9 : ADD . /opt
---> Using cache
---> c3582af0b104
Step 7/9 : RUN go build -o main ./main.go
---> Running in 0ab89aa177d8
go: downloading github.com/gin-gonic/gin v1.9.1
# 省略30行
Removing intermediate container 0ab89aa177d8
---> 8ee2c83ecc8b
Step 8/9 : EXPOSE 8000
---> Running in f7c7fa56bd04
Removing intermediate container f7c7fa56bd04
---> dfae7d54f087
Step 9/9 : CMD ["/opt/main"]
---> Running in 5f50b1d099c0
Removing intermediate container 5f50b1d099c0
---> 8692607af8d4
Successfully built 8692607af8d4
Successfully tagged ade_ade-server:latest
Dockerfile(第1版)
FROM golang:1.21.5
MAINTAINER "dongnan"
# proxy
ENV GO111MODULE=on
ENV GOPROXY=https://goproxy.cn,direct
# work
WORKDIR /opt
ADD . /opt
# build
RUN go build -o main ./main.go
# port
EXPOSE 8000
# cmd
CMD ["/opt/main"]
这个 Dockerfile
有两个问题:
- 每次
build
时都需要重新下载依赖包,浪费时间与带宽。 - 每次
build
后的镜像太过臃肿,达到1.2GB
。
解决方法
目标是,解决应用镜像 build
时间长,build
后镜像体积臃肿,这两个问题。
Dockerfile(第2版)
# builder
FROM golang:1.21.5 as builder
# proxy
ENV GO111MODULE=on
ENV GOPROXY=https://goproxy.cn,direct
# build cache
WORKDIR /go/cache
ADD go.mod .
ADD go.sum .
RUN go mod download
# build app
WORKDIR /go/app
ADD . .
RUN GOOS=linux CGO_ENABLED=0 go build -ldflags="-s -w" -installsuffix cgo -o server main.go
# prod
#FROM scratch as prod
FROM alpine:3.19.0 as prod
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \
&& apk update \
&& apk upgrade \
&& apk add --no-cache tzdata \
&& cp /usr/share/zoneinfo/Asia/Chongqing /etc/localtime
WORKDIR /ade
COPY --from=builder /go/app .
# port
EXPOSE 8000
# cmd
CMD ["/ade/server"]
第2版的 Dockerfile
有如下优化:
- 使用 多阶段构建 Docker镜像,主要分为
builder
与prod
。 - 在
builder
阶段,将go下载的模块缓存起来,只要go.mod
和go.sum
文件没有变化,就会使用该缓存层。 - 在
prod
阶段,使用轻量级的alpine
基础镜像 ,而不是完整的Debian
系统镜像 。 - go 编译时增加了
-ldflags="-s -w"
参数,禁用调试信息,以减小镜像大小。
验证
构建花费时间
再次build镜像,花费时间35秒 (使用缓存)
$ time docker-compose up -d --build
.
Building ade-server
Step 1/16 : FROM golang:1.21.5 as builder
---> e11dfa244376
Step 2/16 : ENV GO111MODULE=on
---> Using cache
---> b56e54272dcc
Step 3/16 : ENV GOPROXY=https://goproxy.cn,direct
---> Using cache
---> 3b39a04000f6
Step 4/16 : WORKDIR /go/cache
---> Using cache
---> bd805342f276
Step 5/16 : ADD go.mod .
---> Using cache
---> 99ea693efa5d
Step 6/16 : ADD go.sum .
---> Using cache
---> 92f56934d29f
Step 7/16 : RUN go mod download
---> Using cache
---> 26d4380cd883
Step 8/16 : WORKDIR /go/app
---> Using cache
---> 1785cf489d1e
Step 9/16 : ADD . .
---> 22fe961577d4
Step 10/16 : RUN GOOS=linux CGO_ENABLED=0 go build -ldflags="-s -w" -installsuffix cgo -o server main.go
---> Running in 4845069fdcce
Removing intermediate container 4845069fdcce
---> 65e9249e6012
Step 11/16 : FROM alpine:3.19.0 as prod
---> f8c20f8bbcb6
# 省略...
Step 13/16 : WORKDIR /ade
---> Running in 5afcfbac6683
Removing intermediate container 5afcfbac6683
---> dc3bdf6257f0
Step 14/16 : COPY --from=builder /go/app .
---> 708111f907c2
Step 15/16 : EXPOSE 8000
---> Running in 318581fd6a30
Removing intermediate container 318581fd6a30
---> f17f7a7ba87f
Step 16/16 : CMD ["/ade/server"]
---> Running in 7fcae6721855
Removing intermediate container 7fcae6721855
---> c9a42f5c909e
Successfully built c9a42f5c909e
Successfully tagged ade_ade-server:latest
Recreating ade_ade-server_1 ... done
real 0m34.606s
user 0m0.482s
sys 0m0.070s
注意
- 这里使用
time
命令测试build
构建镜像的时间。 - 镜像构建过程中,注意
Using cache
关键字,特别是go mod download
最重要的缓存层。
构建镜像大小
再次build镜像,大小为 28.5MB
$ docker images
.
REPOSITORY TAG IMAGE ID CREATED SIZE
ade_ade-server latest c9a42f5c909e 2 minutes ago 28.5MB
其它
关于 golang
编译参数,有请 chatGPT
回答一下: