在不使docker缓存无效的情况下碰撞package.json版本

Jac*_*Guy 7 node.js docker

我正在使用一个非常标准的Dockerfile来容器化Node.js应用程序:

# Simplified version
FROM node:alpine

# Copy package.json first for docker build's layer caching
COPY package.json package-lock.json foo/
RUN npm install

COPY src/ foo/
RUN npm run build
Run Code Online (Sandbox Code Playgroud)

将我COPY分为两部分是有利的,因为它允许Docker缓存(长)npm install步骤。

但是最近,我开始package.json使用semver修改我的版本。这样做的副作用是使Docker缓存无效,从而npm install大大延长了我的构建时间。

我是否可以使用其他替代缓存策略,以便npm install仅在依赖项更改时运行?

Sta*_*nas 9

您可以在 Dockerfile 中添加额外的“准备”步骤,以创建一个固定字段的临时package.json文件"version"。然后在安装依赖项时使用该文件,然后替换为“real” package.json
\n由于所有这些都发生在 Docker 构建过程中,因此不会触及您的实际源存储库(因此您可以npm_package_version在构建期间和运行 docker 脚本时使用环境变量,例如标记),并且解决方案是可移植的:

\n\n

Dockerfile:

\n\n
# PREPARATION\nFROM node:lts-alpine as preparation\nCOPY package.json package-lock.json ./\n# Create temporary package.json where version is set to 0.0.0\n# \xe2\x80\x93 this way the cache of the build step won\'t be invalidated\n# if only the version changed.\nRUN ["node", "-e", "\\\nconst pkg = JSON.parse(fs.readFileSync(\'package.json\', \'utf-8\'));\\\nconst pkgLock = JSON.parse(fs.readFileSync(\'package-lock.json\', \'utf-8\'));\\\nfs.writeFileSync(\'package.json\', JSON.stringify({ ...pkg, version: \'0.0.0\' }));\\\nfs.writeFileSync(\'package-lock.json\', JSON.stringify({ ...pkgLock, version: \'0.0.0\' }));\\\n"]\n\n# BUILD\nFROM node:lts-alpine as build\n# Install deps, using temporary package.json from preparation step\nCOPY --from=preparation package.json package-lock.json ./\nRUN npm ci\n# Copy source files (including "real" package.json) and build app\nCOPY . .\nRUN npm run build\n
Run Code Online (Sandbox Code Playgroud)\n\n


\n

\n\n

如果你认为内联 Node 脚本是不确定的(我喜欢它,因为这样可以在 Dockerfile 中找到整个 Docker 构建过程),你当然可以将其提取到一个单独的 JS 文件中:

\n\n

创建-tmp-pkg.js:

\n\n
# PREPARATION\nFROM node:lts-alpine as preparation\nCOPY package.json package-lock.json ./\n# Create temporary package.json where version is set to 0.0.0\n# \xe2\x80\x93 this way the cache of the build step won\'t be invalidated\n# if only the version changed.\nRUN ["node", "-e", "\\\nconst pkg = JSON.parse(fs.readFileSync(\'package.json\', \'utf-8\'));\\\nconst pkgLock = JSON.parse(fs.readFileSync(\'package-lock.json\', \'utf-8\'));\\\nfs.writeFileSync(\'package.json\', JSON.stringify({ ...pkg, version: \'0.0.0\' }));\\\nfs.writeFileSync(\'package-lock.json\', JSON.stringify({ ...pkgLock, version: \'0.0.0\' }));\\\n"]\n\n# BUILD\nFROM node:lts-alpine as build\n# Install deps, using temporary package.json from preparation step\nCOPY --from=preparation package.json package-lock.json ./\nRUN npm ci\n# Copy source files (including "real" package.json) and build app\nCOPY . .\nRUN npm run build\n
Run Code Online (Sandbox Code Playgroud)\n\n

并将准备步骤更改为:

\n\n
# PREPARATION\nFROM node:lts-alpine as preparation\nCOPY package.json package-lock.json create-tmp-pkg.js ./\n# Create temporary package.json where version is set to "0.0.0"\n# \xe2\x80\x93 this way the cache of the build step won\'t be invalidated\n# if only the version changed.\nRUN node create-tmp-pkg.js\n
Run Code Online (Sandbox Code Playgroud)\n


n3t*_*0de 9

这是我对此的看法,基于其他答案,但更短,并且使用了jq

Dockerfile:

FROM endeveit/docker-jq AS deps

# /sf/answers/4094120341/
# To prevent cache invalidation from changes in fields other than dependencies

COPY package.json /tmp

RUN jq '{ dependencies, devDependencies }' < /tmp/package.json > /tmp/deps.json

FROM node:12-alpine

WORKDIR /app

COPY --from=deps /tmp/deps.json ./package.json
COPY package-lock.json .

RUN npm ci
# https://docs.npmjs.com/cli/ci.html#description

COPY . .

RUN npm run build

LABEL maintainer="Alexey Vishnyakov <n3tn0de@gmail.com>"
Run Code Online (Sandbox Code Playgroud)

我提取物dependenciesdevDependencies一个单独的文件中的字段,然后在下次生成步骤,我从前面的步骤将它复制为package.jsonCOPY --from=deps /tmp/deps.json ./package.json)。

After RUN npm ci,COPY . .将覆盖掉package.json原来的内容(您可以通过添加RUN cat package.jsonafterCOPY . .命令来测试它。

需要注意的是NPM-脚本命令喜欢postinstall将无法运行,因为他们不存在在文件中npm ci并且如果npm ci 从运行root和无--unsafe-perm

COPY . .或/和(如果需要)之后运行命令通过jq(更改命令将使缓存层无效)或添加--unsafe-perm

Dockerfile:

FROM endeveit/docker-jq AS deps

COPY package.json /tmp

RUN jq '{ dependencies, devDependencies, peerDependencies, scripts: (.scripts | { postinstall }) }' < /tmp/package.json > /tmp/deps.json
# keep postinstall script 

FROM node:12-alpine

WORKDIR /app

COPY --from=deps /tmp/deps.json ./package.json
COPY package-lock.json .

# RUN npm ci --unsafe-perm 
# allow postinstall to run from root (security risk)

RUN npm ci
# https://docs.npmjs.com/cli/ci.html#description

RUN npm run postinstall

...
Run Code Online (Sandbox Code Playgroud)