提问人:Yuval 提问时间:11/16/2023 最后编辑:Yuval 更新时间:11/16/2023 访问量:21
GitHub Action“gha”Docker 缓存比重新创建映像慢得多
GitHub Action `gha` Docker cache much slower than recreating the image
问:
我设置了一个 GitHub 操作来在我的 Docker 映像上构建和运行测试。
在构建 Docker 映像时,正在下载一个相当重的文件(来自 Huggingface 的 6Gb 模型)。(顺便说一句 - 我可能会将其降低到 2Gb,因为出于愚蠢的原因,权重被下载了三次。
我以为我可以通过使用缓存来加快速度。缓存可以工作,但速度要慢得多。gha
这是我设置的要点
非缓存 GitHub 操作
- name: Build Image
shell: bash
run: |
docker buildx build -t ${IMAGE_TAG} -f ./Dockerfile .
耗时 3 分 23 秒
缓存 GitHub 操作
- name: Build Image
uses: docker/build-push-action@v5
with:
push: false # do not push to remote registry
load: true # keep in local Docker registry
context: .
file: ./Dockerfile
tags: ${{env.IMAGE_TAG}}
cache-from: type=gha
cache-to: type=gha,mode=max
需要 7 分 58 秒(使用新设置执行首次提交时的初始构建时间为 12 分 53 秒)。
从 huggingface 下载 6Gb 大约需要 30 秒。从 GitHub 下载 4Gb 映像本身需要 279 秒。
这是 GitHub 的问题吗?有什么办法可以绕过它吗?
这可能与这个问题有关。
编辑:显然我不是唯一一个在Docker的GitHub上遭受这个问题的人
答:
从您提到的问题来看,您似乎需要等待 docker buildx 版本来解决此问题。
检查您是否使用的是 buildx v0.12.0,这可能会有所帮助。
与此同时(等待新的 buildx 版本),您需要避免使用该选项,因为它会导致速度显着变慢。
如果 GitHub Container Registry 提供更好的性能,请考虑将其用于某些操作。--load
您能否建议在不使用默认驱动程序的情况下实现任何其他方法?
docker-container
--load
您可以考虑使用本地注册表进行缓存:该方法涉及在一个作业中将构建的映像推送到本地注册表,并在后续作业中从那里拉取它。这比使用 GitHub 的缓存系统更快,并避免了 .--load
+------------------------+ +---------------------------+ +------------------------+
| Start Local Registry | | Build and Push to Local | | Pull from Local |
| | | Registry | | Registry in Subsequent |
| docker run registry | | docker build & push | | Job |
| │ | | │ | | │ |
| ▼ | | ▼ | | ▼ |
| Local Docker Registry | | localhost:5000/my-image | | docker pull |
+------------------------+ +---------------------------+ +------------------------+
在您的第一个作业中,启动本地 Docker 注册表容器。
- name: Start Local Registry
run: docker run -d -p 5000:5000 --name registry registry:2
然后,您将构建 Docker 映像并将其推送到本地注册表:
- name: Build and Push to Local Registry
run: |
docker build -t localhost:5000/my-image .
docker push localhost:5000/my-image
在后续作业中,从本地注册表拉取映像。
- name: Pull from Local Registry
run: docker pull localhost:5000/my-image
另一种方法:
直接保存和加载图像。您可以将 Docker 映像另存为 tarball,并使用 GitHub 的缓存缓存 tarball,而不是使用该选项。在后续作业中,您可以从缓存中恢复 tarball 并将其加载到 Docker 中。该方法可能比使用驱动程序更有效。--load
docker-container
+-----------------------+ +-----------------------+ +--------------------------+
| Build and Save Image | | Cache the Tarball | | Load Image in Subsequent |
| | | | | Job |
| docker build & save | | actions/cache | | docker load |
| │ | | │ | | │ |
| ▼ | | ▼ | | ▼ |
| my-image.tar | | Cache my-image.tar | | Restore & Load Image |
+-----------------------+ +-----------------------+ +--------------------------+
构建 Docker 映像并将其另存为 tarball。
- name: Build and Save Image
run: |
docker build -t my-image .
docker save my-image > my-image.tar
使用 GitHub Actions 的缓存来存储 tarball。
- uses: actions/cache@v2
with:
path: my-image.tar
key: ${{ runner.os }}-my-image-${{ hashFiles('**/Dockerfile') }}
从缓存中恢复 tarball 并将其加载到 Docker 中。
- name: Load Image from Tarball
run: |
if [ -f my-image.tar ]; then
docker load < my-image.tar
fi
似乎这也保存了一个 TAR 文件并加载它,这正是它的作用。直接使用 Github 缓存很有趣,但无论如何您都会构建映像。
--load
诚然,这两种方法本质上都涉及保存和加载 TAR 文件,这在概念上类似于该选项在 Docker 中的作用。但是,主要区别在于 TAR 文件的管理方式和位置:--load
本地注册表方法涉及将映像推送到在 GitHub Actions 环境中运行的本地 Docker 注册表或从中拉取映像。它通过直接与本地注册表交互来避免一些相关的 I/O 开销。
--load
直接 TAR 文件缓存使用 GitHub 自己的缓存机制来存储和检索 TAR 文件。虽然它仍然涉及构建映像并将其保存为 TAR 文件,但与使用 .
--load
在这两种情况下,目标都是绕过与驱动程序和 相关的一些性能瓶颈,但它们仍然从根本上依赖于保存和加载 Docker 映像的类似机制。docker-container
--load
评论
docker-container
--load
--load
在使用 Docker 缓存的 GitHub Actions 中遇到的速度变慢可能是由于几个因素造成的。如果缓存大小超出 GitHub Actions 的限制,可能会减慢缓存检索过程。确保缓存大小在建议的范围内。缓存命中率低,缓存经常失效或与预期层不匹配,可能会导致构建速度变慢,因为 Docker 必须获取更多数据。定期清理 Docker 映像中的旧层或不必要的层,以减小缓存大小并缩短检索时间。尝试并行化生成步骤,看看这是否能缩短整体生成时间。GitHub Actions 支持并行作业。或者尝试不同的缓存策略,例如热缓存方法,即定期按计划的方式更新缓存。
实现缓存的另一种方法:
如果要使用缓存并在后续 GitHub 作业步骤之间共享 Docker 映像,而不依赖于默认驱动程序,可以考虑使用 GitHub Container Registry 来存储和检索 Docker 映像。您可以构建并推送到 GitHub 容器注册表:docker-container
--load
第一生成 Docker 映像。使用版本或唯一标识符标记映像。将这些映像推送到 GitHub Container Registry。
例如:
- name: Build and Push Image
run: |
docker build -t ${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} .
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
docker push ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}
构建并推送后,在后续步骤中使用缓存的镜像,直接从 GitHub Container Registry 使用缓存的 Docker 镜像,而不是依赖本地缓存。
例如:
- name: Use Cached Image
run: |
docker pull ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }}
# Continue with subsequent steps using the pulled image
这样,您就可以利用 GitHub Container Registry 跨作业存储和检索 Docker 映像,从而确保一致性和可重现性。希望我已经回答了您的问题,并且 Clearfield 是解决缓存问题的好方法
评论