如何在不发布的情况下在 lerna monorepo 中构建 docker 镜像

Posted

技术标签:

【中文标题】如何在不发布的情况下在 lerna monorepo 中构建 docker 镜像【英文标题】:How to build docker images in a lerna monorepo without publishing 【发布时间】:2020-04-06 18:35:57 【问题描述】:

这个用例是 Lerna monorepos 中的分支构建和部署。

问题是 Lerna monorepos 要么在 NPM 中提升依赖项,要么使用 yarn 工作空间来收集工作空间/monorepo 的 node_modules 文件夹中的所有依赖项。这意味着由于 docker build contexts 的工作方式,在子文件夹中构建 Dockerfile 时将无法访问它们。

我想这里需要的是一种“较低”(而不是提升)功能,用于在运行 docker build 之前将包依赖项拉入 Docker/package.json 项目的 node_modules 中。

问题是,有没有人有更好的想法,或者知道已经存在的方法来做到这一点?

【问题讨论】:

我使用的方法是将本地依赖项发布到本地 npm 服务器 (verdaccio) 并在需要构建的每个包中创建 Dockerfile 并使用 -f 选项运行 docker build 并使用本地 npm 服务器安装每个包。 这是我一直在考虑的一个选项。您对这种方法的复杂性和速度是否满意? 我们使用这个方法:***.com/questions/56294568/… 因为我只需要 dockerize 几个包(并且不使用纱线),我一直是“tar chf”。啜食 node_modules (取消引用带有“h”参数的符号链接)并将 tarball 添加到 Docker。它丑陋而缓慢,但很容易。 【参考方案1】:

这是一个很好的问题。对我来说,使用 yarn 工作空间的全部意义在于摆脱私有的 npm 注册表。

我的方法是将 webpack 与 webpack-node-externalsgenerate-package-json-webpack-plugin 结合使用,请参阅 npmjs.com/package/generate-package-json-webpack-plugin。

使用节点外部,我们可以将来自其他工作区(libs)的所有依赖项捆绑到应用程序中(这使得私有 npm 注册表过时)。使用 generate package json 插件,创建一个新的包 json,其中包含所有依赖项除了我们的工作空间依赖项。有了这个包旁边的 json 包,我们可以在 dockerfile 中执行 npm 或 yarn install。

设置 webpack 配置需要几个小时,但我认为它可以很好地扩展并保持 dockerfile 干净。

【讨论】:

【参考方案2】:

由于没有答案让我满意,我已经构建了一个 npm 包,它为 lerna 项目生成一个 Dockerfile。它使用 Docker 的 stage 特性,为每个包创建 stage。

您只需要至少两个 Dockerfile:

包含全局设置的基础 Dockerfile

Dockerfile.base:

FROM node:14 as base
COPY ./package.json ./
RUN npm i
COPY ./lerna.json ./

以及包的模板

Dockerfile.template:

FROM base as build
COPY ./package.json ./
RUN npm install
RUN --if-exists npm run build

您还可以通过在包中添加自己的 Dockerfile 来为包自定义 Dockerfile。这将用自定义 Dockerfile 替换模板。

之后你可以通过 npx 运行命令来生成你的 dockerfile:

npx lerna-dockerize

【讨论】:

【参考方案3】:

对于我自己的项目,解决方案是使用docker BuildKit先构建所有工作区,然后再利用之前构建的文件为项目工作区构建一个docker镜像。

详细来说,您已经在 docker 文件中复制了带有纱线锁的顶部 package.json,然后挑选了所需工作区的 package.json。 然后运行 ​​yarn install 和 yarn build 让一切正常运行。

这是我的项目:

# base image
FROM @myscope/base:latest as base

# set working directory
WORKDIR /app

# add `/app/node_modules/.bin` to $PATH
ENV PATH /app/node_modules/.bin:$PATH

# install and cache app dependencies
COPY ["package.json","yarn.lock", "./"]
COPY ./packages/server/package.json ./packages/server/
COPY ./packages/shared/package.json ./packages/shared/
COPY ./packages/app/package.json ./packages/app/

RUN yarn install --frozen-lockfile --non-interactive --production=false --ignore-scripts
COPY . /app
RUN yarn build



FROM nodejs:14.15 as serverapp

WORKDIR /app

COPY ["package.json","yarn.lock", "./"]
COPY ./packages/server/package.json ./packages/server/
COPY ./packages/shared/package.json ./packages/shared/

RUN yarn install --frozen-lockfile --non-interactive --production=true --ignore-scripts

# copy artifact build from the 'build environment'
COPY --from=base /app/packages/shared/dist /app/packages/shared/dist

COPY ["./packages/server/", "./packages/server/"]

WORKDIR /app/packages/server
VOLUME ["/app/packages/server/logs", "/app/packages/server/uploads"]
EXPOSE $PORT
CMD ["yarn", "start"]

shared 是一个私有工作区,它是 server 工作区的依赖项。

【讨论】:

这适用于小型工作空间,但不能很好地扩展,因为您需要为所有更改重建*** docker。一旦你有几十个包和项目,这就会变得很昂贵。

以上是关于如何在不发布的情况下在 lerna monorepo 中构建 docker 镜像的主要内容,如果未能解决你的问题,请参考以下文章

如何在不登录的情况下在 Android 中发布到 Facebook Wall?

如何在不使用 AudioQueueRef 的情况下在 AudioQueue 中设置音量?

如何在不发布和Eclipse的情况下在真机上安装Android应用程序?

如何在不重新加载的情况下在同一页面上提交文件[重复]

如何在不重叠的情况下在 UIButton 中实现两个 IBAction?

如何在不堆叠回调的情况下在 jQuery 中制作动画?