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

所以我有一个 Nginx 在 docker 容器中运行,我有一个 mysql 在本地主机上运行,我想从我的 Nginx 内部连接到 MySql。 MySql 在 localhost 上运行,并且没有将端口暴露给外界,因此它绑定在 localhost 上,而不绑定在计算机的 IP 地址上。

有什么方法可以从此 Docker 容器中连接到此 MySql 或 localhost 上的任何其他程序吗?

此问题与 “如何从 Docker 容器内部获取 Docker 主机的 IP 地址” 不同,这是因为 Docker 主机的 IP 地址可以是网络中的公共 IP 或私有 IP,这可能是也可能是无法从 docker 容器中访问(我的意思是公共 IP,如果托管在 AWS 或其他地方)。即使您拥有 Docker 主机的 IP 地址,也并不意味着您可以从容器内部连接到 Docker 主机,因为 IP 地址可能会覆盖您的 Docker 网络,主机,网桥,macvlan,无其他网络,从而限制了 Docker 的可达性该 IP 地址。

答案

编辑:如果您正在使用Docker-for-macDocker-for-Windows 18.03+,只需使用主机host.docker.internal连接到您的 mysql 服务。

从 Docker 18.09.3 开始,这不适用于 Linux 上的 Docker。已在 2019 年 3 月 8 日提交修复程序 ,有望将其合并到代码库中。在此之前,解决方法是使用qoomon 的 answer 中所述的容器。


TLDR

--network="host" docker run命令中使用--network="host" ,然后--network="host"容器中的127.0.0.1将指向您的 docker 主机。

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


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

所以我的172.17.42.1主机在docker0网络接口上的 IP 地址为docker0

现在启动一个新容器并在其上安装一个外壳: 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

因此, 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

泊坞窗运行 --network =“host”

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

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

我的 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)中具有bind-address = 172.17.42.1bind-address = 0.0.0.0

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

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

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

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

注意 2:如果您使用bind-address = 172.17.42.1您的 MySQL 服务器将不会监听与127.0.0.1建立的连接。要连接到 MySQL 的172.17.42.1主机上运行的进程必须使用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 -h 127.0.0.1而不是mysql -h localhost ; 否则,MySQL 客户端将尝试使用 unix 套接字进行连接。

对于 macOS 和 Windows

Docker v 18.03 及更高版本(自 2018 年 3 月 21 日起)

使用您的内部 IP 地址或连接到特殊的 DNS 名称host.docker.internal ,它将解析为主机使用的内部 IP 地址。

Linux 支持未决https://github.com/docker/for-linux/issues/264

带有较早版本 Docker 的 MacOS

适用于 Mac 的 Docker v 17.12 至 v 18.02

与上述相同,但改用docker.for.mac.host.internal

适用于 Mac 的 Docker v 17.06 至 v 17.11

与上述相同,但改用docker.for.mac.localhost

适用于 Mac 17.05 及更低版本的 Docker

要从 docker 容器访问主机,必须将 IP 别名附加到网络接口。您可以绑定所需的任何 IP,只需确保您没有将其用于其他任何 IP。

sudo ifconfig lo0 alias 123.123.123.123/24

然后确保服务器正在侦听上述 IP 或0.0.0.0 。如果正在 localhost 127.0.0.1上侦听,它将不接受连接。

然后只需将您的 docker 容器指向该 IP,即可访问主机!

要进行测试,您可以在容器内运行curl -X GET 123.123.123.123:3000类的东西。

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

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

我在做类似于以上文章的技巧,将本地 IP 映射到容器中的别名(DNS)。主要的问题是要使用一个可以在Linux 和 OSX 中运行的主机 IP 地址的简单脚本来动态获取。我做了在两个环境中都可以使用的脚本(即使在配置了"$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,完整的配置将是:

启动脚本(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脚本的更高级指南,请查看此帖子,并对其工作方式进行说明。

这在 NGINX / PHP-FPM 堆栈上为我工作,而无需接触应用程序仅希望能够连接到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
/Applications/MAMP/tmp/mysql/mysql.sock # if running via MAMP

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

好的,您的本地主机服务器具有默认docker接口docker0 ,其 IP 地址为172.17.0.1 。您的容器以默认的网络设置--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. 创建具有 '%' 访问权限的 mysql 用户 - 表示从任何人(不包括本地主机)访问:
    CREATE USER 'user'@'%' IDENTIFIED BY 'password';
  4. 在脚本中将 mysql 服务器地址更改为 172.17.0.1


内核文档中

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

host.docker.internal适用于所有平台之前,无需任何手动设置即可将我的容器用作 NAT 网关:

https://github.com/qoomon/docker-host

Windows 10 解决方案

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

您可以使用主机docker.for.win.localhost DNS 名称来解析为内部 IP。 (警告某些消息来源提到了windows但应该win

总览
我需要做类似的事情,即从 Docker 容器连接到运行Azure Storage EmulatorCosmosDB Emulator localhost。

默认情况下, Azure Storage Emulator侦听127.0.0.1 ,同时您也可以更改 IP 的绑定,但我一直在寻找一种适用于默认设置的解决方案。

这也适用于从我的 Docker 容器连接到SQL ServerIIS ,它们都以默认端口设置在我的主机本地运行。

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

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

怎么样?

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

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

在容器内执行ifconfig (需要对容器进行apt-get install net-tools以使ifconfig可以调用)时,您会看到网络接口与 Docker 主机上的网络接口相同(例如,笔记本电脑) )。

需要特别注意的是,我是 Mac 用户,但是我在 Parallels 下运行 Ubuntu,因此使用 Mac 并非不利。 ;-)

这就是将 NGINX 容器连接到在localhost上运行的 MySQL 的方式。

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