【问题标题】:How to use docker build cache when version bumping a React app?版本碰撞 React 应用程序时如何使用 docker build cache?
【发布时间】:2021-01-05 18:11:11
【问题描述】:

当 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】:

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


    解决方案一: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 中提取dependenciesdevDependencies 标签并将这些标签复制到一个新文件中,例如package-dependencies.json

    例如package-dependencies.json:

    {
      "dependencies": {
        "react": "^16.13.1"
      },
      "devDependencies": {
        "gulp": "^4.0.2",
      }
    }
    

    DockerfileCOPYpackage-dependencies.jsonpackage-lock.json中安装依赖。然后,复制原始的package.json。除非package-lock.jsonpackage.jsondependencies/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。这些解决方案可以一起使用,以进一步加快构建时间。

    【讨论】:

    • 谢谢,好的,当我将依赖项提取到一个单独的文件中时,假设主机系统将创建此文件,但它将创建为一个新文件,docker是否检查文件的内容缓存或文件创建日期?在下一个 npm install 阶段,它会缓存正确 嗯,好吧,让我试试这个。
    • @webmaster 应该是检查相关文件的内容来导出层缓存,而不是文件创建或修改日期。
    • 我想我找到了我的问题,这是因为我在我的 docker 映像的最顶部包含了 REACT_APP_BUILD_NUMBER,而且每个构建当然是不同的 :)
    猜你喜欢
    • 1970-01-01
    • 2015-12-22
    • 2023-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-06
    • 2011-08-14
    相关资源
    最近更新 更多