当前位置: 首页 > 知识库问答 >
问题:

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

娄利
2023-03-14

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

我的要求似乎超级简单。

  1. 下载nodejs 14和RejectDB 2.3.5的默认图像。
  2. 将我当前目录中的所有内容复制到nodejs 14映像中
  3. 我希望nodejs图像依赖于DB图像
  4. 在nodejs 14图像中运行2个命令<代码>npm ci和npm测试
  5. 请参阅测试中的标准输出
  1. 主机可以访问的任何端口
  2. 自定义任何Dockerfile或对默认图像进行任何更改
  3. 从主机文件系统到容器的任何更新
  4. 将任何数据从主机复制到数据库容器

我希望测试能够在所有开发人员机器上重复-目前没有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

共有1个答案

邓翼
2023-03-14

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

更正后的 docker-compose.yml

version: "3"
services:
  tests:
    image: "node:14"
    user: "node"
    working_dir: /home/node/app
    volumes:
      - ./:/home/node/app
    container_name: nodejs
    depends_on:
      - rethinkdb
    command: bash -c "npm ci && npm test"

  rethinkdb:
    image: rethinkdb:2.3.5
    container_name: rethinkdb

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

如果下面的任何一个是错误的——那就归咎于可怕的文档。

不,除非您计划构建自己的映像,否则您不需要Dockerfile。

因此,在浪费了一个小时左右的时间在不同的过时示例上之后,您可能会幸运地发现,您尝试使用上下文来绕过绝对路径示例的所有内容......这根本不重要,除非你从头开始创建自己的镜像(90%的 docker 用户不需要)。

提示:使用<code>docker system prune</code>删除您通过以下示例创建的所有不幸无用的docker容器。

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

没有一个地方写着<code>图像</code>。你只需要知道。

version: "3"

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

services:

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

services:
  tests:
    ...
  rethinkdb:
    ...

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

    image: "node:14"

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

图像变体:事实图像<代码>节点:

    image: rethinkdb:2.3.5

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

问:那么更主要的支持标签和各自的docker文件链接部分呢?

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

    user: "node"
    working_dir: /home/node/app
    volumes:
      - ./:/home/node/app

如果您在docs.docker.com上查找userworking_dir,那么您在没有事先了解docker的情况下就倒霉了。它只声明:

每一个都是一个单独的值,类似于docker运行的对应值。请注意,mac_ address是一个传统选项。

这里的目标是将当前目录(即< code>docker-compose.yml所在的目录)复制到< code>tests服务,该服务提取< code>node:14图像。我们需要在容器中创建一个目录,我们机器上的当前目录将被复制到这个目录中。稍后,我们希望在该目录中执行一些命令,并且我们不希望以< code>sudo的身份运行,所以自然的位置是在我们的用户主目录(< code>~/)中。

我们需要:

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

在阅读了nodejs开发人员编写的如何使用此图像后,我们可以在示例中看到图像中有一个节点用户。该示例还显示了我们需要哪些Docker组合配置。

    user: "node"

大概使用图像中定义的节点用户:“node: 14”

    working_dir: /home/node/app

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

    volumes:
      - ./:/home/node/app

将我们机器上的当前目录映射到< code>tests容器(使用< code>image: "node:14")中的/home/node/app目录。

    container_name: nodejs

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

docker-pscontainer_name,同时运行:

CONTAINER ID        IMAGE               PORTS                            NAMES
5272576f8555        node:14                                              nodejs
fb11d5ce049b        rethinkdb:2.3.5     8080/tcp, 28015/tcp, 29015/tcp   rethinkdb

docker ps运行时不带容器_name

CONTAINER ID        IMAGE               PORTS                            NAMES
528e5ee37956        node:14                                              data_access_layer_tests_1
e80682b806fc        rethinkdb:2.3.5     8080/tcp, 28015/tcp, 29015/tcp   data_access_layer_rethinkdb_1
    depends_on:
      - rethinkdb

depends_on是神奇的!它告诉 Docker Compose,在此容器之前,rethinkdb 容器必须 联机 启动。

这是Docker Compose文档(depends_on)的一个罕见案例,它实际上很好。

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

    command: "npm ci && npm test"

现在我们可以在docker容器中运行一些命令, 就好像我们在项目目录中的机器上运行它们一样 npm ci。请参阅使用Docker Compose的SO答案,以及如何执行多个命令。Docker使用了一种晦涩难懂的shell脚本变体,它可以执行一个命令,而且只能执行一个带有参数的命令。文档中说它类似于docker<code>CMD</code>键,但这两个站点都没有解释为什么某些POSIX shell命令可以工作,而其他命令不能工作,但我们认为这让docker开发人员的生活更轻松:)

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

    command: bash -c "npm ci && npm test"

请参见“使用Docker-Compose,如何执行多个命令”。

npm ci从package-lock.json安装所有npm包,并且npm test在package.json.中运行我们的"test"脚本

您可能认为可以在一个YAML数组中编写多个命令,但在这种情况下,这样做显然会告诉node将<code>作为容器中的模块要求<code>这些命令,并且会出现如下错误:

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

上述方法将不起作用!

而是使用通常的

实际上,忘记上述内容,使用entrypoint指向一个bash文件,您可以在其中编写所有命令。

不要使用< code>docker-compose up -d

您不必在这里做任何事情。即使每个Docker Compose教程都会告诉您以分离模式运行。您不会看到stdout或stderr。只需松开-d

docker-compose up是要走的路!

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

原来Docker有很多合理的默认值,除非你真的必须这样做,否则你不应该乱动这些默认值。

    ports:
      - "28015:28015"
      - "8080:8080"

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

ports:
  - "8080:8080"

您的其他服务/容器将可以访问端口280158080,使用服务名称作为主机名,而无需在docker compose.ymlDB:28015。有关更多信息,请参见下文。

    links:
      - rethinkdb

链接似乎是连接两个不同容器的一种方式,但事实证明,所有容器都共享网络,因此您不必这样做。官方文档中有一个大红色警告,建议您使用用户定义的网络。这又表明:

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

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

因此,如果容器A的图像暴露了 172.18.0.2:28015,那么您可以使用该确切地址从容器B连接。E、 i.IP: 172.18.0.2和端口: 28015

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

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

因此,如果容器<code>RejectDB</code>公开了端口<code>28015</code>则可以通过<code>RejectDB:28015nodejs</code>容器访问该端口。

注意文件上说:

version: "3.9"
services:
  web:
    build: .
    ports:
      - "8000:8000"
  db:
    image: postgres
    ports:
      - "8001:5432"

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

在Compose postgres中的网络示例中是 imagedb是服务名称, 5432是端口号。这在我的经验中不起作用。您需要使用服务名称,而不是 image名称。因此,从 web连接到 db的正确方法是使用URL db: 5232@jonrshape指出协议(postgres://)恰好与图像名称匹配。

以下是我之前对该示例理解的更正。

在Composepostgres中的网络示例中是协议(类似于https),db是服务名称,5432是端口号。您需要使用服务名称,才能获得正确的IP地址。因此,从web连接到db的正确方法是使用URL[协议]://db: 5232。其中协议可以是超文本传输协议httpsprores等。

因为所有容器都在同一个网络中,所以不需要< code>ports键,除非您需要向本地机器(或外部网络)公开服务。

    volumes:
      - "./data: /data"

每个RethinkDB < code > docker file 都有这个,但是只有当您想要将文件从本地机器复制到容器中时才需要这个。在这种情况下,我们不想预加载任何数据库,所以我们没有任何文件来作为数据库的种子,因此不需要< code>volumes键。

 类似资料:
  • 问题内容: 我正在尝试设置spring xml配置,而不必创建进一步的。但是,即使我将数据库属性包括在 spring.xml: 我在这里想念什么? 问题答案: 在entityManagerFactory bean定义中指定“ packagesToScan”和“ persistenceUnitName”属性。 请注意,这适用于Spring版本> 3.1

  • 问题内容: 我目前正在使用不带Spring配置文件的CXF在Web Service客户端上工作。 它工作得很好,但是我不知道如何使用Java Api设置绑定的SoapVersion。使用Spring文件,可以按以下步骤完成: 你们知道如何在Java代码中执行此操作(在Port上,在SOAPBinding上…)? 在此先感谢您的帮助! 编辑 - - - - - - - - - - - 我仍然遇到这个

  • 问题内容: 我在一个项目中工作,我需要为每个对象添加一个快捷键,同时查找另一个类似的问题,并且在使用其他自定义s时,我决定在每个s 上使用该方法,但是这需要我按- “触发” 我班上的方法。 我如何修改此类以仅按- 并获得相同的行为? 这是我编写的用于演示此问题的代码: 问题答案: 您如何将任何键绑定到Swing中的组件?按键绑定:按键绑定教程。 等一下,我看看您的代码… 例如 因为通常JRadio

  • 我正在尝试设置SpringXML配置,而不必创建进一步的。但是我经常遇到以下异常,即使我在 spring.xml: 我错过了什么?

  • 问题内容: 我正在学习使用Selenium(v2.20)来领先一些 即将使用它创建浏览器测试的程序员。我想在 陷阱到达之前发现它们,而我却跌入了一个陷阱。 当我创建ChromeDriver时,它始终会弹出“ Google Chrome EULA”并 显示两个按钮:“接受并运行”和“取消”。因为我希望这是一个 自动化测试,所以让用户单击按钮是不可能的。 我查看了Chromium CommandSwi

  • 我已经安装了Android SDK最新版本和Eclipse。但我也想试试Android Studio。 我看过这个和这个帖子,但是那些解决方案改变了Android Studio(一旦下载并安装)使用的SDK实例。我想要的不是下载另一个SDK,当我已经在我的机器上安装了它。