Share feedback
Answers are generated based on the documentation.

使用 Bake 构建 Compose 项目

本指南演示如何使用 Bake 为 Docker Compose 项目构建生产级镜像。

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

Docker Buildx Bake 是一个构建编排工具,它让你可以用声明式配置来定义构建过程,就像 Docker Compose 用于定义运行时栈那样。对于使用 Docker Compose 在本地开发中启动服务的项目,Bake 提供了一种无缝扩展项目、加入生产级构建配置的方式。

前提条件

本指南假定你已熟悉:

项目概览

本指南将使用 dvdksn/example-voting-app 仓库作为示例,展示一个使用 Docker Compose 的 monorepo 如何通过 Bake 进行扩展。

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

该仓库在 compose.yaml 文件中使用 Docker Compose 定义运行应用所需的运行时配置。该应用由以下服务组成:

服务 描述
vote 一个用 Python 编写的前端 Web 应用,可让你在两个选项之间投票。
result 一个 Node.js Web 应用,实时展示投票结果。
worker 一个 .NET worker,消费投票数据并将其存入数据库。
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,以及该 group 中的所有 target。group 是一组构建的集合,而 target 代表单次构建。

{
  "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 创建了一个包含四个 target 的 default group:

  • seed
  • vote
  • result
  • worker

该 group 是根据你的 Compose 文件自动创建的;它包含了所有含构建配置的服务。要使用 Bake 构建这一组服务,运行:

$ docker buildx bake

自定义构建 group

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

要自定义 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 现在只包含你关心的 target。

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

在这里,每个 target 的构建配置(context、tags 等)都取自 compose.yaml 文件,而 group 则由 docker-bake.hcl 文件定义。

自定义 target

目前 Compose 文件将 dev 阶段定义为 vote 服务的构建目标。对于你在本地开发中运行的镜像来说这很合适,因为 dev 阶段包含了额外的开发依赖和配置。然而对于生产镜像,你会希望改为以 final 镜像为目标。

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

target "vote" {
  target = "final"
}

当你使用 Bake 运行构建时,这会用另一个值覆盖 Compose 文件中指定的 target 属性。Compose 文件中的其他构建选项(tag、context)保持不变。你可以用 docker buildx bake --print vote 检查 vote target 的构建配置来验证:

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

其他构建特性

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

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

为什么不直接在 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 target,它定义了可复用的构建配置,用于为镜像添加多平台支持、注解和证明。各个构建 target 继承这个可复用的 target。

做出这些改动后,用 Bake 构建该项目会为 linux/amd64 和 linux/arm64 架构生成三组多平台镜像。每个镜像都带有作者注解,以及 SBOM 和来源 (provenance) 证明记录。

结论

本指南展示的模式为在使用 Docker Compose 的项目中管理生产级 Docker 镜像提供了一种实用方法。使用 Bake 让你能够使用 Buildx 和 BuildKit 的全部强大功能,同时以合理的方式将开发配置与构建配置分离开来。

延伸阅读

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