2

我正在研究一种在 CircleCI 中进行 Docker 层缓存的方法,并且我有一个可行的解决方案。但是,我正在努力改进它。任何形式的 CI 的问题是每次构建都会擦除映像历史记录,因此需要使用 CI 系统的缓存指令确定要恢复哪些文件,然后将哪些文件load返回到 Docker。

首先我尝试了这个,灵感来自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) 第一个构建阶段中的非缓存更改具有 new mtime,因此当它们被复制到最终阶段时,它们被视为新的。

为了解决这个问题,我决定尝试将所有图像保存到缓存中:

docker save $(docker images -a -q) | gzip > /caches/${CIRCLE_PROJECT_REPONAME}.tar.gz

这行得通!但是,它有一个新问题:当我修改 my 时Dockerfile,会加载旧图像缓存,添加新图像,然后将所有内容都存储在缓存中。这将累积我再也不需要的死层,大概直到达到 CI 提供者的缓存大小限制。

我认为这可以通过缓存构建的所有阶段来解决,但我不确定如何引用第一阶段。是否有一个我可以运行的命令,类似于docker history -q -a,它将为所有非最后阶段(因为我已经可以完成最后一个阶段)或包括最后阶段在内的所有阶段提供哈希值?

我希望docker build -q可以这样做,但它只打印最终哈希,而不是所有中间哈希。

更新

我有一个不优雅的解决方案,它确实有效,但肯定有比这更好的方法!我搜索docker buildfor的输出--->,这是 Docker 宣布层哈希和缓存信息的方式。我去掉了缓存消息和箭头,只留下了所有构建阶段的完整构建层哈希列表:

docker build -t imagename . | grep '\-\-\->' | grep -v 'Using cache' | sed -e 's/[ >-]//g'

(我实际上做了两次构建——一次是正确的构建 CI 步骤,第二次是收集哈希值。我可以只做一次,但是在单独的步骤中进行实际构建感觉很好。第二次构建将总是被缓存,并且只需要几秒钟即可运行)。

这可以改进吗,也许使用 Docker 命令?

4

1 回答 1

1

这是评论中对话的摘要。

一种选择是将所有构建阶段推送到远程。如果有两个构建阶段,第一个被命名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 原生功能,而不必使用 grep 构建输出。如果您的 CI 解决方案不提供文件缓存系统,它也将非常有用,因为它使用远程注册表。

于 2018-04-22T14:33:06.470 回答