Skip to content

Commit 686ee98

Browse files
authored
Merge pull request #17051 from dvdksn/build/guide
build: add "Build with Docker" guide
2 parents 51569a8 + da6586c commit 686ee98

21 files changed

+14119
-0
lines changed

_config.yml

+4
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ distribution_version: "2.7"
5353
compose_switch_version: "1.0.4"
5454
buildkit_version: "0.10.5"
5555

56+
# Strings for use in doc examples, e.g. runtime versions.
57+
example_go_version: "1.20"
58+
example_golangci_lint_version: "v1.52"
59+
5660
# Options for displaying minimum API version requirements in the reference pages.
5761
#
5862
# The reference pages show badges for commands and options (flags) that require

_data/toc.yaml

+24
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,30 @@ guides:
155155
title: Security best practices
156156
- path: /develop/remote-development/
157157
title: Remote development
158+
159+
- sectiontitle: Build with Docker
160+
section:
161+
- path: /build/guide/
162+
title: Overview
163+
- path: /build/guide/intro/
164+
title: 1. Introduction
165+
- path: /build/guide/layers/
166+
title: 2. Layers
167+
- path: /build/guide/multi-stage/
168+
title: 3. Multi-stage
169+
- path: /build/guide/mounts/
170+
title: 4. Mounts
171+
- path: /build/guide/build-args/
172+
title: 5. Build arguments
173+
- path: /build/guide/export/
174+
title: 6. Export binaries
175+
- path: /build/guide/test/
176+
title: 7. Test
177+
- path: /build/guide/multi-platform/
178+
title: 8. Multi-platform
179+
- path: /build/guide/next-steps/
180+
title: Next steps
181+
158182
- sectiontitle: Deploy your app to the cloud
159183
section:
160184
- path: /cloud/aci-integration/

build/guide/build-args.md

+156
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
---
2+
title: Build arguments
3+
description: Introduction to configurable builds, using build args
4+
keywords: >
5+
build, buildkit, buildx, guide, tutorial, build arguments, arg
6+
---
7+
8+
{% include_relative nav.html selected="5" %}
9+
10+
Build arguments is a great way to add flexibility to your builds. You can pass
11+
build arguments at build-time, and you can set a default value that the builder
12+
uses as a fallback.
13+
14+
## Change runtime versions
15+
16+
A practical use case for build arguments is to specify runtime versions for
17+
build stages. Your image uses the `golang:{{site.example_go_version}}-alpine`
18+
image as a base image.
19+
But what if someone wanted to use a different version of Go for building the
20+
application? They could update the version number inside the Dockerfile, but
21+
that’s inconvenient, it makes switching between versions more tedious than it
22+
has to be. Build arguments make life easier:
23+
24+
```diff
25+
# syntax=docker/dockerfile:1
26+
- FROM golang:{{site.example_go_version}}-alpine AS base
27+
+ ARG GO_VERSION={{site.example_go_version}}
28+
+ FROM golang:${GO_VERSION}-alpine AS base
29+
WORKDIR /src
30+
RUN --mount=type=cache,target=/go/pkg/mod/ \
31+
--mount=type=bind,source=go.sum,target=go.sum \
32+
--mount=type=bind,source=go.mod,target=go.mod \
33+
go mod download -x
34+
35+
FROM base AS build-client
36+
RUN --mount=type=cache,target=/go/pkg/mod/ \
37+
--mount=type=bind,target=. \
38+
go build -o /bin/client ./cmd/client
39+
40+
FROM base AS build-server
41+
RUN --mount=type=cache,target=/go/pkg/mod/ \
42+
--mount=type=bind,target=. \
43+
go build -o /bin/server ./cmd/server
44+
45+
FROM scratch AS client
46+
COPY --from=build /bin/client /bin/
47+
ENTRYPOINT [ "/bin/client" ]
48+
49+
FROM scratch AS server
50+
COPY --from=build /bin/server /bin/
51+
ENTRYPOINT [ "/bin/server" ]
52+
```
53+
54+
The `ARG` keyword is interpolated in the image name in the `FROM` instruction.
55+
The default value of the `GO_VERSION` build argument is set to `{{site.example_go_version}}`.
56+
If the build doesn't receive a `GO_VERSION` build argument, the `FROM` instruction
57+
resolves to `golang:{{site.example_go_version}}-alpine`.
58+
59+
Try setting a different version of Go to use for building, using the
60+
`--build-arg` flag for the build command:
61+
62+
```console
63+
$ docker build --build-arg="GO_VERSION=1.19" .
64+
```
65+
66+
Running this command results in a build using the `golang:1.19-alpine` image.
67+
68+
## Inject values
69+
70+
You can also make use of build arguments to modify values in the source code of
71+
your program, at build time. This is useful for dynamically injecting
72+
information, avoiding hard-coded values. With Go, consuming external values at
73+
build time is done using linker flags, or `-ldflags`.
74+
75+
The server part of the application contains a conditional statement to print the
76+
app version, if a version is specified:
77+
78+
```go
79+
// cmd/server/main.go
80+
var version string
81+
82+
func main() {
83+
if version != "" {
84+
log.Printf("Version: %s", version)
85+
}
86+
```
87+
88+
You could declare the version string value directly in the code. But, updating
89+
the version to line up with the release version of the application would require
90+
updating the code ahead of every release. That would be both tedious and
91+
error-prone. A better solution is to pass the version string as a build
92+
argument, and inject the build argument into the code.
93+
94+
The following example adds an `APP_VERSION` build argument to the `build-server`
95+
stage. The Go compiler uses the value of the build argument to set the value of
96+
a variable in the code.
97+
98+
```diff
99+
# syntax=docker/dockerfile:1
100+
ARG GO_VERSION={{site.example_go_version}}
101+
FROM golang:${GO_VERSION}-alpine AS base
102+
WORKDIR /src
103+
RUN --mount=type=cache,target=/go/pkg/mod/ \
104+
--mount=type=bind,source=go.sum,target=go.sum \
105+
--mount=type=bind,source=go.mod,target=go.mod \
106+
go mod download -x
107+
108+
FROM base AS build-client
109+
RUN --mount=type=cache,target=/go/pkg/mod/ \
110+
--mount=type=bind,target=. \
111+
go build -o /bin/client ./cmd/client
112+
113+
FROM base AS build-server
114+
+ ARG APP_VERSION="v0.0.0+unknown"
115+
RUN --mount=type=cache,target=/go/pkg/mod/ \
116+
--mount=type=bind,target=. \
117+
- go build -o /bin/server ./cmd/server
118+
+ go build -ldflags "-X main.version=$APP_VERSION" -o /bin/server ./cmd/server
119+
120+
FROM scratch AS client
121+
COPY --from=build-client /bin/client /bin/
122+
ENTRYPOINT [ "/bin/client" ]
123+
124+
FROM scratch AS server
125+
COPY --from=build-server /bin/server /bin/
126+
ENTRYPOINT [ "/bin/server" ]
127+
```
128+
129+
Now the version of the server when building the binary, without having to update
130+
the source code. To verify this, you can the `server` target and start a
131+
container with `docker run`. The server outputs `v0.0.1` as the version on
132+
startup.
133+
134+
```console
135+
$ docker build --target=server --build-arg="APP_VERSION=v0.0.1" --tag=buildme-server .
136+
$ docker run buildme-server
137+
2023/04/06 08:54:27 Version: v0.0.1
138+
2023/04/06 08:54:27 Starting server...
139+
2023/04/06 08:54:27 Listening on HTTP port 3000
140+
```
141+
142+
## Summary
143+
144+
This section showed how you can use build arguments to make builds more
145+
configurable, and inject values at build-time.
146+
147+
Related information:
148+
149+
- [`ARG` Dockerfile reference](../../engine/reference/builder.md#arg)
150+
151+
## Next steps
152+
153+
The next section of this guide shows how you can use Docker builds to create not
154+
only container images, but executable binaries as well.
155+
156+
[Export binaries](export.md){: .button .primary-btn }

build/guide/export.md

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
---
2+
title: Export binaries
3+
description: Using Docker builds to create and export executable binaries
4+
keywords: >
5+
build, buildkit, buildx, guide, tutorial, build arguments, arg
6+
---
7+
8+
{% include_relative nav.html selected="6" %}
9+
10+
Did you know that you can use Docker to build your application to standalone
11+
binaries? Sometimes, you don’t want to package and distribute your application
12+
as a Docker image. Use Docker to build your application, and use exporters to
13+
save the output to disk.
14+
15+
The default output format for `docker build` is a container image. That image is
16+
automatically loaded to your local image store, where you can run a container
17+
from that image, or push it to a registry. Under the hood, this uses the default
18+
exporter, called the `docker` exporter.
19+
20+
To export your build results as files instead, you can use the `local` exporter.
21+
The `local` exporter saves the filesystem of the build container to the
22+
specified directory on the host machine.
23+
24+
## Export binaries
25+
26+
To use the `local` exporter, pass the `--output` option to the `docker build`
27+
command. The `--output` flag takes one argument: the destination on the host
28+
machine where you want to save the files.
29+
30+
The following commands exports the files from of the `server` target to the
31+
current working directory on the host filesystem:
32+
33+
```console
34+
$ docker build --output=. --target=server .
35+
```
36+
37+
Running this command creates a binary at `./bin/server`. It’s created under the
38+
`bin/` directory because that’s where the file was located inside the build
39+
container.
40+
41+
```console
42+
$ ls -l ./bin
43+
total 14576
44+
-rwxr-xr-x 1 user user 7459368 Apr 6 09:27 server
45+
```
46+
47+
If you want to create a build that exports both binaries, you can create another
48+
build stage in the Dockerfile that copies both of the binaries from each build
49+
stage:
50+
51+
```diff
52+
# syntax=docker/dockerfile:1
53+
ARG GO_VERSION={{site.example_go_version}}
54+
FROM golang:${GO_VERSION}-alpine AS base
55+
WORKDIR /src
56+
RUN --mount=type=cache,target=/go/pkg/mod/ \
57+
--mount=type=bind,source=go.sum,target=go.sum \
58+
--mount=type=bind,source=go.mod,target=go.mod \
59+
go mod download -x
60+
61+
FROM base as build-client
62+
RUN --mount=type=cache,target=/go/pkg/mod/ \
63+
--mount=type=bind,target=. \
64+
go build -o /bin/client ./cmd/client
65+
66+
FROM base as build-server
67+
ARG APP_VERSION="0.0.0+unknown"
68+
RUN --mount=type=cache,target=/go/pkg/mod/ \
69+
--mount=type=bind,target=. \
70+
go build -ldflags "-X main.version=$APP_VERSION" -o /bin/server ./cmd/server
71+
72+
FROM scratch AS client
73+
COPY --from=build-client /bin/client /bin/
74+
ENTRYPOINT [ "/bin/client" ]
75+
76+
FROM scratch AS server
77+
COPY --from=build-server /bin/server /bin/
78+
ENTRYPOINT [ "/bin/server" ]
79+
+
80+
+ FROM scratch AS binaries
81+
+ COPY --from=build-client /bin/client /
82+
+ COPY --from=build-server /bin/server /
83+
```
84+
85+
Now you can build the `binaries` target using the `--output` option to export
86+
both the client and server binaries.
87+
88+
```console
89+
$ docker build --output=bin --target=binaries .
90+
$ ls -l ./bin
91+
total 29392
92+
-rwxr-xr-x 1 user user 7581933 Apr 6 09:33 client
93+
-rwxr-xr-x 1 user user 7459368 Apr 6 09:33 server
94+
```
95+
96+
## Summary
97+
98+
This section has demonstrated how you can use Docker to build and export
99+
standalone binaries. These binaries can be distributed freely, and don’t require
100+
a container runtime like the Docker daemon.
101+
102+
The binaries you've generated so far are Linux binaries. That's because the
103+
build environment is Linux. If your host OS is Linux, you can run these files.
104+
Building binaries that work on Mac or Windows machines requires cross-compilation.
105+
This is explored later on in this guide.
106+
107+
Related information:
108+
109+
- [`docker build --output` CLI reference](../../engine/reference/commandline/build.md#output)
110+
- [Build exporters](../exporters/index.md)
111+
112+
## Next steps
113+
114+
The next topic of this guide is testing: how you can use Docker to run
115+
application tests.
116+
117+
[Test](test.md){: .button .primary-btn }

build/guide/images/cache-bust.png

160 KB
Loading
171 KB
Loading

build/guide/images/emulation.png

182 KB
Loading

0 commit comments

Comments
 (0)