我有一个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地址的可达性。


当前回答

在7年的时间里,人们提出了一个问题,要么码头工人变了,要么没有人这样做。因此,我将包括我自己的答案。

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

在主机上使用ipconfig或ifconfig,并记下所有IP地址。容器至少可以使用其中两个。我在WiFi LAN适配器上有一个固定的本地网络地址:192.168.1.101。这可能是10.0.101。结果会因路由器而异我在windows上使用WSL,它有自己的vEthernet地址:172.19.192.1使用host.docker.internal。根据操作系统的不同,大多数答案都有这种或另一种形式。这个名字表明它现在被docker全球使用。

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

其他回答

编辑:

如果您使用Docker for mac或Docker for Windows 18.03+,请使用主机host.Docker.internal(而不是连接字符串中的127.0.0.1)连接到mysql服务。

如果您使用的是Docker for Linux 20.10.0+,如果您使用--add host host.Docker.internal:host gateway选项启动Docker容器,则也可以使用host host.Docker.internal。

否则,请阅读以下内容


TLDR

在docker run命令中使用--network=“host”,那么docker容器中的127.0.0.1将指向docker主机。

注意:根据文档,此模式仅适用于Docker for Linux。


docker容器网络模式说明

Docker在运行容器时提供了不同的网络模式。根据您选择的模式,您将以不同的方式连接到docker主机上运行的MySQL数据库。

docker run--network=“bridge”(默认)

Docker默认创建一个名为docker0的网桥。docker主机和docker容器在该桥上都有一个IP地址。

在Docker主机上,键入sudo ip addr show docker0,您将得到如下输出:

[vagrant@docker:~] $ sudo ip addr show docker0
4: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 56:84:7a:fe:97:99 brd ff:ff:ff:ff:ff:ff
    inet 172.17.42.1/16 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::5484:7aff:fefe:9799/64 scope link
       valid_lft forever preferred_lft forever

因此,我的docker0主机在docker0网络接口上具有IP地址172.17.42.1。

现在启动一个新容器并在其上获取shell:docker run--rm-it ubuntu:trusty bash,并在容器中键入ip-addr show eth0,以了解其主网络接口的设置方式:

root@e77f6a1b3740:/# ip addr show eth0
863: eth0: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 66:32:13:f0:f1:e3 brd ff:ff:ff:ff:ff:ff
    inet 172.17.1.192/16 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::6432:13ff:fef0:f1e3/64 scope link
       valid_lft forever preferred_lft forever

这里我的容器的IP地址是172.17.1.192。现在查看路由表:

root@e77f6a1b3740:/# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         172.17.42.1     0.0.0.0         UG    0      0        0 eth0
172.17.0.0      *               255.255.0.0     U     0      0        0 eth0

因此,docker主机172.17.42.1的IP地址被设置为默认路由,可以从您的容器访问。

root@e77f6a1b3740:/# ping 172.17.42.1
PING 172.17.42.1 (172.17.42.1) 56(84) bytes of data.
64 bytes from 172.17.42.1: icmp_seq=1 ttl=64 time=0.070 ms
64 bytes from 172.17.42.1: icmp_seq=2 ttl=64 time=0.201 ms
64 bytes from 172.17.42.1: icmp_seq=3 ttl=64 time=0.116 ms

docker run--network=“host”

或者,您可以运行网络设置设置为host的docker容器。这样的容器将与docker主机共享网络堆栈,从容器的角度来看,localhost(或127.0.0.1)将引用docker主机。

请注意,在docker容器中打开的任何端口都将在docker主机上打开。这不需要-p或-p docker运行选项。

docker主机上的IP配置:

[vagrant@docker:~] $ ip addr show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:98:dc:aa brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::a00:27ff:fe98:dcaa/64 scope link
       valid_lft forever preferred_lft forever

以及从主机模式下的docker容器:

[vagrant@docker:~] $ docker run --rm -it --network=host ubuntu:trusty ip addr show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:98:dc:aa brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::a00:27ff:fe98:dcaa/64 scope link
       valid_lft forever preferred_lft forever

如您所见,docker主机和docker容器共享完全相同的网络接口,因此具有相同的IP地址。


从容器连接到MySQL

桥接模式

要以网桥模式从容器访问docker主机上运行的MySQL,需要确保MySQL服务正在侦听172.17.42.1 IP地址上的连接。

为此,请确保MySQL配置文件(my.cnf)中的绑定地址=172.17.42.1或绑定地址=0.0.0.0。

如果需要使用网关的IP地址设置环境变量,可以在容器中运行以下代码:

export DOCKER_HOST_IP=$(route -n | awk '/UG[ \t]/{print $2}')

然后在应用程序中,使用DOCKER_HOST_IP环境变量打开到MySQL的连接。

注意:如果您使用bindaddress=0.0.0.0,MySQL服务器将侦听所有网络接口上的连接。这意味着您的MySQL服务器可以从Internet访问;确保相应地设置防火墙规则。

注意2:如果您使用绑定地址=172.17.42.1,MySQL服务器将不会侦听到127.0.0.1的连接。要连接到MySQL的docker主机上运行的进程必须使用172.17.42.1 IP地址。

主机模式

要以主机模式从容器访问docker主机上运行的MySQL,您可以在MySQL配置中保持bind address=127.0.0.1,并从容器连接到127.0.0.1:

[vagrant@docker:~] $ docker run --rm -it --network=host mysql mysql -h 127.0.0.1 -uroot -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 36
Server version: 5.5.41-0ubuntu0.14.04.1 (Ubuntu)

Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

注意:请使用mysql-h127.0.0.1而不是mysql-hlocalhost;否则MySQL客户端将尝试使用unix套接字进行连接。

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

这不是对实际问题的回答。这就是我解决类似问题的方法。解决方案完全来自:定义Docker容器网络,以便容器可以通信。感谢Nic Raboy

这里留给其他可能希望在一个容器和另一个容器之间进行REST调用的人。回答问题:在docker环境中使用什么来代替localhost?

了解您的网络看起来像docker网络

创建一个新的网络docker网络创建-d我的网络

启动第一个容器docker运行-d-p 5000:5000--network=“my net”--name“first_container”<MyImage1:v0.1>

查看第一个集装箱码头工人检查first_container的网络设置。“网络”:应该有“我的网”

启动第二个容器docker运行-d-p 6000:6000--network=“my net”--name“second_container”<MyImage2:v0.1>

检查第二个集装箱码头的网络设置,检查second_container。“网络”:应该有“我的网”

ssh到第二个容器docker exec-it secondcontainer sh或docker exec-it secondtainer bash中。

在第二个容器内部,可以通过ping first_container ping第一个容器。此外,您的代码调用如http://localhost:5000可以替换为http://first_container:5000

对于Windows上的应用程序,假设您使用的是网桥网络驱动程序,则需要将MySQL专门绑定到hyper-v网络接口的IP地址。

这是通过通常隐藏的C:\ProgramData\MySQL文件夹下的配置文件完成的。

绑定到0.0.0.0将不起作用。所需的地址也显示在docker配置中,在我的例子中是10.0.75.1。

编辑:我最终在GitHub上制作了这个概念的原型。退房:https://github.com/sivabudh/system-in-a-box


首先,我的答案面向两组人:使用Mac的人和使用Linux的人。

主机网络模式在Mac上不起作用。您必须使用IP别名,请参阅:https://stackoverflow.com/a/43541681/2713729

什么是主机网络模式?参见:https://docs.docker.com/engine/reference/run/#/network-设置

其次,对于那些正在使用Linux的人(我的直接经验是Ubuntu 14.04 LTS,我很快就会升级到16.04 LTS),是的,您可以让Docker容器中运行的服务连接到Docker主机上运行的localhost服务(例如,您的笔记本电脑)。

How?

关键是当你运行Docker容器时,你必须以主机模式运行它。命令如下所示:

docker run--network=“host”-id<docker image id>

当你在容器中执行ifconfig(你需要apt-get-install-net-tools你的容器才能调用ifconfig)时,你会看到网络接口与Docker主机上的相同(例如你的笔记本电脑)。

需要注意的是,我是Mac用户,但我在Parallels下运行Ubuntu,所以使用Mac不是缺点

这就是将NGINX容器连接到本地主机上运行的MySQL的方法。