我正在研究一种在 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 build
for的输出--->
,这是 Docker 宣布层哈希和缓存信息的方式。我去掉了缓存消息和箭头,只留下了所有构建阶段的完整构建层哈希列表:
docker build -t imagename . | grep '\-\-\->' | grep -v 'Using cache' | sed -e 's/[ >-]//g'
(我实际上做了两次构建——一次是正确的构建 CI 步骤,第二次是收集哈希值。我可以只做一次,但是在单独的步骤中进行实际构建感觉很好。第二次构建将总是被缓存,并且只需要几秒钟即可运行)。
这可以改进吗,也许使用 Docker 命令?