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

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

我在Linux机器上本机运行docker。

更新:

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

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

当在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地址是相当容易的 脚本:< / p >
#!/bin/sh


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

您可能需要修改主机上的iptables规则以允许 Docker容器的连接。像这样的东西会 技巧:< / p >

# iptables -A INPUT -i docker0 -j ACCEPT
这将允许Docker访问主机上的任何端口 容器。注意:< / p >
  • iptables规则是有序的,该规则可以执行,也可以不执行

    .

    .

    .
  • 您将只能访问(a)的主机服务 监听INADDR_ANY(又名0.0.0.0)或显式的

    .监听docker0接口

如果你在MacOS窗户 18.03+上使用Docker,你可以连接到神奇的主机名host.docker.internal


最后,在Linux下,您可以通过设置--net=host在主机网络命名空间中运行容器;在这种情况下,主机上的localhost与容器内的localhost相同,因此容器化服务将像非容器化服务一样,无需任何额外配置即可访问。

当你已经有两个docker映像时;创建并希望将两个容器彼此通信。

为此,您可以方便地使用自己的——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机器的回声系统能够完美地相互访问,没有太多障碍

对此的解释将在以下文档中详细描述。

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

本文给出了两个快速解决方案,可以帮助您降低网络安全性。

最简单的选择是关闭防火墙——或者允许所有防火墙。这意味着运行必要的命令,可以是systemctl stop firewall, iptables -F或等效命令。

适用于所有平台

Docker v 20.10及以上版本(自2020年12月14日起)

在Linux上,在Docker命令中添加--add-host=host.docker.internal:host-gateway来启用该功能。(Docker Compose配置见下文)

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

要在Linux上的Docker Compose中启用此功能,请在容器定义中添加以下行:

extra_hosts:
- "host.docker.internal:host-gateway"

适用于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

Mac v 17.12到v 18.02的Docker

与上面相同,但是使用docker.for.mac.host.internal代替。

Mac v 17.06到v 17.11的Docker

与上面相同,但是使用docker.for.mac.localhost代替。

Docker适用于Mac 17.05及以下版本

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

# EYZ0

然后确保你的服务器正在监听上面提到的IP或0.0.0.0。如果它在localhost# EYZ1上监听,它将不接受连接。

然后只需将docker容器指向这个IP,就可以访问主机了!

为了进行测试,您可以在容器中运行类似curl -X GET 123.123.123.123:3000的代码。

别名将在每次重新启动时重置,因此如果需要,创建一个启动脚本。

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

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

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

用docker-compose解决方案: 要访问基于主机的服务,可以使用network_mode参数 # EYZ0 < / p >
version: '3'
services:
jenkins:
network_mode: host

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

编辑2021-09-21:ihavehandedinmyresign写道,它不适用于Mac和Windows。选项仅支持Linux操作系统

我创建了一个docker容器来完成https://github.com/qoomon/docker-host

你可以简单地使用容器名dns访问主机系统。 # EYZ0 < / p >

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

  1. 定义网桥网关的静态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”访问主机上的端口。

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

  1. 使用公网IP的方式1

    在Jenkins docker容器中使用主机公网IP地址访问webserver

  2. 与主机网络接近2

    使用"——net host"将Jenkins docker容器添加到主机的网络堆栈上。部署在主机堆栈上的容器具有对主机接口的全部访问权。您可以使用主机的私有IP地址访问docker容器中的本地webserver

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。

# EYZ0

$ 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

我们发现,对于所有这些网络垃圾,一个更简单的解决方案是仅为服务使用域套接字。如果您无论如何都试图连接到主机,只需将套接字作为卷挂载,就可以了。对于postgresql,这很简单:

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

然后,我们只需设置数据库连接以使用套接字而不是网络。就是这么简单。

对于使用桥接网络在容器之间创建专用网络的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是不可预测的,并且每次compose必须重新创建网络时(例如在主机重新启动时)都会改变。我的解决方案是在一个已知子网中创建私有网络,并配置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"

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

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

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

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

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

这是一个老问题,有很多答案,但没有一个足够适合我的背景。在我的例子中,容器非常精简,不包含从容器中提取主机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中使用它。

对于linux系统,可以从docker引擎的主版本20.04开始,现在也可以通过host.docker.internal与主机通信。自动不会起作用,但你需要提供以下运行标志:

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

看到

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

  1. change the host/ip to host.docker.internal
    例如:LOGGER_URL = "http://host.docker.internal:8085/log"
  2. 设置network_mode为(如果你想保持端口转发;如果没有使用宿主):
    <代码>版本:“3.7” 服务: 服务器: 构建:。 港口: ——“5000:5000” network_mode:桥 或者:如果你不使用docker-compose(类似于https://stackoverflow.com/a/48806927/7924573),使用--net="bridge"
    正如在之前的回答中指出的:这应该是只能在本地开发环境中使用.
    更多信息请阅读:https://docs.docker.com/compose/compose-file/#network_modehttps://docs.docker.com/docker-for-windows/networking/#use-cases-and-workarounds
对我来说最简单的选择是, 我使用我的机器在本地网络上的IP地址(由路由器分配)

您可以使用ifconfig命令找到它

ifconfig


en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
options=400<CHANNEL_IO>
ether f0:18:98:08:74:d4
inet 192.168.178.63 netmask 0xffffff00 broadcast 192.168.178.255
media: autoselect
status: active

然后使用inet地址。这可以让我连接机器上的任何端口。

答案是……

http://127.0.0.1http://localhost替换为http://host.docker.internal

为什么?

# EYZ0。

我的谷歌搜索把我带到了这里,在挖掘评论后,我发现它是如何从Docker容器内部连接到机器的本地主机?的副本。我投票认为这是重复的,但由于人们(包括我自己!)经常向下滚动答案,而不是仔细阅读评论,这里是一个简短的答案。

在近7年的时间里,这个问题被提出了,要么docker已经改变了,要么没有人尝试过这种方法。所以我会给出我自己的答案。

我发现所有的答案都使用了复杂的方法。今天,我需要这些,并找到了两种非常简单的方法:

  • 在你的主机上使用ipconfigifconfig,并记下所有的IP地址。容器至少可以使用其中的两个。

    • 我有一个固定的本地网络地址在WiFi局域网适配器:192.168.1.101。这个可以是10.0.1.101。结果会根据你的路由器而改变
    • 我在windows上使用WSL,它有自己的vEthernet地址:172.19.192.1
  • < p > # EYZ0使用。大多数答案都有这样或那样的形式,这取决于操作系统。从这个名字可以看出,docker现在在全球范围内使用它。

第三种选择是使用计算机的WAN地址,或者换句话说,由服务提供者提供的IP地址。然而,如果IP不是静态的,并且需要路由和防火墙设置,这可能不起作用。


PS:虽然和这个问题非常相似,我在那里发布了这个答案,但我是先找到这个帖子的,所以我也把它贴在这里,以免忘记我自己的答案。