Skip to content

Commit 0dd4d1f

Browse files
committed
guides: build compose projects with bake
Signed-off-by: David Karlsson <[email protected]>
1 parent 1252967 commit 0dd4d1f

File tree

1 file changed

+396
-0
lines changed

1 file changed

+396
-0
lines changed
Lines changed: 396 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,396 @@
1+
---
2+
title: Building Compose projects with Bake
3+
description: Learn how to build Docker Compose projects with Docker Buildx Bake
4+
summary: |
5+
This guide demonstrates how you can use Bake to build production-grade images for Docker Compose projects.
6+
languages: []
7+
tags: [devops]
8+
params:
9+
time: 20 minutes
10+
---
11+
12+
This guide explores how you can use Bake to build images for Docker Compose
13+
projects with multiple services.
14+
15+
[Docker Buildx Bake](/manuals/build/bake/_index.md) is a build orchestration
16+
tool that enables declarative configuration for your builds, much like Docker
17+
Compose does for defining runtime stacks. For projects where Docker Compose is
18+
used to spin up services for local development, Bake offers a way of seamlessly
19+
extending the project with a production-ready build configuration.
20+
21+
## Prerequisites
22+
23+
This guide assumes that you're familiar with
24+
25+
- Docker Compose
26+
- [Multi-stage builds](/manuals/build/building/multi-stage.md)
27+
- [Multi-platform builds](/manuals/build/building/multi-platform.md)
28+
29+
## Orientation
30+
31+
This guide will use the
32+
[dvdksn/example-voting-app](https://github.com/dvdksn/example-voting-app)
33+
repository as an example of a monorepo using Docker Compose that can be
34+
extended with Bake.
35+
36+
```console
37+
$ git clone https://github.com/dvdksn/example-voting-app.git
38+
$ cd example-voting-app
39+
```
40+
41+
This repository uses Docker Compose to define the runtime configurations for
42+
running the application, in the `compose.yaml` file. This app consists of the
43+
following services:
44+
45+
| Service | Description |
46+
| -------- | ---------------------------------------------------------------------- |
47+
| `vote` | A front-end web app in Python which lets you vote between two options. |
48+
| `result` | A Node.js web app which shows the results of the voting in real time. |
49+
| `worker` | A .NET worker which consumes votes and stores them in the database. |
50+
| `db` | A Postgres database backed by a Docker volume. |
51+
| `redis` | A Redis instance which collects new votes. |
52+
| `seed` | A utility container that seeds the database with mock data. |
53+
54+
The `vote`, `result`, and `worker` services are built from code in this
55+
repository, whereas `db` and `redis` use pre-existing images Postgres and Redis
56+
images from Docker Hub. The `seed` service is a utility that invokes requests
57+
against the front-end service to populate the database, for testing purposes.
58+
59+
## Build with Compose
60+
61+
When you spin up a Docker Compose project, any services that define the `build`
62+
property is automatically built before the service is started. Here's the build
63+
configuration for the `vote` service in the example repository:
64+
65+
```yaml {title="compose.yaml"}
66+
services:
67+
vote:
68+
build:
69+
context: ./vote # Build context
70+
target: dev # Dockerfile stage
71+
```
72+
73+
The `vote`, `result`, and `worker` services all have a build configuration
74+
specified. Running `docker compose up` will trigger a build of these services.
75+
76+
Did you know that you can also use Compose just to build the service images?
77+
The `docker compose build` command lets you invoke a build using the build
78+
configuration specified in the Compose file. For example, to build the `vote`
79+
service with this configuration, run:
80+
81+
```console
82+
$ docker compose build vote
83+
```
84+
85+
Omit the service name to build all services at once:
86+
87+
```console
88+
$ docker compose build
89+
```
90+
91+
The `docker compose build` command is useful when you only need to build images
92+
without running services.
93+
94+
The Compose file format supports a number of properties for defining your
95+
build's configuration. For example, to specify the tag name for the images, set
96+
the `image` property on the service.
97+
98+
```yaml
99+
services:
100+
vote:
101+
image: username/vote
102+
build:
103+
context: ./vote
104+
target: dev
105+
#...
106+
107+
result:
108+
image: username/result
109+
build:
110+
context: ./result
111+
#...
112+
113+
worker:
114+
image: username/worker
115+
build:
116+
context: ./worker
117+
#...
118+
```
119+
120+
Running `docker compose build` creates three service images with fully
121+
qualified image names that you can push to Docker Hub.
122+
123+
The `build` property supports a [wide range](/reference/compose-file/build.md)
124+
of options for configuring builds. However, building production-grade images
125+
are often different from images used in local development. To avoid cluttering
126+
your Compose file with build configurations that might not be desirable for
127+
local builds, consider separating the production builds from the local builds
128+
by using Bake to build images for release. This approach separates concerns:
129+
using Compose for local development and Bake for production-ready builds, while
130+
still reusing service definitions and fundamental build configurations.
131+
132+
## Build with Bake
133+
134+
Like Compose, Bake parses the build definition for a project from a
135+
configuration file. Bake supports HashiCorp Configuration Language (HCL), JSON,
136+
and the Docker Compose YAML format. When you use Bake with multiple files, it
137+
will find and merge all of the applicable configuration files into one unified
138+
build configuration. The build options defined in your Compose file are
139+
extended, or in some cases overridden, by options specified in the Bake file.
140+
141+
The following section explores how you can use Bake to extend the build options
142+
defined in your Compose file for production.
143+
144+
### View the build configuration
145+
146+
Bake automatically creates a build configuration from the `build` properties of
147+
your services. Use the `--print` flag for Bake to view the build configuration
148+
for a given Compose file. This flag evaluates the build configuration and
149+
outputs the build definition in JSON format.
150+
151+
```console
152+
$ docker buildx bake --print
153+
```
154+
155+
The JSON-formatted output shows the group that would be executed, and all the
156+
targets of that group. A group is a collection of builds, and a target
157+
represents a single build.
158+
159+
```json
160+
{
161+
"group": {
162+
"default": {
163+
"targets": ["seed", "vote", "result", "worker"]
164+
}
165+
},
166+
"target": {
167+
"result": {
168+
"context": "result",
169+
"dockerfile": "Dockerfile",
170+
"tags": ["username/result"]
171+
},
172+
"seed": {
173+
"context": "seed-data",
174+
"dockerfile": "Dockerfile"
175+
},
176+
"vote": {
177+
"context": "vote",
178+
"dockerfile": "Dockerfile",
179+
"tags": ["username/vote"],
180+
"target": "dev"
181+
},
182+
"worker": {
183+
"context": "worker",
184+
"dockerfile": "Dockerfile",
185+
"tags": ["username/worker"]
186+
}
187+
}
188+
}
189+
```
190+
191+
As you can see, Bake has created a `default` group that includes four targets:
192+
193+
- `seed`
194+
- `vote`
195+
- `result`
196+
- `worker`
197+
198+
This group is created automatically from your Compose file; it includes all of
199+
your services containing a build configuration. To build this group of services
200+
with Bake, run:
201+
202+
```console
203+
$ docker buildx bake
204+
```
205+
206+
### Customize the build group
207+
208+
Start by redefining the default build group that Bake executes. The current
209+
default group includes a `seed` target — a Compose service used solely to
210+
populate the database with mock data. Since this target doesn't produce a
211+
production image, it doesn’t need to be included in the build group.
212+
213+
To customize the build configuration that Bake uses, create a new file at the
214+
root of the repository, alongside your `compose.yaml` file, named
215+
`docker-bake.hcl`.
216+
217+
Open the Bake file and add the following configuration:
218+
219+
```hcl {title=docker-bake.hcl}
220+
group "default" {
221+
targets = ["vote", "result", "worker"]
222+
}
223+
```
224+
225+
Now, print your Bake definition again.
226+
227+
```console
228+
$ docker buildx bake --print
229+
```
230+
231+
The JSON output shows that the `default` group only includes the targets you
232+
care about.
233+
234+
```json
235+
{
236+
"group": {
237+
"default": {
238+
"targets": ["vote", "result", "worker"]
239+
}
240+
},
241+
"target": {
242+
"result": {
243+
"context": "result",
244+
"dockerfile": "Dockerfile",
245+
"tags": ["username/result"]
246+
},
247+
"vote": {
248+
"context": "vote",
249+
"dockerfile": "Dockerfile",
250+
"tags": ["username/vote"],
251+
"target": "dev"
252+
},
253+
"worker": {
254+
"context": "worker",
255+
"dockerfile": "Dockerfile",
256+
"tags": ["username/worker"]
257+
}
258+
}
259+
}
260+
```
261+
262+
Here, the build configuration for each target (context, tags, etc.) is picked
263+
up from the `compose.yaml` file. The group is defined by the `docker-bake.hcl`
264+
file.
265+
266+
### Customize targets
267+
268+
The Compose file currently defines the `dev` stage as the build target for the
269+
`vote` service. That's appropriate for the image that you would run in local
270+
development, because the `dev` stage includes additional development
271+
dependencies and configurations. For the production image, however, you'll want
272+
to target the `final` image instead.
273+
274+
To modify the target stage used by the `vote` service, add the following
275+
configuration to the Bake file:
276+
277+
```hcl
278+
target "vote" {
279+
target = "final"
280+
}
281+
```
282+
283+
This overrides the `target` property specified in the Compose file with a
284+
different value when you run the build with Bake. The other build options in
285+
the Compose file (tag, context) remain unmodified. You can verify by inspecting
286+
the build configuration for the `vote` target with `docker buildx bake --print
287+
vote`:
288+
289+
```json
290+
{
291+
"group": {
292+
"default": {
293+
"targets": ["vote"]
294+
}
295+
},
296+
"target": {
297+
"vote": {
298+
"context": "vote",
299+
"dockerfile": "Dockerfile",
300+
"tags": ["username/vote"],
301+
"target": "final"
302+
}
303+
}
304+
}
305+
```
306+
307+
### Additional build features
308+
309+
Production-grade builds often have different characteristics from development
310+
builds. Here are a few examples of things you might want to add for production
311+
images.
312+
313+
Multi-platform
314+
: For local development, you only need to build images for your local platform,
315+
since those images are just going to run on your machine. But for images that
316+
are pushed to a registry, it's often a good idea to build for multiple
317+
platforms, arm64 and amd64 in particular.
318+
319+
Attestations
320+
: [Attestations](/manuals/build/metadata/attestations/_index.md) are manifests
321+
attached to the image that describe how the image was created and what
322+
components it contains. Attaching attestations to your images helps ensure that
323+
your images follow software supply chain best practices.
324+
325+
Annotations
326+
: [Annotations](/manuals/build/metadata/annotations.md) provide descriptive
327+
metadata for images. Use annotations to record arbitrary information and attach
328+
it to your image, which helps consumers and tools understand the origin,
329+
contents, and how to use the image.
330+
331+
> [!TIP]
332+
> Why not just define these additional build options in the Compose file
333+
> directly?
334+
>
335+
> The `build` property in the Compose file format does not support all build
336+
> features. Additionally, some features, like multi-platform builds, can
337+
> drastically increase the time it takes to build a service. For local
338+
> development, you're better off keeping your build step simple and fast,
339+
> saving the bells and whistles for release builds.
340+
341+
To add these properties to the images you build with Bake, update the Bake file
342+
as follows:
343+
344+
```hcl
345+
group "default" {
346+
targets = ["vote", "result", "worker"]
347+
}
348+
349+
target "_common" {
350+
annotations = ["org.opencontainers.image.authors=username"]
351+
platforms = ["linux/amd64", "linux/arm64"]
352+
attest = [
353+
"type=provenance,mode=max",
354+
"type=sbom"
355+
]
356+
}
357+
358+
target "vote" {
359+
inherits = ["_common"]
360+
target = "final"
361+
}
362+
363+
target "result" {
364+
inherits = ["_common"]
365+
}
366+
367+
target "worker" {
368+
inherits = ["_common"]
369+
}
370+
```
371+
372+
This defines a new `_common` target that defines reusable build configuration
373+
for adding multi-platform support, annotations, and attestations to your
374+
images. The reusable target is inherited by the build targets.
375+
376+
With these changes, building the project with Bake produces three sets of
377+
multi-platform images for the `linux/amd64` and `linux/arm64` architectures.
378+
Each image is decorated with an author annotation, and both SBOM and provenance
379+
attestation records.
380+
381+
## Conclusions
382+
383+
The pattern demonstrated in this guide provides a useful approach for managing
384+
production-ready Docker images in projects using Docker Compose. Using Bake
385+
gives you access to all the powerful features of Buildx and BuildKit, and also
386+
helps separate your development and build configuration in a reasonable way.
387+
388+
### Further reading
389+
390+
For more information about how to use Bake, check out these resources:
391+
392+
- [Bake documentation](/manuals/build/bake/_index.md)
393+
- [Building with Bake from a Compose file](/manuals/build/bake/compose-file.md)
394+
- [Bake file reference](/manuals/build/bake/reference.md)
395+
- [Mastering multi-platform builds, testing, and more with Docker Buildx Bake](/guides/bake/index.md)
396+
- [Bake GitHub Action](https://github.com/docker/bake-action)

0 commit comments

Comments
 (0)