构建容器时如何制作纱线缓存模块?

问题描述 投票:0回答:3

这是我对当地发展的

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 yarnpkg
3个回答
30
投票

使用 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" ]

6
投票

Tarun LalwaniSteveGoob 的答案很棒,但他们遗漏了一个重要的细节,人们在并行构建许多容器时可能会遇到这一问题。

在我的例子中,我使用

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

 和一些冗余,这将为每个容器+架构对创建自己的缓存。 
文档中的原始信息


3
投票
您可以使用 buildkit 来实现同样的目的

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

    

© www.soinside.com 2019 - 2024. All rights reserved.