docker 在构建容器时如何制作Yarn缓存模块?

neskvpey  于 2023-02-18  发布在  Docker
关注(0)|答案(3)|浏览(261)

这是我的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在某个地方缓存模块,这样它就不会在每次构建时拉取所有模块。

yshpjwxd

yshpjwxd1#

用Docker构建容器的下一代是使用Buildkit,我推荐使用它,特别是因为它对缓存问题有一个优雅的解决方案,目前在普通Docker中确实没有一个好的解决方案;虽然您可以绕过它,但它非常麻烦。
我将在此列出这两种解决方案:

带构建工具包

Tarun's answer是正确的,但有一个更干净的方法。Buildkit支持将挂载指定为缓存。一旦您设置Docker以使用Buildkit,我们需要做的就是:

...
RUN --mount=type=cache,target=/root/.yarn YARN_CACHE_FOLDER=/root/.yarn yarn install
...

这将自动拉入上一次运行的缓存,或者在缓存不存在或已过期时创建缓存。

香草 Docker

或者,如果不使用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

现在,虽然这个工作,它是非常黑客y和一些缺点:

  • 您需要手动创建和更新该高速缓存。
  • 整个.yarn_cache目录需要包含在构建上下文中,这可能非常慢,更不用说它必须在每次构建时都这样做,* 即使没有任何更改 *。

由于这些原因,前一种解决方案是优选的。

**额外的专业提示:**在上面的任何一种情况下包含Yarn缓存仍然会将其留在最终图像中,从而增加其大小。如果您使用多级构建,您可以缓解此问题:

# 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" ]
tuwxkamq

tuwxkamq2#

您可以使用buildkit实现相同的功能
https://docs.docker.com/develop/develop-images/build_enhancements/
--mount=type=cache in buildkit
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

rsaldnfx

rsaldnfx3#

Tarun LalwaniSteveGoob的答案很好,但是他们忽略了一个重要的细节,人们可能会面临何时并行构建许多容器的问题。
在我的例子中,我使用buildx bake命令为两个体系结构并行地构建包含许多容器的docker组合文件:

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

它打印了当前的yarn缓存目录路径,在我的例子中(可能在大多数其他例子中),它将是/usr/local/share/.cache/yarn/v6,所以不需要创建任何额外的文件夹并将其作为env变量传递。
下一步是在--mount中添加sharing=locked参数,使用这个参数,它将依次等待每个并行安装,第一个(用于第一个容器和第一个架构)将拉取所有的包,将它们保存该高速缓存中,所有接下来的yarn install将重用该缓存。
如果你不喜欢它们互相等待的时候,你可以使用sharing=private和一些冗余,这将为每个容器+拱对创建自己的缓存。

相关问题