如何使用docker-compose多个图像标签

How to use multiple image tags with docker-compose

根据这一点和Github的问题,目前,在使用docker-compose构建一个或多个图像时,没有本地方法可以为服务的图像提供多个标签。

我的用例是构建在docker-compose.yml文件中定义的图像,并用一些定制的标记(例如一些构建号或日期或类似的标记)标记它们一次,另一次作为latest标记。

虽然使用docker标记的普通docker很容易实现这一点,但是docker-compose只允许在image键中设置一个标记。使用docker tagdocker-compose对我来说不是一个选择,因为我想把所有与docker相关的定义保存在docker-compose.yml文件中,而不是复制到我的构建脚本中。

在不需要先硬编码/复制图像名称的情况下,用docker-compose设置多个标签是一个很好的解决方案吗?


我想出了几个不同复杂性的解决办法。它们都依赖于这样的假设:${IMAGE_TAG}存储了表示构建号的定制标记,我们希望用这个标记以及latest标记所有服务的图像。

grep来自docker-compose.yml文件的映像名

1
2
3
4
5
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...

建两次

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

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

1
2
IMAGE_TAG="${IMAGE_TAG}" docker-compose build
IMAGE_TAG=latest docker-compose build

第二个构建过程应该比第一个快得多,因为从第一次运行开始,所有图像层都应该缓存。

这种方法的缺点是,它将为每个服务注入两个后续的构建过程,这可能会使在日志输出中搜索有用的内容变得更加困难。

此外,如果您的Dockerfile中有任何命令总是刷新构建缓存(例如,从远程位置提取的ADD命令,自动更新last-modified头文件,添加由外部进程不断更新的文件等),那么额外的构建可能会显著降低速度。

使用一些内联python代码解析docker-compose.yml文件中的图像名称

使用python(或任何其他语言,如Rubyperl或安装在系统上的任何其他语言)中的真正yaml解析器比前面提到的grep方法更强大,因为它不会被注释或奇怪但有效的写入yml文件的方式所混淆。

在python中,可能如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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("
".join(image_names))
EOF
)

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

这种方法的缺点是,执行构建的机器必须安装python3以及pyyaml库。如前所述,此模式同样可以用于python2或安装的任何其他编程语言。

结合一些docker命令获取图像名称

下面的方法使用一些本地的dockerdocker-compose命令(使用go模板)编写起来有点复杂,但也很好地工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 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方法更安全,但在创建和删除容器的大型设置上执行可能需要几秒钟的时间(但通常不是问题)。


您还可以采用以下方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 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,就可以构建并推送正确的标记图像集。


我有一些使用环境变量(默认变量值的bash语法,在我的例子中是latest,但是您可以使用任何东西)的好的、干净的解决方案,这是我的组合:

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

使用默认标记生成并推送(如果需要推送到注册表),使用环境变量更改版本,然后再次生成并推送:

1
2
3
4
5
docker-compose build
docker-compose push
export version=0.0.1
docker-compose build
docker-compose push