多个docker-compose项目之间的通信


252

docker-compose.yml在两个不同的文件夹中有两个单独的文件:

  • ~/front/docker-compose.yml
  • ~/api/docker-compose.yml

如何确保in中的容器front可以向in中的容器发送请求api

我知道--default-gateway可以docker run为单个容器使用选项设置,以便可以为该容器分配特定的IP地址,但是使用时似乎没有此选项docker-compose

目前,我最终做了一个,docker inspect my_api_container_id然后查看输出中的网关。它可以工作,但问题是该IP是随机分配的,因此我不能依靠它。

因此,此问题的另一种形式可能是:

  • 我可以使用docker-compose将固定IP地址分配给特定容器吗?

但最后我要照顾的是:

  • 两个不同的docker-compose项目如何相互通信?

4
我今天才再次调查。开发人员终于松懈了,并允许任意网络命名。使用撰写文件版本3.5,您可以在“网络”键下指定默认网络的名称。这将创建一个没有通常的项目名称前缀命名的网络,如果它不存在..
cstrutton

Answers:


325

您只需要确保要互相交谈的容器在同一网络上即可。网络是一流的docker构造,并不特定于组合。

# front/docker-compose.yml
version: '2'
services:
  front:
    ...
    networks:
      - some-net
networks:
  some-net:
    driver: bridge

...

# api/docker-compose.yml
version: '2'
services:
  api:
    ...
    networks:
      - front_some-net
networks:
  front_some-net:
    external: true

注意:为您的应用程序的网络命名基于“项目名称”,该名称基于其所在目录的名称,在这种情况下front_,添加了前缀

然后,他们可以使用服务名称相互交谈。从front您可以做到ping api,反之亦然。


1
Jivan这不是解决方案。您的容器不需要知道有关主机的任何信息,也不必像这样被操纵。我的回答很短,但是我已经更新了更多细节。
johnharris85 '16

3
罗伯特·莫斯卡尔(Robert Moskal)仅在您四处乱窜以将Docker主机的IP放入容器中时。最好让它们在公用的docker定义的网络上进行通信。
johnharris85 '16

2
请注意,网络的“ front_”前缀是从其运行文件夹自动创建的。因此,如果您的第一个docker-compose文件位于“ example / docker-compose.yml”中,则其名为“ example_default”。
AngryUbuntuNerd

7
您还可以使用name属性为网络提供名称,这将禁用自动在项目名称前添加前缀。然后,任何一个项目都可以使用该网络并自动创建它(如果尚不存在)。
SteveB

2
@SteveB-请注意,name属性仅适用于
docker

78

只是@ johnharris85的一个好答案,当您运行docker compose文件时,default会创建一个“ ”网络,因此您可以将其作为外部网络添加到另一个compose文件中:

# front/docker-compose.yml 
version: '2' 
  services:   
    front_service:
    ...

...

# api/docker-compose.yml
version: '2'
services:
  api_service:
    ...
    networks:
      - front_default
networks:
  front_default:
    external: true

对我来说,这种方法更合适,因为我没有第一个docker-compose文件,而是想与之通信。


只是徘徊了为外部网络分配静态IP的正确方法。我要在services:标签内完成该操作,networks:然后将嵌套sintax front_default:(删除“-”),然后嵌套一个静态IP:ipv4_address: '172.20.0.44'
JuniorMayhé18年

76

更新:从撰写文件版本3.5开始:

现在可以使用:

version: "3.5"
services:
  proxy:
    image: hello-world
    ports:
      - "80:80"
    networks:
      - proxynet

networks:
  proxynet:
    name: custom_network

docker-compose up -d将加入一个名为“ custom_network”的网络。如果不存在,它将被创建!

root@ubuntu-s-1vcpu-1gb-tor1-01:~# docker-compose up -d
Creating network "custom_network" with the default driver
Creating root_proxy_1 ... done

现在,您可以执行以下操作:

version: "2"
services:
  web:
    image: hello-world
    networks:
      - my-proxy-net
networks:
  my-proxy-net:
    external:
      name: custom_network

这将创建一个将在外部网络上的容器。

我在文档中找不到任何参考,但是它有效!


您是否必须按特定顺序启动这两项服务?您可以启动任何一个,第一个创建网络,第二个创建网络吗?
slashdottir

4
第一个服务(上面的代理)创建网络。第二个示例中的语法将其加入。
cstrutton

2
@slashdottir您不能在第二个服务中将网络标记为外部网络,如果尚不存在,它将被创建。
SteveB

2
确实有效。我只是用最新的docker compose旋转了DO滴。我已将示例编辑为实际的工作示例。
cstrutton

1
就我而言,这比接受的答案更合适。外部网络的问题在于,它需要以预定义的顺序启动容器。对于我的客户,这是不可接受的。事实证明,命名网络(自3.5起)是完美的解决方案。谢谢。
ygor

24

来自的所有容器api都可以通过以下配置加入front 默认网络:

# api/docker-compose.yml

...

networks:
  default:
    external:
      name: front_default

请参阅Docker撰写指南:使用现有网络(请参阅底部)


12

前面的帖子信息是正确的,但是它没有有关如何链接容器的详细信息,这些容器应该以“ external_links”的形式连接。

希望此示例使您更清楚:

  • 假设您有带两个服务(svc11和svc12)的app1 / docker-compose.yml和带两个以上服务(svc21和svc22)的app2 / docker-compose.yml,并假设您需要以交叉方式连接:

  • svc11需要连接到svc22的容器

  • svc21需要连接到svc11的容器。

因此配置应如下所示:

这是app1 / docker-compose.yml:


version: '2'
services:
    svc11:
        container_name: container11
        [..]
        networks:
            - default # this network
            - app2_default # external network
        external_links:
            - container22:container22
        [..]
    svc12:
       container_name: container12
       [..]

networks:
    default: # this network (app1)
        driver: bridge
    app2_default: # external network (app2)
        external: true

这是app2 / docker-compose.yml:


version: '2'
services:
    svc21:
        container_name: container21
        [..]
        networks:
            - default # this network (app2)
            - app1_default # external network (app1)
        external_links:
            - container11:container11
        [..]
    svc22:
       container_name: container22
       [..]

networks:
    default: # this network (app2)
        driver: bridge
    app1_default: # external network (app1)
        external: true

6

从Compose 1.18(规范3.5)开始,您可以使用自己的自定义名称覆盖所需的所有Compose YAML文件,从而覆盖默认网络。只需向其添加以下内容即可:

networks:
  default:
    name: my-app

以上假设您已将version设置为3.5(或如果未在4+中弃用,则设置为)。

其他答案也指出了相同的观点。这是一个简化的摘要。


2

我将使用以下方法将所有容器docker-compose同时组合在一起,以确保所有容器都到达同一网络

docker compose --file ~/front/docker-compose.yml --file ~/api/docker-compose.yml up -d

例如,这是否会让我将一个linkdepends_on一个前端容器制作成一个api容器?
吉万

实际上,当我按照您的建议进行操作时,docker-compose会build path ~/front/api either does not exist or is not accessible以其他方式或以其他方式答复build path ~/api/front either does not exist or is not accessible
Jivan 2016年

1
如果您要同时进行合成,则无需这样做。将创建一个包含所有容器的网络,它们都将能够通过撰写文件中的服务名称(而不是容器名称)进行通信。
Nauraushaun

如果两个撰写文件位于同一文件夹中,则可能会更容易。但我认为这不是必需的-我认为这应该以任何一种方式起作用。
Nauraushaun

2
该解决方案不起作用,请参阅我对此线程的评论:github.com/docker/compose/issues/3530#issuecomment-222490501
johnharris85 '16

2

更新:从撰写文件版本3.5开始:

我遇到了类似的问题,并通过在docker-compose.yml项目之一中添加了一个小的更改来解决了该问题。

例如,我们有两个api scoringnerScoringapi需要向api发送请求以ner处理输入请求。为了做到这一点,他们都假定共享同一网络。

注意:每个容器都有其自己的网络,该网络是在docker中运行应用程序时自动创建的。例如,将创建ner api网络,将ner_default评分api网络命名为scoring default。此解决方案适用于版本:“ 3”。

在上述情况下,我的计分api要与ner api通信,那么我将添加以下几行。这意味着每当我为ner api创建容器时,它就会自动添加到scoring_default网络。

networks:
  default:
      external:
        name: scoring_default

ner / docker-compose.yml

version: '3'
services:
  ner:
    build: .
    ...

networks:
  default:
      external:
        name: scoring_default

评分/docker-compose.yml

version: '3'
services:
  api:
    build: .
    ...

我们可以看到上述容器现在如何成为scoring_default使用命令调用的同一网络的一部分 :

码头工人检查scoring_default

{
    "Name": "scoring_default",
        ....
    "Containers": {
    "14a6...28bf": {
        "Name": "ner_api",
        "EndpointID": "83b7...d6291",
        "MacAddress": "0....",
        "IPv4Address": "0.0....",
        "IPv6Address": ""
    },
    "7b32...90d1": {
        "Name": "scoring_api",
        "EndpointID": "311...280d",
        "MacAddress": "0.....3",
        "IPv4Address": "1...0",
        "IPv6Address": ""
    },
    ...
}

1

您可以.env在所有包含的项目中添加文件COMPOSE_PROJECT_NAME=somename

COMPOSE_PROJECT_NAME会覆盖用于命名资源的前缀,因此您的所有项目都将使用somename_default用作其网络,从而使服务可以像在同一项目中一样相互通信。

注意:您将收到从其他项目创建的“孤立”容器的警告。


0
version: '2'
services:
  bot:
    build: .
    volumes:
      - '.:/home/node'
      - /home/node/node_modules
    networks:
      - my-rede
    mem_limit: 100m
    memswap_limit: 100m
    cpu_quota: 25000
    container_name: 236948199393329152_585042339404185600_bot
    command: node index.js
    environment:
      NODE_ENV: production
networks:
  my-rede:
    external:
      name: name_rede_externa

0

对于使用另一个docker-compose网络,您只需执行以下操作(即可在docker-compose之间共享网络):

  1. 通过运行第一个docker-compose项目 up -d
  2. 查找第一个docker-compose的网络名称:(docker network ls包含根目录项目的名称)
  3. 然后在下面的第二个docker-compose文件中通过该结构使用该名称。

第二个docker-compose.yml

version: '3'
services:
  service-on-second-compose:  # Define any names that you want.
    .
    .
    .
    networks:
      - <put it here(the network name that comes from "docker network ls")>

networks:
  - <put it here(the network name that comes from "docker network ls")>:
    external: true

0

另一个选择是只运行带有'docker-compose'的第一个模块,检查与该模块相关的ip,然后将第二个模块与先前的网络(例如外部)连接,并指向内部ip。

示例app1-在服务行中创建的新网络,在底部将其标记为外部:true app2-在上升时指示由app1创建的“新网络”,在底部将其标记为外部:true,并在配置中进行设置连接,app1在此网络中拥有的ip。

有了这个,你应该能够互相交谈

*这种方式仅用于本地测试,以免进行过于复杂的配置**我知道这是非常“修补方式”,但对我有用,我认为很简单,其他人可以利用此优势


0

如果你是

  • 试图在来自不同docker-compose项目的两个容器之间进行通信,并且不想使用同一网络(因为假设它们在同一端口上具有PostgreSQL或Redis容器,并且您宁愿不更改这些端口而不使用它在同一网络上)
  • 在本地发展并想模仿之间的交流 两个docker compose项目
  • 在本地主机上运行两个docker-compose项目
  • 开发特别是Django应用程序或Django Rest Framework(drf)API并在某个暴露端口上的容器内运行应用程序
  • Connection refused尝试在两个容器之间进行通信时得到

而你想

  • 容器api_aapi_b没有相同“ docker网络”的情况下进行通信(反之亦然)

(下面的示例)

您可以将第二个容器的“主机”用作计算机的IP和从Docker容器内部映射的端口。您可以使用以下脚本获取计算机的IP(来自:使用Python的stdlib查找本地IP地址):

import socket
def get_ip():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        # doesn't even have to be reachable
        s.connect(('10.255.255.255', 1))
        IP = s.getsockname()[0]
    except:
        IP = '127.0.0.1'
    finally:
        s.close()
    return IP

例:

project_api_a/docker-compose.yml

networks:
  app-tier:
    driver: bridge

services:
  api:
    container_name: api_a
    image: api_a:latest
    depends_on:
      - postgresql
    networks:
      - app-tier

api_a容器中,您正在运行Django应用程序: manage.py runserver 0.0.0.0:8000

第二个来自其他项目的docker-compose.yml:

project_api_b/docker-compose-yml

networks:
  app-tier:
    driver: bridge

services:
  api:
    container_name: api_b
    image: api_b:latest
    depends_on:
      - postgresql
    networks:
      - app-tier

api_b容器中,您正在运行Django应用程序: manage.py runserver 0.0.0.0:8001

并尝试从容器连接api_aapi_bURL的api_b容器将是: http://<get_ip_from_script_above>:8001/

如果您要使用两个(三个或三个以上)docker-compose项目,并且很难为所有项目提供通用网络,则这可能特别有价值-这是一个很好的解决方法和解决方案

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.