我有一个由Yarn管理的monorepo,我想利用Docker缓存层来加速我的构建,为此我想首先复制package.json
和yarn.lock
个文件,运行yarn install
,然后复制其余文件。
这是我的回购结构:
packages/one/package.json
packages/one/index.js
packages/two/package.json
packages/two/index.js
package.json
yarn.lock
这是Dockerfile的感兴趣部分:
COPY package.json .
COPY yarn.lock .
COPY packages/**/package.json ./
RUN yarn install --pure-lockfile
COPY . .
问题是第3 COPY
命令不能复制任何东西,我怎样才能达到预期的效果呢?
答案 0 :(得分:9)
有一个基于multistage-build功能的解决方案:
FROM node:12.18.2-alpine3.11
WORKDIR /app
COPY ["package.json", "yarn.lock", "./"]
# Step 2: Copy whole app
COPY packages packages
# Step 3: Find and remove non-package.json files
RUN find packages \! -name "package.json" -mindepth 2 -maxdepth 2 -print | xargs rm -rf
# Step 4: Define second build stage
FROM node:12.18.2-alpine3.11
WORKDIR /app
# Step 5: Copy files from the first build stage.
COPY --from=0 /app .
RUN yarn install --frozen-lockfile
COPY . .
# To restore workspaces symlinks
RUN yarn install --frozen-lockfile
CMD yarn start
在Step 5
上,即使packages
目录中的任何文件已更改,层缓存也将被重用。
答案 1 :(得分:3)
正如official Dockerfile reference for COPY <src> <dest>
COPY指令从
<src>
复制新文件或目录,并将它们添加到路径<dest>
的容器的文件系统中。
对于你的情况
每个都可能包含通配符,匹配将使用Go的
filepath.Match
规则进行。
这是the rules。它们包含:
'*'匹配任何非分隔符字符序列
因此,请尝试在您的模式中使用*
而不是**
。
答案 2 :(得分:3)
要跟进@ FezVrasta对我的first answer的评论,如果你不能枚举Dockerfile中所有的子目录,但想要分两步复制所有文件要利用Docker缓存功能,您可以尝试以下解决方法:
package.json
文件复制到使用类似层次结构构建的单独目录(例如.deps/
),然后调用docker build …
< / LI>
yarn install --pure-lockfile
... 所有事情放在一起,这可能会导致以下文件:
### ./build.bash ###
#!/bin/bash
tag=copy-example:latest
rm -f -r .deps # optional, to be sure that there is
# no extraneous "package.json" from a previous build
find . -type d \( -path \*/.deps \) -prune -o \
-type f \( -name "package.json" \) \
-exec bash -c 'dest=".deps/$1" && \
mkdir -p -- "$(dirname "$dest")" && \
cp -av -- "$1" "$dest"' bash '{}' \;
# instead of mkdir + cp, you may also want to use
# rsync if it is available in your environment...
sudo docker build -t "$tag" .
和
### ./Dockerfile ###
FROM ...
WORKDIR /usr/src/app
# COPY package.json . # subsumed by the following command
COPY .deps .
# and not "COPY .deps .deps", to avoid doing an extra "mv"
COPY yarn.lock .
RUN yarn install --pure-lockfile
COPY . .
# Notice that "COPY . ." will also copy the ".deps" folder; this is
# maybe a minor issue, but it could be avoided by passing more explicit
# paths than just "." (or by adapting the Dockerfile and the script and
# putting them in the parent folder of the Yarn application itself...)
答案 3 :(得分:1)
使用 Docker 的新 BuildKit 执行器,可以在 Docker 上下文中使用绑定安装,然后您可以根据需要从中复制任何文件。
例如,以下代码段将所有 package.json 文件从 Docker 上下文复制到映像的 /app/
目录(以下示例中的 workdir)
不幸的是,更改挂载中的任何文件仍会导致图层缓存未命中。这可以使用多阶段方法 as presented by @mbelsky 解决,但这次不再需要显式删除。
# syntax = docker/dockerfile:1.2
FROM ... AS packages
WORKDIR /app/
RUN --mount=type=bind,target=/docker-context \
cd /docker-context/; \
find . -name "package.json" -mindepth 0 -maxdepth 4 -exec cp --parents "{}" /app/ \;
FROM ...
WORKDIR /app/
COPY --from=packages /app/ .
指定 mindepth
/maxdepth
参数以减少要搜索的目录数量,可以根据您的用例进行调整/删除。
可能需要使用环境变量 DOCKER_BUILDKIT=1
启用 BuildKit 执行器,因为传统的执行器会默默地忽略绑定安装。
有关 BuildKit 和绑定边界 can be found here 的更多信息。
答案 4 :(得分:1)
根据@Joost 的建议,我创建了一个 dockerfile
,它利用 BuildKit 的强大功能来实现以下目标:
npm install
npm install
文件中没有任何变化,则跳过 package.json
伪代码:
package.json
文件package.json
文件进行比较npm install
并缓存 package.json
文件 + node_modules
文件夹node_modules
(新鲜的或缓存的)复制到图像中的所需位置# syntax = docker/dockerfile:1.2
FROM node:14-alpine AS builder
# https://github.com/opencollective/opencollective/issues/1443
RUN apk add --no-cache ncurses
# must run as root
RUN npm config set unsafe-perm true
WORKDIR /app
# get a temporary copy of the package.json files from the build context
RUN --mount=id=website-packages,type=bind,target=/tmp/builder \
cd /tmp/builder/ && \
mkdir /tmp/packages && \
chown 1000:1000 /tmp/packages && \
find ./ -name "package.json" -mindepth 0 -maxdepth 6 -exec cp --parents "{}" /tmp/packages/ \;
# check if package.json files were changed since the last successful build
RUN --mount=id=website-build-cache,type=cache,target=/tmp/builder,uid=1000 \
mkdir -p /tmp/builder/packages && \
cd /tmp/builder/packages && \
(diff -qr ./ /tmp/packages/ || (touch /tmp/builder/.rebuild && echo "Found an updated package.json"));
USER node
COPY --chown=node:node . /app
# run `npm install` if package.json files were changed, or use the cached node_modules/
RUN --mount=id=website-build-cache,type=cache,target=/tmp/builder,uid=1000 \
echo "Creating NPM cache folders" && \
mkdir -p /tmp/builder/.npm && \
mkdir -p /tmp/builder/modules && \
echo "Copying latest package.json files to NPM cache folders" && \
/bin/cp -rf /tmp/packages/* /tmp/builder/modules && \
cd /tmp/builder/modules && \
echo "Using NPM cache folders" && \
npm config set cache /tmp/builder/.npm && \
if test -f /tmp/builder/.rebuild; then (echo "Installing NPM packages" && npm install --no-fund --no-audit --no-optional --loglevel verbose); fi && \
echo "copy cached NPM packages" && \
/bin/cp -rfT /tmp/builder/modules/node_modules /app/node_modules && \
rm -rf /tmp/builder/packages && \
mkdir -p /tmp/builder/packages && \
cd /app && \
echo "Caching package.json files" && \
find ./ -name "package.json" -mindepth 0 -maxdepth 6 -exec cp --parents "{}" /tmp/builder/packages/ \; && \
(rm /tmp/builder/.rebuild 2> /dev/null || true);
注意:
我只使用根文件夹的 node_modules
,就我而言,内部文件夹中的所有包都被提升到根目录
答案 5 :(得分:0)
正如我在上面的评论中所述,使用带有glob的COPY
指令来指定包含具有相同名称的文件的多个子文件夹将不起作用;但是一个简单的解决方案是通过写下这样的东西来硬编码子文件夹的路径:
# ...
WORKDIR /usr/src/app
COPY package.json .
COPY yarn.lock .
COPY packages/one/package.json packages/one/
COPY packages/two/package.json packages/two/
RUN yarn install --pure-lockfile
COPY . .
# ...
我记得COPY
指令负责创建目标文件夹(即没有需要RUN mkdir -p packages/one
左右)
答案 6 :(得分:-3)
只需使用.dockerignore
过滤掉不需要的文件。 refer this reference
在您的情况下,将其添加到您的.dockerignore。
*.js
any file to skip copy
我认为您的文件位于/home/package.json
,并希望将这些文件复制到docker中的/dest
。
Dockerfile看起来像这样。
COPY /home /dest
这会将所有文件复制到/ home目录,但.dockerignore