首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Docker为monorepo环境构建

Docker为monorepo环境构建
EN

Stack Overflow用户
提问于 2020-10-07 07:53:26
回答 1查看 9.8K关注 0票数 3

基本上,服务foobar都依赖于common库。

让我们假设common包已经发布到npm注册中心。

代码语言:javascript
复制
|
├── packages
|    ├── common
|    |    ├── src
|    |    ├── package.json
|    |    ├── tsconfig.build.json
|    |    ├── tsconfig.json
|    ├── foo
|    |    ├── src
|    |    ├── Dockerfile
|    |    ├── package.json
|    |    ├── tsconfig.build.json
|    |    ├── tsconfig.json
|    ├── bar
|    |    ├── src
|    |    ├── Dockerfile
|    |    ├── package.json
|    |    ├── tsconfig.build.json
|    |    ├── tsconfig.json
├── tsconfig.json
├── package.json
├── yarn.lock
├── docker-compose.init.yml
├── docker-compose.yml
├── Dockerfile
├── Dockerfile.init
├── .dockerignore

我添加了根package.json中所有包中常见的所有package.json,如下所示:

代码语言:javascript
复制
"scripts": {
  "build": "lerna run build --stream",
  "setup": "yarn && yarn build",
  "docker:bootstrap": "docker-compose --file=docker-compose.init.yml build",
  "docker:up": "docker-compose up --build"
},
"devDependencies": {
  "@nestjs/cli": "^7.5.1",
  "@nestjs/common": "^7.4.4",
  "@nestjs/core": "^7.4.4",
  "@nestjs/platform-express": "^7.4.4",
  "@nestjs/schematics": "^7.1.2",
  "@nestjs/testing": "^7.4.4",
  "@types/express": "^4.17.8",
  "@types/jest": "^26.0.13",
  "@types/node": "^14.10.2",
  "@types/supertest": "^2.0.10",
  "@typescript-eslint/eslint-plugin": "^4.1.1",
  "@typescript-eslint/parser": "^4.1.1",
  "eslint": "^7.9.0",
  "eslint-config-prettier": "^6.11.0",
  "eslint-plugin-prettier": "^3.1.4",
  "express": "^4.17.1",
  "husky": "^4.3.0",
  "jest": "^26.4.2",
  "lerna": "^3.22.1",
  "lint-staged": "^10.4.0",
  "prettier": "^2.1.2",
  "reflect-metadata": "^0.1.13",
  "rimraf": "^3.0.2",
  "rxjs": "^6.6.3",
  "supertest": "^4.0.2",
  "ts-jest": "^26.3.0",
  "ts-loader": "^8.0.3",
  "ts-node": "^9.0.0",
  "typescript": "3.9.5"
}

foo包需要使用关系数据库,因此我已经独立安装了以下软件包。

代码语言:javascript
复制
$ yarn workspace foo add @nestjs/typeorm mysql typeorm

要解决错误消息"<package> has unmet peer dependency <package>",我已经按了以下命令。

代码语言:javascript
复制
$ yarn workspace foo add @nestjs/common @nestjs/core @nestjs/platform-express rxjs

我有点迷路了。如果我一直重复自己从一个包安装到另一个包,那么以一种单一的方式组织多个应用程序有什么意义呢?毕竟,这使我更难编写Dockerfile。

我的第一个问题是,当开发人员工作于单块代码库时,如果有必要,这是否是将库安装到特定包中的正常行为?

我的Dockerfile是这样的:

代码语言:javascript
复制
// docker-compose.init.yml
# This file triggers the initial build
version: "3.8"

services:
  pkg_builder:
    image: pkg-builder
    build:
      context: .
      dockerfile: Dockerfile.init

首先,执行下面的命令。

代码语言:javascript
复制
$ yarn docker:bootstrap

Dockerfile.init创建一个初始构建器映像,“真实”构建器映像可以从其中复制构建目录。

代码语言:javascript
复制
// Dockerfile.init
FROM scratch

# Copy files from the root to build directory
COPY package.json lerna.json yarn.lock tsconfig.json /build/

# This line is required to install dependencies from foo's package.json
COPY ./packages/foo/package.json /build/packages/foo/package.json

从那时起,使用以下命令构建映像:

代码语言:javascript
复制
$ yarn docker:up
代码语言:javascript
复制
// docker-compose.yml
version: "3.8"

services:
  pkg_builder:
    image: pkg-builder
    build: .
  mariadb:
    image: mariadb:10.3
    ports:
      - "3306:3306"
    environment:
      - MYSQL_USER=root
      - MYSQL_ROOT_PASSWORD=root
      - MYSQL_DATABASE=tutorial
    restart: always
  foo:
    container_name: foo
    build: ./packages/foo
    ports:
      - "8000:8000"
    depends_on:
      - mariadb
    restart: always
代码语言:javascript
复制
// Dockerfile
FROM node:12-alpine

COPY --from=pkg-builder /build /build

WORKDIR /build

RUN rm -rf node_modules
RUN yarn

CMD ["true"]

问题是,图像的大小太大了。这是因为,所有的dev依赖项都是从pkg-builder复制的。

代码语言:javascript
复制
// foo's Dockerfile

FROM node:12-alpine

WORKDIR /app/current

COPY --from=pkg-builder /build/node_modules /app/current/packages/foo/node_modules
COPY --from=pkg-builder /build/tsconfig.json ./tsconfig.json

WORKDIR /app/current/packages/foo

COPY . .

RUN yarn build

EXPOSE 8000

CMD [ "node", "./dist/main" ]

最后,我应该如何减少图像?在这种情况下,我认为多级构建不是减少尺寸的正确策略。我在这里错过了什么?

EN

回答 1

Stack Overflow用户

发布于 2020-10-07 12:03:07

每个package.json 文件都需要列出其应用程序的完全直接依赖关系。I应该能够检查您的源存储库,运行yarn install,并有一个工作的应用程序树。在您的问题中,“顺便说一下,这些其他依赖项是安装在环境中的,而我只是假设它们是”,对于那些没有在您的确切系统上工作的人来说,这是一个问题,更具体地说,对于Docker和其他自动构建工具来说,这是一个问题。

库依赖项可以有自己的库依赖项。这些内容将在yarn.lock文件中列出,但不需要直接在package.json文件中列出。

以数据库访问库为例:如果您的主要应用程序使用它们,则需要将它们包括在您的dependencies中。但是,如果所有数据库访问都封装在common共享库中,则应用程序只需引用该库(在foo/package.json中),库需要包含数据库依赖项(在common/package.json中)。

您应该将dependencies devDependencies**.**与分开,运行应用程序(express)所需的内容需要在dependencies中列出;只需要构建应用程序(eslint)的内容应该是devDependencies。您将讨论映像大小;这为您在实际运行容器时在容器中安装一组小得多的软件包提供了一种方法。

(请注意,Yarn实际上不支持不安装devDependencies;npm支持,尽管在其他方面使用起来要慢得多。)

然后,多阶段构建可以生成一个更小的映像,这里的思想是,第一阶段安装整个dev依赖项并构建应用程序;第二阶段只包含运行时依赖项和构建的代码。这或多或少看上去像:

代码语言:javascript
复制
ARG node_version=12-current
FROM node:${node_version} AS build
WORKDIR /app
COPY package.json yarn.lock .
RUN yarn install --immutable
COPY . .
RUN yarn build

FROM node:${node_version}
WORKDIR /app
ENV NODE_ENV=production
COPY package.json yarn.lock .
RUN yarn install --immutable
# RUN npm ci  # in production mode, skips devDependencies
COPY --from=build /app/dist dist
CMD ["node", "/app/dist/main.js"]

您不应该需要一个“构建器容器”;您显示的设置与多阶段构建基本相同,只是分散在三个独立的Dockerfile中。特别是,如果您的映像不运行命令,但只包含文件,那么在多阶段构建中成为早期阶段是一个很好的选择。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64239637

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档