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


277

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

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

更新:

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

ip addr show docker0 | grep -Po 'inet \K[\d.]+'

使用注释,因为这是一个很糟糕的答案,但是我相信您通常可以在172.17.1.78上访问它-除非这是boot2docker设置。
CashIsClay15年

@CashIsClay我尝试了一下,但仍然收到此错误curl: (7) Failed to connect to 172.17.1.78 port 7000: No route to host
Tri Nguyen

您没有指定;您是在运行boot2docker还是在Linux上本地运行Docker?
larsk's

@larsks对不起,我刚刚更新了问题-我在Linux上本地运行它。
Tri Nguyen

Answers:


206

在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

在容器内:

# ip route show
default via 172.17.0.1 dev eth0 
172.17.0.0/16 dev eth0  src 172.17.0.4 

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

#!/bin/sh

hostip=$(ip route show | awk '/default/ {print $3}')
echo $hostip

您可能需要修改iptables主机上的规则,以允许来自Docker容器的连接。这样的事情将达到目的:

# iptables -A INPUT -i docker0 -j ACCEPT

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

  • iptables规则是有序的,根据其他规则的不同,该规则可能会也可能不会做正确的事情。

  • 您将只能访问(a)监听INADDR_ANY(aka 0.0.0.0)或显式监听docker0接口的主机服务。


1
谢谢。我只需要获取IP地址,而无需修改iptables :)
Tri Nguyen


7
Docker for MAC怎么样?AFAIK没有适用于“ Docker for MAC”的docker0网络。在这种情况下,如何从容器连接到主机?
维杰2016年

17
如果您将Docker用于MAC v 17.06或更高版本,请使用docker.for.mac.localhost代替localhost127.0.0.1。这是doc
merito

1
我使用主机的主机名而不是获取IP地址(主机上的主机名命令)
Marek F

311

对于macOS和Windows

Docker v 18.03及更高版本(自2018年3月21日起)

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

Linux支持未决https://github.com/docker/for-linux/issues/264

带有较早版本Docker的MacOS

适用于Mac的Docker v 17.12至v 18.02

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

适用于Mac的Docker v 17.06至v 17.11

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

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

要从docker容器访问主机,必须将IP别名附加到网络接口。您可以绑定所需的任何IP,只需确保您没有将其用于其他任何IP。

sudo ifconfig lo0 alias 123.123.123.123/24

然后,确保您的服务器正在侦听上面提到的IP 0.0.0.0。如果在本地主机上侦听,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


我已经成功测试过了。无需关闭防火墙
alvaro g

15
从2017年6月发布的17.06起,他们的建议是连接到特殊的Mac专用DNS名称docker.for.mac.localhost,它将解析为主机使用的内部IP地址!...我已经对其进行了测试,并且它实际上正在工作!:)
Kyr

为像我这样的Mac用户提供即时解答。谢谢。我没有得到的一件事就是为什么ip route show | awk '/default/ {print $3}'给予者一个IP而docker.for.mac.localhost另一个给IP 。
dmmd


1
建议host.docker.internal在docker容器中使用,并127.0.0.1 host.docker.internalhosts文件中进行配置。
junlin

83

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


15
据我了解,由于容器在虚拟环境中运行,因此对于使用Windows Dpcker / Mac Docker的用户不起作用:Hyper-V(Windows)/ xhyve(Mac)
ninjaboy

6
这个!这就是答案!
user3751385

13
仅作记录:在Docker Componse中 network_mode: "host"
jbarros,

在客户端和服务器上的Docker for Mac版本19.03.1上,这不适用于我。我希望它能正常工作,但事实并非如此。
粘土

1
即使我在Mac上,当您要在两个docker容器之间进行通信时,它也可以工作,并且当我将所有应用程序转换为
docker

26

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

version: '3'
services:
  jenkins:
    network_mode: host

编辑2020-04-27:建议仅在本地开发环境中使用。


那么如何访问詹金斯呢?如果使用主机网络模式,则端口转发似乎无法正常工作
Jeff Tian

1
这是一个非常冒险的解决方案,完全不建议使用。除非明确需要,否则我们不应该向容器开放主机网络
Fatemeh Majd


12

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

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


这是一个聪明的解决方案。您是否知道有很多流量在使用它?代理通过此容器的所有流量可能会有开销。
bcoughlan

3
是的,它工作得很好,几乎没有任何开销,因为它只在环回设备上工作
qoomon

11

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

docker run -v /var/run/postgresql:/var/run/postgresql

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


这是一个很好的解决方案。不错的工作!
的付费书呆子

2
仅供参考,我们遇到了一个大问题:适用于Mac的Docker不支持将套接字作为已装载卷使用。直到Mac的人尝试了,它才如鱼得水。:(
mlissner

谢谢!在Linux上像魔咒一样工作!
Marcelo Cardoso

7

我探索了各种解决方案,并发现这是最不容易破解的解决方案:

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

唯一的缺点是,如果您有多个网络或项目正在执行此操作,则必须确保其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

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



3

您可以通过两种方式访问​​在主机中运行的本地Web服务器。

  1. 使用公共IP的方法1

    使用主机的公共IP地址访问Jenkins Docker容器中的Web服务器。

  2. 主机网络的方法2

    使用“ --net host”将Jenkins docker容器添加到主机的网络堆栈中。部署在主机堆栈上的容器可以完全访问主机接口。您可以使用主机的私有IP地址访问docker容器中的本地Web服务器。

NETWORK ID          NAME                      DRIVER              SCOPE
b3554ea51ca3        bridge                    bridge              local
2f0d6d6fdd88        host                      host                local
b9c2a4bc23b2        none                      null                local

从主机网络启动一个容器, Eg: docker run --net host -it ubuntu然后运行ifconfig以列出可从docker容器访问的所有可用网络IP地址。

例如:我在本地主机上启动了nginx服务器,并且能够从Ubuntu docker容器访问nginx网站URL。

docker run --net host -it ubuntu

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
a604f7af5e36        ubuntu              "/bin/bash"         22 seconds ago      Up 20 seconds                           ubuntu_server

使用私有网络IP地址从Ubuntu Docker容器访问Nginx Web服务器(在本地主机上运行)。

root@linuxkit-025000000001:/# curl 192.168.x.x -I
HTTP/1.1 200 OK
Server: nginx/1.15.10
Date: Tue, 09 Apr 2019 05:12:12 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 26 Mar 2019 14:04:38 GMT
Connection: keep-alive
ETag: "5c9a3176-264"
Accept-Ranges: bytes

3

对于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

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

撰写文件片段:

version: "3.7"

services:
  mongodb:
    image: mongo:4.2.2
    networks:
    - mynet
    # rest of service config and other services removed for clarity

networks:
  mynet:
    name: mynet
    ipam:
      driver: default
      config:
      - subnet: "192.168.32.0/20"

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

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

$ iptables -I INPUT 1 -s 192.168.32.0/20 -j ACCEPT

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

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


我看到了多个建议的答案iptables -A INPUT -i docker0 -j ACCEPT,但这并没有帮助我,而iptables -I INPUT 1 -s 192.168.32.0/20 -j ACCEPT这里的建议解决了我的问题。
特里·布朗

1

这是一个古老的问题,有很多答案,但是没有一个适合我的情况。就我而言,这些容器非常精简,并且不包含从容器内部提取主机ip地址所需的任何联网工具。

同样,该--net="host"方法是一种非常粗糙的方法,当要使用多个容器进行隔离的网络配置时不适用。

因此,我的方法是在主机端提取主机的地址,然后使用--add-host参数将其传递给容器:

$ docker run --add-host=docker-host:`ip addr show docker0 | grep -Po 'inet \K[\d.]+'` image_name

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

$ DOCKERIP=`ip addr show docker0 | grep -Po 'inet \K[\d.]+'`
$ docker run --add-host=docker-host:$DOCKERIP image_name

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


0

当您已经创建了两个“已经”泊坞窗映像,并且要放置两个容器以彼此通信时。

为此,您可以使用自己的--name方便地运行每个容器,并使用--link标志启用它们之间的通信。不过,在Docker构建期间您不会得到此信息。

当您处于像我这样的场景中时,这就是您的

docker build -t "centos7/someApp" someApp/ 

当您尝试破坏

curl http://172.17.0.1:localPort/fileIWouldLikeToDownload.tar.gz > dump.tar.gz

并且卡在“ curl / wget”上,不返回“路由到主机”。

原因是docker设置了适当的安全性,默认情况下,该安全性禁止从容器到主机或主机上运行的其他容器的通信。我必须说,这让我感到非常惊讶,您可以期望在本地计算机上运行的docker机器的echosystem可以完美地相互访问而不会带来太多障碍。

以下文档中对此进行了详细说明。

http://www.dedoimedo.com/computers/docker-networking.html

提供了两种快速的解决方法,可帮助您降低网络安全性,从而助您一臂之力。

最简单的选择就是关闭防火墙-或全部允许。这意味着运行必要的命令,该命令可以是systemctl stop firewalld,iptables -F或等效命令。

希望这些信息对您有所帮助。


3
谨记一下,--link现在已弃用
Budris先生

0

对我来说(Windows 10,Docker Engine v19.03.8)是https://stackoverflow.com/a/43541732/7924573https://stackoverflow.com/a/50866007/7924573的混合体。

  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(类似于https://stackoverflow.com/a/48806927/7924573),请使用
    如之前的答案所指出:这仅应在本地开发环境中使用
    有关更多信息,请阅读:https : //docs.docker.com/compose/compose-file/#network_modehttps://docs.docker.com/docker-for-windows/networking/#use-cases-and-workarounds
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.