如何减少 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 回答一下:
