如何从Docker容器内部连接到机器的localhost?

所以我有一个在docker容器内运行的Nginx,我有一个在主机系统上运行的mysql,我想从我的容器内连接到MySql。MySql仅绑定到localhost设备。

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

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

1888066 次浏览

编辑:

如果您使用的是Docker-for-mac开发版适用于Windows的Docker 18.03+,只需使用主机host.docker.internal(而不是连接字符串中的127.0.0.1)连接到您的mysql服务。

如果您使用的是Docker-for-Linux20.10.0+,您还可以使用主机host.docker.internal如果,您使用--add-host host.docker.internal:host-gateway选项启动了Docker容器。

否则,阅读下面


TLDR

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

注意:此模式仅适用于DockerLinux,根据留档


关于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 docker04: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group defaultlink/ether 56:84:7a:fe:97:99 brd ff:ff:ff:ff:ff:ffinet 172.17.42.1/16 scope global docker0valid_lft forever preferred_lft foreverinet6 fe80::5484:7aff:fefe:9799/64 scope linkvalid_lft forever preferred_lft forever

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

现在启动一个新容器并在其上获取一个shell:docker run --rm -it ubuntu:trusty bash并在容器类型ip addr show eth0中发现其主网络接口是如何设置的:

root@e77f6a1b3740:/# ip addr show eth0863: eth0: <BROADCAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether 66:32:13:f0:f1:e3 brd ff:ff:ff:ff:ff:ffinet 172.17.1.192/16 scope global eth0valid_lft forever preferred_lft foreverinet6 fe80::6432:13ff:fef0:f1e3/64 scope linkvalid_lft forever preferred_lft forever

这里我的容器的IP地址为172.17.1.192。现在查看路由表:

root@e77f6a1b3740:/# routeKernel IP routing tableDestination     Gateway         Genmask         Flags Metric Ref    Use Ifacedefault         172.17.42.1     0.0.0.0         UG    0      0        0 eth0172.17.0.0      *               255.255.0.0     U     0      0        0 eth0

因此,docker主机172.17.42.1的IP地址被设置为默认路由,可以从您的容器访问。

root@e77f6a1b3740:/# ping 172.17.42.1PING 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 ms64 bytes from 172.17.42.1: icmp_seq=2 ttl=64 time=0.201 ms64 bytes from 172.17.42.1: icmp_seq=3 ttl=64 time=0.116 ms

docker run--网络="主机"

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

请注意,在docker容器中打开的任何端口都将在docker主机上打开。这不需要#0或#1#2选项

我的docker主机上的IP配置:

[vagrant@docker:~] $ ip addr show eth02: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether 08:00:27:98:dc:aa brd ff:ff:ff:ff:ff:ffinet 10.0.2.15/24 brd 10.0.2.255 scope global eth0valid_lft forever preferred_lft foreverinet6 fe80::a00:27ff:fe98:dcaa/64 scope linkvalid_lft forever preferred_lft forever

主机模式的docker容器:

[vagrant@docker:~] $ docker run --rm -it --network=host ubuntu:trusty ip addr show eth02: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether 08:00:27:98:dc:aa brd ff:ff:ff:ff:ff:ffinet 10.0.2.15/24 brd 10.0.2.255 scope global eth0valid_lft forever preferred_lft foreverinet6 fe80::a00:27ff:fe98:dcaa/64 scope linkvalid_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服务器将侦听所有网络接口上的连接。这意味着您的MySQL服务器可以从Internet访问;确保相应地设置防火墙规则。

注2:如果您使用bind-address = 172.17.42.1,您的MySQL服务器将不会侦听与127.0.0.1的连接。在docker主机上运行的想要连接到MySQL的进程必须使用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 -pEnter password:Welcome to the MySQL monitor.  Commands end with ; or \g.Your MySQL connection id is 36Server 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套接字进行连接。

我不同意托马斯维尔的回答。

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

将mysql绑定到0.0.0.0会将数据库开放给外界,这不仅是一件非常糟糕的事情,而且与原问题作者想要做的相反。他明确地说“MySql运行在localhost上,没有向外界公开端口,所以它的绑定在localhost”

回复Ivant的评论

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

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

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

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

我做了一个类似于上面帖子的黑客攻击,让本地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://localhost更改为http://dockerhost

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

对于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 run --network="host" -id <Docker image ID>

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

需要注意的是,我是Mac用户,但我在Parallels下运行Ubuntu,所以使用Mac并不是缺点。;-)

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

对于所有平台

Docker v 20.10及以上(自2020年12月14日起)

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

在Linux,使用Docker命令,将--add-host=host.docker.internal:host-gateway添加到Docker命令以启用此功能。

要在Linux上的Docker Compose中启用此功能,请将以下行添加到容器定义中:

extra_hosts:- "host.docker.internal:host-gateway"

对于旧的macOS和Windows版本的Docker

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代替。

Docker for Mac 17.05及以下

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

sudo ifconfig lo0 alias 123.123.123.123/24

然后确保您的服务器正在监听上面提到的IP或0.0.0.0。如果它正在监听localhost127.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

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

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

  • 通过注释将本地mysql服务器设置为公共访问#绑定地址127.0.0.1/etc/mysql/mysql.conf.d

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

  • 运行以下命令打开用户root访问任何主机mysql-uroot源代码将*.*上的所有权限授予由'root'标识的'root'@'%'授予选项;刷新权限;

  • 创建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作曲家运行

    版本:'2.1'

    服务:
    tomcatwar:extra_hosts:-"本地:10.1.2.232"图片来源:sopheamak/springboot_docker_mysql
    端口:-8080:8080环境:DATABASE_HOST=本地DATABASE_USER=rootDATABASE_PASSWORD=root-DATABASE_NAME=演示-DATABASE_PORT=3306

C组和命名空间在容器生态系统中发挥着重要作用。

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

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

使用网络命名空间

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

$ docker run -it alpine ifconfig

通过将命名空间更改为host,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 server。这将创建一个新的网络和进程命名空间。此容器将自身绑定到新创建的网络接口的端口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,运行和调试您的应用程序。但是,不建议这样做。希望它有帮助。

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

内核留档

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

Windows 10解决方案

Docker社区版17.06.0-ce-win18 2017-06-28(稳定)

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

概览
我需要做一些类似的事情,那就是从我的Docker容器连接到我的localhost,它正在运行Azure Storage EmulatorCosmosDB Emulator

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

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

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

对于Windows机器:-

运行以下命令以在构建时随机公开docker端口

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

在此处输入图片描述

在此处输入图片描述

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

localhost:32768

您可以查看Mediawiki页面

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

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

在Windows 10 Home上使用Docker Toolbox时,没有一个答案对我有用,但10.0.2.2做到了,因为它使用VirtualBox将主机暴露给此地址上的VM。

非常简单快捷,使用ifconfig(linux)或ipconfig(windows)检查您的主机IP,然后创建docker-compose.yml

version: '3' # specify docker-compose versionservices:nginx:build: ./ # specify the directory of the Dockerfileports:- "8080:80" # specify port mappingextra_hosts:- "dockerhost:<yourIP>"

这样,您的容器将能够访问您的主机。访问数据库时,请记住使用您之前指定的名称,在本例中为dockerhost和运行数据库的主机的port

直到修复没有合并到master分支,获取主机IP只需从容器内部运行:

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

(如@陈志立这里所示)。

我想到了几个解决方案:

  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 route get 1 | sed -n 's/^.*src \([0-9.]*\) .*$/\1/p')" ...

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

稍微不那么便携的是使用host.docker.internal。这适用于当前版本的Docker for Windows和Docker for Mac。在20.10中,当您通过以下特殊主机条目时,该功能已添加到DockerLinux:

docker run --add-host host.docker.internal:host-gateway ...

host-gateway是Docker 20.10中添加的一个特殊值,它会自动扩展到主机IP。有关详细信息,请参阅本pr

备选案文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的东西,然后连接到mysql使用套接字转换为的localhost

这不是实际问题的答案。这是我解决类似问题的方法。解决方案完全来自:定义Docker容器网络,以便容器可以通信。感谢Nic Raboy

将此留给可能希望在一个容器和另一个容器之间进行REST调用的其他人。回答问题:在docker环境中使用什么来代替localhost?

了解您的网络看起来像docker network ls

创建一个新的网络docker network create -d my-net

启动第一个容器docker run -d -p 5000:5000 --network="my-net" --name "first_container" <MyImage1:v0.1>

查看第一个容器docker inspect first_container的网络设置。“网络”:应该有“my-net”

启动第二个容器docker run -d -p 6000:6000 --network="my-net" --name "second_container" <MyImage2:v0.1>

查看第二个容器docker inspect second_container的网络设置。“网络”:应该有“my-net”

ssh到第二个容器docker exec -it second_container shdocker exec -it second_container bash

在第二个容器内,您可以通过ping first_container ping第一个容器。此外,您的代码调用(例如http://localhost:5000)可以替换为http://first_container:5000

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

有两个问题我们需要解决

  1. 获取主机的IP
  2. 使我们的localhost服务可用于Docker

第一个问题可以使用qoomon的docker-host镜像解决,如其他答案所示。

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

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

现在,更难的问题是,使服务可供docker访问。

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

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

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

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

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

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

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

一旦我们有了这个网桥的名字,我们需要允许从这个网桥路由到localhost。出于安全原因,默认情况下这是禁用的:

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

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

为此,我们将所有请求转发到<docker_bridge>:portlocalhost: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 1025220 127.0.0.1 ESMTP Service Ready

我通过在MySQL中为容器的ip创建一个用户来解决它:

$ sudo mysql<br>mysql> create user 'username'@'172.17.0.2' identified by 'password';<br>Query OK, 0 rows affected (0.00 sec)
mysql> grant all privileges on database_name.* to 'username'@'172.17.0.2' with grant option;<br>Query OK, 0 rows affected (0.00 sec)
$ sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf<br>bind-address        = 172.17.0.1
$ sudo systemctl restart mysql.service

然后在容器上:jdbc:mysql://<b>172.17.0.1</b>:3306/database_name

你需要知道网关!我对本地服务器的解决方案是将其暴露在0.0.0.0:8000下,然后使用子网运行容器运行docker,如下所示:

docker network create --subnet=172.35.0.0/16 --gateway 172.35.0.1 SUBNET35docker run -d -p 4444:4444 --net SUBNET35 <container-you-want-run-place-here>

所以,现在您可以通过http://172.35.0.1:8000访问您的环回

我这样做的方法是将主机IP作为环境变量传递给容器。容器然后通过该变量访问主机。

对于Windows,

我在Spring配置中更改了数据库url:spring.datasource.url=jdbc:postgresql://host.docker.internal:5432/apidb

然后构建图像并运行。它为我工作。

试试这个:

version: '3.5'services:yourservice-here:container_name: container_nameports:- "4000:4000"extra_hosts: # <---- here- localhost:192.168.1.202- or-vitualhost.local:192.168.1.202

为了得到192.168.1.202,使用ifconfig

这对我有用。希望这能有所帮助!

要使一切正常工作,您需要为您的服务器(caddy,nginx)创建一个配置,其中主域将是“docker.for.mac.localhost”。

docker-compose.yml

backend:restart: alwaysimage: backendbuild:dockerfile: backend.Dockerfilecontext: .environment:# add django setting.py os.getenv("var") to bd config and ALLOWED_HOSTS CORS_ORIGIN_WHITELISTDJANGO_ALLOWED_PROTOCOL: httpDJANGO_ALLOWED_HOSTS: docker.for.mac.localhostPOSTGRES_PASSWORD: 123456POSTGRES_USER: userPOSTGRES_DB: bd_nameWAITDB: "1"volumes:- backend_static:/app/static- backend_media:/app/mediadepends_on:- db
frontend:restart: alwaysbuild:dockerfile: frontend.Dockerfilecontext: .image: frontendenvironment:#  replace baseURL for axiosAPI_URL: http://docker.for.mac.localhost/b/apiAPI_URL_BROWSER: http://docker.for.mac.localhost/b/apiNUXT_HOST: 0.0.0.0depends_on:- backend
caddy:image: abiosoft/caddyrestart: alwaysvolumes:- $HOME/.caddy:/root/.caddy- ./Caddyfile.local:/etc/Caddyfile- backend_static:/static- backend_media:/mediaports:- 80:80depends_on:- frontend- backend- db

Caddyfile.local

http://docker.for.mac.localhost {
proxy /b backend:5000 {header_upstream Host {host}header_upstream X-Real-IP {remote}header_upstream X-Forwarded-For {remote}header_upstream X-Forwarded-Port {server_port}header_upstream X-Forwarded-Proto {scheme}}
proxy / frontend:3000 {header_upstream Host {host}header_upstream X-Real-IP {remote}header_upstream X-Forwarded-For {remote}header_upstream X-Forwarded-Port {server_port}header_upstream X-Forwarded-Proto {scheme}}
root /
log stdouterrors stdoutgzip}
http://docker.for.mac.localhost/static {root /static}
http://docker.for.mac.localhost/media {root /media}

djangosettings.py

ALLOWED_HOSTS = [os.getenv("DJANGO_ALLOWED_HOSTS")]    
CORS_ORIGIN_WHITELIST = [f'{os.getenv("DJANGO_ALLOWED_PROTOCOL")}://{os.getenv("DJANGO_ALLOWED_HOSTS")}']
DATABASES = {"default": {"ENGINE": "django.db.backends.postgresql_psycopg2","NAME": os.getenv("POSTGRES_DB"),"USER": os.getenv("POSTGRES_USER"),"PASSWORD": os.getenv("POSTGRES_PASSWORD"),"HOST": "db","PORT": "5432",}}

nuxt.config.js(basURL变量将覆盖环境API_URL)

axios: {baseURL: 'http://127.0.0.1:8000/b/api'},

如果你使用docker-compose,也许它可以工作:

iptables -I INPUT ! -i eth0 -p tcp --dport 8001 -j ACCEPT

eth0是连接互联网的网络接口,8081是主机服务器端口

iptables规则的最佳方式是iptables TRACE

使用

host.docker.internal

而不是

localhost

工作完美无瑕对我来说。👍

首先,请参阅这个的答案,了解您必须解决此问题的选项。但是如果您使用docker-compose,您可以将network_mode: host添加到您的服务,然后使用127.0.0.1连接到本地主机。这只是上面答案中描述的选项之一。下面您可以找到我如何从https://github.com/geerlingguy/php-apache-container.git修改docker-compose.yml

 ---version: "3"services:php-apache:+    network_mode: hostimage: geerlingguy/php-apache:latestcontainer_name: php-apache...

+表示我添加的行。


[附加信息]这在版本2.2中也有效。和“主机”或只是“主机”都在docker-compose中工作。

 ---version: "2.2"
services:php-apache:+    network_mode: "host"or+    network_mode: host...

您可以简单地在主机上执行ifconfig来检查您的主机IP,然后从您的容器内连接到IP,非常适合我。

连接到网关地址。

❯ docker network inspect bridge | grep Gateway"Gateway": "172.17.0.1"

确保主机上的进程正在侦听此接口或所有接口,并在docker之后启动。如果使用system d,您可以添加以下内容以确保它在docker之后启动。

[Unit]After=docker.service

示例

❯ python -m http.server &> /dev/null &[1] 149976
❯ docker run --rm python python -c  "from urllib.request import urlopen;print(b'Directory listing for' in urlopen('http://172.17.0.1:8000').read())"True

主机上的服务器正在监听端口5000;它将发送一个字符串response from host作为响应

echo "response from host" | nc -l -p 5000

docker默认运行在网桥模式下(比运行在主机网络模式下更安全);docker中的进程获取主机上第一个不127.0.0.1的网络接口的ipv4地址;这个ipv4地址通过环境变量HOSTIP传递给docker;docker内部有一个nc命令连接到主机ip和端口5000上的服务器;该命令还读取服务器的响应。

HOST=$(ifconfig | grep 'inet ' | grep -v 127.0.0.1 | awk '{ print $2 }' | head -1 | sed -n 's/[^0-9]*\([0-9\.]*\)/\1/p'); docker run -e HOSTIP="${HOST}" --rm -it alpine /bin/sh -c 'echo "greetings from container" | nc $HOSTIP 5000 -v'

计算第一个列出的接口的ipv4地址的表达式确实适用于osx和linux:

HOST=$(ifconfig | grep 'inet ' | grep -v 127.0.0.1 | awk '{ print $2 }' | head -1 | sed -n 's/[^0-9]*\([0-9\.]*\)/\1/p')

为了使其更加通用:您可以在另一个docker容器中运行服务器,如下所示:

docker run --expose 5000 -p :5000:5000 --rm -it alpine /bin/sh -c 'echo "response from host" | nc -l -p 5000'

--暴露5000 docker容器可以接受端口5000上的连接

-p: 5000:5000主机端的docker引擎正在监听任何接口上的端口5000,并将连接转发到容器网络上的端口5000(nc正在运行的地方)。

尽管有很多冗长,令人困惑的答案很短。如果您的docker网桥网络172.17.0.0/16,请执行以下操作:

  1. 将容器绑定到172.17.0.1例如-p172.17.0.1:8080: 8080(无论Docker网桥网络中的网关是什么)
  2. 只需从其他容器访问172.17.0.1端口8080,例如curlhttp://172.17.0.1:8080/

在7年的时间里,这个问题被问到,要么是docker已经改变了,要么是没有人尝试过这种方式。所以我将包括我自己的答案。

我发现所有的答案都使用复杂的方法。今天,我需要这个,并找到了两个非常简单的方法:

  • 在主机上使用ipconfigifconfig并记下所有IP地址。容器至少可以使用其中两个。

    • 我在WiFi LAN适配器上有一个固定的本地网络地址:192.168.1.101。这可能是10.0.1.101。结果将根据您的路由器而变化
    • 我在Windows上使用WSL,它有自己的vEthernet地址:172.19.192.1
  • 使用host.docker.internal。大多数答案都有这种或另一种形式,具体取决于操作系统。这个名字表明它现在被docker全局使用。

第三种选择是使用机器的WAN地址,换句话说,是服务提供商提供的IP。但是,如果IP不是静态的,并且需要路由和防火墙设置,这可能不起作用。

如果您使用--net=host运行,localhost应该可以正常工作。如果您使用默认网络,请使用静态IP172.17.0.1。

看到这个-https://stackoverflow.com/a/48547074/14120621

对我来说,在主机上启用此命令以获取IP确实起到了作用:

docker run busybox ping -c 1 host.docker.internal | awk 'FNR==2 {print $4}' | sed s'/.$//'

或在mac

docker run busybox ping -c 1 docker.for.mac.localhost | awk 'FNR==2 {print $4}' | sed s'/.$//'

docker-compose.yml

version: '3.8'
services:spring-app-container:image: spring-app:1build:context: ./dockerfile: Dockerfileports:- "50800:50800"

您可以为您的机器使用网络别名

sudo ifconfig lo0 alias 123.123.123.123/24 up

然后从容器中,您可以通过123.123.123.123看到机器

不幸的是,目前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/

另外一点可能很有用,尤其是对于Docker容器中的NGINX服务器配置。因此,如果您的主机服务应该出于不同的目的读取客户端的url,则可以定义它,只需稍微修改之前在答案中提到的方法:

--add-host brand-A-client.local:host-gateway

或者docker-compose.yml

extra_hosts:- brand-B-client.local:host-gateway

并在容器中分别引用它们。适用于Docker Windows/Macos/Linux。