42

我们目前正在与我们的团队一起为我们的网站研究 CI/CD。我们最近还适应了 monorepo 结构,因为这使我们的依赖关系和概览变得更加容易。目前测试等已为 CI 做好准备,但我现在正在部署。我想创建所需包的 docker 映像。

我考虑的事情:

1) 将完整的 monorepo 拉入 docker 项目,但在我们的项目中运行 yarn install 会导致项目总大小约为 700MB,这主要是由于我们的 react native 应用程序甚至不应该有 docker 映像。此外,每次我们必须部署新版本时,这都会导致很长的图像拉取时间

2)以某种方式捆绑我的项目。使用我们的前端,我们有工作设置,所以应该没问题。但是我只是尝试将 webpack 添加到我们的 express api 并由于这个问题而在我的包中出现错误:https ://github.com/mapbox/node-pre-gyp/issues/308

3)我尝试只在需要的项目中运行 yarn install ,但这仍然会为我的所有项目安装我的 node_modules。

4) 运行 npm 包:pkg。这导致单个文件准备好在具有特定节点版本的特定系统上运行。这确实有效,但我不确定这将如何处理错误和崩溃。

5)另一种解决方案可能是将项目复制到工作区之外并在那里运行纱线安装。这样做的问题是纱线工作空间(隐式链接依赖项)的使用已经过去了。我必须明确添加我的其他工作区依赖项。一种可能性是从某个提交哈希中引用它们,我现在将对其进行测试。(编辑:您似乎不能将子目录作为纱线包引用)

6)???

我想知道我是否缺少一个选项,让某个项目只拥有所需的 node_modules,这样我就可以保持我的 docker 图像很小。

4

4 回答 4

26

我参与了一个类似于你的结构的项目,它看起来像:

project
├── package.json
├── packages
│   ├── package1
│   │   ├── package.json
│   │   └── src
│   ├── package2
│   │   ├── package.json
│   │   └── src
│   └── package3
│       ├── package.json
│       └── src
├── services
│   ├── service1
│   │   ├── Dockerfile
│   │   ├── package.json
│   │   └── src
│   └── service2
│       ├── Dockerfile
│       ├── package.json
│       └── src
└── yarn.lock

services/文件夹的每个子文件夹包含一项服务。每个服务都是用 node.js 编写的,并且有自己的 package.json 和 Dockerfile。它们通常是基于 Express 的 Web 服务器或 REST API。

packages/文件夹包含所有不是服务的包,通常是内部库。

一项服务可以依赖于一个或多个包,但不能依赖于另一项服务。一个包可以依赖于另一个包,但不能依赖于服务。

主要的 package.json(项目根文件夹中的那个)只包含一些 devDependencies,例如eslint,测试运行器等。

一个人Dockerfile看起来像这样,假设service1取决于两者package1& package3

FROM node:8.12.0-alpine AS base

WORKDIR /project

FROM base AS dependencies

# We only copy the dependencies we need
COPY packages/package1 packages/package1
COPY packages/package3 packages/package3

COPY services/services1 services/services1

# The global package.json only contains build dependencies
COPY package.json .

COPY yarn.lock .

RUN yarn install --production --pure-lockfile --non-interactive --cache-folder ./ycache; rm -rf ./ycache

我使用的实际Dockerfiles 更复杂,因为它们必须构建子包、运行测试等。但是您应该通过这个示例了解想法。

如您所见,诀窍是仅复制特定服务所需的包。该yarn.lock文件包含一个 package@version 列表,其中包含已解决的确切版本和依赖项。在没有所有子包的情况下复制它不是问题,yarn 将在安装包含的包的依赖项时使用那里解析的版本。

在您的情况下,react-native 项目永远不会成为任何 Dockerfile 的一部分,因为它不依赖于任何服务,从而节省了大量空间。

为了简洁起见,我在那个答案中省略了很多细节,如果有什么不是很清楚,请随时在评论中要求精确。

于 2018-10-14T18:36:21.083 回答
1

经过大量试验和错误后,我发现仔细使用文件.dockerignore是控制最终图像的好方法。当在 monorepo 下运行以排除“其他”包时,这非常有用。

对于每个包,我们都有一个类似命名的 dockerignore 文件,它在构建之前替换了实时的 .dockerignore 文件。

例如, cp admin.dockerignore .dockerignore

下面是一个例子admin.dockerignore。请注意该*文件顶部的意思是“忽略所有内容”。前缀的!意思是“不要忽略”,即保留。该组合意味着忽略除指定文件之外的所有内容。

*
# Build specific keep
!packages/admin

# Common Keep
!*.json
!yarn.lock
!.yarnrc
!packages/common

**/.circleci
**/.editorconfig
**/.dockerignore
**/.git
**/.DS_Store
**/.vscode
**/node_modules
于 2019-07-30T23:34:19.730 回答
0

我们最近将后端服务放到了一个 monorepo 上,这是我们必须解决的几个问题之一。Yarn 在这方面没有任何帮助我们的东西,所以我们不得不寻找其他地方。

首先我们尝试了@zeit/ncc,有一些问题,但最终我们设法获得了最终版本。它会生成一个包含所有代码以及所有依赖项代码的大文件。看起来很棒。我只需要将几个文件(js、源映射、静态资产)复制到 docker 映像。图像要小得多,并且该应用程序可以正常工作。但是运行时内存消耗增加了很多。正在运行的容器消耗了 ~250MB 而不是 ~70MB。不确定我们是否做错了什么,但我没有找到任何解决方案,只有一个问题提到了这一点。我猜 Node.js 加载解析并加载包中的所有代码,即使其中大部分代码从未使用过。

我们所需要的只是分离每个包的生产依赖项以构建一个苗条的 docker 映像。看起来做起来没那么简单,但毕竟我们找到了一个工具。

我们现在使用fleggal/monopack。它将我们的代码与 Webpack 捆绑在一起并将其转译为 Babel。所以它也生成一个文件包,但它不包含所有依赖项,只包含我们的代码。这一步是我们并不真正需要的,但我们不介意它的存在。对我们来说重要的部分是 - Monopack 仅将包的生产依赖树复制到 dist/bundled node_modules。这正是我们所需要的。Docker 镜像现在有 100MB-150MB 而不是 700MB。

有一种更简单的方法。如果你只有几个非常大的 npm 模块,你可以在你的 rootnode_modules中使用nohoistpackage.json。这样,yarn 将这些模块保存在包的本地node_modules,并且不必将其复制到所有其他服务的 Docker 映像中。

例如。:

"nohoist": [
  "**/puppeteer",
  "**/puppeteer/**",
  "**/aws-sdk",
  "**/aws-sdk/**"
]
于 2019-03-04T20:13:23.537 回答
0

我在我的项目中有一个与Anthony Garcia-Labiad非常相似的设置,并设法使用skaffold启动并运行它,它允许我指定上下文和 docker 文件,如下所示:

apiVersion: skaffold/v2beta22
kind: Config
metadata:
  name: project
deploy:
  kubectl:
    manifests:
      - infra/k8s/*
build:
  local:
    push: false
  artifacts:
    - image: project/service1
      context: services
      sync:
        manual:
          - src: "services/service1/src/**/*.(ts|js)"
            dest: "./services/service1"
          - src: "packages/package1/**/*.(ts|js)"
            dest: "./packages/package1"
      docker:
        dockerfile: "services/service1/Dockerfile"
于 2021-11-14T13:05:32.763 回答