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

Posted

技术标签:

【中文标题】构建容器时如何制作纱线缓存模块?【英文标题】:How do I make yarn cache modules when building containers? 【发布时间】:2021-04-30 23:11:48 【问题描述】:

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

【问题讨论】:

【参考方案1】:

使用 Docker 构建容器的下一代正在使用 Buildkit。我推荐使用它,特别是因为它有一个优雅的缓存问题解决方案。目前在 vanilla Docker 中确实没有一个好的解决方案。虽然您可以解决它,但它非常麻烦。

我将在这里列出这两种解决方案:

使用 Buildkit

Tarun's answer 走在正确的轨道上,但有一种更简洁的方法。 Buildkit 支持specifying a mount as a cache。一旦你set up Docker to use 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

虽然这可行,但它非常老套,并且有一些缺点:

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

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


Bonus Pro Tip:在上述任何一种情况下都包含纱线缓存,但仍将其保留在最终图像中,从而增加其大小。如果您使用多阶段构建,则可以缓解此问题:

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

【讨论】:

Vanilla Docker 不可移植——您在与容器可能运行的环境不同的环境中构建 node_modules。节点包能够指定它们安装在哪个操作系统或架构上,因此这个解决方案会出现问题。【参考方案2】:

你可以使用 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

【讨论】:

以上是关于构建容器时如何制作纱线缓存模块?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 GitHub Actions 中缓存纱线包

Docker制作镜像

docker如何制作自己的镜像

如何在 docker-compose 中制作 React 应用程序?构建步骤完成后容器正在退出

Docker镜像构建

纱线在构建中找不到模块'logform'winston