在GitHub Action自定义脚本构建Docker镜像时,如何缓存npm/yarn依赖与Docker层?
问题描述
我需要在GitHub Action的Docker镜像中编译React单页应用,希望优化缓存与构建时间,当前情况如下:
- 通过自定义脚本在GitHub Action中执行
docker build; - Dockerfile中调用yarn(yarn 1)安装node_modules。
我的GitHub Action配置:
name: Build and push docker image on AWS on: push: jobs: build: steps: [...] - name: Build docker images & push to AWS run: ./scripts/deploy/build_and_push_docker_image_to_aws.sh env: DOCKER_BUILDKIT: 1
注:不想直接通过GitHub Action构建Docker镜像(已有相关方案),因为需要完成重复镜像检查、推送到AWS等额外操作,且希望脚本可在GitHub Action外运行以提升可维护性。
我的build_and_push_docker_image_to_aws.sh脚本:
[...] docker build \ --file ./docker/nginx/Dockerfile \ --build-arg FRIENDLY_TAG=$(./scripts/get_friendly_tag.sh) \ --tag xxxx \ docker/nginx/. [...]
Dockerfile内容:
FROM node:15.14 as build-deps COPY package.json yarn.lock .yarnrc ./ RUN --mount=type=secret,id=npmrc,target=/root/.npmrc yarn
我的思路是完成两项操作:
- 在GitHub Action与docker build间共享Docker层缓存,实现
docker/setup-buildx-action@v1+docker/build-push-action@v2的缓存效果,但通过bash脚本执行; - 在GitHub Action运行器与Docker上下文间共享本地node_modules目录,并通过GitHub Action缓存该目录(类似
docker build --secret的作用,但针对文件夹)。
请问我的思路是否正确?现有方案均直接通过特定GitHub Action构建Docker镜像,无法直接复用。
解决方案
你的思路完全正确,以下是具体实现步骤:
一、实现Docker层缓存(替代build-push-action的缓存效果)
因为你已启用Docker BuildKit(设置了DOCKER_BUILDKIT: 1),可以通过以下两种方式实现层缓存:
1. 用GitHub Cache存储Docker构建层
在GitHub Action的steps中添加缓存步骤,持久化Docker BuildKit的缓存目录:
- name: Cache Docker layers uses: actions/cache@v3 with: path: /tmp/.buildx-cache key: ${{ runner.os }}-buildx-${{ github.sha }} restore-keys: | ${{ runner.os }}-buildx-
修改build_and_push_docker_image_to_aws.sh中的docker build命令,添加BuildKit缓存参数:
docker build \ --file ./docker/nginx/Dockerfile \ --build-arg FRIENDLY_TAG=$(./scripts/get_friendly_tag.sh) \ --tag xxxx \ --cache-from type=local,src=/tmp/.buildx-cache \ --cache-to type=local,dest=/tmp/.buildx-cache,mode=max \ docker/nginx/.
2. 可选:用AWS ECR作为远程缓存
如果镜像最终推送到AWS ECR,可将缓存存储到ECR镜像中,规避GitHub Cache的空间限制:
# 尝试拉取已存在的缓存镜像 docker pull xxxx-cache || true docker build \ --file ./docker/nginx/Dockerfile \ --build-arg FRIENDLY_TAG=$(./scripts/get_friendly_tag.sh) \ --tag xxxx \ --tag xxxx-cache \ --cache-from type=registry,ref=xxxx-cache \ --cache-to type=registry,ref=xxxx-cache,mode=max \ docker/nginx/. # 推送缓存镜像到ECR docker push xxxx-cache
二、共享并缓存node_modules目录
1. 在GitHub Action中缓存node_modules
在GitHub Action steps中添加依赖缓存步骤:
- name: Cache node_modules uses: actions/cache@v3 with: path: ./docker/nginx/node_modules key: ${{ runner.os }}-yarn-${{ hashFiles('./docker/nginx/yarn.lock') }} restore-keys: | ${{ runner.os }}-yarn-
2. 在Docker构建时复用本地node_modules
修改Dockerfile,通过bind挂载本地node_modules,仅在目录不存在时执行安装:
FROM node:15.14 as build-deps COPY package.json yarn.lock .yarnrc ./ # 挂载本地node_modules,存在则复用,不存在则执行yarn安装 RUN --mount=type=bind,source=./node_modules,target=./node_modules \ --mount=type=secret,id=npmrc,target=/root/.npmrc \ if [ ! -d "node_modules" ]; then yarn; fi # 执行后续构建步骤 COPY . ./ RUN yarn build
注意事项
- 确保Docker BuildKit始终处于启用状态;
- 依赖缓存的key基于
yarn.lock生成,确保仅当依赖变更时才重新安装; - 本地缓存挂载需配合
actions/cache,因为GitHub Action运行器环境是临时的,无法自动持久化数据。
内容的提问来源于stack exchange,提问作者Cyril Duchon-Doris
相关产品推荐
相关产品推荐

