Develop with Docker-Use multi-stage to build images

Multi-stage build is a new feature and requires Docker 17.05 or higher daemon and client. For those struggling to optimize Dockerfiles and make them easy to read and maintain, multi-stage builds are very useful.

Before the multi-stage build

One of the most challenging things when building an image is to reduce the image size. Each instruction in the Dockerfile will add a layer to the image. Before going to the next layer, you need to remember to remove all unnecessary artifacts. To write a truly efficient Dockerfile, it is traditionally necessary to use shell techniques and other logic to keep the layers as small as possible, and to ensure that each layer has the artifacts it needs from the previous layer, and nothing else.

In fact, it is very common to have a Dockerfile for the development environment (contains all the content needed to build the application), and a streamlined Dockerfile for the production environment (contains only the application and the content required to run the application) at the same time . This is called "builder mode". Maintaining two Dockerfiles is not ideal.

Here's an example Dockerfile.buildfile and builders meet the above modes Dockerfile:

Dockerfile.build

FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
COPY app.go .
RUN go get -d -v golang.org/x/net/html \
  && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

Note that this example also uses the operator Bash &&two RUNartificial compressed together in order to avoid creating additional layers in the mirror. This is prone to failure and difficult to maintain. For example, it is easy to insert another command and forget to use the \character to continue the line.

Dockerfile

FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY app .
CMD ["./app"]  

build.sh

#!/bin/sh
echo Building alexellis2/href-counter:build

docker build --build-arg https_proxy=$https_proxy --build-arg http_proxy=$http_proxy \  
    -t alexellis2/href-counter:build . -f Dockerfile.build

docker container create --name extract alexellis2/href-counter:build  
docker container cp extract:/go/src/github.com/alexellis/href-counter/app ./app  
docker container rm -f extract

echo Building alexellis2/href-counter:latest

docker build --no-cache -t alexellis2/href-counter:latest .
rm ./app

When you run the build.shscript, it needs to build the first image, create a container from which to copy the work, then build a second mirror. The two mirrors take up space on your system, and still there on your local disk appworkpiece.

Multi-stage construction greatly simplifies this situation!

Use multi-stage build

For multi-stage build, you can use multiple Dockerfile in the FROMstatement. Each FROMinstruction can use a different group of mirror, and they are beginning to build a new stage. You can selectively copy artifacts from one stage to another, discarding everything you don't want in the final image. To illustrate how this works, let's adjust the Dockerfile in the previous section using a multi-stage build.

Dockerfile

FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html  
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]  

You only need a Dockerfile. You also don't need a separate build script. Just run docker build.

$ docker build -t alexellis2/href-counter:latest .

The end result is the same tiny production mirror as before, and significantly reduced complexity. You don't need to create any intermediate mirrors, and you don't need to extract any artifacts to the local system.

How does it work? The second FROMinstruction is used alpine:latestthe mirror as a basis to begin a new build phase. COPY --from=0Only the build artifacts from the previous stage are copied to this new stage. The Go SDK and any intermediate artifacts will be left behind and will not be saved in the final image.

Name the build phase

By default, the phases are not named, and they can be referenced by their integers. FROMThe first integer of the instruction starts from 0. However, you can add one AS <NAME>to the FROMdirective named stage. The following examples and by naming stage COPYusing an improved example of the name of the previous instruction. This means that even if the instructions in the Dockerfile are reordered later, COPYthey will not be broken.

FROM golang:1.7.3 AS builder
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html  
COPY app.go    .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]  

Stop at a specific build stage

When building the image, it is not necessary to build the entire Dockerfile including each stage. You can specify the target build phase. The following command assumes that you are using before Dockerfile, but called builderstop phase:

$ docker build --target builder -t alexellis2/href-counter:latest .

A few scenarios where this can be very powerful are:

  • Debug a specific build phase
  • All use a tool or debugging symbols enabled 调试(debug)phase and a lean 生产(production)phase
  • Use a 测试(testing)stage where your application will be filled with test data, but when building a product, use a different stage that uses real data.

docker-multi-stage

Use external mirrors as "stages"

When using a multi-stage build, you are not restricted to copying from previously created stages in the Dockerfile. You can use the COPY --frominstructions from a separate copy of the image can be used are available on the local image name, the local registry, or Docker label or ID. The Docker client pulls the image and copies artifacts from it when necessary. The syntax is:

COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf

Treat the previous stage as the new stage

In use FROMinstruction, you can refer to the contents of the previous stage. E.g:

FROM alpine:latest as builder
RUN apk --no-cache add build-base

FROM builder as build1
COPY source1.cpp source.cpp
RUN g++ -o /binary source.cpp

FROM builder as build2
COPY source2.cpp source.cpp
RUN g++ -o /binary source.cpp

Author: Docker's official website
translator: Technical Zemin
Publisher: Technical Verses
links: English text

Public Number: Technical Translation Station

Guess you like

Origin blog.csdn.net/weixin_47498376/article/details/107140149