当 React 应用程序版本发生冲突时,如何使用 docker 构建缓存?

web*_*ter 4 caching build npm docker package.json

当 package.json 或 package-lock.json 中的某些内容发生更改时,Docker 不会使用构建缓存,即使这只是文件中的版本号,也不会更改任何依赖项。

我怎样才能实现它,以便 docker 每次都使用旧的构建缓存并跳过 npm install (npm ci)?我知道 docker 会查看文件的修改日期。但 package.json 根本没有改变,只是版本号改变了。

下面是我的 Dockerfile

FROM node:10 as builder

ARG REACT_APP_BUILD_NUMBER=X
ENV REACT_APP_BUILD_NUMBER="${REACT_APP_BUILD_NUMBER}"

RUN mkdir -p /usr/src/app

WORKDIR /usr/src/app

COPY .npmrc ./
COPY package*.json ./ 

RUN npm ci

COPY . .

RUN npm run build

FROM nginx:alpine

COPY nginx/nginx.conf /etc/nginx/nginx.conf

COPY --from=builder /usr/src/app/build /usr/share/nginx/html

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]
Run Code Online (Sandbox Code Playgroud)

con*_*ion 9

以下是一些有助于缓解此问题的解决方案。每种方法都需要权衡,但它们不一定是相互排斥的——它们可以混合在一起以获得更好的整体构建性能。


解决方案一:Docker BuildKit 缓存挂载

Docker BuildKit使用实验标志可以部分缓解此问题RUN --mount=type=cache。它支持在映像构建过程中安装可重用的缓存。

这里需要注意的是,对 Docker BuildKit 的支持在 CI/开发环境之间可能会有很大差异。检查文档和构建环境以确保它有适当的支持(否则,它将出错)。以下是一些要求(但不一定是详尽的列表):

  • Docker 守护进程需要支持 BuildKit(需要 Docker 18.09+)。
  • DOCKER_BUILDKIT=1Docker BuildKit 需要通过守护进程/cli 配置显式启用或默认启用。
  • 需要在开头添加注释Dockerfile以启用实验支持:# syntax=docker/dockerfile:1.2

Dockerfile下面是一个利用此功能的示例,在本地缓存 npm 依赖项以便/usr/src/app/.npm在后续构建中重用:

# syntax=docker/dockerfile:1.2
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
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 这将在本地缓存获取的依赖项,但npm仍然需要将它们安装到node_modules目录中。对中型项目的测试表明,这确实减少了一些构建时间,但构建node_modules仍然是不可忽略的。
  • /usr/src/app/.npm不会包含在最终构建中,并且仅在构建期间可用(但是,将.npm存在一个挥之不去的目录)。
  • 如果需要,可以清除构建缓存,请参阅此 Docker 论坛帖子。
  • node_modules不建议缓存。删除依赖项package.json可能无法正确传播。如果尝试的话,您的里程可能会有所不同。

解决方案二:在复制之前安装依赖项package.json

在主机上,脚本仅从中提取dependencies和标签,并将这些标签复制到新文件中,例如.devDependenciespackage.jsonpackage-dependencies.json

例如package-dependencies.json:

# syntax=docker/dockerfile:1.2
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
Run Code Online (Sandbox Code Playgroud)

在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
# ...
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 如果互斥地使用,对于小的更改(例如版本提升),此解决方案将比第一个解决方案更快,因为它不需要重新运行npm ci。
  • package-dependencies.json将在图层历史记录中。虽然该文件的大小可以忽略不计/无关紧要,但它仍然是“浪费的空间”,因为最终图像中不需要它。
  • 需要一个快速脚本来生成package-dependencies.json. 根据构建环境的不同,这可能实施起来很烦人。这是使用 cli 实用程序的示例jq:
    cat package.json | jq -S '. | with_entries(select (.key as $k | ["dependencies", "devDependencies"] | index($k)))' > package-dependencies.json
    
    Run Code Online (Sandbox Code Playgroud)

解决方案三:以上全部

解决方案我将在本地启用 npm 依赖项缓存,以便更快地获取依赖项。仅当更新依赖项或开发依赖项时,解决方案 II 才会触发npm ci/ 。npm install这些解决方案可以一起使用,以进一步加快构建时间。