不管是在咱们的生产环境或测试环境中,一旦涉及到docker pull和push的时候,咱们都迫切但愿Docker镜像是一个很是小的file,一方面在网络带宽有限的状况下,image的size越小下载所付出的时间代价就越小,另外一方面image始终是一个文件,size对存储空间是有必定影响的,看来这是个提升生产力的问题,那么咱们如何去构建一个size很小的image呢?linux
本文内容
注:多阶段构建是 Docker 17.05 及更高版本提供的功能git
查看一下image列表:github
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE golang 1.10.3 d0e7a411e3da 6 weeks ago 794MB alpine 3.8 11cd0b38bc3c 8 weeks ago 4.41MB
一般在咱们什么都不考虑的状况下,构建一个golang的应用镜像是很是简单的,只须要pull官方的golang环境,把咱们的程序copy进去就能够了,下面咱们先建立一个工程的目录以下:golang
$ tree -L 2 -C . ├── Dockerfile └── src └── main.go 1 directory, 2 files
FROM golang:1.10.3 WORKDIR /go/src/test RUN go get github.com/gin-gonic/gin COPY src src RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main src/main.go CMD ["./main"]
$ docker build -t zev/test:1.0.0 . Sending build context to Docker daemon 17.41kB Step 1/6 : FROM golang:1.10.3 ---> d0e7a411e3da Step 2/6 : WORKDIR /go/src/test ---> Running in 94d1ede51e17 Removing intermediate container 94d1ede51e17 ---> 2b643ce8b3cf Step 3/6 : RUN go get github.com/gin-gonic/gin ---> Running in de5e9adb7c10 Removing intermediate container de5e9adb7c10 ---> ff970f45de1e Step 4/6 : COPY src src ---> 6b79fef06e45 Step 5/6 : RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main src/main.go ---> Running in 6d4ef8c0b580 Removing intermediate container 6d4ef8c0b580 ---> 59678a3ab4d8 Step 6/6 : CMD ["./main"] ---> Running in a5cea54f2ccb Removing intermediate container a5cea54f2ccb ---> a253cfcddd6a Successfully built a253cfcddd6a Successfully tagged zev/test:1.0.0
$ docker run -it -p 8080:8080 zev/test:1.0.0 [GIN-debug] [WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon. [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode) [GIN-debug] GET /ping --> main.main.func1 (3 handlers) [GIN-debug] Listening and serving HTTP on :8080
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE zev/test 1.0.0 a253cfcddd6a 4 minutes ago 857MB
image的size为857MB,内部包含了整个golang环境,这么大的文件在传输中绝对是个灾难,接下来咱们用多阶段构建一个相对比较小的image。docker
FROM golang:1.10.3 as builder WORKDIR /go/src/test RUN go get github.com/gin-gonic/gin COPY src src RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main src/main.go FROM alpine:3.8 WORKDIR /root COPY --from=builder /go/src/test/main . CMD ["./main"]
$ docker build -t zev/test:1.0.1 . Sending build context to Docker daemon 17.41kB Step 1/9 : FROM golang:1.10.3 as builder ---> d0e7a411e3da Step 2/9 : WORKDIR /go/src/test ---> Using cache ---> 2b643ce8b3cf Step 3/9 : RUN go get github.com/gin-gonic/gin ---> Using cache ---> ff970f45de1e Step 4/9 : COPY src src ---> Using cache ---> 6b79fef06e45 Step 5/9 : RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main src/main.go ---> Using cache ---> 59678a3ab4d8 Step 6/9 : FROM alpine:3.8 ---> 11cd0b38bc3c Step 7/9 : WORKDIR /root ---> Running in 1640c71479d6 Removing intermediate container 1640c71479d6 ---> ec68dc839562 Step 8/9 : COPY --from=builder /go/src/test/main . ---> 5bb444c91aff Step 9/9 : CMD ["./main"] ---> Running in a80305feba6e Removing intermediate container a80305feba6e ---> 5923597f59c2 Successfully built 5923597f59c2 Successfully tagged zev/test:1.0.1
$ docker run -it -p 8080:8080 zev/test:1.0.1 [GIN-debug] [WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon. [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode) [GIN-debug] GET /ping --> main.main.func1 (3 handlers) [GIN-debug] Listening and serving HTTP on :8080
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE zev/test 1.0.1 5923597f59c2 2 minutes ago 19.8MB
多阶段构建让images缩小了40倍,19.8M的size不管在测试环境仍是生产环境都能很好的工做了,可是这样就结束了吗?
固然不是了,咱们的目的是让image变得更小,下面看咱们的操做。网络
FROM golang:1.10.3 as builder RUN apt-get update && apt-get install -y xz-utils \ && rm -rf /var/lib/apt/lists/* ADD https://github.com/upx/upx/releases/download/v3.95/upx-3.95-amd64_linux.tar.xz /usr/local RUN xz -d -c /usr/local/upx-3.95-amd64_linux.tar.xz | tar -xOf - upx-3.95-amd64_linux/upx > /bin/upx && \ chmod a+x /bin/upx WORKDIR /go/src/test RUN go get github.com/gin-gonic/gin COPY src src RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main src/main.go RUN strip --strip-unneeded main RUN upx main FROM alpine:3.8 WORKDIR /root COPY --from=builder /go/src/test/main . CMD ["./main"]
$ docker build -t zev/test:1.0.2 . Sending build context to Docker daemon 17.92kB Step 1/14 : FROM golang:1.10.3 as builder ---> d0e7a411e3da Step 2/14 : RUN apt-get update && apt-get install -y xz-utils && rm -rf /var/lib/apt/lists/* ---> Running in 65772cb8fdab Ign:1 http://deb.debian.org/debian stretch InRelease Get:2 http://security.debian.org/debian-security stretch/updates InRelease [94.3 kB] Get:3 http://deb.debian.org/debian stretch-updates InRelease [91.0 kB] Get:4 http://security.debian.org/debian-security stretch/updates/main amd64 Packages [392 kB] .....此处省略 Step 10/14 : RUN upx main ---> Running in d802406ee44a Ultimate Packer for eXecutables Copyright (C) 1996 - 2018 UPX 3.95 Markus Oberhumer, Laszlo Molnar & John Reiser Aug 26th 2018 File size Ratio Format Name -------------------- ------ ----------- ----------- 9848136 -> 2945384 29.91% linux/amd64 main Packed 1 file. Removing intermediate container d802406ee44a ---> 0c29f4b2272d Step 11/14 : FROM alpine:3.8 ---> 11cd0b38bc3c Step 12/14 : WORKDIR /root ---> Using cache ---> ec68dc839562 Step 13/14 : COPY --from=builder /go/src/test/main . ---> a2c265cc9aff Step 14/14 : CMD ["./main"] ---> Running in 7e350a4620ee Removing intermediate container 7e350a4620ee ---> a4d7753c8112 Successfully built a4d7753c8112 Successfully tagged zev/test:1.0.2
$ docker run -it -p 8080:8080 zev/test:1.0.2 [GIN-debug] [WARNING] Now Gin requires Go 1.6 or later and Go 1.7 will be required soon. [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode) [GIN-debug] GET /ping --> main.main.func1 (3 handlers) [GIN-debug] Listening and serving HTTP on :8080
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE zev/test 1.0.2 a4d7753c8112 4 minutes ago 7.36MB
OK 很是漂亮,到如今咱们已经看到image的size已经缩小到7.36MB,这已经很是的小了,换算过来咱们的程序只有2.95M。
咱们来一张全景对比看下:测试
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE zev/test 1.0.2 a4d7753c8112 6 minutes ago 7.36MB zev/test 1.0.1 5923597f59c2 About an hour ago 19.8MB zev/test 1.0.0 a253cfcddd6a About an hour ago 857MB golang 1.10.3 d0e7a411e3da 6 weeks ago 794MB alpine 3.8 11cd0b38bc3c 8 weeks ago 4.41MB
那么怎么作到的呢,原理很简单,由于alpine的size已经固定了,能让image变得更小的入手点只能是可执行文件,利用UPX的加壳技术能够压缩main可执行程序,能够把main体积缩小50%-70%。ui
Tip:UPX(the Ultimate Packer for eXecutables)是一个免费且开源的可执行程序文件加壳器,支持许多不一样操做系统下的可执行文件格式。想了解更多关于UPX的信息能够 点击这里 , 也可点击这里.
总结:spa
好的,到此为止本文首先展现了单阶段构建镜像,并获得了一个~857MB的镜像,而后利用多阶段构建了一个只包含可执行文件的镜像~19.8MB,最后咱们利用多阶段+UPX压缩把咱们的镜像缩小到了~7.36MB,这样的镜像不管在测试环境仍是生产环境,都必定能大大的提升咱们的生产力。操作系统