从Docker容器内部,如何连接到本机的本地主机?

所以我有一个在Docker容器中运行的Nginx,我有一个在localhost上运行的mysql,我想从我的Nginx中连接到MySql。 MySql是在本地主机上运行的,而不是向外界暴露一个端口,所以它绑定在本地主机上,不绑定在机器的IP地址上。

有没有办法从这个docker集装箱连接到这个MySql或本地主机上的任何其他程序?

docker集装箱联网模式的注意事项

Docker在运行容器时提供了不同的联网模式 。 根据您select的模式,您将不同地连接到在Docker主机上运行的MySQL数据库。

docker运行–net =“bridge”(默认)

Docker默认创build一个名为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 

所以在这里,我的docker主机在docker0networking接口上的IP地址为docker0

现在启动一个新的容器并获得一个shell: docker run --rm -it ubuntu:trusty bash -il在容器typesip addr show eth0来发现它的主networking接口是如何设置的:

 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 

因此, 172.17.42.1主机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运行–net =“主机”

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

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

我的docker主机上的IPconfiguration:

 [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 --net=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集装箱共享完全相同的networking接口,因此具有相同的IP地址。


从容器连接到MySQL

桥接模式

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

为此,请确保您的MySQLconfiguration文件(my.cnf)中的bind-address = 172.17.42.1bind-address = 0.0.0.0

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

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

那么在你的应用程序中,使用DOCKER_HOST_IP环境variables打开到MySQL的连接。

注意:如果你使用bind-address = 0.0.0.0 MySQL服务器将侦听所有networking接口上的连接。 这意味着你的MySQL服务器可以通过Internet访问; 确保相应地设置防火墙规则。

注2:如果你使用bind-address = 172.17.42.1你的MySQL服务器将不会侦听127.0.0.1连接。 在172.17.42.1主机上运行的想要连接到MySQL的172.17.42.1必须使用172.17.42.1 IP地址。

主机模式

要以主机模式从容器中访问在docker主机上运行的MySQL,可以将bind-address = 127.0.0.1在MySQLconfiguration中,您只需要从容器连接到127.0.0.1

 [vagrant@docker:~] $ docker run --rm -it --net=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 -h 127.0.0.1而不是mysql -h localhost ; 否则MySQL客户端会尝试使用unix套接字进行连接。

macOS的解决scheme

Docker for Mac v 17.06及以上版本(2017年6月)

使用您的Mac的内部IP地址或连接到特殊的仅限Mac的DNS名称docker.for.mac.localhost ,这将parsing为主机使用的内部IP地址。 (感谢用户@Kyr)

Docker for Mac 17.05及以下版本

要从Docker容器访问主机,您必须将IP别名附加到您的networking接口。 你可以绑定任何你想要的IP,只要确保你没有使用它就可以了。

sudo ifconfig lo0 alias 123.123.123.123/24

然后确保你的服务器正在监听上面提到的IP或0.0.0.0 。 如果它正在本地主机127.0.0.1上侦听,它将不会接受连接。

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

为了testing你可以在容器里运行curl -X GET 123.123.123.123:3000这样的东西。

别名将在每次重新启动时重置,因此必要时创build启动脚本。

解决scheme和更多文档在这里: https : //docs.docker.com/docker-for-mac/networking/#use-cases-and-workarounds

我做了一个类似于上面的post获取本地IP映射到容器中的别名(DNS)的黑客入侵。 主要的问题是通过一个简单的脚本dynamic获得,这个脚本可以在Linux和OSX中同时运行主机IP地址 。 我做了这个脚本,可以在两种环境下工作(甚至在configuration"$LANG" != "en_*" Linux发行版中):

 ifconfig | grep -E "([0-9]{1,3}\.){3}[0-9]{1,3}" | grep -v 127.0.0.1 | awk '{ print $2 }' | cut -f2 -d: | head -n1 

所以,使用Docker Compose,完整的configuration将是:

启动脚本(docker-run.sh)

 export DOCKERHOST=$(ifconfig | grep -E "([0-9]{1,3}\.){3}[0-9]{1,3}" | grep -v 127.0.0.1 | awk '{ print $2 }' | cut -f2 -d: | head -n1) docker-compose -f docker-compose.yml up 

docker-compose.yml

 myapp: build: . ports: - "80:80" extra_hosts: - "dockerhost:$DOCKERHOST" 

然后在代码http://dockerhost http://localhost更改为http://dockerhost

有关如何自定义DOCKERHOST脚本的更多高级指南,请DOCKERHOST 此文章 ,并解释其工作原理。

这对我在NGINX / PHP-FPM堆栈上工作,而不触及任何代码或networking的应用程序只是希望能够连接到localhost

mysqld.sock从主机装载到容器中。

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

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

mysqld.sock的可能位置:
/tmp/mysqld.sock /var/run/mysqld/mysqld.sock /var/lib/mysql/mysql.sock

对于那些在Windows上,假设你使用桥接networking驱动程序,你需要特别绑定MySQL的Hyper-Vnetworking接口的IP地址。

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

绑定到0.0.0.0将不起作用。 所需的地址也显示在dockerconfiguration中,在我的情况下是10.0.75.1。

编辑:我结束了原型在GitHub上的概念。 签出: https //github.com/sivabudh/system-in-a-box


首先,我的回答是面向2个人群:使用Mac的人和使用Linux的人。

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

什么是主机networking模式? 请参阅: https : //docs.docker.com/engine/reference/run/#/network-settings

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

怎么样?

关键是在运行Docker容器时,必须使用主机模式运行它。 该命令如下所示:

docker run --network="host" -id <Docker image ID>

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

需要注意的是,我是Mac用户,但是我在Parallels下运行Ubuntu,所以使用Mac并不是什么坏处。 😉

这就是你如何将NGINX容器连接到localhost上运行的MySQL。

我不同意Thomasleveil的回答。

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

使mysql绑定到0.0.0.0会打开外部世界的数据库,这不仅是一个非常糟糕的事情,而且也违背了原来的问题作者想做的事情。 他明确地说“MySql在本地主机上运行,​​而不是将端口暴露给外部世界,所以它绑定在本地主机上”

回答ivant的评论

“为什么不把mysql绑定到docker0呢?”

这不可能。 mysql / mariadb文档明确指出不可能绑定到几个接口。 您只能绑定到0,1或全部接口。

作为一个结论,我还没有find任何方式从docker集装箱到达主机上的(仅本地主机)数据库。 这绝对是一个非常普遍的模式,但我不知道该怎么做。

这是我的解决scheme:它适用于我的情况

  • 通过/etc/mysql/mysql.conf.d中的注释#bind-address = 127.0.0.1将本地mysql服务器设置为公共访问

  • 重启mysql服务器sudo /etc/init.d/mysql restart

  • 运行以下命令打开用户root访问任何主机mysql -uroot -proot GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT OPTION; FLUSH PRIVILEGES; mysql -uroot -proot GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT OPTION; FLUSH PRIVILEGES;

  • 创buildsh脚本:run_docker.sh

     #!斌/庆典

     HOSTIP =`ip -4 addr show scope global dev eth0 |  grep inet |  awk'{print \ $ 2}'| 剪下-d / -f 1`


      docker运行-it -d --name web-app \
                   --add-host = local:$ {HOSTIP} \
                   -p 8080:8080 \
                   -e DATABASE_HOST = $ {HOSTIP} \
                   -e DATABASE_PORT = 3306 \
                   -e DATABASE_NAME = demo \
                   -e DATABASE_USER = root \
                   -e DATABASE_PASSWORD = root \
                   sopheamak / springboot_docker_mysql

  
  • 与dockercomposer php一起运行

    版本:'2.1' 

    服务:
    tomcatwar:
    extra_hosts:
    – “本地:10.1.2.232”
    image:sopheamak / springboot_docker_mysql
    端口:
    – 8080:8080
    环境:
    – DATABASE_HOST =本地
    – DATABASE_USER = root
    – DATABASE_PASSWORD = root
    – DATABASE_NAME =演示
    – DATABASE_PORT = 3306

最简单的Mac OSX解决scheme

只需使用您的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

CG组和名称空间在集装箱生态系统中扮演着重要的angular色。

命名空间提供了一个隔离层。 每个容器都在一个单独的名称空间中运行,并且其访问仅限于该名称空间。 Cgroups控制每个容器的资源利用率,而名称空间控制进程可以看到和访问相应资源的内容。

以下是您可以遵循的解决scheme的基本理解,

使用networking命名空间

当一个容器产生图像时,一个networking接口被定义和创build。 这给了容器唯一的IP地址和接口。

 $ docker run -it alpine ifconfig 

通过将命名空间更改为主机,cotainersnetworking不会与其接口保持隔离,该进程将有权访问主机networking接口。

 $ docker run -it --net=host alpine ifconfig 

如果进程在端口上侦听,则会在主机接口上侦听并映射到容器。

使用PID命名空间通过更改Pid命名空间,容器可以与超出其正常范围的其他进程交互。

这个容器将运行在它自己的名字空间中。

 $ docker run -it alpine ps aux 

通过将名称空间更改为主机,容器也可以看到系统上运行的所有其他进程。

 $ docker run -it --pid=host alpine ps aux 

共享命名空间

在生产环境中这样做是不好的做法,因为您正在摆脱容易被安全漏洞攻击的容器安全模式,并且容易被窃听。 这只是用于debugging工具和低估集装箱安全的漏洞。

第一个容器是nginx服务器。 这将创build一个新的networking和进程命名空间。 该容器将自己绑定到新创build的networking接口的端口80。

 $ docker run -d --name http nginx:alpine 

另一个容器现在可以重用这个名字空间,

 $ docker run --net=container:http mohan08p/curl curl -s localhost 

而且,这个容器可以看到共享容器中的进程的接口。

 $ docker run --pid=container:http alpine ps aux 

这将允许您在不更改或重新启动应用程序的情况下为容器提供更多权限。 以类似的方式,你可以连接到主机上的MySQL,运行和debugging你的应用程序。 但是,不build议这样走。 希望它有帮助。

Linux解决scheme(内核> = 3.6)。

好的,你的localhost服务器有默认的docker接口docker0 ,IP地址为172.17.0.1 。 您的容器以默认的networking设置启动–net =“bridge”

  1. 为docker0接口启用route_localnet:
    $ sysctl -w net.ipv4.conf.docker0.route_localnet=1
  2. 将这个规则添加到iptables中:
    $ iptables -t nat -I PREROUTING -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
  3. 从'%'创buildmysql用户,这意味着从任何人,不包括本地主机:
    CREATE USER 'user'@'%' IDENTIFIED BY 'password';
  4. 在脚本中将mysql-server地址更改为172.17.0.1

从内核文档 :

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

针对Windows 10的解决scheme

Docker Community Edition 17.06.0-ce-win18 2017-06-28(stable)

您可以使用主机docker.for.windows.localhost DNS名称parsing为内部IP。

概观
我需要做类似的事情,即从我的Docker容器连接到运行Azure Storage EmulatorCosmosDB Emulator本地主机。

默认情况下, Azure Storage Emulator监听127.0.0.1 ,而您也可以更改IP的绑定,我正在寻找一个可以使用默认设置的解决scheme。

这也适用于从我的Docker容器连接到SQL ServerIIS ,都在我的主机本地运行。

您可以使用ngrok创build一个到本地主机的安全隧道,然后将该隧道暴露给您的docker容器。

ngrok自由使用截止到2017年5月22日。

脚步:

1)去ngrok

2) 下载 ngrok客户端并按照安装说明进行操作

3)注册一个帐户,他们将提供一个身份validation令牌。 注册是必需的,因为注册后ngrok只给你tcp端口通道。 没有成本或信用卡需要注册。

4)在你的terminal上做ngrok tcp 33063306是mysql在本地运行的端口,你也可以用其他端口来运行。

5)您将收到来自步骤4的地址,例如: tcp://0.tcp.ngrok.io:10117 。 这是到本地机器的隧道连接。 0.tcp.ngrok.io映射到您的localhost ,端口10117映射到您的本地端口3306 。 现在你可以从任何地方使用这个地址访问你的本地主机端口3306,包括在这台机器上运行的任何docker集装箱。 在你的docker容器(无论它在哪里),假设你已经安装了MySQL客户端,请执行以下操作:

mysql --host 0.tcp.ngrok.io --port 10117 -u root

您将能够从docker集装箱内login到本地机器的root帐户!

我已经博客了解这个解决scheme在这里看到更多的细节