这是我对当地发展的
Dockerfile
:
FROM node:12-alpine
WORKDIR /usr/app
ENV __DEV__ 1
COPY package.json ./
COPY yarn.lock ./
RUN yarn --frozen-lockfile
COPY tsconfig.json ./
COPY nodemon.json ./
RUN apk add --no-cache tini
ENTRYPOINT ["/sbin/tini", "--"]
CMD [ "yarn", "dev" ]
这就是我构建它的方式:
docker build --rm -f Dockerfile.dev --tag my-app .
这就是我的运行方式:
docker run --rm -it --volume $(pwd)/src:/usr/app/src -p 3000:3000 my-app
仅当
src
文件夹之外的内容发生更改时,我才需要构建它。例如,当我安装节点模块时。如何制作 yarn
将模块缓存在某处,这样就不会在每个构建上提取所有模块。
使用 Docker 构建下一代容器是使用 Buildkit。我建议使用它,特别是因为它对缓存问题有一个优雅的解决方案。目前在普通 Docker 中确实没有一个好的解决方案;虽然你可以解决这个问题,但它非常麻烦。
我将在这里列出这两种解决方案:
Tarun 的答案是在正确的轨道上,但有一个更干净的方法来做到这一点。 Buildkit 支持将挂载指定为缓存。一旦您设置 Docker 使用 Buildkit,我们需要做的就是:
...
RUN --mount=type=cache,target=/root/.yarn YARN_CACHE_FOLDER=/root/.yarn yarn install
...
这将自动提取上一次运行的缓存,如果尚不存在或已过期,则创建它。就这么简单。
或者,如果无法使用 Buildkit,则可以使用 vanilla Docker。我们在这里可以做的最好的事情是使用
COPY
指令复制位于构建上下文中的某种“缓存”。例如,如果我们在构建上下文的根目录中创建一个目录.yarn_cache
,那么我们可以提供一个缓存:
...
COPY .yarn_cache /root/.yarn
RUN yarn --frozen-lockfile
...
构建映像时,此外部缓存将不会更新,并且需要在映像之外初始化并定期更新。您可以使用以下 shell 命令来执行此操作(清除任何本地
node_modules
)第一次运行时强制其预热缓存):
$ YARN_CACHE_FOLDER=.yarn_cache yarn install
虽然这可行,但它非常 hacky 并且有一些缺点:
.yarn_cache
目录需要包含在构建上下文中,这可能会非常慢,更不用说它必须在每个构建上执行此操作,即使没有任何更改。出于这些原因,首选前一种解决方案。
额外专业提示: 在上述任一情况下包含纱线缓存仍会将其保留在最终图像中,从而增加其大小。如果您使用多阶段构建,可以缓解这个问题:
# syntax = docker/dockerfile:1.2
FROM node:12-alpine as BUILDER
WORKDIR /usr/app
COPY package.json ./
COPY yarn.lock ./
RUN --mount=type=cache,target=/root/.yarn YARN_CACHE_FOLDER=/root/.yarn yarn --frozen-lockfile
FROM node:12-alpine
WORKDIR /usr/app
COPY --from=BUILDER node_modules ./node_modules
COPY package.json ./
COPY yarn.lock ./
COPY tsconfig.json ./
COPY nodemon.json ./
RUN apk add --no-cache tini
ENTRYPOINT [ "/sbin/tini", "--" ]
ENV __DEV__=1
CMD [ "yarn", "dev" ]
Tarun Lalwani 和 SteveGoob 的答案很棒,但他们遗漏了一个重要的细节,人们在并行构建许多容器时可能会遇到这一问题。
在我的例子中,我使用buildx bake
命令并行地为两个架构构建了包含许多容器的 docker compose 文件:
docker buildx bake -f ./docker-compose.yml --set *.platform=linux/amd64,linux/arm64/v8 --pull --push
如果我按照建议插入 --mount
参数,构建将会失败,因为 buildx 将尝试并行执行几个
yarn install
,这会使缓存不一致并完全破坏它。所以我稍微改变了 RUN 命令。这是新版本:
RUN --mount=type=cache,target=/usr/local/share/.cache/yarn/v6,sharing=locked yarn install
首先,我决定不创建自己的缓存目录,而是挂载到默认目录。我怎么得到默认的呢?我只是跑步
docker run -it node:18-alpine yarn cache dir
它打印了纱线缓存目录的当前路径。就我而言(也可能是大多数其他人),它将是/usr/local/share/.cache/yarn/v6
。因此无需创建任何额外的文件夹并将其作为环境变量传递。下一步是将
sharing=locked
参数添加到
--mount
。使用此参数,它将按顺序等待每个并行安装。第一个(对于第一个容器和第一个架构)将拉取所有包,将它们保存到缓存中,接下来的所有
yarn install
将重用该缓存。如果你不喜欢它们互相等待,你可以使用
sharing=private
和一些冗余,这将为每个容器+架构对创建自己的缓存。文档中的原始信息
https://docs.docker.com/develop/develop-images/build_enhancements/
--buildkit 中的 mount=type=cache
Yarn 可以缓存构建期间下载的包。查看您可用的所有选项
https://classic.yarnpkg.com/en/docs/cli/cache/
YARN_CACHE_FOLDER=<path> yarn <command>
所以你会在你的dockerfile
RUN --mount=type=bind,source=./.yarn,target=/root/.yarn,rw YARN_CACHE_FOLDER=/root/.yarn yarn install
您可以提前在 dockerfile 中使用 ENV
,这样您就不需要一次又一次地重复
YARN_CACHE_FOLDER