我正在使用一个非常标准的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仅在依赖项更改时运行?
您可以在 Dockerfile 中添加额外的“准备”步骤,以创建一个固定字段的临时package.json文件"version"。然后在安装依赖项时使用该文件,然后替换为“real” package.json。
\n由于所有这些都发生在 Docker 构建过程中,因此不会触及您的实际源存储库(因此您可以npm_package_version在构建期间和运行 docker 脚本时使用环境变量,例如标记),并且解决方案是可移植的:
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\nRun Code Online (Sandbox Code Playgroud)\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\nRun 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\nRun Code Online (Sandbox Code Playgroud)\n
这是我对此的看法,基于其他答案,但更短,并且使用了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)
我提取物dependencies和devDependencies一个单独的文件中的字段,然后在下次生成步骤,我从前面的步骤将它复制为package.json(COPY --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)
| 归档时间: |
|
| 查看次数: |
644 次 |
| 最近记录: |