我有一个Nginx在码头集装箱里运行。我在主机系统上运行了MySql。我想从我的容器中连接到MySql。MySql仅绑定到本地主机设备。

是否有任何方法可以从这个docker容器连接到这个MySql或本地主机上的任何其他程序?

这个问题与“如何从docker容器中获取docker主机的IP地址”不同,因为docker主机IP地址可以是网络中的公共IP或私有IP,而这些IP可能在docker容器内无法访问(如果托管在AWS或其他地方,我指的是公共IP)。即使你有docker主机的IP地址,这并不意味着你可以从容器内连接到docker主机,因为你的docker网络可能是overlay、host、bridge、macvlan、none等,这限制了该IP地址的可达性。


当前回答

Linux解决方案(内核>=3.6)。

好的,本地主机服务器有一个默认的docker接口docker0,IP地址为172.17.0.1。容器以默认网络设置--net=“bridge”启动。

为docker0接口启用route_localnet:$sysctl-w net.ipv4.conf.docker0.route_localnet=1将这些规则添加到iptables:$iptables-t nat-I预路由-I docker0-d 172.17.0.1-p tcp--dport 3306-j DNAT--to 127.0.0.1:3306$iptables-t filter-I INPUT-I docker0-d 127.0.0.1-p tcp--dport 3306-j ACCEPT创建具有从“%”访问权限的MySQL用户,这意味着-来自任何人,不包括本地主机:CREATE USER“USER”@“%”IDENTIFIED BY“password”;在脚本中将mysql服务器地址更改为172.17.0.1。

从内核文档中:

route_localnet-BOOLEAN:在路由时,不要将环回地址视为火星源或目的地。这允许将127/8用于本地路由目的(默认为FALSE)。

其他回答

首先查看此答案,了解解决此问题所需的选项。但如果使用docker compose,则可以将network_mode:host添加到服务中,然后使用127.0.0.1连接到本地主机。这只是上述答案中描述的选项之一。下面你可以看到我是如何修改docker-compose.yml的https://github.com/geerlingguy/php-apache-container.git:

 ---
 version: "3"
 services:
   php-apache:
+    network_mode: host
     image: geerlingguy/php-apache:latest
     container_name: php-apache
...

+表示我添加的行。


[附加信息]这在2.2版中也有效。和“host”或“host”都在docker compose中工作。

 ---
 version: "2.2"

 services:
   php-apache:
+    network_mode: "host"
        or
+    network_mode: host
...

这在NGINX/PHP-FPM堆栈上对我有效,而无需接触任何代码或网络,应用程序只是希望能够连接到本地主机

将mysqld.sock从主机装载到容器内部。

在运行mysql的主机上查找mysql.sock文件的位置:netstat-ln|awk'/mysql(.*)?\。袜子/{打印$9}'

将该文件装载到docker中预期的位置:docker运行-v/hostpath/to/mysqld.sock:/containerpath/to/mysqld.sock

mysqld.sock的可能位置:

/tmp/mysqld.sock
/var/run/mysqld/mysqld.sock 
/var/lib/mysql/mysql.sock
/Applications/MAMP/tmp/mysql/mysql.sock # if running via MAMP

对于Linux,不能更改localhost服务绑定到的接口

我们需要解决两个问题

获取主机的IP使我们的本地主机服务可用于Docker

第一个问题可以使用qoomon的docker主机映像来解决,正如其他答案所给出的那样。

您需要将此容器添加到与其他容器相同的网桥网络中,以便可以访问它。打开容器中的一个终端,确保可以ping dockerhost。

bash-5.0# ping dockerhost
PING dockerhost (172.20.0.2): 56 data bytes
64 bytes from 172.20.0.2: seq=0 ttl=64 time=0.523 ms

现在,更难的问题是,让docker可以访问该服务。

我们可以使用telnet检查是否可以访问主机上的端口(您可能需要安装此端口)。

问题是,我们的容器只能访问绑定到所有接口的服务,例如SSH:

bash-5.0# telnet dockerhost 22
SSH-2.0-OpenSSH_7.6p1 Ubuntu-4ubuntu0.3

但仅绑定到localhost的服务将无法访问:

bash-5.0# telnet dockerhost 1025
telnet: can't connect to remote host (172.20.0.2): Connection refused

这里的正确解决方案是将服务绑定到dockers网桥网络。然而,这个答案假设你不可能改变这一点。因此,我们将改用iptables。

首先,我们需要找到docker在ifconfig中使用的网桥网络的名称。如果您使用的是未命名的桥,则这将只是docker0。然而,如果你使用的是一个命名网络,你将有一个以br开头的网桥,docker将使用它。我的是br-5cd80298d6f4。

一旦我们有了这个桥的名称,我们就需要允许从这个桥到本地主机的路由。出于安全原因,默认情况下禁用此功能:

sysctl -w net.ipv4.conf.<bridge_name>.route_localnet=1

现在设置iptables规则。由于我们的容器只能访问docker网桥网络上的端口,所以我们将假装我们的服务实际上绑定到这个网络上的一个端口。

为此,我们将所有请求转发到<docker_bridge>:port到localhost:port

iptables -t nat -A PREROUTING -p tcp -i <docker_bridge_name> --dport <service_port> -j DNAT --to-destination 127.0.0.1:<service_port>

例如,对于端口1025上的服务

iptables -t nat -A PREROUTING -p tcp -i br-5cd80298d6f4 --dport 1025 -j DNAT --to-destination 127.0.0.1:1025

您现在应该可以从容器访问服务:

bash-5.0# telnet dockerhost 1025
220 127.0.0.1 ESMTP Service Ready

我这样做的方式是将主机IP作为环境变量传递给容器。然后容器通过该变量访问主机。

如果你使用docker compose,也许它可以工作:

iptables -I INPUT ! -i eth0 -p tcp --dport 8001 -j ACCEPT

eth0是连接internet的网络接口,8081是主机服务器端口

iptables规则的最佳方式是iptables TRACE