协慌网

登录 贡献 社区

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

1
88250
贡献值 105
贡献次数 1

我在 docker 容器中运行了一个 Nginx,在 localhost 上运行了一个 mysql,我想从 Nginx 中连接到 MySql。 MySql 在 localhost 上运行,没有将端口暴露给外部,即绑定在 localhost 上,没有绑定在机器的 ip 地址上。

有没有办法从这个 docker 容器中连接到宿主机的 MySql 或宿主机 localhost 上的任何其他程序?

答案

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

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


TLDR

--network="host" docker run命令中使用--network="host" ,然后--network="host"容器中的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

所以这里我的172.17.42.1主机在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

因此,泊坞主机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 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

MacOS 与早期版本的 Docker

Docker for Mac v 17.12 至 v 18.02

与上面相同,但使用docker.for.mac.host.internal

Docker for Mac v 17.06 至 v 17.11

与上面相同,但使用docker.for.mac.localhost

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

要从 docker 容器访问主机,必须将 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

我做了一个类似于以上帖子的 hack,让本地 IP 映射到容器中的别名(DNS)。主要问题是使用一个简单的脚本动态获取,该脚本在Linux 和 OSX 中都可以使用主机 IP 地址 。我做了这个在两种环境下工作的脚本(即使在配置了"$LANG" != "en_*" Linux 发行版中"$LANG" != "en_*"配置):

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脚本的更高级指南,请查看此文章并解释其工作原理。