【问题标题】:Why can't I curl one docker container from another via the host为什么我不能通过主机从另一个容器中卷曲一个 docker 容器
【发布时间】:2016-12-15 18:37:45
【问题描述】:

我真的不明白这里发生了什么。我只是想从一个 docker 容器内部执行一个 http 请求,通过主机,使用 主机的公共 ip,在 发布的端口上执行一个 http 请求。

这是我的设置。我有我的开发机器。我有一个带有两个容器的 docker 主机。 CONT_A 在端口 3000 上侦听并发布 Web 服务。

DEV-MACHINE

HOST (Public IP = 111.222.333.444)
  CONT_A (Publish 3000)
  CONT_B

在我的开发机器上(完全不同的机器)

我可以毫无问题地卷曲

curl http://111.222.333.444:3000 --> OK

当我通过 SSH 连接到主机时

我可以毫无问题地卷曲

curl http://111.222.333.444:3000 --> OK

当我在 CONT_B 内执行时

不可能,只是超时。 Ping 没问题...

docker exec -it CONT_B bash
$ curl http://111.222.333.444:3000 --> TIMEOUT
$ ping 111.222.333.444 --> OK

为什么?

Ubuntu 16.04、Docker 1.12.3(默认网络设置)

【问题讨论】:

  • 我认为这可能是因为 Docker 如何使用 IPTables & Bridge Network 处理网络。我必须查一下,但我猜你提供的 IP 地址可能是从主机看到的 IP 地址,而不是在 docker 网络中分配的“内部 IP”。 ping 可能解析的原因是因为它解析到 docker 主机(而不是容器)。但是 docker 实现这一点的方法是不在容器 A 中发布端口,而是制作一个覆盖网络docs.docker.com/engine/userguide/networking
  • @RikNauta 我使用的 IP 地址是 公共主机 ip。当然,除非它与内部 docker 网络发生冲突,否则应该始终正确解决。这是一个104.... IP 地址。
  • 应该可以。 docker 运行一个用户态代理进程来监听主机并将 tcp 连接转发到容器中。在 Docker 1.11 中是否同样有效?您是否在 docker 守护程序上将 userland-proxy 设置为 false?
  • @Matt 我还没有测试过 Docker 1.11,我不确定 userland-proxy。我没有碰过它,所以我假设它是默认的。有趣的是,当我用谷歌搜索“userland-proxy”时,我发现了这个github.com/docker/docker/issues/21860
  • 很确定这一直是容器无法与自己主机上的暴露端口通信的情况。如果 iptables、nat/mangle 规则或 docker-proxy 未配置为开箱即用地支持这一点,也不会感到惊讶。因为你可以直接从容器到容器,所以这对我来说从来都不是一个值得调试的问题。

标签: docker


【解决方案1】:

我知道这不是对问题的严格回答,但有一种更 Docker-ish 的方式来解决您的问题。我会完全忘记发布用于容器间通信的端口。而是使用 docker swarm 创建一个覆盖网络。您可以找到完整指南here,但实际上您可以执行以下操作:

//create network    
docker network create --driver overlay --subnet=10.0.9.0/24 my-net
//Start Container A
docker run -d --name=A --network=my-net producer:latest
//Start Container B
docker run -d --name=B --network=my-net consumer:latest

//Magic has occured
docker exec -it B /bin/bash
> curl A:3000 //MIND BLOWN!

然后在容器中,您只需 curl 主机名 A,它就会为您解析(即使您开始进行缩放等)

如果您不热衷于使用 Docker swarm,您仍然可以使用 Docker 旧链接:

docker run -d --name B --link A:A consumer:latest

这将链接您的 A 容器中任何暴露(未发布)的端口。

最后,如果您开始转向生产...完全忘记链接和覆盖网络...使用 Kubernetes :-) 初始设置有点困难,但它们引入了一系列概念和工具来创建链接和扩展集群容器更容易!但这只是我个人的看法。

【讨论】:

  • 单台主机不需要overlay网络,默认网桥驱动就可以工作文件。
【解决方案2】:

我遇到了类似的问题,我在一个容器(我们称之为 web)中有一个 nginx 服务器,其中包含多个服务器块,并且 cron 安装在另一个容器中(我们称之为 cron)。我使用码头工人撰写。我想不时使用从 cron 到 web 的 curl 在其中一个应用程序上执行一些 php 脚本。它应该如下所示:

curl http://app1.example.com/some_maintance.php

但我总是在一段时间后无法访问主机。

第一个解决方案是更新 cron 容器中的 /etc/hosts,并添加:

1.2.3.4 app1.example.com

其中 1.2.3.4 是 Web 容器的 ip,它可以工作 - 但这是一个 hack - 据我所知,不鼓励此类手动更新。您应该在 docker compose 中使用extra_hosts,这需要显式 IP 地址而不是容器名称来指定 IP 地址。

我尝试使用自定义网络解决方案,正如我所见,这是处理此问题的正确方法,但我在这里从未成功。如果我知道如何做到这一点,我保证会更新这个答案。

最后我使用 curl 功能指定服务器的 IP 地址,并在单独的参数中将域名作为标头传递:

curl -H'Host: app1.example.com' web/some_maintance.php

不是很漂亮,但确实有效。

(这里的 web 是我的 nginx 容器的名称)

【讨论】:

    【解决方案3】:

    通过使用--network host 参数运行你的容器B,你可以简单地使用本地主机访问你的容器A,不需要公共IP。

    > docker run -d --name containerB --network host yourimagename:version
    

    使用上述命令运行容器 B 后,您可以像这样尝试从容器 B 卷曲容器 A

    > docker exec -it containerB /bin/bash
    > curl http://localhost:3000
    

    【讨论】:

      【解决方案4】:

      当前的答案都没有解释为什么 docker 容器的行为与问题中描述的一样

      Docker 可以为一个或多个容器提供主机资源的轻量级隔离。

      Docker network 默认情况下与主机网络隔离,并使用bridge network(同样,默认情况下;您有覆盖网络)进行容器间通信。

      以及如何在没有 docker 网络的情况下解决问题。

      来自“How to connect to the Docker host from inside a Docker container?

      从 Docker 版本 18.03 开始​​,您可以使用 host.docker.internal 主机名从 Docker 容器内连接到您的 Docker 主机。

      这在 Docker for Mac 和 Docker for Windows 上运行良好,但不幸的是,直到 2020 年 12 月发布 Docker 20.10.0,Linux 才支持此功能。

      从 20.10 版本开始,Docker 引擎现在还支持在 Linux 上通过host.docker.internal 与 Docker 主机通信。
      不幸的是,这在 Linux 上无法开箱即用,因为您需要添加额外的 --add-host 运行标志:

      --add-host=host.docker.internal:host-gateway
      

      这是出于开发目的,不适用于 Docker Desktop for Windows/Mac 之外的生产环境。

      这样,您不必将网络驱动程序更改为--network=host,仍然可以通过host.docker.internal访问主机。

      【讨论】:

      • 感谢您的回答!我仍然不明白的是,如果公共域名的目标是自己的主机或另一台服务器,为什么容器的行为会有所不同?我在这里详细概述了我的设置和日志:github.com/ONLYOFFICE/Docker-DocumentServer/issues/…
      • @RoBeaToZ 因为容器背后的所有想法都是相信它是它的“自己的机器”,这意味着默认情况下查看主机资源(CPU、磁盘、网络)。
      • 我明白这一点。在这种情况下,容器 B 会尝试访问HOST_PUBLIC_IP:300 后面的服务。对我来说,这很直观,该服务是在另一台服务器、AWS 云还是在同一主机上的容器中运行都无关紧要。在不同的环境中可能会有所不同。容器 B 没有这些信息,必须以特殊的方式处理后一种情况,这对我来说感觉不直观。
      • @RoBeaToZ 确实不直观,但符合容器的使命:处于自己的生态系统中,不知道任何“外部”(容器及其自己的网络外部)“公共”IP。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-01-07
      • 1970-01-01
      • 1970-01-01
      • 2015-06-04
      • 2023-01-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多