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


当前回答

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

其他回答

这不是对实际问题的回答。这就是我解决类似问题的方法。解决方案完全来自:定义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

这在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

您可以使用alpine映像获取主机ip

docker run --rm alpine ip route | awk 'NR==1 {print $3}'

这将更加一致,因为您总是使用alpine来运行命令。

类似于Mariano的回答,您可以使用相同的命令设置环境变量

DOCKER_HOST=$(docker run --rm alpine ip route | awk 'NR==1 {print $3}') docker-compose up

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

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

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

回答ivant的评论

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

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

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

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

首先将依赖项移动到容器中使您的其他服务可从外部访问,并通过该外部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。