React 应用版本升级时如何使用 docker 构建缓存?

问题描述 投票:0回答:1

当 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;"]
docker npm caching build package.json
1个回答
9
投票

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


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

Docker BuildKit 使用实验性

RUN --mount=type=cache
标志可以部分缓解此问题。它支持在映像构建过程中可重用的缓存挂载。

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

  • 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
    目录will存在)。
  • 如果需要,可以清除构建缓存,请参阅此 Docker 论坛帖子
  • 不建议缓存
    node_modules
    。删除
    package.json
    中的依赖关系可能无法正确传播。如果尝试的话,您的里程可能会有所不同。

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

在主机上,脚本仅从

dependencies
中提取
devDependencies
package.json
标签,并将这些标签复制到新文件中,例如
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 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
    

解决方案三:以上全部

解决方案我将在本地启用 npm 依赖项缓存,以加快依赖项获取速度。仅当依赖项或开发依赖项更新时,解决方案 II 才会触发

npm ci
/
npm install
。这些解决方案可以一起使用,以进一步加快构建时间。

© www.soinside.com 2019 - 2024. All rights reserved.