如何在没有 Dockerfile 的情况下设置 Docker Compose?

dot*_*ter 13 node.js rethinkdb docker-compose

我花了过去几个小时尝试设置 Nodejs 14 和 rethinkdb 2.3.5 的 2 个默认映像,如果语气有点沮丧,我很抱歉,但我目前很沮丧。

我的要求看似非常简单。

我想要:

  1. 下载nodejs 14和rethinkdb 2.3.5的默认镜像
  2. 将当前目录中的所有内容复制到 Nodejs 14 映像中。
  3. 我希望 Nodejs 映像依赖于 RethinkDB 映像。
  4. 在nodejs 14镜像中运行2个命令;npm cinpm test
  5. 查看测试中的标准输出。

我不需要:

  1. 主机可访问的任何端口。
  2. 自定义任何 Dockerfile 或对默认映像进行任何更改。
  3. 从主机文件系统到容器的任何更新。
  4. 将任何数据从主机复制到 RethinkDB 容器。

为什么?

我希望测试能够在所有开发人员机器上重现 - 目前没有 CI。无论开发人员将项目保存在硬盘上的哪个位置。

我有一个docker-compose.yml文件。

version: "3"
services:
  tests:
    image: node:14
    ports:
      - "3000:3000"
    # command:
    #  - npm ci
    #  - npm test
    volumes:
      - ".:/cli-app"
    depends_on:
      - rethinkdb

  rethinkdb:
    image: rethinkdb
    ports:
      - "28015:28015"
      - "8080:8080"
    volumes:
      - "./data: /data"
    command: rethinkdb --bindall --data /data
Run Code Online (Sandbox Code Playgroud)

dot*_*ter 26

\n

这个答案的目的不是给出尽可能简洁的解释(简短而清晰地表达),而是强调 docs.docker.com 和 hub.docker.com 上当前文档造成的所有混乱。最终我/我们会做对并且可以写出一个简洁的答案。

\n
\n

更正后的docker-compose.yml

\n
version: "3"\nservices:\n  tests:\n    image: "node:14"\n    user: "node"\n    working_dir: /home/node/app\n    volumes:\n      - ./:/home/node/app\n    container_name: nodejs\n    depends_on:\n      - rethinkdb\n    command: bash -c "npm ci && npm test"\n\n  rethinkdb:\n    image: rethinkdb:2.3.5\n    container_name: rethinkdb\n
Run Code Online (Sandbox Code Playgroud)\n

令人沮丧的部分!

\n

立刻,docs.docker.com 和 hub.docker.com 上的文档可以说是有史以来最糟糕的文档,因为它 a)错误,b)假设先验知识

\n

如果以下任何一个是错误的 - 归咎于可怕的文档。

\n

在此输入图像描述

\n

不,除非您打算构建自己的映像,否则您不需要 Dockerfile

\n

因此,在不同的过时 示例上浪费了一个小时左右之后,您可能会幸运地发现,您为context绕过绝对路径示例而尝试过的所有操作都无关紧要,除非您从头开始创建自己的图像(这90%的docker用户,不需要)。

\n
\n

提示使用docker system prune以下示例删除您创建的所有不幸的无用 docker 容器。

\n
\n

接下来,找到正确的 docker 容器。

\n

展示:nodejs官方 docker 镜像!\n在此输入图像描述\n在此输入图像描述

\n

你看到我们需要的图片名称了吗?

\n

在此输入图像描述

\n

没有一处提到image。你只需要知道。

\n

逐行解释docker-compose.yml

\n
version: "3"\n
Run Code Online (Sandbox Code Playgroud)\n

使用 3.x 版本的语法。语法与Compose 和 Docker 兼容性矩阵中列出的 Docker 引擎版本不同。

\n
services:\n
Run Code Online (Sandbox Code Playgroud)\n

每个容器镜像都是 Docker Compose 术语中的一个服务。testsrethinkdb是我的两张图片的名字。您可以根据需要命名它们,但我们稍后将使用此名称来创建两个图像之间的依赖关系(一个需要在另一个之前上线)。

\n
services:\n  tests:\n    ...\n  rethinkdb:\n    ...\n
Run Code Online (Sandbox Code Playgroud)\n

testsrethinkdb是我们将让 Docker Compose 为我们运行的两个服务。

\n

修复:1.下载nodejs 14和rethinkdb 2.3.5的默认镜像

\n
    image: "node:14"\n
Run Code Online (Sandbox Code Playgroud)\n

幸运的是,nodejs Docker 开发人员在hub.docker.com/_/node上提供了非常好的文档来代替适当的标准文档。

\n
\n

镜像变体:事实上的镜像node:<version>和镜像基于流行的 Alpine Linux 项目,该项目比大多数发行版基础镜像(约 5MB)小得多,因此通常会导致镜像更薄。node:<version>-alpine node:<version>-alpine

\n
\n
    image: rethinkdb:2.3.5\n
Run Code Online (Sandbox Code Playgroud)\n

不幸的是,资金较少的 RethinkDB 项目,Docker Hub 页面 没有这样的信息。实际上,如果你一直滚动到页面底部,你会发现图像变体(抱歉,我无法链接到 hub.docker.com 上的标题,因为它们没有idorname属性)。您要使用的 2 个版本是rethinkdb:<version>rethinkdb:<version>-slim

\n

问:\n那么更占主导地位的Supported 标签和相应的 Dockerfile 链接部分又如何呢?

\n

答:\n它们的使用频率较低,并且是专门的 docker 镜像。在 RethinkDB 案例中,它是 Debian Buster 和 CentOS 上安装的 RethinkDB 数据库。还有一些版本的链接,但不是全部。所以这是一个选定的图像列表,您可能不想要。请记住,您必须单击标签或灰色小链接;查看可用标签(请参见上面带有红色方块的图像 - 抱歉,SO 上也没有锚链接支持)。

\n

修复:2.将当前目录中的所有内容复制到nodejs 14图像中。

\n
    user: "node"\n    working_dir: /home/node/app\n    volumes:\n      - ./:/home/node/app\n
Run Code Online (Sandbox Code Playgroud)\n

如果您查找docs.docker.comuser或在docs.docker.comworking_dir上查找,那么您在没有事先了解的情况下就不走运了docker. 它仅指出:

\n
\n

其中每个都是单个值,类似于其 docker run 对应项。请注意,mac_address 是一个旧选项。

\n
\n

这里的目标是将当前目录(所在docker-compose.yml目录)复制到tests拉取node:14映像的服务。我们需要在容器中创建一个目录,将机器上的当前目录复制到其中。稍后,我们想要在该目录中执行一些命令,但我们不想将其运行为sudo,因此自然放置在我们的用户主目录 ( ~/) 中。

\n

我们需要:

\n
    \n
  1. 我们容器中的用户nodejs
  2. \n
  3. 在容器的用户主目录中创建一个目录nodejs
  4. \n
  5. 将机器上的当前目录映射到nodejs容器中的新目录。
  6. \n
\n

阅读完Nodejs 开发人员编写的如何使用此镜像后,我们可以在示例中看到node镜像中有一个用户。\n该示例还显示了我们需要哪些 Docker Compose 配置。

\n
    user: "node"\n
Run Code Online (Sandbox Code Playgroud)\n

大概使用 中定义的节点image: "node:14"用户。

\n
    working_dir: /home/node/app\n
Run Code Online (Sandbox Code Playgroud)\n

在用户的目录中创建一个应用程序目录。node

\n
    volumes:\n      - ./:/home/node/app\n
Run Code Online (Sandbox Code Playgroud)\n

将机器上的当前目录映射到容器内的/home/node/apptests目录(使用image: "node:14")。

\n

修复:3.我希望nodejs镜像依赖于RethinkDB镜像。

\n
    container_name: nodejs\n
Run Code Online (Sandbox Code Playgroud)\n

事实证明,定义container_name纯粹是装饰性的。它不能帮助您链接网络或在另一个容器之前启动一个容器。这只是一个名字。当容器运行时,您可以用来docker ps查看它们或docker ps -a查看所有容器,甚至是已关闭的容器。在NAMES列中container_name写入。如果您没有定义container_name,那么它们将被称为您的目录名称和服务名称,并后缀有增量数字。

\n

docker ps运行时container_name

\n
CONTAINER ID        IMAGE               PORTS                            NAMES\n5272576f8555        node:14                                              nodejs\nfb11d5ce049b        rethinkdb:2.3.5     8080/tcp, 28015/tcp, 29015/tcp   rethinkdb\n
Run Code Online (Sandbox Code Playgroud)\n

docker ps container_name运行时没有:

\n
CONTAINER ID        IMAGE               PORTS                            NAMES\n528e5ee37956        node:14                                              data_access_layer_tests_1\ne80682b806fc        rethinkdb:2.3.5     8080/tcp, 28015/tcp, 29015/tcp   data_access_layer_rethinkdb_1\n
Run Code Online (Sandbox Code Playgroud)\n
CONTAINER ID        IMAGE               PORTS                            NAMES\n5272576f8555        node:14                                              nodejs\nfb11d5ce049b        rethinkdb:2.3.5     8080/tcp, 28015/tcp, 29015/tcp   rethinkdb\n
Run Code Online (Sandbox Code Playgroud)\n

depends_on是神奇的!它告诉 Docker Composerethinkdb容器必须是在线的在此容器之前开始。

\n

在极少数情况下,Docker Compose 文档 ( depends_on)实际上很好。

\n

不幸的是,depends_on不能保证依赖的图像在线,正如您所期望的那样,但在这种情况下,文档也非常清楚,并提供了另一种解决方案。在我们的例子中,这并不重要,因为rethinkdb容器启动得足够快(npm ciinnodejs将比 的启动时间长得多rethinkdb)。

\n

修复:4.在nodejs 14镜像中运行2条命令;npm ci 和 npm 测试。

\n
CONTAINER ID        IMAGE               PORTS                            NAMES\n528e5ee37956        node:14                                              data_access_layer_tests_1\ne80682b806fc        rethinkdb:2.3.5     8080/tcp, 28015/tcp, 29015/tcp   data_access_layer_rethinkdb_1\n
Run Code Online (Sandbox Code Playgroud)\n

现在我们可以在 docker 容器中运行一些命令,就好像我们在我们的机器上的项目目录中运行它们一样。 当然不是。上面只会执行npm ci. 请参阅SO答案Using Docker-Compose, how to执行多个命令。Docker 使用一种晦涩难懂的 shell 脚本变体,它可以执行一个且仅一个带参数的命令。文档说它与docker key类似,但两个网站都没有解释为什么某些 POSIX shell 命令有效而其他命令无效,但我们认为它让 Docker 开发人员的生活更轻松:)CMD

\n

执行多个命令的正确方法是:

\n
    depends_on:\n      - rethinkdb\n
Run Code Online (Sandbox Code Playgroud)\n

请参阅《使用Docker-Compose,如何执行多个命令》

\n

npm ci安装package-lock.json中的所有 npm 包并npm test运行package.json"test"中的脚本。

\n

您可能认为您可以在 YAML 数组中编写多个命令,但在这种情况下,这样做显然会告诉节点require它们作为容器内的模块,您将收到如下错误:

\n
nodejs       | internal/modules/cjs/loader.js:883\nnodejs       |   throw err;\nnodejs       |   ^\nnodejs       |\nnodejs       | Error: Cannot find module \'/home/node/app/npm ci\'\nnodejs       |     at Function.Module._resolveFilename (internal/modules/cjs/loader.js:880:15)\nnodejs       |     at Function.Module._load (internal/modules/cjs/loader.js:725:27)\nnodejs       |     at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)\nnodejs       |     at internal/main/run_main_module.js:17:47 {\nnodejs       |   code: \'MODULE_NOT_FOUND\',\nnodejs       |   requireStack: []\nnodejs       | }\n
Run Code Online (Sandbox Code Playgroud)\n
    command: "npm ci && npm test"\n
Run Code Online (Sandbox Code Playgroud)\n

上面的方法不行

\n

而是使用通常的方式&&执行一系列成功的命令。再次强调,使用 Docker-Compose,如何执行多个命令是正确的文档。

\n

实际上,忘记上面的内容并使用entrypoint指向您编写所有命令的 bash 文件。

\n

修复:5.查看测试中的标准输出。

\n

不使用docker-compose up -d

\n

您无需在这里执行任何操作。尽管每个 Docker Compose 教程都会告诉您以分离模式运行。您将看不到标准输出或标准错误。只要松开-d.

\n

docker-compose up是要走的路!

\n

如果您确实必须在分离模式下运行,您可以通过或来查看输出docker logs [container id]并获取容器 ID 。如果您的容器当前未运行,则使用后者。docker psdocker ps -a

\n

docker-compose.yml看起来像是需要的钥匙

\n

事实证明,Docker 附带了许多合理的默认设置,除非确实必要,否则您不应该弄乱这些默认设置。

\n
    command: bash -c "npm ci && npm test"\n
Run Code Online (Sandbox Code Playgroud)\n

您不需要公开 a 中的端口,docker-compose.yml除非您需要将端口公开到本地计算机或外部网络,即使那里的每个示例都这样做。例如,如果您需要访问localhost:8080(在 RethinkDB 中是仪表板),那么您必须添加:

\n
nodejs       | internal/modules/cjs/loader.js:883\nnodejs       |   throw err;\nnodejs       |   ^\nnodejs       |\nnodejs       | Error: Cannot find module \'/home/node/app/npm ci\'\nnodejs       |     at Function.Module._resolveFilename (internal/modules/cjs/loader.js:880:15)\nnodejs       |     at Function.Module._load (internal/modules/cjs/loader.js:725:27)\nnodejs       |     at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)\nnodejs       |     at internal/main/run_main_module.js:17:47 {\nnodejs       |   code: \'MODULE_NOT_FOUND\',\nnodejs       |   requireStack: []\nnodejs       | }\n
Run Code Online (Sandbox Code Playgroud)\n

您的其他服务/容器将可以使用服务名称作为主机名来访问端口280158080,而无需在docker-compose.yml. 例如在这种情况下rethinkdb:28015。请参阅下面的详细信息。

\n\n您不需要在“docker-compose.yml”中公开端口,即使每个示例都这样做。您使用的映像很可能已经公开了您所需的容器化软件使用的默认端口。上面的示例是 RethinkDB 使用的默认端口,无需您编写它们即可公开。\n\n
\n
    command:\n      - "npm ci"\n      - "npm test"\n
Run Code Online (Sandbox Code Playgroud)\n

links似乎是连接两个不同容器的一种方法,但事实证明所有容器共享网络,因此您不必这样做。官方文档中带有一个大红色的WARNING,建议你使用用户自定义的网络。反过来说:

\n
\n

默认情况下,Compose 会为您的应用程序设置一个网络。服务的每个容器都会加入默认网络,并且可以被该网络上的其他容器访问,并且可以通过与容器名称相同的主机名被它们发现。

\n
\n

这是一种复杂的说法,在 Docker Compose 容器内,您可以像在本地计算机上一样连接到端口和 IP 地址。

\n

因此,如果容器 A 的映像公开,172.18.0.2:28015那么您可以使用该确切地址从容器 B 进行连接。Ei IP:172.18.0.2和端口: 28015刮掉那个!暴露的IP地址不稳定。Docker 会出于各种(未知)原因更改它们。

\n
\n

服务的每个容器都会加入默认网络,并且可以被该网络上的其他容器访问,并且可以通过与容器名称相同的主机名被它们发现。

\n
\n

意味着您的服务名称将用作主机名,就像您在/etc/hosts文件中定义它一样。或者类似于 DNS 如何将 stackoverflow.com 链接到 151.101.193.69。

\n

因此,如果容器rethinkdb公开端口28015,则可以nodejs通过容器访问该端口rethinkdb:28015

\n

请注意文档

\n
    ports:\n      - "28015:28015"\n      - "8080:8080"\n
Run Code Online (Sandbox Code Playgroud)\n
\n

现在,每个容器都可以查找主机名webdb获取相应的容器\xe2\x80\x99s IP 地址。例如,web\xe2\x80\x99s 应用程序代码可以连接到 URLpostgres://db:5432并开始使用 Postgres 数据库。

\n
\n

Compose 中的网络示例 中,postgres是服务名称,是端口号。根据我的经验,这是行不通。您需要使用服务名称,而不是名称。所以从to连接的正确方法是使用 URL 。imagedb5432imagewebdbdb:5232@jonrsharpe 指出该协议(postgres:// ) 恰好与图像名称匹配。

\n

下面是我之前对例子的理解的修正。

\n

Compose 中的 Networking 示例 postgres中, 是协议(类似于https),db是服务名称,5432是端口号。您需要使用服务名称来获取正确的 IP 地址。web所以从to连接的正确方法db是使用 URL [protocol]://db:5232。哪里protocol可以http,,,等等httpsprogres

\n

由于所有容器都位于同一网络内,因此除非需要向本地计算机(或外部网络)公开服务,否则不需要密钥。ports

\n
\n
ports:\n  - "8080:8080"\n
Run Code Online (Sandbox Code Playgroud)\n

每个 RethinkDBDockerfile都有这个,但只有当您想将文件从本地计算机复制到容器中时才需要这个。在这种情况下,我们不想向数据库预加载任何内容,因此我们没有任何文件来为数据库提供种子,因此volumes不需要密钥。

\n

  • 如果我突然精力充沛,我会用相同的信息写另一个答案,但不会感到沮丧......可能会是这个的三分之一长度。 (2认同)

归档时间:

查看次数:

10011 次

最近记录:

4 年,8 月 前