使用 docker 构建 Yarn 工作区

Posted

技术标签:

【中文标题】使用 docker 构建 Yarn 工作区【英文标题】:Yarn workspaces build with docker 【发布时间】:2021-08-21 12:05:55 【问题描述】:

考虑以下纱线工作区的文件结构:

.
├── docker-compose.yaml
├── package.json
├── packages
│   └── pkg-1
│       ├── dist
│       ├── package.json
│       ├── src
│       └── tsconfig.json
├── services
│   ├── api-1
│   │   ├── dist
│   │   ├── Dockerfile
│   │   ├── package.json
│   │   ├── src
│   │   ├── tsconfig.json
│   │   └── yarn.lock
│   └── client-1
│       ├── package.json
│       ├── src
│       └── yarn.lock
├── tsconfig.json
└── yarn.lock

我已经编写了 Dockerfile 来为 api-1 创建镜像:

ARG APP_DIR=/usr/app

# Build stage
FROM node:16.2-alpine AS build

ARG APP_DIR

WORKDIR $APP_DIR
COPY package.json ./
COPY yarn.lock ./
COPY tsconfig.json ./

WORKDIR $APP_DIR/packages/pkg-1
COPY packages/pkg-1/package.json ./
RUN yarn --pure-lockfile --non-interactive
COPY packages/pkg-1/tsconfig.json ./
COPY packages/pkg-1/src/ ./src
RUN yarn build

WORKDIR $APP_DIR/services/api-1
COPY services/api-1/package.json ./
COPY services/api-1/yarn.lock ./
RUN yarn --pure-lockfile --non-interactive
COPY services/api-1/tsconfig.json ./
COPY services/api-1/src/ ./src
RUN yarn build

# Production stage
FROM node:16.2-alpine AS prod

ARG APP_DIR

WORKDIR $APP_DIR
COPY --from=build $APP_DIR/package.json ./
COPY --from=build $APP_DIR/yarn.lock ./

WORKDIR $APP_DIR/packages/pkg-1
COPY --from=build $APP_DIR/packages/pkg-1/package.json ./
RUN yarn --pure-lockfile --non-interactive --production
COPY --from=build $APP_DIR/packages/pkg-1/dist ./dist

WORKDIR $APP_DIR/services/api-1
COPY --from=build $APP_DIR/services/api-1/package.json ./
COPY --from=build $APP_DIR/services/api-1/yarn.lock ./
RUN yarn --pure-lockfile --non-interactive --production
COPY --from=build $APP_DIR/services/api-1/dist ./dist

CMD ["node", "dist"]

构建从根 docker-compose.yaml 运行以获得正确的上下文:

services:
  api-1:
    image: project/api-1
    container_name: api-1
    build:
      context: ./
      dockerfile: ./services/api-1/Dockerfile
      target: prod
    ports:
      - 3000:3000

它正在工作,但这样在应用程序增长时会有很多重复。问题在于包的构建方式。

包可以是例如客户端服务之间使用的规范化组件集合或api服务之间使用的规范化错误集合。

每当我要构建一些服务时,我都需要首先构建它的依赖包,这是不必要的重复任务。更不用说各个包的构建步骤在使用该包的每个服务的 Dockerfile 中一遍又一遍地定义。

所以我的问题是。有没有办法创建例如将用于构建服务的包的图像以避免在服务 Dockerfile 中定义包的构建步骤?

【问题讨论】:

【参考方案1】:

似乎您正在寻找可以让您选择拥有“父”package.json 的东西,因此您只需在其中调用“build”并构建整个依赖关系树。

例如:

- package.json // root package
  | - a
    | - package.json // module a package
  | - b
    | - package.json // module b package

您可能需要查看以下内容:

npm workspaces lerna

两种支持结构都像上面提到的那样,lerna 具有更多功能。要快速了解差异,请看这里:Is Lerna needed anymore with NPM 7.0.0's workspaces?

【讨论】:

我使用带有 lerna 的纱线工作区,所以我有父 package.json。我只是不小心把它从我的文件结构图中去掉了,但你可以在我的 Dockerfile 中阅读它。我正在寻找的是类似于 docker 的父构建(包构建)。 @TomášVavřinka 抱歉,我好像错过了你的问题。 “父构建(包构建)”是什么意思?您只是想构建相关的依赖关系而不是整个图,还是有什么问题? 没问题。当我为 service-1 构建 docker 映像时,我需要在 Dockerfile 中定义 pkg-1 的构建步骤。当我为 service-2 构建 docker 映像时,我还需要在 Dockerfile 中定义 pkg-1 的构建步骤,因为该服务也使用 pkg-1。当我为 service-3 构建 docker 映像时,我还需要在 Dockerfile 中定义 pkg-1 的构建步骤,因为该服务也使用 pkg-1。所以我想在 Dockerfile 中避免这种重复。我想要一个用于 pkg-1 的可重用 docker 构建,它可以用于任何服务的 id Dockerfile。 @TomášVavřinka 抱歉,我很难追随你。为什么不直接构建完整的树并在每个 Dockerfile 中挑选出您需要的路径?【参考方案2】:

前段时间我发了an answer detailing how I structured a monorepo with multiple services and packages。

“诀窍”是复制您的服务所依赖的所有包,以及项目根 package.json。然后运行yarn --pure-lockfile --non-interactive --production 一次将安装所有子包的依赖项,因为它们是工作区的一部分。

链接的示例没有使用打字稿,但我相信这可以通过在每个运行yarn buildpackage.json 中的postinstall 脚本轻松实现。

【讨论】:

你描述的是我所做的,但没有打字稿编译。对您来说,只需复制软件包并安装产品依赖项就足够了。我需要在每个包的第一阶段安装所有依赖项以获取我可以构建包的打字稿。在第二阶段,我可以仅复制构建文件夹并仅安装产品依赖项。这是我需要为 pkg-1 做的过程。请参阅我的 Dockerfile。我正在寻找一种方法来避免在构建 docker 映像时在 service-1、service-2、service-3 中一遍又一遍地定义它。

以上是关于使用 docker 构建 Yarn 工作区的主要内容,如果未能解决你的问题,请参考以下文章

docke--Dockerfile构建docker镜像

nodejs docker 开发最好选择yarn 进行包管理而不是npm

Spring Cloud Config Server 不适用于 Docker 构建

创建使用 yarn 链接的项目的 docker 镜像

docker之构建镜像

docker 系列 - Dock高阶知识点文章汇集