如何从docker容器访问主机端口

Tri*_*yen 217 docker docker-container

我有一个运行jenkins的码头集装箱.作为构建过程的一部分,我需要访问在主机上本地运行的Web服务器.有没有办法将主机Web服务器(可以配置为在端口上运行)暴露给jenkins容器?

编辑:我在Linux机器上本地运行docker.

更新:

除了下面的@larsks答案,要从主机获取主机IP的IP地址,我还会执行以下操作:

ip addr show docker0 | grep -Po 'inet \K[\d.]+'
Run Code Online (Sandbox Code Playgroud)

Jan*_*ala 235

对于macOS和Windows

Docker v 18.03及以上(自2018年3月21日起)

使用内部IP地址或连接到特殊DNS名称host.docker.internal,该名称将解析为主机使用的内部IP地址.

Linux支持等待https://github.com/docker/for-linux/issues/264

MacOS与早期版本的Docker

Docker for Mac v 17.12至v 18.02

与上述相同,但docker.for.mac.host.internal改为使用.

Docker for Mac v 17.06至v 17.11

与上述相同,但docker.for.mac.localhost改为使用.

适用于Mac 17.05及更低版本的Docker

要从docker容器访问主机,必须将IP别名附加到网络接口.你可以绑定你想要的任何IP,只要确保你没有将它用于其他任何东西.

sudo ifconfig lo0 alias 123.123.123.123/24

然后确保您的服务器正在侦听上述IP或0.0.0.0.如果它正在侦听localhost 127.0.0.1,它将不接受连接.

然后只需将您的docker容器指向此IP,即可访问主机!

要测试你可以curl -X GET 123.123.123.123:3000在容器内部运行.

别名将在每次重新启动时重置,因此如有必要,请创建启动脚本.

解决方案和更多文档:https://docs.docker.com/docker-for-mac/networking/#use-cases-and-workarounds

  • 从2017年6月17.06开始,他们的建议是连接到特殊的Mac-DNS名称`docker.for.mac.localhost`,它将解析为主机使用的内部IP地址!...我测试了它,它实际上正在工作!:) (15认同)
  • 改为`host.docker.internal` https://docs.docker.com/docker-for-mac/networking/#i-cannot-ping-my-containers (7认同)
  • 建议在 docker 容器中使用 `host.docker.internal` 并在 **hosts** 文件中配置 `127.0.0.1 host.docker.internal`。 (2认同)

lar*_*sks 167

在Linux上本机运行Docker时,您可以使用docker0接口的IP地址访问主机服务.从容器内部,这将是您的默认路由.

例如,在我的系统上:

$ ip addr show docker0
7: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::f4d2:49ff:fedd:28a0/64 scope link 
       valid_lft forever preferred_lft forever
Run Code Online (Sandbox Code Playgroud)

在容器内:

# ip route show
default via 172.17.0.1 dev eth0 
172.17.0.0/16 dev eth0  src 172.17.0.4 
Run Code Online (Sandbox Code Playgroud)

使用简单的shell脚本提取此IP地址相当容易:

#!/bin/sh

hostip=$(ip route show | awk '/default/ {print $3}')
echo $hostip
Run Code Online (Sandbox Code Playgroud)

您可能需要修改iptables主机上的规则以允许来自Docker容器的连接.像这样的东西可以解决这个问题:

# iptables -A INPUT -i docker0 -j ACCEPT
Run Code Online (Sandbox Code Playgroud)

这将允许从Docker容器访问主机上的任何端口.注意:

  • iptables规则是有序的,这个规则可能会也可能不会做正确的事情,这取决于之前的其他规则.

  • 您将只能访问(a)监听INADDR_ANY(又名0.0.0.0)或明确监听docker0接口的主机服务.

  • 如果你使用Docker for MAC v 17.06或更高版本,只需使用`docker.for.mac.localhost`而不是`localhost`或`127.0.0.1`.这是[**doc**](https://docs.docker.com/docker-for-mac/networking/#use-cases-and-workarounds). (11认同)
  • Docker for MAC怎么样?AFAIK没有docker0网络可用于"Docker for MAC".在那种情况下,我如何从容器连接到主机? (6认同)
  • 相关链接:[将条目添加到容器的/etc/hosts](https://docs.docker.com/engine/reference/commandline/run/#add-entries-to-container-hosts-file-add-host) (2认同)

Aid*_*din 158

答案是...

替换http://127.0.0.1http://localhosthttp://host.docker.internal.

为什么?

来源在 Docker 的文档中

我的谷歌搜索将我带到了这里,在深入研究评论后,我发现它与From inside of a Docker container, how do I connect to the localhost of the machine? 重复。。我投票赞成将其作为重复项关闭,但由于人们(包括我自己!)经常向下滚动答案而不是仔细阅读评论,因此这里有一个简短的答案。

  • 这不会自动工作,但您需要提供以下运行标志:`--add-host=host.docker.internal:host-gateway` (9认同)
  • 这是一个完美的解决方案,除非您不必在代码库中进行本地更改 (2认同)
  • 是的,就是这样。@DeekshithAnand,一般来说,我建议构建您的代码,以便 URL 等属于环境变量(即类似于带有适当加载程序的 .env 文件)。这样您就可以在不影响生产可重复性的情况下进行类似的操作 (2认同)
  • 您不知道我在这上面浪费了多少时间只是为了找到简洁的答案,谢谢。 (2认同)

sam*_*est 49

--net="host"在您的docker run命令中使用,然后localhost在您的docker容器中将指向您的docker主机.

  • 据我所知,由于容器在虚拟化环境中运行,因此对于那些使用Dpcker for Windows/Docker for Mac的人来说不会起作用:Hyper-V(Windows)/ xhyve(Mac) (12认同)
  • 如果我理解正确的话,这不是答案,因为它使 Docker 使用[主机网络](https://docs.docker.com/network/host/):“网络堆栈没有与 Docker 主机隔离(容器共享主机的网络命名空间)”。这可能“有效”,但可能不是OP需要或想要的。 (11认同)
  • 仅作记录:在Docker Componse中,`network_mode:“ host”` (5认同)
  • 这个!这就是答案! (4认同)
  • 即使我在 Mac 上,当您想要两个 docker 容器之间进行通信时它也可以工作,并且当我将所有应用程序转换为 docker 映像时,这对我来说就足够了 (3认同)

Sam*_*uel 48

对于 linux 系统,您可以——从20.04docker 引擎的主要版本开始——现在也可以通过host.docker.internal. 这不会自动工作,但您需要提供以下运行标志:

--add-host=host.docker.internal:host-gateway
Run Code Online (Sandbox Code Playgroud)

  • 对于早期的 Linux Docker 版本,我们可以使用 `--add-host host.docker.internal:$(ip addr show docker0 | grep -Po 'inet \K[\d.]+') 以类似的方式轻松解决这个问题` (7认同)
  • Docker Engine 20.10 刚刚发布,“host-gateway”应该终于可用了。 (3认同)
  • 主要版本是什么? (2认同)
  • “host-gateway”是关键字还是必须是主机的显式网关?嗯 (2认同)

vov*_*van 17

使用docker-compose的解决方案:要访问基于主机的服务,您可以使用network_mode参数 https://docs.docker.com/compose/compose-file/#network_mode

version: '3'
services:
  jenkins:
    network_mode: host
Run Code Online (Sandbox Code Playgroud)

  • 这是一个非常危险的解决方案,根本不推荐。除非明确需要,否则我们不应该向容器开放主机网络 (3认同)

bco*_*lan 14

我探索了各种解决方案,我发现这是最简单的解决方案:

  1. 为网桥网关 IP 定义一个静态 IP 地址。
  2. 添加网关 IP 作为extra_hosts指令中的额外条目。

唯一的缺点是如果您有多个网络或项目这样做,您必须确保它们的 IP 地址范围不冲突。

这是一个 Docker Compose 示例:

version: '2.3'

services:
  redis:
    image: "redis"
    extra_hosts:
      - "dockerhost:172.20.0.1"

networks:
  default:
    ipam:
      driver: default
      config:
      - subnet: 172.20.0.0/16
        gateway: 172.20.0.1
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用主机名“dockerhost”从容器内部访问主机上的端口。


Tom*_*far 11

目前,在Mac和Windows上执行此操作的最简单方法是使用主机host.docker.internal,该主机解析为主机的IP地址.不幸的是它还没有在Linux上工作(截至2018年4月).


And*_*own 9

对于docker-compose使用桥接网络在容器之间创建专用网络,使用的公认解决方案docker0不起作用,因为来自容器的出口接口不是docker0,而是随机生成的接口 ID,例如:

$ ifconfig

br-02d7f5ba5a51: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.32.1  netmask 255.255.240.0  broadcast 192.168.47.255
Run Code Online (Sandbox Code Playgroud)

不幸的是,随机 id 是不可预测的,并且每次 compose 必须重新创建网络时都会更改(例如,在主机重新启动时)。我对此的解决方案是在已知子网中创建专用网络并配置iptables为接受该范围:

编写文件片段:

$ ifconfig

br-02d7f5ba5a51: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.32.1  netmask 255.255.240.0  broadcast 192.168.47.255
Run Code Online (Sandbox Code Playgroud)

如果您的环境需要,您可以更改子网。我192.168.32.0/20通过使用任意选择docker network inspect来查看默认创建的内容。

iptables在主机上配置以允许私有子网作为源:

$ iptables -I INPUT 1 -s 192.168.32.0/20 -j ACCEPT
Run Code Online (Sandbox Code Playgroud)

这是最简单的iptables规则。您可能希望添加其他限制,例如通过目标端口。当您对 iptables 的工作感到满意时,不要忘记坚持您的 iptables 规则。

这种方法的优点是可重复,因此可自动化。我使用 ansible 的template模块通过变量替换来部署我的撰写文件,然后分别使用iptablesshell模块来配置和保留防火墙规则。

  • 我看到多个答案建议“iptables -A INPUT -i docker0 -j ACCEPT”,但这对我没有帮助,而此处建议的“iptables -I INPUT 1 -s 192.168.32.0/20 -j ACCEPT”解决了我的问题。 (3认同)

qoo*_*mon 7

我创建了一个docker容器来完全做到这一点https://github.com/qoomon/docker-host

然后,您可以简单地使用容器名称dns访问主机系统,例如 curl http://dockerhost:9200

  • 是的,它工作得很好,几乎没有任何开销,因为它只在环回设备上工作 (2认同)
  • 超级干净的解决方案,谢谢! (2认同)

mli*_*ner 7

我们发现,针对所有这些网络垃圾的更简单解决方案是仅将域套接字用于服务。如果仍然尝试连接到主机,只需将套接字作为卷安装,就可以了。对于PostgreSQL,这很简单:

docker run -v /var/run/postgresql:/var/run/postgresql
Run Code Online (Sandbox Code Playgroud)

然后,我们只需将数据库连接设置为使用套接字而不是网络即可。从字面上看很容易。

  • 仅供参考,我们遇到了一个大问题:Mac 版 Docker 不支持将套接字作为挂载卷。一切进展顺利,直到一位 Mac 人员尝试了它。:( (3认同)

Pas*_*day 7

这是一个老问题,有很多答案,但没有一个足够适合我的背景。就我而言,容器非常精简,不包含从容器内提取主机 IP 地址所需的任何网络工具。

此外,使用该--net="host"方法是一种非常粗略的方法,当想要与多个容器建立良好隔离的网络配置时,该方法不适用。

所以,我的方法是在主机端提取主机的地址,然后将其传递给带有--add-host参数的容器:

$ docker run --add-host=docker-host:`ip addr show docker0 | grep -Po 'inet \K[\d.]+'` image_name
Run Code Online (Sandbox Code Playgroud)

或者,将主机的 IP 地址保存在环境变量中并稍后使用该变量:

$ DOCKERIP=`ip addr show docker0 | grep -Po 'inet \K[\d.]+'`
$ docker run --add-host=docker-host:$DOCKERIP image_name
Run Code Online (Sandbox Code Playgroud)

然后将其docker-host添加到容器的主机文件中,您可以在数据库连接字符串或 API URL 中使用它。


tsc*_*ker 5

对我来说(Windows 10,Docker Engine v19.03.8),它是/sf/answers/3047921271//sf/answers/3560620521/的混合体。

  1. 将主机/IP 更改为host.docker.internal
    例如:LOGGER_URL = " http://host.docker.internal:8085/log "
  2. 将network_mode设置为bridge(如果您想维护端口转发;如果不使用host):
    version: '3.7' services: server: build: . ports: - "5000:5000" network_mode: bridge 或者:--net="bridge"如果您不使用docker-compose,则使用(类似于/sf/answers/3416484921/
    正如前面的答案中指出的:这应该只在本地开发环境中使用
    有关更多信息,请阅读: https: //docs.docker.com/compose/compose-file/#network_modehttps://docs.docker.com/docker-for-windows/networking/#use-cases-and-workarounds