如何在docker-compose中使用多个镜像标签

40
根据这个这个GitHub问题,目前在使用docker-compose构建一个或多个镜像时,没有本地的方法来为服务的镜像提供多个标签。
我的用例是在docker-compose.yml文件中定义映像并将其一次标记为某些自定义标记(例如某些构建编号、日期或类似内容),一次标记为latest
虽然这可以很容易地通过普通的docker使用docker tag来实现,但docker-compose仅允许在image key中设置一个单一标签。与docker-compose一起使用docker tag不是我的选择,因为我想将所有与docker相关的定义都保留在docker-compose.yml文件中,而不是将它们复制到我的构建脚本中。
有什么好的解决方法可以实现使用docker-compose设置多个标签,而无需预先硬编码/复制镜像名称吗?
6个回答

29

我有一些使用环境变量的简洁明了的解决方案(默认值为 Bash 语法中的变量,例如 latest,但您可以使用任何值),这是我的组合:

version: '3'
services:
  app:
    build: .
    image: myapp-name:${version:-latest}

如果需要推送到注册表,请使用默认标签构建和推送,使用环境变量更改版本并再次构建和推送:

docker-compose build
docker-compose push
export version=0.0.1
docker-compose build
docker-compose push

1
我会为版本环境变量使用不同的名称。但除此之外,看起来非常干净。有关环境变量替换的详细信息,请参阅:https://docs.docker.com/compose/compose-file/#variable-substitution - Charlie Reitzel
非常流畅,但我同意@CharlieReitzel关于使用更好的变量名称。 - David
1
目前我们能做的最好的事情。虽然它构建了两次。我知道,我知道,第二次使用缓存图像进行构建,但仍然。如果组合文件可以直接支持多个标签,那就更好了。 - Stefan Haberl
2
只是过来更新一下 @CharlieReitzel 的文档链接:https://docs.docker.com/compose/compose-file/#interpolation - Joe Sadoski
关于文档链接的另一个更新: https://docs.docker.com/compose/compose-file/12-interpolation/ - undefined

26

你也可以采用以下方法:

# build is your actual build spec
build:
  image: myrepo/myimage
  build:
  ...
  ...
# these extend from build and just add new tags statically or from environment variables or 
version_tag:
  extends: build
  image: myrepo/myimage:v1.0
some_other_tag:
  extends: build
  image: myrepo/myimage:${SOME_OTHER_TAG}
你可以直接运行docker-compose builddocker-compose push,就可以构建并推送正确标记的镜像集。

13
很遗憾它只能在版本小于3的Compose文件中运行。 - whoan
1
同意,这个语法已经不再被 docker-compose.yml 支持了。@Maoz Zadok 在下面提出了正确的方法。为了清晰起见,使用您自己的环境变量,不要与任何 Docker 关键字冲突(例如 version)。 - Charlie Reitzel
3
3.4及以上版本的文件格式支持扩展字段。您可以使用它来执行相同的步骤,尽管需要使用更加晦涩的YAML格式。参考链接:https://docs.docker.com/compose/compose-file/#extension-fields - Jordan Deyton
@JordanDeyton 好主意,我已经根据你的建议发布了答案。谢谢! - Romain
对于那些稍后查看此线程的人 - extendsimage 语法似乎仍然适用于 docker-compose 文件版本 3.8,尽管官方的 docker-compose 文档表示该语法已被弃用...(我亲自测试过)参考:https://forums.docker.com/t/docker-compose-version-3-8-or-3-9-for-latest/102439/5 - kachow6

17
根据@JordanDeyton的建议,在Compose文件格式3之后不能再使用extends,而在版本3.4中添加了扩展字段功能来替代它以实现相同的目标。以下是一个示例。
version: "3.4"
# Define common behavior
x-ubi-httpd:
  &default-ubi-httpd
  build: ubi-httpd
  # Other settings can also be shared
  image: ubi-httpd:latest

# Define one service by wanted tag
services:
  # Use the extension as is
  ubi-httpd_latest:
    *default-ubi-httpd
  # Override the image tag
  ubi-httpd_major:
    << : *default-ubi-httpd
    image: ubi-httpd:1
  ubi-httpd_minor:
    << : *default-ubi-httpd
    image: ubi-httpd:1.0
  # Using an environment variable defined in a .env file for e.g.
  ubi-httpd_patch:
    << : *default-ubi-httpd
    image: "ubi-httpd:${UBI_HTTPD_PATCH}"

现在可以使用所有定义的标签构建图像。

$ docker-compose build
# ...

$ docker images | grep ubi-httpd
# ubi-httpd  1       8cc412411805  3 minutes ago  268MB
# ubi-httpd  1.0     8cc412411805  3 minutes ago  268MB
# ubi-httpd  1.0.1   8cc412411805  3 minutes ago  268MB
# ubi-httpd  latest  8cc412411805  3 minutes ago  268MB

对于那些正在阅读的人,需要注意的是,docker-compose 1.17.0版本已经支持文件格式版本3.4,详情请见https://github.com/docker/compose/releases/tag/1.17.0-rc1。Compose文件参考页面包括一个矩阵,其中列出了各种格式和Docker引擎版本,但我一直不清楚如何将Docker引擎版本映射到docker-compose版本。 - Jordan Deyton
这对我来说在构建两个标签方面是有效的,但它有两个缺陷:它会构建两次(不是很大的问题),但它还会创建第二个服务,在“docker-compose up”时启动,这当然不是预期的目标。 - benelgiac
更新的扩展字段链接:https://docs.docker.com/compose/compose-file/compose-file-v3/#extension-fields - agconti
使用Docker版本24.0.4和docker compose插件v2.19.1,我按照这个解决方案得到了不同的镜像哈希值。原因是它添加了默认标签com.docker.compose.service,并将其填充为服务的名称。链接。这应该没有问题,因为镜像只有那个标签不同,所有层都是相同的。 - hraphrap

15

我想出了几种不同复杂度的解决方案。它们都基于这样一个假设:${IMAGE_TAG} 存储着自定义标签,例如构建号码,并且我们希望将所有服务的映像文件都使用此标签以及 latest 进行标记。

docker-compose.yml 文件中使用 grep 命令提取映像名称

images=$(cat docker-compose.yml | grep 'image: ' | cut -d':' -f 2 | tr -d '"')
for image in $images
do
  docker tag "${image}":"${IMAGE_TAG}" "${image}":latest
done

然而,如果有人在docker-compose.yml中添加了注释,例如# Purpose of this image: do something useful...,那么这种方法就容易出现错误。

两次构建

在您的docker-compose.yml文件中使用${IMAGE_TAG}作为环境变量,如此处第一个示例所述。

然后,只需运行两次构建过程,每次用不同的值替换${IMAGE_TAG}即可:

IMAGE_TAG="${IMAGE_TAG}" docker-compose build
IMAGE_TAG=latest docker-compose build
第二次构建过程应该比第一次快得多,因为所有的镜像层都应该仍然从第一次运行中被缓存。
这种方法的缺点是它将用两个连续的构建过程淹没您的日志输出,每个单独的服务都会如此,这可能会使搜索有用信息变得更加困难。
此外,如果您的 Dockerfile 中有任何命令总是刷新构建缓存(例如,使用自动更新的 last-modified 标头从远程位置获取的 ADD 命令、添加外部进程不断更新的文件等),那么额外的构建可能会显著减慢速度。
使用一些内联 Python 代码从 docker-compose.yml 文件中解析图像名称
在 Python 中,这可能看起来像这样:
images=$(python3 <<-EOF # make sure below to indent with tabs, not spaces; or omit the "-" before "EOF" and use no indention at all
    import yaml
    content = yaml.load(open("docker-compose.build.yml"))
    services = content["services"].values()
    image_names = (service["image"].split(":")[0] for service in services)
    print("\n".join(image_names))
EOF
)

for image in ${images}
do
docker tag ${image}:${IMAGE_TAG} ${image}:latest
done

这种方法的缺点是执行构建的机器必须已经安装了Python3,以及PyYAML库。正如已经提到的,这种模式同样可以与Python2或任何其他已安装的编程语言一起使用。

使用一些docker命令获取图像名称的组合

以下方法使用一些本地dockerdocker-compose命令(使用go-templates)编写略微复杂,但也很好用。

# this should be set to something unique in order to avoid conflicts with other running docker-compose projects
compose_project_name=myproject.tagging

# create containers for all services without starting them
docker-compose --project-name "${compose_project_name}" up --no-start

# get image names without tags for all started containers
images=$(docker-compose --project-name "${compose_project_name}" images -q | xargs docker inspect --format='{{ index .RepoTags 0}}' | cut -d':' -f1)

# iterate over images and re-tag
for image in ${images}
do
    docker tag "${image}":"${IMAGE_TAG}" "${image}":latest
done

# clean-up created containers again
docker-compose --project-name "${compose_project_name}" down

虽然这种方法没有任何外部依赖,比使用 grep 方法更安全,但在创建和删除容器的大型设置上执行可能需要多几秒钟(尽管通常不是问题)。


7
在您的第一个示例中,您可以运行 docker-compose config 命令,它会剥离所有注释并只留下配置语句。例如,我会运行:docker-compose config | grep 'image: ' | awk -F ':' '{ print "image "$2", tag "$3 }' - Dalibor Karlović
docker-compose images <service_name> 输出 Container | Repository | Tag | Image Id | Size,其中包含像 project_foo_1 | project_foo | latest | 590caffee098 | 1.1 GB 这样的值。(添加了 | 以适应这种混乱的行布局。)也许可以利用这个来改进最后一个示例,从而不需要使用 --project-name - luckydonald

8
现在有一个内置的解决方案,使用 buildx bake,发布于 v.0.7.0。这个功能是根据我的建议在 https://github.com/docker/buildx/issues/396 中实现的。
Docker捆绑了安装了buildx,但是,如果您正在Mac上运行Docker Desktop,则捆绑的buildx版本在编写本文时较旧,您需要安装正确版本的buildx以及Docker。
x-bake扩展字段添加到您的docker-compose.yaml中:
version: '3.9'

services:
  my-app:
    image: my-repo/my-image:latest
    build:
      context: .
      dockerfile: Dockerfile
      x-bake:
        tags:
        - my-repo/my-image:${MY_TAG_1}
        - my-repo/my-image:${MY_TAG_2}
        - my-repo/my-image:${MY_TAG_3}

        - my-other-repo/my-image:${MY_TAG_1}
        - my-other-repo/my-image:${MY_TAG_2}
        - my-other-repo/my-image:${MY_TAG_3}

要构建并标记镜像,请运行:

buildx bake --load

构建、标记并将镜像推送到仓库甚至多个仓库中:
buildx bake --push

这应该是被接受的答案,而且似乎在构建时不需要额外的 x-bake 部分。文档在这里 - RiverHeart

1

正如许多人建议的那样,使用extends并不是最佳选择,因为它会导致每个生成的图像具有不同的校验和。这通常是不好的,因为您不能确定在生产环境中使用的图像是否一致。

# cat docker-compose.yml 
version: "3.8"
services:

  container:
    build:
      context: .
    image: test/image:1

 # adds a copy of the image tagged with the build id
  extra-tags:
    extends: container
    image: test/image:2
  latest:
    extends: container
    image: test/image:3# 
# cat Dockerfile 
# syntax=docker/dockerfile:1
FROM alpine:3.15

# docker compose build
[+] Building 2.9s (13/13) FINISHED                                                                                                                                                                                                               
 => [latest internal] load build definition from Dockerfile                                                                                                                                                                                 0.0s
 => => transferring dockerfile: 84B                                                                                                                                                                                                         0.0s
 => [latest internal] load .dockerignore                                                                                                                                                                                                    0.0s
 => => transferring context: 2B                                                                                                                                                                                                             0.0s
 => [latest] resolve image config for docker.io/docker/dockerfile:1                                                                                                                                                                         1.2s
 => [container internal] load build definition from Dockerfile                                                                                                                                                                              0.0s
 => => transferring dockerfile: 84B                                                                                                                                                                                                         0.0s
 => [container internal] load .dockerignore                                                                                                                                                                                                 0.0s
 => => transferring context: 2B                                                                                                                                                                                                             0.0s
 => [extra-tags internal] load build definition from Dockerfile                                                                                                                                                                             0.0s
 => => transferring dockerfile: 84B                                                                                                                                                                                                         0.0s
 => [extra-tags internal] load .dockerignore                                                                                                                                                                                                0.0s
 => => transferring context: 2B                                                                                                                                                                                                             0.0s
 => CACHED [container] docker-image://docker.io/docker/dockerfile:1@sha256:39b85bbfa7536a5feceb7372a0817649ecb2724562a38360f4d6a7782a409b14                                                                                                 0.1s
 => => resolve docker.io/docker/dockerfile:1@sha256:39b85bbfa7536a5feceb7372a0817649ecb2724562a38360f4d6a7782a409b14                                                                                                                        0.0s
 => [extra-tags internal] load metadata for docker.io/library/alpine:3.15                                                                                                                                                                   1.0s
 => CACHED [container 1/1] FROM docker.io/library/alpine:3.15@sha256:3362f865019db5f14ac5154cb0db2c3741ad1cce0416045be422ad4de441b081                                                                                                       0.1s
 => => resolve docker.io/library/alpine:3.15@sha256:3362f865019db5f14ac5154cb0db2c3741ad1cce0416045be422ad4de441b081                                                                                                                        0.0s
 => [latest] exporting to image                                                                                                                                                                                                             0.2s
 => => exporting layers                                                                                                                                                                                                                     0.0s
 => => exporting manifest sha256:67e737bfe3d8955deeebd531025697ed4e8c4a4c263b9f1ea9bc7b4bc0330105                                                                                                                                           0.0s
 => => exporting config sha256:b2fe3df223ec66ebfa2c8e2dac5760b42a79eefb9abc83c2f0c5972e371c6e52                                                                                                                                             0.0s
 => => exporting attestation manifest sha256:1a18ee3574147462eea9d4b46ffaa30a69b5211b70238ba3f548e67b24a06aa8                                                                                                                               0.1s
 => => exporting manifest list sha256:19a2d2908947fa148c536b1d7b487e01d0aaa5ae4a8250fae41ac53ce2ae103d                                                                                                                                      0.0s
 => => naming to docker.io/test/image:3                                                                                                                                                                                                     0.0s
 => => unpacking to docker.io/test/image:3                                                                                                                                                                                                  0.0s
 => [container] exporting to image                                                                                                                                                                                                          0.2s
 => => exporting layers                                                                                                                                                                                                                     0.0s
 => => exporting manifest sha256:1f4ea8de74e009308a75606c3eb0adfad16bd5a0c495b1e5e790414adbb9e09e                                                                                                                                           0.0s
 => => exporting config sha256:021dc0c803e68082939d5a0ecd37d6f8e25be82eb9425b45296ad3cbd2373c29                                                                                                                                             0.0s
 => => exporting attestation manifest sha256:a297fc379c4b1a509de3b106f3f4e478d8f64b95abb1f516d584ce148f42cbec                                                                                                                               0.1s
 => => exporting manifest list sha256:73cb361e578dab7451a61a3381bb1d3fa228273de97dc7ed8ff5032498ac7049                                                                                                                                      0.0s
 => => naming to docker.io/test/image:1                                                                                                                                                                                                     0.0s
 => => unpacking to docker.io/test/image:1                                                                                                                                                                                                  0.0s
 => [extra-tags] exporting to image                                                                                                                                                                                                         0.2s
 => => exporting layers                                                                                                                                                                                                                     0.0s
 => => exporting manifest sha256:03f127f826024b1a0e194aa00bae4feba34af4471832d6d4b19d6b4cebd26d7f                                                                                                                                           0.0s
 => => exporting config sha256:3e45d21e99ff3ca466cf3197927e614d542da5d548f929422dfb7889fa55e546                                                                                                                                             0.0s
 => => exporting attestation manifest sha256:419d1dff28c183ab66a3c7724cf26248c58d4e452eae54e605cf3504ce15eefb                                                                                                                               0.1s
 => => exporting manifest list sha256:d671e4950218e44569fca1a74aebb100999deb24acaa2003149bc743cf1316d4                                                                                                                                      0.0s
 => => naming to docker.io/test/image:2                                                                                                                                                                                                     0.0s
 => => unpacking to docker.io/test/image:2                                                                                                                                                                                                  0.0s
# docker images --digests | grep ^test/
test/image                                        1                                                       sha256:73cb361e578dab7451a61a3381bb1d3fa228273de97dc7ed8ff5032498ac7049   73cb361e578d   2 minutes ago    9.07MB
test/image                                        2                                                       sha256:d671e4950218e44569fca1a74aebb100999deb24acaa2003149bc743cf1316d4   d671e4950218   2 minutes ago    9.07MB
test/image                                        3                                                       sha256:19a2d2908947fa148c536b1d7b487e01d0aaa5ae4a8250fae41ac53ce2ae103d   19a2d2908947   2 minutes ago    9.07MB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接