mirror of https://github.com/docker/docs.git
Merge pull request #17267 from dvdksn/fresh/multistage
build: refresh text and style for multi-stage desc
This commit is contained in:
commit
f00ce9e9be
|
@ -14,26 +14,31 @@ Dockerfiles while keeping them easy to read and maintain.
|
|||
>
|
||||
> Special thanks to [Alex Ellis](https://twitter.com/alexellisuk){:target="blank" rel="noopener" class=""}
|
||||
> for granting permission to use his blog post [Builder pattern vs. Multi-stage builds in Docker](https://blog.alexellis.io/mutli-stage-docker-builds/){:target="blank" rel="noopener" class=""}
|
||||
> as the basis of the examples below.
|
||||
> as the basis of the examples on this page.
|
||||
|
||||
## Before multi-stage builds
|
||||
|
||||
One of the most challenging things about building images is keeping the image
|
||||
size down. Each `RUN`, `COPY`, and `ADD` instruction in the Dockerfile adds a layer to the image, and you
|
||||
need to remember to clean up any artifacts you don't need before moving on to
|
||||
the next layer. To write a really efficient Dockerfile, you have traditionally
|
||||
needed to employ shell tricks 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.
|
||||
One problem you may face as you build and publish images, is that the size of
|
||||
those images can sometimes grow quite large. Traditionally, before multi-stage
|
||||
builds were a thing, keeping the size of images down would require you to
|
||||
manually clean up resources from the image, so as to keep it small.
|
||||
|
||||
It was actually very common to have one Dockerfile to use for development (which
|
||||
contained everything needed to build your application), and a slimmed-down one
|
||||
to use for production, which only contained your application and exactly what
|
||||
was needed to run it. This has been referred to as the "builder
|
||||
pattern". Maintaining two Dockerfiles is not ideal.
|
||||
In the past, it was common practice to have one Dockerfile for development,
|
||||
and another, slimmed-down one to use for production.
|
||||
The development version contained everything needed to build your application.
|
||||
The production version only contained your application
|
||||
and the dependencies needed to run it.
|
||||
|
||||
Here's an example of a `build.Dockerfile` and `Dockerfile` which adhere to the
|
||||
builder pattern above:
|
||||
To write a truly efficient Dockerfile, you had to come up with shell tricks and
|
||||
arcane solutions to keep the layers as small as possible.
|
||||
All to ensure that each layer contained only the artifacts it needed,
|
||||
and nothing else.
|
||||
|
||||
This has been referred to as the _builder pattern_.
|
||||
The following examples show two Dockerfiles that adhere to this pattern:
|
||||
|
||||
- `build.Dockerfile`, for development builds
|
||||
- `Dockerfile`, for slimmed-down production builds
|
||||
|
||||
**`build.Dockerfile`**:
|
||||
|
||||
|
@ -46,10 +51,11 @@ RUN go get -d -v golang.org/x/net/html \
|
|||
&& CGO_ENABLED=0 go build -a -installsuffix cgo -o app .
|
||||
```
|
||||
|
||||
Notice that this example also artificially compresses two `RUN` commands together
|
||||
using the Bash `&&` operator, to avoid creating an additional layer in the image.
|
||||
This is failure-prone and hard to maintain. It's easy to insert another command
|
||||
and forget to continue the line using the `\` character, for example.
|
||||
Notice how this example artificially compresses two `RUN` commands together
|
||||
using the Bash `&&` operator. This is done to avoid creating an additional
|
||||
layer in the image. Writing Dockerfiles like this is failure-prone and hard to
|
||||
maintain. It's easy to insert another command and forget to continue the line
|
||||
using the `\` character, for example.
|
||||
|
||||
**`Dockerfile`**:
|
||||
|
||||
|
@ -62,7 +68,11 @@ COPY app ./
|
|||
CMD ["./app"]
|
||||
```
|
||||
|
||||
**`build.sh`**:
|
||||
The following example is a utility script that:
|
||||
|
||||
1. Builds the first image.
|
||||
2. Creates a container from it to copy the artifact out.
|
||||
3. Builds the second image.
|
||||
|
||||
```bash
|
||||
#!/bin/sh
|
||||
|
@ -78,12 +88,10 @@ docker build --no-cache -t alexellis2/href-counter:latest .
|
|||
rm ./app
|
||||
```
|
||||
|
||||
When you run the `build.sh` script, it needs to build the first image, create
|
||||
a container from it to copy the artifact out, then build the second
|
||||
image. Both images take up room on your system and you still have the `app`
|
||||
Both images take up room on your system and you still end up with the `app`
|
||||
artifact on your local disk as well.
|
||||
|
||||
Multi-stage builds vastly simplify this situation!
|
||||
Multi-stage builds simplifies this situation!
|
||||
|
||||
## Use multi-stage builds
|
||||
|
||||
|
@ -91,7 +99,7 @@ With multi-stage builds, you use multiple `FROM` statements in your Dockerfile.
|
|||
Each `FROM` instruction can use a different base, and each of them begins a new
|
||||
stage of the build. You can selectively copy artifacts from one stage to
|
||||
another, leaving behind everything you don't want in the final image. To show
|
||||
how this works, let's adapt the `Dockerfile` from the previous section to use
|
||||
how this works, you can adapt the `Dockerfile` from the previous section to use
|
||||
multi-stage builds.
|
||||
|
||||
```dockerfile
|
||||
|
@ -110,8 +118,9 @@ COPY --from=0 /go/src/github.com/alexellis/href-counter/app ./
|
|||
CMD ["./app"]
|
||||
```
|
||||
|
||||
You only need the single Dockerfile. You don't need a separate build script,
|
||||
either. Just run `docker build`.
|
||||
You only need the single Dockerfile.
|
||||
No need for a separate build script.
|
||||
Just run `docker build`.
|
||||
|
||||
```console
|
||||
$ docker build -t alexellis2/href-counter:latest .
|
||||
|
@ -128,7 +137,7 @@ intermediate artifacts are left behind, and not saved in the final image.
|
|||
|
||||
## Name your build stages
|
||||
|
||||
By default, the stages are not named, and you refer to them by their integer
|
||||
By default, the stages aren't named, and you refer to them by their integer
|
||||
number, starting with 0 for the first `FROM` instruction. However, you can
|
||||
name your stages, by adding an `AS <NAME>` to the `FROM` instruction. This
|
||||
example improves the previous one by naming the stages and using the name in
|
||||
|
@ -162,7 +171,7 @@ the stage named `builder`:
|
|||
$ docker build --target builder -t alexellis2/href-counter:latest .
|
||||
```
|
||||
|
||||
A few scenarios where this might be very powerful are:
|
||||
A few scenarios where this might be useful are:
|
||||
|
||||
- Debugging a specific build stage
|
||||
- Using a `debug` stage with all debugging symbols or tools enabled, and a
|
||||
|
@ -172,7 +181,7 @@ A few scenarios where this might be very powerful are:
|
|||
|
||||
## Use an external image as a "stage"
|
||||
|
||||
When using multi-stage builds, you are not limited to copying from stages you
|
||||
When using multi-stage builds, you aren't limited to copying from stages you
|
||||
created earlier in your Dockerfile. You can use the `COPY --from` instruction to
|
||||
copy from a separate image, either using the local image name, a tag available
|
||||
locally or on a Docker registry, or a tag ID. The Docker client pulls the image
|
||||
|
@ -202,10 +211,6 @@ COPY source2.cpp source.cpp
|
|||
RUN g++ -o /binary source.cpp
|
||||
```
|
||||
|
||||
## Version compatibility
|
||||
|
||||
Multi-stage build syntax was introduced in Docker Engine 17.05.
|
||||
|
||||
## Differences between legacy builder and BuildKit
|
||||
|
||||
The legacy Docker Engine builder processes all stages of a Dockerfile leading
|
||||
|
@ -279,5 +284,5 @@ Removing intermediate container bbc025b93175
|
|||
Successfully built 09fc3770a9c4
|
||||
```
|
||||
|
||||
`stage1` gets executed when BuildKit is disabled, even if `stage2` does not
|
||||
depend on it.
|
||||
The legacy builder processes `stage1`,
|
||||
even if `stage2` doesn't depend on it.
|
||||
|
|
Loading…
Reference in New Issue