docs/content/guides/compose-bake/index.md

403 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Building Compose projects with Bake
description: Learn how to build Docker Compose projects with Docker Buildx Bake
summary: |
This guide demonstrates how you can use Bake to build production-grade images for Docker Compose projects.
languages: []
tags: [devops]
params:
time: 20 minutes
---
This guide explores how you can use Bake to build images for Docker Compose
projects with multiple services.
[Docker Buildx Bake](/manuals/build/bake/_index.md) is a build orchestration
tool that enables declarative configuration for your builds, much like Docker
Compose does for defining runtime stacks. For projects where Docker Compose is
used to spin up services for local development, Bake offers a way of seamlessly
extending the project with a production-ready build configuration.
## Prerequisites
This guide assumes that you're familiar with
- Docker Compose
- [Multi-stage builds](/manuals/build/building/multi-stage.md)
- [Multi-platform builds](/manuals/build/building/multi-platform.md)
## Orientation
This guide will use the
[dvdksn/example-voting-app](https://github.com/dvdksn/example-voting-app)
repository as an example of a monorepo using Docker Compose that can be
extended with Bake.
```console
$ git clone https://github.com/dvdksn/example-voting-app.git
$ cd example-voting-app
```
This repository uses Docker Compose to define the runtime configurations for
running the application, in the `compose.yaml` file. This app consists of the
following services:
| Service | Description |
| -------- | ---------------------------------------------------------------------- |
| `vote` | A front-end web app in Python which lets you vote between two options. |
| `result` | A Node.js web app which shows the results of the voting in real time. |
| `worker` | A .NET worker which consumes votes and stores them in the database. |
| `db` | A Postgres database backed by a Docker volume. |
| `redis` | A Redis instance which collects new votes. |
| `seed` | A utility container that seeds the database with mock data. |
The `vote`, `result`, and `worker` services are built from code in this
repository, whereas `db` and `redis` use pre-existing Postgres and Redis images
from Docker Hub. The `seed` service is a utility that invokes requests against
the front-end service to populate the database, for testing purposes.
## Build with Compose
When you spin up a Docker Compose project, any services that define the `build`
property are automatically built before the service is started. Here's the
build configuration for the `vote` service in the example repository:
```yaml {title="compose.yaml"}
services:
vote:
build:
context: ./vote # Build context
target: dev # Dockerfile stage
```
The `vote`, `result`, and `worker` services all have a build configuration
specified. Running `docker compose up` will trigger a build of these services.
Did you know that you can also use Compose just to build the service images?
The `docker compose build` command lets you invoke a build using the build
configuration specified in the Compose file. For example, to build the `vote`
service with this configuration, run:
```console
$ docker compose build vote
```
Omit the service name to build all services at once:
```console
$ docker compose build
```
The `docker compose build` command is useful when you only need to build images
without running services.
The Compose file format supports a number of properties for defining your
build's configuration. For example, to specify the tag name for the images, set
the `image` property on the service.
```yaml
services:
vote:
image: username/vote
build:
context: ./vote
target: dev
#...
result:
image: username/result
build:
context: ./result
#...
worker:
image: username/worker
build:
context: ./worker
#...
```
Running `docker compose build` creates three service images with fully
qualified image names that you can push to Docker Hub.
The `build` property supports a [wide range](/reference/compose-file/build.md)
of options for configuring builds. However, building production-grade images
are often different from images used in local development. To avoid cluttering
your Compose file with build configurations that might not be desirable for
local builds, consider separating the production builds from the local builds
by using Bake to build images for release. This approach separates concerns:
using Compose for local development and Bake for production-ready builds, while
still reusing service definitions and fundamental build configurations.
## Build with Bake
Like Compose, Bake parses the build definition for a project from a
configuration file. Bake supports HashiCorp Configuration Language (HCL), JSON,
and the Docker Compose YAML format. When you use Bake with multiple files, it
will find and merge all of the applicable configuration files into one unified
build configuration. The build options defined in your Compose file are
extended, or in some cases overridden, by options specified in the Bake file.
The following section explores how you can use Bake to extend the build options
defined in your Compose file for production.
### View the build configuration
Bake automatically creates a build configuration from the `build` properties of
your services. Use the `--print` flag for Bake to view the build configuration
for a given Compose file. This flag evaluates the build configuration and
outputs the build definition in JSON format.
```console
$ docker buildx bake --print
```
The JSON-formatted output shows the group that would be executed, and all the
targets of that group. A group is a collection of builds, and a target
represents a single build.
```json
{
"group": {
"default": {
"targets": [
"vote",
"result",
"worker",
"seed"
]
}
},
"target": {
"result": {
"context": "result",
"dockerfile": "Dockerfile",
},
"seed": {
"context": "seed-data",
"dockerfile": "Dockerfile",
},
"vote": {
"context": "vote",
"dockerfile": "Dockerfile",
"target": "dev",
},
"worker": {
"context": "worker",
"dockerfile": "Dockerfile",
}
}
}
```
As you can see, Bake has created a `default` group that includes four targets:
- `seed`
- `vote`
- `result`
- `worker`
This group is created automatically from your Compose file; it includes all of
your services containing a build configuration. To build this group of services
with Bake, run:
```console
$ docker buildx bake
```
### Customize the build group
Start by redefining the default build group that Bake executes. The current
default group includes a `seed` target — a Compose service used solely to
populate the database with mock data. Since this target doesn't produce a
production image, it doesnt need to be included in the build group.
To customize the build configuration that Bake uses, create a new file at the
root of the repository, alongside your `compose.yaml` file, named
`docker-bake.hcl`.
```console
$ touch docker-bake.hcl
```
Open the Bake file and add the following configuration:
```hcl {title=docker-bake.hcl}
group "default" {
targets = ["vote", "result", "worker"]
}
```
Save the file and print your Bake definition again.
```console
$ docker buildx bake --print
```
The JSON output shows that the `default` group only includes the targets you
care about.
```json
{
"group": {
"default": {
"targets": ["vote", "result", "worker"]
}
},
"target": {
"result": {
"context": "result",
"dockerfile": "Dockerfile",
"tags": ["username/result"]
},
"vote": {
"context": "vote",
"dockerfile": "Dockerfile",
"tags": ["username/vote"],
"target": "dev"
},
"worker": {
"context": "worker",
"dockerfile": "Dockerfile",
"tags": ["username/worker"]
}
}
}
```
Here, the build configuration for each target (context, tags, etc.) is picked
up from the `compose.yaml` file. The group is defined by the `docker-bake.hcl`
file.
### Customize targets
The Compose file currently defines the `dev` stage as the build target for the
`vote` service. That's appropriate for the image that you would run in local
development, because the `dev` stage includes additional development
dependencies and configurations. For the production image, however, you'll want
to target the `final` image instead.
To modify the target stage used by the `vote` service, add the following
configuration to the Bake file:
```hcl
target "vote" {
target = "final"
}
```
This overrides the `target` property specified in the Compose file with a
different value when you run the build with Bake. The other build options in
the Compose file (tag, context) remain unmodified. You can verify by inspecting
the build configuration for the `vote` target with `docker buildx bake --print
vote`:
```json
{
"group": {
"default": {
"targets": ["vote"]
}
},
"target": {
"vote": {
"context": "vote",
"dockerfile": "Dockerfile",
"tags": ["username/vote"],
"target": "final"
}
}
}
```
### Additional build features
Production-grade builds often have different characteristics from development
builds. Here are a few examples of things you might want to add for production
images.
Multi-platform
: For local development, you only need to build images for your local platform,
since those images are just going to run on your machine. But for images that
are pushed to a registry, it's often a good idea to build for multiple
platforms, arm64 and amd64 in particular.
Attestations
: [Attestations](/manuals/build/metadata/attestations/_index.md) are manifests
attached to the image that describe how the image was created and what
components it contains. Attaching attestations to your images helps ensure that
your images follow software supply chain best practices.
Annotations
: [Annotations](/manuals/build/metadata/annotations.md) provide descriptive
metadata for images. Use annotations to record arbitrary information and attach
it to your image, which helps consumers and tools understand the origin,
contents, and how to use the image.
> [!TIP]
> Why not just define these additional build options in the Compose file
> directly?
>
> The `build` property in the Compose file format does not support all build
> features. Additionally, some features, like multi-platform builds, can
> drastically increase the time it takes to build a service. For local
> development, you're better off keeping your build step simple and fast,
> saving the bells and whistles for release builds.
To add these properties to the images you build with Bake, update the Bake file
as follows:
```hcl
group "default" {
targets = ["vote", "result", "worker"]
}
target "_common" {
annotations = ["org.opencontainers.image.authors=username"]
platforms = ["linux/amd64", "linux/arm64"]
attest = [
"type=provenance,mode=max",
"type=sbom"
]
}
target "vote" {
inherits = ["_common"]
target = "final"
}
target "result" {
inherits = ["_common"]
}
target "worker" {
inherits = ["_common"]
}
```
This defines a new `_common` target that defines reusable build configuration
for adding multi-platform support, annotations, and attestations to your
images. The reusable target is inherited by the build targets.
With these changes, building the project with Bake produces three sets of
multi-platform images for the `linux/amd64` and `linux/arm64` architectures.
Each image is decorated with an author annotation, and both SBOM and provenance
attestation records.
## Conclusions
The pattern demonstrated in this guide provides a useful approach for managing
production-ready Docker images in projects using Docker Compose. Using Bake
gives you access to all the powerful features of Buildx and BuildKit, and also
helps separate your development and build configuration in a reasonable way.
### Further reading
For more information about how to use Bake, check out these resources:
- [Bake documentation](/manuals/build/bake/_index.md)
- [Building with Bake from a Compose file](/manuals/build/bake/compose-file.md)
- [Bake file reference](/manuals/build/bake/reference.md)
- [Mastering multi-platform builds, testing, and more with Docker Buildx Bake](/guides/bake/index.md)
- [Bake GitHub Action](https://github.com/docker/bake-action)