如何在不发布的情况下在 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-externals
和 generate-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应用程序?