问题描述
我正在尝试从一个容器向同一主机上的另一个容器发出请求。事实证明,请求被拒绝,我无法获得我想要的信息。为了说明,我做了一个小图:
在“机器 1”上,我有 2 个服务在 docker-compose 上运行:App1 有 2 个服务,需要向 App2 发出请求。 App2 是接收请求的服务,只有 1 个服务,监听 4202 端口。
docker-compose.yaml
用于 app1
version: '3.9'
services:
main_app:
build: .
depends_on:
- db_app
db_app:
restart: always
image: redis
docker-compose.yaml
for app2
version: '3.9'
services:
main_app:
build: .
ports:
- "4202:5000"
连接不起作用。这很奇怪,因为其他连接有效,例如:
在此图像中,在另一台机器 machine 2
上,我使用 curl
尝试访问在机器 1 内运行的容器。在端口 4202 上,我请求域 {{1}位于内部网络上,由内部 DNS 解析,之后,它确实成功请求了机器 1 上的容器。
在另一种情况下,我在机器 2 内创建了一个容器并使用 curl 向 name.domain.example 发出请求,结果是相同的:成功。我可以到达机器 1 内的容器。
这里,我在机器 1(运行将被请求的服务的同一台机器)上使用 curl。向name.domain.example发出请求,服务成功到达。
在这种情况下,这是请求失败的地方。这一次,我尝试从 app1 容器内发出请求,请求被发送到 name.domain.example 并且 DNS 能够解析它,但是当发出请求时,它最终被拒绝。
在 app1 中,我尝试通过 python 发出请求,来自请求库:
name.domain.example
我尝试将 app1 放在网络“主机”import requests
r = requests.get("name.domain.example:4202")
# But,Exception...:
requests.exceptions.ConnectionError: httpconnectionPool(host='name.domain.example',port=4202): Max retries exceeded with url: / (Caused by NewConnectionError('<urllib3.connection.httpconnection object at 0x7f74fbcb1100>: Failed to establish a new connection: [Errno 111] Connection refused'))
或 docker-compose--network=host
上,我可以访问 app2 的容器。但是,这对我的情况没有用,因为我需要来自组合堆栈的其他服务,此外,随着服务的增加和相互依赖程度的提高,这最终会变成蜘蛛网。
解决方法
这里的技巧是 docker compose 为您的每个应用程序创建 docker 网络(或网络命名空间)。尝试运行 docker network ls
,我敢打赌您会看到类似的内容(确切名称会有所不同):
❯ docker network ls
NETWORK ID NAME DRIVER SCOPE
c6a3be245bd1 bridge bridge local
f8e91845fd46 myapp2_default bridge local
677668cbb02b myapp1_default bridge local
默认情况下,这些“桥接”网络都不知道另一个。当您指定 --network=host
或 network_mode="host"
时,您会告诉容器跳过创建 docker/命名空间网络并驻留在主机的主命名空间中。从这里,他们可以看到您通常可以看到的一切。
所以让我们教这两个组合网络他们应该相互了解。
首先,我们在docker-compose范围之外创建一个docker网络:
❯ docker network create myglobalnetwork
6adbd64eed5dd12a2e6d60dffe01b5c222ec4701535322fc17dfeb51aff3b801
我们在 docker-compose 范围之外创建它,因为我们需要在任何一个 docker-compose 范围之外使用它。
我们现在可以更新我们的 docker-compose 配置以引用这个网络。我使用了不同的图像来简化示例,但想法保持不变:
# docker-compose.yml
version: '3.9'
services:
main_app:
image: busybox
command: ping main_app
depends_on:
- db_app
networks:
- default
- global
db_app:
restart: always
image: redis
networks:
global:
external:
name: myglobalnetwork
# docker-compose-other.yml
version: '3.9'
services:
other_app:
image: busybox
command: ping main_app
networks:
- default
- global
networks:
global:
external:
name: myglobalnetwork
然后我们可以启动主要的 docker-compose 项目:
✖2 ❯ docker-compose -f docker-compose.yml up
Starting stackoverflow_db_app_1 ... done
Starting stackoverflow_main_app_1 ... done
Attaching to stackoverflow_db_app_1,stackoverflow_main_app_1
db_app_1 | 1:C 18 Mar 2021 00:00:41.103 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
db_app_1 | 1:C 18 Mar 2021 00:00:41.103 # Redis version=6.2.1,bits=64,commit=00000000,modified=0,pid=1,just started
db_app_1 | 1:C 18 Mar 2021 00:00:41.103 # Warning: no config file specified,using the default config. In order to specify a config file use redis-server /path/to/redis.conf
main_app_1 | PING main_app (172.28.0.2): 56 data bytes
main_app_1 | 64 bytes from 172.28.0.2: seq=0 ttl=64 time=0.028 ms
db_app_1 | 1:M 18 Mar 2021 00:00:41.104 * monotonic clock: POSIX clock_gettime
<snip some redis logs>
db_app_1 | 1:M 18 Mar 2021 00:00:41.105 * Ready to accept connections
main_app_1 | 64 bytes from 172.28.0.2: seq=1 ttl=64 time=0.067 ms
main_app_1 | 64 bytes from 172.28.0.2: seq=2 ttl=64 time=0.059 ms
main_app_1 | 64 bytes from 172.28.0.2: seq=3 ttl=64 time=0.120 ms
这个能够 ping 自己,这很好,但没有那么令人兴奋或不同。
真正的魔法是当我们开始第二个时,它会尝试引用第一个。
❯ docker-compose -f docker-compose-other.yml up
Starting other_other_app_1 ... done
Attaching to other_other_app_1
other_app_1 | PING main_app (172.28.0.2): 56 data bytes
other_app_1 | 64 bytes from 172.28.0.2: seq=0 ttl=64 time=0.112 ms
other_app_1 | 64 bytes from 172.28.0.2: seq=1 ttl=64 time=0.112 ms
这就是我们教 docker 容器相互了解的方式!如果您有兴趣,可以在此处阅读更多内容:https://docs.docker.com/compose/networking/