mirror of https://github.com/docker/docs.git
403 lines
12 KiB
Markdown
403 lines
12 KiB
Markdown
---
|
||
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 doesn’t 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)
|