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

问题描述 投票:932回答:19

所以我在Docker容器中运行了一个Nginx,我在localhost上运行了一个mysql,我想从我的Nginx中连接到MySql。 MySql在localhost上运行,而不是将端口暴露给外部世界,所以它绑定在localhost上,而不是绑定在机器的ip地址上。

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

nginx docker reverse-proxy docker-networking
19个回答
1427
投票

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

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


TLDR

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

注意:此模式仅适用于Docker for Linux,per the documentation


Note on docker container networking modes

Docker在运行容器时提供different networking modes。根据您选择的模式,您将以不同方式连接到在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

所以这里我的docker主机在172.17.42.1网络接口上有IP地址docker0

现在启动一个新容器并在其上获取一个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”

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

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

我的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地址。


Connecting to MySQL from containers

桥接模式

要在桥接模式下从容器访问在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的连接。在docker主机上运行的想要连接MySQL的进程必须使用172.17.42.1 IP地址。

主机模式

要从主机模式下的容器访问在docker主机上运行的MySQL,可以将bind-address = 127.0.0.1保留在MySQL配置中,您需要做的就是从容器连接到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套接字进行连接。


6
投票

最简单的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


4
投票

我不同意Thomasleveil的回答。

使mysql绑定到172.17.42.1将阻止使用主机上的数据库的其他程序到达它。这仅在所有数据库用户都已停靠时才有效。

使mysql绑定到0.0.0.0会将db打开到外部世界,这不仅是一件非常糟糕的事情,而且与原作者想要做的事情相反。他明确地说“MySql在localhost上运行,而不是将端口暴露给外部世界,所以它绑定在localhost上”

回答ivant的评论

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

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

作为结论,我还没有找到任何方法从docker容器到达主机上的(仅限localhost)数据库。这绝对是一个非常常见的模式,但我不知道该怎么做。


4
投票

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

  • 通过/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;
  • 创建sh脚本:run_docker.sh
    #!bin/bash

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


      docker run -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

  
  • 与docker-composer一起运行 版本:'2.1' 服务: tomcatwar:extra_hosts: - “local:10.1.2.232”image:sopheamak / springboot_docker_mysql 端口: - 8080:8080环境: - DATABASE_HOST = local - DATABASE_USER = root - DATABASE_PASSWORD = root - DATABASE_NAME = demo - DATABASE_PORT = 3306


3
投票

您可以使用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

3
投票

在Windows 10 Home上使用Docker Toolbox时,没有任何答案适用于我,但10.0.2.2确实如此,因为它使用VirtualBox将主机暴露给此地址上的VM。


3
投票

非常简单快捷,用ifconfig(linux)或ipconfig(windows)检查主机IP,然后创建一个

泊坞窗,compose.yml

version: '3' # specify docker-compose version

services:
  nginx:
    build: ./ # specify the directory of the Dockerfile
    ports:
      - "8080:80" # specify port mapping
    extra_hosts:
      - "dockerhost:<yourIP>"

这样,您的容器就可以访问您的主机。访问数据库时,请记住使用之前指定的名称,在本例中为“dockerhost”,以及运行数据库的主机端口


2
投票

CGroups和命名空间在集装箱生态系统中发挥着重要作用。

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

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

使用网络命名空间

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

$ docker run -it alpine ifconfig

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

$ 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,运行和调试您的应用程序。但是,不建议这样做。希望能帮助到你。


2
投票

我想到了几种解决方案:

  1. 首先将依赖项移动到容器中
  2. 使您的其他服务可从外部访问,并使用该外部IP连接到它们
  3. 无需网络隔离即可运行容器
  4. 避免通过网络连接,请使用作为卷安装的套接字

开箱即用的原因是默认情况下容器使用自己的网络命名空间运行。这意味着每个容器的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 r s 0/0 | awk '{print $3}')" ...

这确实要求您的服务正在侦听该外部接口,这可能是一个安全问题。对于从容器内部获取主机IP地址的其他方法,see this post

选项3:在没有网络隔离的情况下运行,即使用--net host运行,意味着您的应用程序正在主机网络命名空间上运行。这对容器的隔离较小,这意味着您无法通过DNS访问共享的docker网络上的其他容器(相反,您需要使用已发布的端口来访问其他容器化应用程序)。但对于需要访问主机上仅在主机上监听127.0.0.1的其他服务的应用程序,这可能是最简单的选择。

选项4:各种服务还允许通过基于文件系统的套接字进行访问。此套接字可以作为绑定安装卷安装到容器中,从而允许您无需通过网络即可访问主机服务。要访问docker引擎,您经常会看到将/var/run/docker.sock安装到容器中的示例(使该容器具有对主机的root访问权限)。使用mysql,你可以尝试像-v /var/run/mysqld/mysqld.sock:/var/run/mysqld/mysql.sock这样的东西,然后连接到localhost,mysql转换为使用套接字。


1
投票

对于Windows机器: -

在构建期间随机运行以下命令到Expose docker port

$docker run -d --name MyWebServer -P mediawiki

enter image description here

enter image description here

在上面的容器列表中,您可以看到分配为32768的端口。尝试访问

localhost:32768 

您可以看到mediawiki页面


0
投票

直到fix没有合并到master分支,才能从容器内部运行主机IP:

ip -4 route list match 0/0 | cut -d' ' -f3

(正如@Mahoney here所建议的那样)。


273
投票

对于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 with earlier versions of 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


-20
投票

您可以使用ngrok创建到localhost计算机的安全隧道,然后将该隧道暴露给docker容器。

ngrok可于2017年5月22日免费使用。

脚步:

1)去ngrok

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

3)SIGN UP为一个帐户,他们将提供身份验证令牌。注册是必需的,因为ngrok仅在注册后为您提供tcp端口隧道。注册无需任何费用或信用卡。

4)在你的终端做ngrok tcp 33063306是mysql在我本地运行的端口,你也可以在任何其他端口上运行它。

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

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

您将能够从docker容器内登录您本地计算机的root帐户!

我发表了关于此解决方案的博文,请参阅here


69
投票

我做了一个类似于以上帖子的hack,让本地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

码头工人,compose.yml:

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

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

有关如何自定义DOCKERHOST脚本的更高级指南,请查看this post并解释其工作原理。


36
投票

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

13
投票

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

好的,您的localhost服务器具有默认的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用户 - 来自任何人,不包括localhost: CREATE USER 'user'@'%' IDENTIFIED BY 'password';
  4. 将脚本中的mysql-server地址更改为172.17.0.1

来自kernel documentation

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


13
投票

对于Vindovs的解释10

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

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

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

Azure Storage Emulator默认侦听127.0.0.1,而你也可以更改它的绑定,我正在寻找一个可以使用默认设置的解决方案。

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


10
投票

对于那些在Windows上,假设您正在使用桥接网络驱动程序,您将需要专门将MySQL绑定到hyper-v网络接口的IP地址。

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

绑定到0.0.0.0将不起作用。所需的地址也显示在docker配置中,在我的例子中是10.0.75.1。


10
投票

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

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


8
投票

编辑:我最终在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容器内运行连接到运行的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。

© www.soinside.com 2019 - 2024. All rights reserved.