Catalog
Multistage Construction Mirror
Using UPX to build smaller Docker images
They will use the following mirrors to build new mirrors:
$ 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
Single-stage construction
Dockerfile
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"]
Build
$ 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
RUN
$ 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
Images
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE zev/test 1.0.0 a253cfcddd6a 4 minutes ago 857MB
Image's size is 857MB, which includes the whole golang environment. Such a large file is absolutely a disaster in transmission. Next, we use multi-stage to build a relatively small image.
Multistage Construction Mirror
Each layer of the Docker image records only file changes. When the container starts up, Docker calculates each layer of the image and finally generates a file system, which is called joint mounting.
Docker's layers are relevant. In the process of joint mounting, the system needs to know on what basis to add new files. This requires a Docker image to have only one starting layer and only one root. Therefore, only one FROM instruction is allowed in the Docker Docker Docker Dockerfile. Because multiple FROM instructions can cause multiple roots, it is impossible to implement.
Docker 17.05 later allows Dockerfile to support multiple FROM instructions. Multiple FROM instructions are not intended to generate multiple layers. The final image generated is still based on the last FROM, and the previous FROM will be discarded. Each FROM instruction is a construction phase, and multiple FROM instructions are multi-stage construction. Although the final image generated can only be the result of the last phase, the files in the pre-stage can be copied to In the later stage, this is the greatest significance of multi-stage construction.
The biggest usage scenario for multi-stage construction is to separate the compilation environment from the runtime environment:
DockerFile
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"]
Build
$ 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
RUN
$ 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
Images
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE zev/test 1.0.1 5923597f59c2 2 minutes ago 19.8MB
Multistage builds have reduced images by 40 times, and 19.8M size works well in both test and production environments, but is that over?
Of course not. Our goal is to make the image smaller. Let's see what we do next.
Using UPX to build smaller Docker images
Dockerfile
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"]
Build
$ 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] .....Omit here 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
Run
$ 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
Images
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE zev/test 1.0.2 a4d7753c8112 4 minutes ago 7.36MB
Now we have seen that the size of image has been reduced to 7.36MB, which is very small. In other words, our program is only 2.95M.
The size of alpine has been fixed. The starting point to make image smaller is only executable files. The main executable program can be compressed by using UPX shell technology, and the main volume can be reduced by 50%-70%.