使用 Bake 构建 Compose 项目

本指南探讨了如何使用 Bake 为包含多个服务的 Docker Compose 项目构建镜像。

Docker Buildx Bake 是一个构建编排工具,它支持对构建进行声明式配置,就像 Docker Compose 用于定义运行时堆栈一样。对于使用 Docker Compose 启动本地开发服务的项目,Bake 提供了一种将生产就绪型构建配置无缝扩展到项目中的方法。

先决条件

本指南假定您熟悉

概览

本指南将使用 dvdksn/example-voting-app 仓库作为使用 Docker Compose 的单体仓库示例,该仓库可以使用 Bake 进行扩展。

$ git clone https://github.com/dvdksn/example-voting-app.git
$ cd example-voting-app

该仓库使用 Docker Compose 在 compose.yaml 文件中定义应用程序的运行时配置。此应用程序包含以下服务:

服务描述
vote一个用 Python 编写的前端 Web 应用,允许您在两个选项之间投票。
result一个 Node.js Web 应用,实时显示投票结果。
worker一个 .NET worker,它消费投票并将其存储在数据库中。
db一个由 Docker 卷支持的 Postgres 数据库。
redis一个收集新投票的 Redis 实例。
seed一个用于向数据库填充模拟数据的实用容器。

voteresultworker 服务是从本仓库中的代码构建的,而 dbredis 使用 Docker Hub 中预先存在的 Postgres 和 Redis 镜像。seed 服务是一个实用程序,用于对前端服务发起请求以填充数据库,用于测试目的。

使用 Compose 构建

当您启动一个 Docker Compose 项目时,任何定义了 build 属性的服务都会在服务启动之前自动构建。以下是示例仓库中 vote 服务的构建配置:

compose.yaml
services:
  vote:
    build:
      context: ./vote # Build context
      target: dev # Dockerfile stage

voteresultworker 服务都指定了构建配置。运行 docker compose up 将触发这些服务的构建。

您知道吗,您也可以只使用 Compose 来构建服务镜像?docker compose build 命令允许您使用 Compose 文件中指定的构建配置来调用构建。例如,要使用此配置构建 vote 服务,请运行:

$ docker compose build vote

省略服务名称以一次性构建所有服务

$ docker compose build

当您只需要构建镜像而不运行服务时,docker compose build 命令非常有用。

Compose 文件格式支持许多属性来定义您的构建配置。例如,要指定镜像的标签名称,请在服务上设置 image 属性。

services:
  vote:
    image: username/vote
    build:
      context: ./vote
      target: dev
    #...

  result:
    image: username/result
    build:
      context: ./result
    #...

  worker:
    image: username/worker
    build:
      context: ./worker
    #...

运行 docker compose build 会创建三个具有完全限定镜像名称的服务镜像,您可以将它们推送到 Docker Hub。

build 属性支持 广泛的选项 用于配置构建。然而,构建生产级镜像通常与用于本地开发的镜像不同。为了避免在 Compose 文件中混入可能不适用于本地构建的构建配置,请考虑使用 Bake 构建用于发布的镜像,从而将生产构建与本地构建分离。这种方法做到了职责分离:使用 Compose 进行本地开发,使用 Bake 进行生产就绪型构建,同时仍然重用服务定义和基本构建配置。

使用 Bake 构建

与 Compose 类似,Bake 从配置文件中解析项目的构建定义。Bake 支持 HashiCorp Configuration Language (HCL)、JSON 和 Docker Compose YAML 格式。当您将 Bake 与多个文件一起使用时,它会找到并合并所有适用的配置文件,形成一个统一的构建配置。在 Bake 文件中指定的选项会扩展或在某些情况下覆盖您 Compose 文件中定义的构建选项。

以下部分探讨了如何使用 Bake 扩展 Compose 文件中为生产环境定义的构建选项。

查看构建配置

Bake 会自动从服务的 build 属性创建构建配置。使用 Bake 的 --print 标志可以查看给定 Compose 文件的构建配置。此标志会评估构建配置并以 JSON 格式输出构建定义。

$ docker buildx bake --print

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",
    }
  }
}

如您所见,Bake 创建了一个包含四个目标的 default 组:

  • seed
  • vote
  • result
  • worker

此组是根据您的 Compose 文件自动创建的;它包括所有包含构建配置的服务。要使用 Bake 构建这组服务,请运行:

$ docker buildx bake

自定义构建组

首先重新定义 Bake 执行的默认构建组。当前的默认组包含一个 seed 目标 —— 这是一个 Compose 服务,仅用于向数据库填充模拟数据。由于此目标不产生生产镜像,因此无需将其包含在构建组中。

要自定义 Bake 使用的构建配置,请在仓库根目录、与 compose.yaml 文件并排放置一个名为 docker-bake.hcl 的新文件。

$ touch docker-bake.hcl

打开 Bake 文件并添加以下配置:

docker-bake.hcl
group "default" {
  targets = ["vote", "result", "worker"]
}

保存文件并再次打印您的 Bake 定义。

$ docker buildx bake --print

JSON 输出显示 default 组仅包含您关心的目标。

{
  "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"]
    }
  }
}

此处,每个目标的构建配置(上下文、标签等)都从 compose.yaml 文件中获取。组由 docker-bake.hcl 文件定义。

自定义目标

Compose 文件目前将 dev 阶段定义为 vote 服务的构建目标。这适用于您在本地开发中运行的镜像,因为 dev 阶段包含额外的开发依赖项和配置。但是,对于生产镜像,您需要将目标更改为 final 镜像。

要修改 vote 服务使用的目标阶段,请将以下配置添加到 Bake 文件中:

target "vote" {
  target = "final"
}

当您使用 Bake 运行构建时,这会用不同的值覆盖 Compose 文件中指定的 target 属性。Compose 文件中的其他构建选项(标签、上下文)保持不变。您可以通过使用 docker buildx bake --print vote 检查 vote 目标的构建配置来验证。

{
  "group": {
    "default": {
      "targets": ["vote"]
    }
  },
  "target": {
    "vote": {
      "context": "vote",
      "dockerfile": "Dockerfile",
      "tags": ["username/vote"],
      "target": "final"
    }
  }
}

附加构建特性

生产级构建通常与开发构建具有不同的特性。以下是您可能希望为生产镜像添加的一些示例:

多平台
对于本地开发,您只需构建适用于您本地平台的镜像即可,因为这些镜像只会在您的机器上运行。但对于要推送到仓库的镜像,通常最好构建多平台镜像,特别是 arm64 和 amd64。
证明
证明 (Attestations) 是附加到镜像上的清单,描述了镜像的创建方式及其包含的组件。将证明附加到镜像有助于确保您的镜像遵循软件供应链的最佳实践。
注解
注解 (Annotations) 为镜像提供描述性元数据。使用注解记录任意信息并将其附加到镜像上,这有助于消费者和工具理解镜像的来源、内容以及如何使用镜像。

提示

为什么不直接在 Compose 文件中定义这些附加构建选项呢?

Compose 文件格式中的 build 属性不支持所有构建特性。此外,一些特性,例如多平台构建,会大幅增加构建服务所需的时间。对于本地开发,最好保持构建步骤简单快速,将那些花哨的功能留给发布构建。

要将这些属性添加到使用 Bake 构建的镜像中,请按如下方式更新 Bake 文件:

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"]
}

这定义了一个新的 _common 目标,该目标定义了可重用的构建配置,用于为您的镜像添加多平台支持、注解和证明。可重用目标会被构建目标继承。

进行这些更改后,使用 Bake 构建项目会为 linux/amd64linux/arm64 架构生成三组多平台镜像。每个镜像都带有作者注解以及 SBOM 和来源证明记录。

结论

本指南演示的模式提供了一种有用的方法,用于在使用 Docker Compose 的项目中管理生产就绪型 Docker 镜像。使用 Bake 可以让您访问 Buildx 和 BuildKit 的所有强大功能,并且还有助于以合理的方式分离您的开发和构建配置。

进一步阅读

有关如何使用 Bake 的更多信息,请查阅以下资源:

页面选项