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


编辑:

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


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

将mysql绑定到172.17.42.1将阻止其他程序使用主机上的数据库来访问它。只有在所有数据库用户都已固定的情况下,这才有效。

将mysql绑定到0.0.0.0将向外部世界打开数据库,这不仅是一件非常糟糕的事情,而且与最初的问题作者想要做的相反

回答ivant的评论

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

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

因此,我还没有找到从docker容器访问主机上(仅限于本地主机)数据库的任何方法。这显然是一种非常常见的模式,但我不知道怎么做。


这在NGINX/PHP-FPM堆栈上对我有效,而无需接触任何代码或网络,应用程序只是希望能够连接到本地主机

将mysqld.sock从主机装载到容器内部。

在运行mysql的主机上查找mysql.sock文件的位置:netstat-ln|awk'/mysql(.*)?\。袜子/{打印$9}'

将该文件装载到docker中预期的位置:docker运行-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-设置

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

How?

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

docker run--network=“host”-id<docker image id>

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

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

这就是将NGINX容器连接到本地主机上运行的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

对于Docker的旧macOS版本

Mac版本17.12至18.02的Docker

同上,但改用docker.for.mac.host.internal。

Mac版Docker 17.06至17.11

同上,但改用docker.for.mac.localhost。

适用于Mac 17.05及以下版本的Docker

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

sudo ifconfig lo0别名123.123.123.123/24

然后确保您的服务器正在侦听上面提到的IP或0.0.0.0。如果它在本地主机127.0.0.1上侦听,它将不接受连接。

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

为了测试,可以在容器内运行类似curl-X GET 123.123.123.123:3000的命令。

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

此处的解决方案和更多文档:https://docs.docker.com/docker-for-mac/networking/#use-案例和解决方案


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中重新启动mysql服务器sudo/etc/init.d/mysql重启运行以下命令以打开用户root访问任何主机mysql-uurot-proot将*.*上的所有特权授予“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 composer一起运行版本:“2.1”服务:tomcatwar公司:外部主机(_H):-“本地:10.1.2.232”图片:sopheamak/springboot_docker_mysql端口:- 8080:8080环境:-DATABASE_HOST=本地-DATABASE_USER=根-DATABASE_PASSWORD=根-DATABASE_NAME=演示-数据库端口=3306


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,运行并调试应用程序。但是,不建议这样做。希望有帮助。


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

好的,本地主机服务器有一个默认的docker接口docker0,IP地址为172.17.0.1。容器以默认网络设置--net=“bridge”启动。

为docker0接口启用route_localnet:$sysctl-w net.ipv4.conf.docker0.route_localnet=1将这些规则添加到iptables:$iptables-t nat-I预路由-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创建具有从“%”访问权限的MySQL用户,这意味着-来自任何人,不包括本地主机:CREATE USER“USER”@“%”IDENTIFIED BY“password”;在脚本中将mysql服务器地址更改为172.17.0.1。

从内核文档中:

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


Windows 10解决方案

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

您可以使用主机docker.for.win.localhost的DNS名称解析为内部IP。(警告一些消息来源提到了窗口,但应该是赢家)

概述我需要做类似的事情,即从Docker容器连接到运行Azure Storage Emulator和CosmosDB Emulator的本地主机。

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

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


您可以使用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 version
services:
  nginx:
    build: ./ # specify the directory of the Dockerfile
    ports:
      - "8080:80" # specify port mapping
    extra_hosts:
      - "dockerhost:<yourIP>"

这样,您的容器将能够访问主机。访问DB时,请记住使用之前指定的名称,在本例中是dockerhost和运行DB的主机的端口。


在修复程序未合并到主分支之前,只需从容器内部运行主机IP即可:

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

(正如@Mahoney在这里所建议的)。


我们想到了几种解决方案:

首先将依赖项移动到容器中使您的其他服务可从外部访问,并通过该外部IP连接到它们在没有网络隔离的情况下运行容器避免通过网络连接,请使用作为卷安装的套接字

这无法开箱即用的原因是,默认情况下,容器使用自己的网络名称空间运行。这意味着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中,当您传递一个特殊的主机条目时,该功能已添加到Docker for Linux:

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

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

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

选项4:各种服务也允许通过基于文件系统的套接字进行访问。此套接字可以作为绑定装载卷装载到容器中,允许您在不通过网络的情况下访问主机服务。为了访问docker引擎,您经常会看到将/var/run/docker.sock装载到容器中的示例(为该容器提供对主机的根访问权限)。对于mysql,您可以尝试类似于-v/var/run/mysqld/mysqld.sock:/varrun/mysqld/mysql.sock的方式,然后连接到localhost,mysql使用套接字将其转换为localhost。


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

我通过在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 SUBNET35
docker run -d -p 4444:4444 --net SUBNET35 <container-you-want-run-place-here>

因此,现在您可以通过http://172.35.0.1:8000


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


对于窗口,

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

然后构建映像并运行。这对我有用。


试试看:

version: '3.5'
services:
  yourservice-here:
    container_name: container_name
    ports:
      - "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”http://localhost/api“在”http://docker.for.mac.localhost/api"

码头组合.yml

backend:
  restart: always
  image: backend
  build:
    dockerfile: backend.Dockerfile
    context: .
  environment:
    # add django setting.py os.getenv("var") to bd config and ALLOWED_HOSTS CORS_ORIGIN_WHITELIST
    DJANGO_ALLOWED_PROTOCOL: http
    DJANGO_ALLOWED_HOSTS: docker.for.mac.localhost
    POSTGRES_PASSWORD: 123456
    POSTGRES_USER: user
    POSTGRES_DB: bd_name
    WAITDB: "1"
  volumes:
    - backend_static:/app/static
    - backend_media:/app/media
  depends_on:
    - db

frontend:
  restart: always
  build:
    dockerfile: frontend.Dockerfile
    context: .
  image: frontend
  environment:
    #  replace baseURL for axios
    API_URL: http://docker.for.mac.localhost/b/api
    API_URL_BROWSER: http://docker.for.mac.localhost/b/api
    NUXT_HOST: 0.0.0.0
  depends_on:
    - backend

caddy:
  image: abiosoft/caddy
  restart: always
  volumes:
    - $HOME/.caddy:/root/.caddy
    - ./Caddyfile.local:/etc/Caddyfile
    - backend_static:/static
    - backend_media:/media
  ports:
  - 80:80
  depends_on:
    - frontend
    - backend
    - db

Caddyfile.local(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 stdout
  errors stdout
  gzip
}

http://docker.for.mac.localhost/static {
  root /static
}

http://docker.for.mac.localhost/media {
  root /media
}

django设置.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(baseURL变量将覆盖环境的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是连接internet的网络接口,8081是主机服务器端口

iptables规则的最佳方式是iptables TRACE


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

 ---
 version: "3"
 services:
   php-apache:
+    network_mode: host
     image: geerlingguy/php-apache:latest
     container_name: php-apache
...

+表示我添加的行。


[附加信息]这在2.2版中也有效。和“host”或“host”都在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之后启动。如果使用systemd,可以添加以下内容以确保在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;它将从主机发回一个字符串响应作为响应

echo“主机响应”|nc-l-p 5000

docker默认在网桥模式下运行(这比在主机网络模式下运行更安全);docker中的进程获取主机上第一个网络接口的ipv4地址,该地址不是127.0.0.1;这个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“来自容器的问候语”|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“来自主机的响应”|nc-l-p 5000'

--暴露5000码头集装箱可以接受端口5000上的连接

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


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

将容器绑定到172.17.0.1,例如-p 172.17.0.1:8080:8080(Docker网桥网络中的网关)只需访问172.17.0.1端口8080,例如curlhttp://172.17.0.1:8080/来自其他容器


在7年的时间里,人们提出了一个问题,要么码头工人变了,要么没有人这样做。因此,我将包括我自己的答案。

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

在主机上使用ipconfig或ifconfig,并记下所有IP地址。容器至少可以使用其中两个。我在WiFi LAN适配器上有一个固定的本地网络地址:192.168.1.101。这可能是10.0.101。结果会因路由器而异我在windows上使用WSL,它有自己的vEthernet地址:172.19.192.1使用host.docker.internal。根据操作系统的不同,大多数答案都有这种或另一种形式。这个名字表明它现在被docker全球使用。

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


如果使用--net=host运行,localhost应该可以正常工作。如果您使用默认网络,请使用静态IP 172.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'/.$//'

码头组合.yml

version: '3.8'

services:
  spring-app-container:
    image: spring-app:1
    build:
      context: ./
      dockerfile: Dockerfile
    ports:
      - "50800:50800"

您可以为计算机使用网络别名

OSX

sudo ifconfig lo0 alias 123.123.123.123/24 up

LINUX操作系统

sudo ifconfig lo:0  123.123.123.123 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。