使用 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 工作器,它使用投票并将它们存储在数据库中。
db一个由 Docker 卷支持的 Postgres 数据库。
redis一个收集新投票的 Redis 实例。
seed一个实用程序容器,用于使用模拟数据填充数据库。

`vote`、`result` 和 `worker` 服务是从此存储库中的代码构建的,而 `db` 和 `redis` 使用来自 Docker Hub 的预先存在的 Postgres 和 Redis 镜像。`seed` 服务是一个实用程序,它会对前端服务发出请求以填充数据库,用于测试目的。

使用 Compose 构建

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

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

`vote`、`result` 和 `worker` 服务都指定了构建配置。运行 `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 配置语言 (HCL)、JSON 和 Docker Compose YAML 格式。当您将 Bake 与多个文件一起使用时,它会查找并将所有适用的配置文件合并到一个统一的构建配置中。您的 Compose 文件中定义的构建选项将由 Bake 文件中指定的选项进行扩展,或者在某些情况下会被覆盖。

下一节将探讨如何使用 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。
证明
证明 是附加到镜像的清单,描述了镜像的创建方式以及它包含的组件。将证明附加到您的镜像有助于确保您的镜像遵循软件供应链最佳实践。
注释
注释 为镜像提供描述性元数据。使用注释来记录任意信息并将其附加到您的镜像,这有助于使用者和工具了解镜像的来源、内容以及如何使用镜像。

提示

为什么不直接在 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 的更多信息,请查看以下资源: