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


当前回答

在host.docker.internal为每个平台工作之前,您可以使用我的容器作为NAT网关,而无需任何手动设置:

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

其他回答

编辑:

如果您使用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套接字进行连接。

CGroups和Namespaces在容器生态系统中发挥着重要作用。

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

以下是您可以遵循的解决方案方法的基本理解,

使用网络命名空间

当容器从图像中产生时,将定义并创建网络接口。这为容器提供了唯一的IP地址和接口。

$ docker run -it alpine ifconfig

通过将名称空间更改为主机,陪护者网络不会与其接口保持隔离,进程将可以访问主机网络接口。

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

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

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

此容器将在其自己的命名空间中运行。

$ docker run -it alpine ps aux

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

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

共享命名空间

在生产中这样做是一种糟糕的做法,因为您正在打破容器安全模型,这可能会导致漏洞,并容易被窃听。这只是为了调试工具和了解容器安全中的漏洞。

第一个容器是nginx服务器。这将创建一个新的网络和进程命名空间。该容器将自身绑定到新创建的网络接口的端口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,运行并调试应用程序。但是,不建议这样做。希望有帮助。

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

对于Linux,不能更改localhost服务绑定到的接口

我们需要解决两个问题

获取主机的IP使我们的本地主机服务可用于Docker

第一个问题可以使用qoomon的docker主机映像来解决,正如其他答案所给出的那样。

您需要将此容器添加到与其他容器相同的网桥网络中,以便可以访问它。打开容器中的一个终端,确保可以ping dockerhost。

bash-5.0# ping dockerhost
PING dockerhost (172.20.0.2): 56 data bytes
64 bytes from 172.20.0.2: seq=0 ttl=64 time=0.523 ms

现在,更难的问题是,让docker可以访问该服务。

我们可以使用telnet检查是否可以访问主机上的端口(您可能需要安装此端口)。

问题是,我们的容器只能访问绑定到所有接口的服务,例如SSH:

bash-5.0# telnet dockerhost 22
SSH-2.0-OpenSSH_7.6p1 Ubuntu-4ubuntu0.3

但仅绑定到localhost的服务将无法访问:

bash-5.0# telnet dockerhost 1025
telnet: can't connect to remote host (172.20.0.2): Connection refused

这里的正确解决方案是将服务绑定到dockers网桥网络。然而,这个答案假设你不可能改变这一点。因此,我们将改用iptables。

首先,我们需要找到docker在ifconfig中使用的网桥网络的名称。如果您使用的是未命名的桥,则这将只是docker0。然而,如果你使用的是一个命名网络,你将有一个以br开头的网桥,docker将使用它。我的是br-5cd80298d6f4。

一旦我们有了这个桥的名称,我们就需要允许从这个桥到本地主机的路由。出于安全原因,默认情况下禁用此功能:

sysctl -w net.ipv4.conf.<bridge_name>.route_localnet=1

现在设置iptables规则。由于我们的容器只能访问docker网桥网络上的端口,所以我们将假装我们的服务实际上绑定到这个网络上的一个端口。

为此,我们将所有请求转发到<docker_bridge>:port到localhost:port

iptables -t nat -A PREROUTING -p tcp -i <docker_bridge_name> --dport <service_port> -j DNAT --to-destination 127.0.0.1:<service_port>

例如,对于端口1025上的服务

iptables -t nat -A PREROUTING -p tcp -i br-5cd80298d6f4 --dport 1025 -j DNAT --to-destination 127.0.0.1:1025

您现在应该可以从容器访问服务:

bash-5.0# telnet dockerhost 1025
220 127.0.0.1 ESMTP Service Ready

不幸的是,目前Windows(至少是docker桌面)不支持--net=host

引用自:https://docs.docker.com/network/network-tutorial-host/#prerequisites

主机网络驱动程序仅在Linux主机上运行,在Docker for Mac、Docker for Windows或Docker EE for Windows Server上不受支持。

您可以尝试使用https://docs.docker.com/toolbox/