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


当前回答

我们想到了几种解决方案:

首先将依赖项移动到容器中使您的其他服务可从外部访问,并通过该外部IP连接到它们在没有网络隔离的情况下运行容器避免通过网络连接,请使用作为卷安装的套接字

这无法开箱即用的原因是,默认情况下,容器使用自己的网络名称空间运行。这意味着localhost(或指向环回接口的127.0.0.1)对于每个容器都是唯一的。连接到这个将连接到容器本身,而不是运行在docker外部或不同docker容器内部的服务。

选项1:如果您的依赖项可以移动到容器中,我会先这样做。当其他人尝试在自己的环境中运行容器时,它使您的应用程序堆栈可移植。您仍然可以在主机上发布端口,其他尚未迁移的服务仍然可以访问该端口。您甚至可以将该端口发布到docker主机上的localhost接口,以避免通过语法(例如:-p 127.0.0.1:3306:3306)从外部访问该端口,以获取已发布的端口。

选项2:有多种方法可以从容器内部检测主机IP地址,但每个方法都有有限的工作场景(例如需要Docker for Mac)。最可移植的选项是将您的主机IP注入到具有类似环境变量或配置文件的容器中,例如:

docker run --rm -e "HOST_IP=$(ip route get 1 | sed -n 's/^.*src \([0-9.]*\) .*$/\1/p')" ...

这确实需要您的服务在该外部接口上侦听,这可能是一个安全问题。有关从容器内部获取主机IP地址的其他方法,请参阅本文。

使用host.docker.internal的便携性稍差。这适用于当前版本的docker for Windows和docker for Mac。在20.10中,当您传递一个特殊的主机条目时,该功能已添加到Docker for Linux:

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

主机网关是Docker 20.10中添加的一个特殊值,它会自动扩展到主机IP。有关详细信息,请参阅本请购单。

选项3:在没有网络隔离的情况下运行,即使用--net host运行,意味着您的应用程序正在主机网络命名空间上运行。这减少了容器的隔离性,这意味着您无法通过具有DNS的共享docker网络访问其他容器(相反,您需要使用已发布的端口来访问其他容器化应用程序)。但是,对于需要访问主机上仅在127.0.0.1上侦听的其他服务的应用程序,这可能是最简单的选择。

选项4:各种服务也允许通过基于文件系统的套接字进行访问。此套接字可以作为绑定装载卷装载到容器中,允许您在不通过网络的情况下访问主机服务。为了访问docker引擎,您经常会看到将/var/run/docker.sock装载到容器中的示例(为该容器提供对主机的根访问权限)。对于mysql,您可以尝试类似于-v/var/run/mysqld/mysqld.sock:/varrun/mysqld/mysql.sock的方式,然后连接到localhost,mysql使用套接字将其转换为localhost。

其他回答

您可以在主机上简单地进行ifconfig以检查您的主机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套接字进行连接。

Mac OSX最简单的解决方案

只需使用Mac的IP地址。在Mac上,运行以下命令以获取IP地址并在容器中使用:

ifconfig | grep 'inet 192'| awk '{ print $2}'

只要在Mac上本地运行的服务器或在另一个docker容器中运行的服务器正在侦听0.0.0.0,docker容器就可以到达该地址。

如果您只想访问另一个侦听0.0.0.0的docker容器,可以使用172.17.0.1

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

我不同意托马斯列维尔的回答。

将mysql绑定到172.17.42.1将阻止其他程序使用主机上的数据库来访问它。只有在所有数据库用户都已固定的情况下,这才有效。

将mysql绑定到0.0.0.0将向外部世界打开数据库,这不仅是一件非常糟糕的事情,而且与最初的问题作者想要做的相反

回答ivant的评论

“为什么不将mysql也绑定到docker0?”

这是不可能的。mysql/mariadb文档明确表示不可能绑定到多个接口。只能绑定到0、1或所有接口。

因此,我还没有找到从docker容器访问主机上(仅限于本地主机)数据库的任何方法。这显然是一种非常常见的模式,但我不知道怎么做。