在一个docker构建过程中标记多个目标



我有一个带有多个目标的Dockerfile。例如:

FROM x as frontend
...
FROM y as backend
...
FROM z as runtime
...
COPY --from=frontend ...
COPY --from=backend ...

为了构建和标记最终图像,我使用:

docker build -t my-project .

为了构建和标记中间目标,我提供了--target参数:

docker build -t my-project-backend --target backend .

但是,有可能构建一个最终图像并标记所有中间图像吗?换句话说,与相同

docker build -t my-project-frontend --target frontend .
docker build -t my-project-backend --target backend .
docker build -t my-project .

但只要一个命令?

我想需要一点解释。如果使用buildkit(export DOCKER_BUILDKIT=1),那么所有独立的目标都是并行构建的。所以这比一个接一个地建造它们要快。我需要标记每个目标,将它们推送到docker注册表以及最终注册表。

目前,我正在CI中构建我的图像,而没有构建工具包,我正在努力加快这个过程。

我做了一些搜索,但dockerCLI目前似乎没有提供任何直接的方法来实现这一点。最接近的是我在评论中提出的想法:构建主图像,然后标记所有中间图像。

以这个Dockerfile为例:

FROM alpine AS frontend
RUN sleep 15 && touch /frontend
FROM alpine AS backend
RUN sleep 15 && touch /backend
FROM alpine AS runtime
COPY --from=frontend /frontend /frontend
COPY --from=backend /backend /backend

(sleep只是为了通过明显的缓存来提高速度)

使用构建

export DOCKER_BUILDKIT=1 # enable buildkit for parallel builds
docker build -t my-project .
docker build -t my-project-backend --target backend .
docker build -t my-project-frontend --target frontend .

  1. 通过首先构建所有所需的中间图像(例如frontendbackend)来构建主图像runtime,并使用my-project仅标记主图像
  2. 使用上一次生成的缓存生成标记为my-project-backend的目标backend
  3. 相同,但适用于backend

这里的每个图像都只会构建一次,但最终这与您在问题中所说的完全相同,只是顺序不同。

如果你真的想在一个命令中做到这一点,你可以使用CCD_;多个图像":

version: "3.8"
services:
my-project:
image: my-project
build: .
backend:
image: my-project-backend
build:
context: .
target: backend
frontend:
image: my-project-frontend
build:
context: .
target: frontend
export DOCKER_BUILDKIT=1 # enable buildkit for parallel builds
export COMPOSE_DOCKER_CLI_BUILD=1 # use docker cli for building
docker-compose build

在这里,docker-compose将基本上为您运行与上面相同的docker build命令。

在这两种情况下,尽管缓存层大大加快了构建速度,但每次都会有一个新的构建发生:

  • 将构建上下文(即当前目录的内容)发送到docker守护进程
  • ADD的任何远程文件下载到映像并且只有在内容再次相同的情况下才使用缓存——对于大文件/慢速网络来说,这将是明显的速度减慢

我在这个论坛线程中找到的另一个解决方法是向映像添加LABEL,并在构建后使用docker image ls --filter获取映像ID。

但经过测试,docker image ls在使用buildkit时似乎不会显示中间图像。此外,这种方法将需要更多的命令/专用脚本,这将再次比您当前的方法需要更多的工作。

现在最接近这一点的是使用Docker的buildx-bake命令。它允许您定义一个HCL文件,语法如下:

group "default" {
targets = ["app", "frontend", "backend"]
}
target "app" {
dockerfile = "Dockerfile"
tags = ["docker.io/username/app"]
}
target "frontend" {
dockerfile = "Dockerfile"
target = "frontend"
tags = ["docker.io/username/frontend"]
}
target "backend" {
dockerfile = "Dockerfile"
target = "backend"
tags = ["docker.io/username/backend"]
}

然后用docker buildx bake -f bake.hcl构建

也就是说,你所做的几乎可以肯定是一个错误多阶段构建旨在将构建环境与运行时环境分离,而不是创建多个不同的映像。换句话说,当你需要螺丝刀时,你用的是锤子,是的,它会起作用,但结果是次优的。

首选且简单得多的解决方案是为要构建的每个映像创建一个单独的Dockerfile。如果你的图像有一个共同的基础,那么考虑把它移到它自己的图像上,并在FROM步骤中引用它。

作为开发人员,要在docker中构建多个映像,通常使用定义所有三个映像的docker-compose.yml文件,然后docker-compose up --build将在构建每个映像后使用单个命令启动整个堆栈。例如,合成文件可能看起来像:

version: 2
services:
app:
build: Dockerfile.app
image: username/app
# ...
frontend:
build: Dockerfile.frontend
image: username/frontend
# ...
backend:
build: Dockerfile.backend
image: username/backend
# ...

对于部署到生产环境,这将是每个映像的独立CI/CD管道,用于执行所需的单元测试、构建,然后进入部署步骤,该步骤使用每个映像的指定版本运行整个堆栈。

在我看来,强制构建多个阶段的最佳解决方案是创建带有副本的阶段"假的";来自我们想要构建的阶段的文件。

FROM scratch AS build-all-stages
COPY --from=first-stage-to-build fake-not-exist-file?.txt .
COPY --from=second-stage-to-build fake-not-exist-file?.txt .
...
COPY --from=x-stage-to-build fake-not-exist-file?.txt .

COPY步骤的目的只是在每个阶段强制构建。

从性能的角度来看,上面的例子比前面的答案中提到的要好,因为我们因为使用空的基本图像"而浪费了最小的内存和时间;划痕;以及";复制";不存在的文件。

接下来,我们应该使用--target参数单独构建每个图像(所有层都将从缓存中获取)。

我认为没有办法为两个应用程序创建一个容器,而且我认为这不是正确的方法

容器docker是为单个应用程序创建的,甚至是为日志创建的,如何处理两个应用程序?是否要停止或重新启动?

我认为正确的方法是docker编写

并使用类似的东西

docker-compose.yml:

version: "3.3"
services:
my-project-frontend:
build: "./my-project-frontend/"
container_name: "front"
restart: always
depends_on:
-  my-project-backend

my-project-backend:
build: "./my-project-backend/"
container_name: "back"
restart: always

并运行:

docker-compose up

docker-compose build

最新更新