如何在GitLab CI/CD中缓存pnpm store与node_modules并优化依赖安装
问题根源分析
- GitLab CI缓存配置无效:当前job使用
docker:20.10.17镜像,该镜像无Node.js环境,node_modules和pnpm store是在Docker构建过程中生成于容器内部,而非job的工作目录,因此GitLab无法捕获这些文件进行缓存(日志中的警告已明确说明这点)。 - 未启用Docker构建缓存:即使
package.json未变更,每次Docker构建都是全新环境,无法复用之前的依赖安装层。
解决方案
方案一:启用Docker构建缓存(最直接有效)
利用Docker自身的层缓存机制,结合--cache-from参数复用已构建的镜像层,跳过依赖安装步骤。
- 修改
.gitlab-ci.yml的script部分,启用缓存拉取和构建缓存:
script: - docker login -u $CI_DEPLOY_USER -p $CI_DEPLOY_PASSWORD $CI_REGISTRY # 拉取最新镜像作为缓存源,不存在则忽略 - docker pull $TAG_LATEST_BACKEND_MANAGER || true # 构建时指定缓存源,复用已有的依赖安装层 - docker build --cache-from $TAG_LATEST_BACKEND_MANAGER -f $BACKEND_MANAGER_DOCKERFILE_PATH -t $TAG_LATEST_BACKEND_MANAGER . - docker push $TAG_LATEST_BACKEND_MANAGER
- 保持Dockerfile层顺序最优:当前Dockerfile已将
COPY package.json放在RUN pnpm install之前,这是正确的——只要package.json内容不变,Docker就会复用该层,跳过依赖安装。
方案二:缓存pnpm store(进一步加速依赖安装)
如果需要缓存pnpm的全局存储,减少重复下载包的时间,可通过以下配置实现:
- 修改Dockerfile,指定pnpm store路径到项目目录(方便GitLab缓存捕获):
FROM node:alpine As development # 指定pnpm store路径为项目内的.pnpm-store ENV PNPM_STORE_PATH="/usr/src/app/.pnpm-store" ENV PNPM_HOME="/usr/local/bin" WORKDIR /usr/src/app COPY package.json ./ RUN npm i -g pnpm # 安装时指定store目录 RUN pnpm install --store-dir $PNPM_STORE_PATH COPY . . RUN pnpm build:nbm FROM node:alpine As production ARG NODE_ENV=production ENV NODE_ENV=${NODE_ENV} ENV PNPM_STORE_PATH="/usr/src/app/.pnpm-store" ENV PNPM_HOME="/usr/local/bin" WORKDIR /usr/src/app COPY package.json ./ RUN npm i -g pnpm RUN pnpm install --prod --store-dir $PNPM_STORE_PATH COPY --from=development /usr/src/app/dist ./dist CMD [ "node", "./dist/apps/backend-manager/main" ]
- 修改
.gitlab-ci.yml的缓存配置,基于package.json内容生成缓存键,并缓存.pnpm-store:
cache: key: # 基于package.json内容生成唯一缓存键,仅当文件变更时更新缓存 files: - ./apps/backend-manager/package.json paths: - .pnpm-store/ # 保留缓存,避免被自动清理 policy: pull-push backend-manager-build: # 其他配置保持不变 script: - docker login -u $CI_DEPLOY_USER -p $CI_DEPLOY_PASSWORD $CI_REGISTRY - docker pull $TAG_LATEST_BACKEND_MANAGER || true # 构建时挂载本地缓存的pnpm store到容器内 - docker build --mount=type=bind,source=.pnpm-store,target=/usr/src/app/.pnpm-store --cache-from $TAG_LATEST_BACKEND_MANAGER -f $BACKEND_MANAGER_DOCKERFILE_PATH -t $TAG_LATEST_BACKEND_MANAGER . - docker push $TAG_LATEST_BACKEND_MANAGER
额外优化建议
- 合并Dockerfile中重复的pnpm安装步骤:新增一个基础阶段,避免在development和production阶段重复安装pnpm:
FROM node:alpine As pnpm-base ENV PNPM_HOME="/usr/local/bin" RUN npm i -g pnpm FROM pnpm-base As development # ... 后续development阶段内容 FROM pnpm-base As production # ... 后续production阶段内容
- 确保
$BACKEND_MANAGER_RULES_CHANGES_PATH包含./apps/backend-manager/package.json,这样只有当相关文件变更时才触发构建(按需配置)。
内容的提问来源于stack exchange,提问作者scripter
相关产品推荐
相关产品推荐

