我可以在非最终阶段 Docker 构建中获得 Docker 图层历史记录吗?
Can I obtain the Docker layer history on non-final stage Docker builds?
我正在研究一种在 CircleCI 中进行 Docker 层缓存的方法,并且我有一个可行的解决方案。但是,我正在努力改进它。 CI 任何形式的问题是每次构建都会擦除图像历史记录,因此需要使用 CI 系统的缓存指令找出要恢复的文件,然后 load
回到 Docker.
受this approach on Travis的启发,我首先尝试了这个。要恢复:
if [ -f /caches/${CIRCLE_PROJECT_REPONAME}.tar.gz ]; then gunzip -c /caches/${CIRCLE_PROJECT_REPONAME}.tar.gz | docker load; docker images; fi
并创建:
docker save $(docker history -q ${CIRCLE_PROJECT_REPONAME}:latest | grep -v '<missing>') | gzip > /caches/${CIRCLE_PROJECT_REPONAME}.tar.gz
这似乎工作正常,但我的 Dockerfile
使用两阶段构建,并且当我从第一个到最后一个 COPY
编辑文件时,它停止引用缓存。我认为这是因为 (a) docker history
仅适用于最终构建,并且 (b) 第一个构建阶段中的非缓存更改具有新的 mtime
,因此当它们被复制到最后阶段,都算是新人了。
为了解决这个问题,我决定尝试将所有图像保存到缓存中:
docker save $(docker images -a -q) | gzip > /caches/${CIRCLE_PROJECT_REPONAME}.tar.gz
这成功了!但是,它有一个新的问题:当我修改我的Dockerfile
时,旧的图像缓存将被加载,新的图像将被添加,然后所有内容都将存储在缓存中。这将累积我再也不需要的死层,大概直到达到 CI 提供程序的缓存大小限制。
我认为这可以通过缓存构建的所有阶段来解决,但我不确定如何引用第一阶段。有没有一个命令我可以 运行,类似于 docker history -q -a
,它会给我所有非最后阶段的哈希值(因为我已经可以做最后一个阶段)或包括最后一个阶段在内的所有阶段舞台?
我希望 docker build -q
可以这样做,但它只打印最终哈希,而不是所有中间哈希。
更新
我有一个不优雅的解决方案,它确实有效,但肯定有比这更好的方法!我在 docker build
的输出中搜索 --->
,这是 Docker 宣布层哈希和缓存信息的方式。我去掉缓存消息和箭头,只留下所有构建阶段的完整构建层哈希列表:
docker build -t imagename . | grep '\-\-\->' | grep -v 'Using cache' | sed -e 's/[ >-]//g'
(我实际上做了两次构建 - 一次是为了正确的构建 CI 步骤,第二次是为了收集哈希值。我只能做一次,但是拥有实际的构建感觉很好在一个单独的步骤中。第二个构建将总是被缓存,并且只需要几秒钟到运行)。
这是否可以改进,也许使用 Docker 命令?
这是评论中的对话摘要。
一种选择是将所有构建阶段推送到远程。如果有两个构建阶段,第一个命名为 build
,第二个未命名,那么可以这样做:
docker build --target build --tag image-name-build .
docker build --tag image-name .
然后可以将 image-name
(最终构建工件)和 image-name-build
(第一阶段,通常被丢弃)推送到远程注册表。
重建映像时,可以 pull
将这两个都放到新的 CI 构建机器上,然后执行:
docker build --cache-from image-name-build --target build --tag image-name-build .
docker build --cache-from image-name --tag image-name .
正如 BMitch 所说,--cache-from
将表明可以信任这些图像,以便将它们用作本地层缓存。
比较
如果您有一个 CI-native 缓存系统来存储文件,那么这个问题的临时解决方案是好的,并且您不想用通常会被丢弃的中间构建阶段图像弄乱您的注册表.
--cache-from
解决方案很好,因为它更整洁,并且使用 Docker-native 功能而不是必须 grep 构建输出。如果您的 CI 解决方案不提供文件缓存系统,它也将非常有用,因为它使用远程注册表。
我正在研究一种在 CircleCI 中进行 Docker 层缓存的方法,并且我有一个可行的解决方案。但是,我正在努力改进它。 CI 任何形式的问题是每次构建都会擦除图像历史记录,因此需要使用 CI 系统的缓存指令找出要恢复的文件,然后 load
回到 Docker.
受this approach on Travis的启发,我首先尝试了这个。要恢复:
if [ -f /caches/${CIRCLE_PROJECT_REPONAME}.tar.gz ]; then gunzip -c /caches/${CIRCLE_PROJECT_REPONAME}.tar.gz | docker load; docker images; fi
并创建:
docker save $(docker history -q ${CIRCLE_PROJECT_REPONAME}:latest | grep -v '<missing>') | gzip > /caches/${CIRCLE_PROJECT_REPONAME}.tar.gz
这似乎工作正常,但我的 Dockerfile
使用两阶段构建,并且当我从第一个到最后一个 COPY
编辑文件时,它停止引用缓存。我认为这是因为 (a) docker history
仅适用于最终构建,并且 (b) 第一个构建阶段中的非缓存更改具有新的 mtime
,因此当它们被复制到最后阶段,都算是新人了。
为了解决这个问题,我决定尝试将所有图像保存到缓存中:
docker save $(docker images -a -q) | gzip > /caches/${CIRCLE_PROJECT_REPONAME}.tar.gz
这成功了!但是,它有一个新的问题:当我修改我的Dockerfile
时,旧的图像缓存将被加载,新的图像将被添加,然后所有内容都将存储在缓存中。这将累积我再也不需要的死层,大概直到达到 CI 提供程序的缓存大小限制。
我认为这可以通过缓存构建的所有阶段来解决,但我不确定如何引用第一阶段。有没有一个命令我可以 运行,类似于 docker history -q -a
,它会给我所有非最后阶段的哈希值(因为我已经可以做最后一个阶段)或包括最后一个阶段在内的所有阶段舞台?
我希望 docker build -q
可以这样做,但它只打印最终哈希,而不是所有中间哈希。
更新
我有一个不优雅的解决方案,它确实有效,但肯定有比这更好的方法!我在 docker build
的输出中搜索 --->
,这是 Docker 宣布层哈希和缓存信息的方式。我去掉缓存消息和箭头,只留下所有构建阶段的完整构建层哈希列表:
docker build -t imagename . | grep '\-\-\->' | grep -v 'Using cache' | sed -e 's/[ >-]//g'
(我实际上做了两次构建 - 一次是为了正确的构建 CI 步骤,第二次是为了收集哈希值。我只能做一次,但是拥有实际的构建感觉很好在一个单独的步骤中。第二个构建将总是被缓存,并且只需要几秒钟到运行)。
这是否可以改进,也许使用 Docker 命令?
这是评论中的对话摘要。
一种选择是将所有构建阶段推送到远程。如果有两个构建阶段,第一个命名为 build
,第二个未命名,那么可以这样做:
docker build --target build --tag image-name-build .
docker build --tag image-name .
然后可以将 image-name
(最终构建工件)和 image-name-build
(第一阶段,通常被丢弃)推送到远程注册表。
重建映像时,可以 pull
将这两个都放到新的 CI 构建机器上,然后执行:
docker build --cache-from image-name-build --target build --tag image-name-build .
docker build --cache-from image-name --tag image-name .
正如 BMitch 所说,--cache-from
将表明可以信任这些图像,以便将它们用作本地层缓存。
比较
如果您有一个 CI-native 缓存系统来存储文件,那么这个问题的临时解决方案是好的,并且您不想用通常会被丢弃的中间构建阶段图像弄乱您的注册表.
--cache-from
解决方案很好,因为它更整洁,并且使用 Docker-native 功能而不是必须 grep 构建输出。如果您的 CI 解决方案不提供文件缓存系统,它也将非常有用,因为它使用远程注册表。