以下是一些有助于缓解此问题的解决方案。每个都有权衡,但它们不一定是相互排斥的 - 它们可以混合在一起以获得更好的整体构建性能。
解决方案一:Docker BuildKit 缓存挂载
Docker BuildKit 使用实验性RUN --mount=type=cache 标志可以部分缓解此问题。它支持在映像构建过程中可重复使用的缓存挂载。
这里的一个重要警告是,对 Docker BuildKit 的支持在 CI/开发环境之间可能会有很大差异。检查文档和构建环境以确保它有适当的支持(否则会出错)。以下是一些要求(但不一定是详尽的清单):
- Docker 守护进程需要支持 BuildKit(需要 Docker 18.09+)。
- Docker BuildKit 需要通过
DOCKER_BUILDKIT=1 显式启用,或者默认情况下通过守护程序/cli 配置启用。
-
Dockerfile 的开头需要注释以启用实验支持:# syntax=docker/dockerfile:experimental
这里是一个示例 Dockerfile,它利用了这个特性,将 npm 依赖项缓存到本地 /usr/src/app/.npm 以便在后续构建中重用:
# syntax=docker/dockerfile:experimental
FROM node
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package.json package-lock.json /usr/src/app
RUN --mount=type=cache,target=/usr/src/app/.npm \
npm set cache /usr/src/app/.npm && \
npm ci
注意事项:
- 这将在本地缓存获取的依赖项,但
npm 仍需要将它们安装到node_modules 目录中。对中型项目的测试表明,这确实节省了一些构建时间,但构建 node_modules 仍然不可忽略。
-
/usr/src/app/.npm不会包含在最终构建中,并且仅在构建期间可用(但是,一个挥之不去的 .npm 目录将存在)。
- 如果需要,可以清除构建缓存,请参阅this Docker forum spost。
- 不推荐缓存
node_modules。删除 package.json 中的依赖项可能无法正确传播。如果尝试,您的里程可能会有所不同。
方案二:复制package.json之前安装依赖
在主机上,脚本仅从package.json 中提取dependencies 和devDependencies 标签并将这些标签复制到一个新文件中,例如package-dependencies.json。
例如package-dependencies.json:
{
"dependencies": {
"react": "^16.13.1"
},
"devDependencies": {
"gulp": "^4.0.2",
}
}
在Dockerfile、COPY、package-dependencies.json和package-lock.json中安装依赖。然后,复制原始的package.json。除非package-lock.json 或package.json 的dependencies/devDependencies 标记发生更改,否则这些层将被缓存并从以前的构建中重复使用,这意味着对package.json 的微小更改将不需要运行npm ci /npm install.
这是一个例子:
FROM node
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# copy dependency list and locked dependencies
COPY package-dependencies.json package-lock.json /usr/src/app/
# install dependencies
RUN npm ci
# copy over the full package configuration
COPY package.json /usr/src/app/
# ...
RUN npm run build
# ...
注意事项:
解决方案三:以上所有
解决方案我将在本地启用缓存 npm 依赖项以更快地获取依赖项。解决方案 II 只会在更新依赖项或开发依赖项时触发 npm ci/npm install。这些解决方案可以一起使用,以进一步加快构建时间。