Whether in our production environment or test environment, when it comes to Docker pull and push, we are eager to hope that the Docker image is a very small file, on the one hand, in the case of limited network bandwidth, the smaller the image size, the less time it takes to download, On the other hand, the image is always a file, size has a certain impact on storage space, it seems to be a productivity improvement problem, then how do we build a small size image?
content of this article
- Single Stage Build image
- Multi-stage Build image
- Smaller image build-up
Note: Multi-stage builds are features offered by Docker 17.05 and later
Take a look at the image list:
$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEgolang 1.10.3 d0e7a411e3da 6 weeks ago 794MBalpine 3.8 11cd0b38bc3c 8 weeks ago 4.41MB
Usually when we do not consider the situation, to build a Golang application image is very simple, only need to pull the official Golang environment, copy our program into it, we will first create a project directory as follows:
$ tree -L 2 -C.├── Dockerfile└── src └── main.go 1 directory, 2 files
Single Stage Build image
FROM golang:1.10.3WORKDIR /go/src/testRUN go get github.com/gin-gonic/ginCOPY src srcRUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main src/main.goCMD ["./main"]
$ docker build -t zev/test:1.0.0 .Sending build context to Docker daemon 17.41kBStep 1/6 : FROM golang:1.10.3 ---> d0e7a411e3daStep 2/6 : WORKDIR /go/src/test ---> Running in 94d1ede51e17Removing intermediate container 94d1ede51e17 ---> 2b643ce8b3cfStep 3/6 : RUN go get github.com/gin-gonic/gin ---> Running in de5e9adb7c10Removing intermediate container de5e9adb7c10 ---> ff970f45de1eStep 4/6 : COPY src src ---> 6b79fef06e45Step 5/6 : RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main src/main.go ---> Running in 6d4ef8c0b580Removing intermediate container 6d4ef8c0b580 ---> 59678a3ab4d8Step 6/6 : CMD ["./main"] ---> Running in a5cea54f2ccbRemoving intermediate container a5cea54f2ccb ---> a253cfcddd6aSuccessfully built a253cfcddd6aSuccessfully 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 imagesREPOSITORY TAG IMAGE ID CREATED SIZEzev/test 1.0.0 a253cfcddd6a 4 minutes ago 857MB
Image has a size of 857MB, the interior contains the entire Golang environment, such a large file in the transmission is absolutely a disaster, next we use multi-stage to build a relatively small image.
Multi-stage Build image
FROM golang:1.10.3 as builderWORKDIR /go/src/testRUN go get github.com/gin-gonic/ginCOPY src srcRUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main src/main.goFROM alpine:3.8WORKDIR /rootCOPY --from=builder /go/src/test/main .CMD ["./main"]
$ docker build-t zev/test:1.0.1. Sending build context to Docker daemon 17.41kBStep 1/9: from golang:1.10.3 as builder---> d0e7a 411e3dastep 2/9: Workdir/go/src/test---> Using cache---> 2b643ce8b3cfstep 3/9: RUN go get github.com/gin-gonic/ Gin---> Using cache---> ff970f45de1estep 4/9: COPY src src---> Using cache---> 6b79fef06e45step 5/9: RUN Cgo_enabled=0 goos=linux Go build-a-installsuffix cgo-o main src/main.go---> Using cache---> 59678a3ab4d8step 6/9: from alpine:3.8---> 11cd0b38bc3cstep 7/9: Workdir/root---> Running in 1640c71479d6removing Intermediate Co Ntainer 1640c71479d6---> ec68dc839562step 8/9: COPY--from=builder/go/src/test/main. ---> 5bb444c91affstep 9/9: CMD ["./main"]---> Running in a80305feba6eremoving Intermediate container a80305feba6e ---> 5923597f59c2successfully built 5923597F59C2SUCCessfully 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 imagesREPOSITORY TAG IMAGE ID CREATED SIZEzev/test 1.0.1 5923597f59c2 2 minutes ago 19.8MB
Multi-stage builds reduce images by 40 times times, and 19.8M size works well in both test and production environments, but is it over?
Of course not, our goal is to make the image smaller, see our operation below.
Smaller image build-up
FROM golang:1.10.3 as builderRUN 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/localRUN 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/upxWORKDIR /go/src/testRUN go get github.com/gin-gonic/ginCOPY src srcRUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main src/main.goRUN strip --strip-unneeded mainRUN upx mainFROM alpine:3.8WORKDIR /rootCOPY --from=builder /go/src/test/main .CMD ["./main"]
$ docker build-t zev/test:1.0.2. Sending build context to Docker daemon 17.92kBStep 1/14:from golang:1.10.3 as builder---> D0e7 A411e3dastep 2/14:run apt-get update && apt-get install-y xz-utils && rm-rf/var/lib/apt/lists/*- -Running in 65772cb8fdabign:1 Http://deb.debian.org/debian stretch inreleaseget: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 omitted here Copyright (C) 1996-2018upx 3.95 Markus Oberhumer, Laszlo Molnar & John Reiser 26th 2 018 File size Ratio Format Name ------------------------------------------------9848136, 2945384 29.91% linux/amd64 mainpacked 1 file. Removing intermediate container d802406ee44a---> 0c29f4b2272dstep 11/14:from alpine:3.8---> 11cd0b38bc3cstep 12/ 14:workdir/root---> Using cache---> Ec68dc839562step 13/14:copy--from=builder/go/src/test/main. ---> A2c265cc9affstep 14/14:cmd ["./main"]---> Running in 7e350a4620eeremoving Intermediate container 7e350a4620e E---> a4d7753c8112successfully built a4d7753c8112successfully 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 imagesREPOSITORY TAG IMAGE ID CREATED SIZEzev/test 1.0.2 a4d7753c8112 4 minutes ago 7.36MB
OK very beautiful, until now we have seen the size of the image has shrunk to 7.36MB, which is very small, converted to our program only 2.95M.
Let's take a look at the panorama comparison:
$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEzev/test 1.0.2 a4d7753c8112 6 minutes ago 7.36MBzev/test 1.0.1 5923597f59c2 About an hour ago 19.8MBzev/test 1.0.0 a253cfcddd6a About an hour ago 857MBgolang 1.10.3 d0e7a411e3da 6 weeks ago 794MBalpine 3.8 11cd0b38bc3c 8 weeks ago 4.41MB
So how to do it, the principle is very simple, because the size of the Alpine has been fixed, can make image smaller starting point can only be executable file, using UPX Shell technology can compress the main executable, you can reduce the main volume of 50%-70%.
tip:upx (The Ultimate Packer for executables) is a free and open source executable program file Shell that supports many different operating systems in the file format. For more information about UPX you can click here, or click here to find out more.
Summarize:
Okay, that's it. This article first shows a single-stage build image, and a ~857mb image, and then builds a multi-stage image containing only the executable ~19.8MB, finally we use multi-stage +UPX compression to reduce our image to ~7.36MB, Such a mirror in both the test environment and production environment, will certainly be able to greatly improve our productivity.