ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

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

为什么不将 mysql 绑定到 docker0 呢?
对于 Windows 机器:- $docker run -d --name MyWebServer -P httpd
如果没有 network: host,您将无法从容器返回到主机。仅托管到容器。这是容器背后的主要思想。出于稳定性和安全原因,它们被隔离。

T
Thomasleveil

编辑:

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

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

否则,请阅读下文

TLDR

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

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

docker 容器网络模式注意事项

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 主机在 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

搬运工运行--network =“主机”

或者,您可以使用 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 地址。

从容器连接到 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 的 docker 主机上运行的进程必须使用 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 套接字进行连接。


谢谢你这么详细的回答!根据我收集到的信息,使用主机模式是通过 localhost 获得此功能的唯一方法。我还没有尝试过,但我假设您可以创建一个单独的网络来通过它们自己的网桥连接容器,为它们提供一个通用的“本地主机”。
OSX 用户注意事项:首先使用“docker-machine ssh default”登录到您的 docker 虚拟机(boot2docker),然后运行“sudo ip addr show docker0”。从那里继续托马斯的指示。
我正在为 Mac 运行 Docker,不再有 172.17.42.1,也没有 docker0。它是 172.17.0.1 作为网关,甚至不能telnet 172.17.0.1 3306
您可以将 mysql 套接字挂载到容器中,而不是像 -v /var/run/mysqld/mysqld.sock:/tmp/mysql.sock 这样的网络。
当您在 Mac 上运行 Docker 而没有使用 boot2docker 时,有人可以解决这种情况,因为没有 docker0 接口?
J
Janne Annala

适用于所有平台

Docker v 20.10 及更高版本(自 2020 年 12 月 14 日起)

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

在 Linux 上,将 --add-host=host.docker.internal:host-gateway 添加到您的 Docker 命令以启用此功能。要在 Linux 上的 Docker Compose 中启用此功能,请将以下几行添加到容器定义中:

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

对于 Docker 的旧 macOS 和 Windows 版本

Docker v 18.03 及更高版本(自 2018 年 3 月 21 日起)

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

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


太棒了,谢谢,这在容器内对我有用。 mysql -uroot -hdocker.for.mac.localhost
docker.for.mac.localhost 正是我想要的。但这同时也是肮脏的。在 docker 中,人们会期望钩子 docker.for.mac.localhost 是一个通用的 docker 内部名称,它适用于任何操作系统,而不仅仅是 Mac。但出于开发目的,这已经足够了。
应该使用 DNS 名称 docker.for.mac.host.internal 而不是 docker.for.mac.localhost(仍然有效)从容器解析主机,因为有一个 RFC 禁止使用 localhost 的子域。请参阅tools.ietf.org/html/draft-west-let-localhost-be-localhost-06
这对我不起作用。当我 docker run -e HOSTNAME= docker.for.mac.host.internal 时,容器已创建,但没有发生任何事情。我必须然后crtl + C。至少使用 --net=host -e HOSTNAME=localhost 容器运行并抱怨它找不到我需要的服务(MySQL db)。
如此快速和有用。谢谢!
D
DeyaEldeen

使用

host.docker.internal

代替

localhost

对我来说完美无缺。 👍


也许这个答案可以扩展和澄清一点?这是否意味着您仍然可以使用桥接模式?
@ArnoldRoa 这仅适用于 Mac/Windows 而不是 Linux
自 2021 年 4 月 26 日起无效。 host.docker.internal 解析为空。
如需使用 docker-compose 设置 host.docker.internal,请参阅 stackoverflow.com/a/67158212/243392
谢谢@DeyaEldeen,这非常适合mac
M
Marco Altran

我做了一个类似于上述帖子的破解,获取本地 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 并解释其工作原理。


根据您的用例,您甚至可以在 docker 容器需要本地连接的任何服务中使用 DOCKERHOST 值而不是“localhost”或 0.0.0.0。
我稍微修改了您的解决方案以与自定义网络兼容:export DOCKERHOST=$(docker network inspect --format='{{range .IPAM.Config}}{{.Gateway}}{{end}}' <NETWORK-NAME> | awk -F "/" 'NR==1{print $1}') 其中 <NETWORK-NAME> 可以是 bridge 或 docker 定义的网络名称-compose(通常是 path-name-network_name)。
您需要添加注释:使用 dockerhost 作为 db 连接的主机(通常替换配置文件中的 localhost)。
奇怪的解决方案,但它是唯一使 xdebug 在 docker 下使用 php cli 工作的解决方案
在 haproxy 中此解决方案后,Acl 停止工作。知道为什么吗?
d
d-_-b

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

这是一个更清洁的解决方案,不会将 Mysql 暴露在外部(如果不使用防火墙)。
套接字的伸缩性不如 TCP,因为它们阻塞的频率更高,并且可能导致奇怪的行为。尽可能使用 TCP。
@JoelESalas 你有这个说法的来源吗?
我发现这是最简单的解决方案。竖起大拇指用户833482!您应该更频繁地为 StackOverflow 做出贡献。
@JoelESalas 我认为你错了。 mysql 客户端库在连接到 localhost 时甚至默认使用 unix 套接字,而不是实际连接到 localhost。 unix 套接字避免了 TCP 堆栈和路由的开销,并且应该运行得更快。
s
simhumileco

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

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

为 docker0 接口启用 route_localnet: $ sysctl -w net.ipv4.conf.docker0.route_localnet=1 将这些规则添加到 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 Create MySQL user with access from '%' 这意味着 - 从任何人,不包括 localhost:CREATE USER 'user'@'%' IDENTIFIED BY 'password';在脚本中将 mysql-server 地址更改为 172.17.0.1。

kernel documentation

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


第二个 iptable 命令的目的是什么?我可以理解第一个是将匹配 172.17.0.1:3306 的所有 tcp 目标重写为 127.0.0.1:3306 但为什么需要第二个 iptable 命令?
这件事情让我感到很快乐!感谢你的分享
最后。我努力寻找正确的 IP 地址。我不知道 docker 有默认界面。
奇怪的是,这对我来说并不稳定,至少在最新的 CentOS 7 上运行内核 3.10 和 Docker 19.03.15。它最初工作,然后在一段时间后停止工作。
s
slhck

host.docker.internal 适用于所有平台之前,您可以将我的容器用作 NAT 网关,而无需任何手动设置:

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


我可以确认这在带有 Windows 容器的 Windows 19.03.2 的 Docker 中不起作用。
知道如何解决吗? (我不是windows用户)
如果您的环境不阻止 mysql 使用的端口,则可以通过托管的计算机名称来引用服务器。因此,在您的连接字符串中,使用您的计算机名称作为服务器名称。
我可以确认这不适用于 Docker for Windows v. 20.10.5。真让人生气。主机(来自容器)的 IP 随机变化。当主机 IP 在运行时更改并且 host.docker.internal 解析为空时,我到底应该如何部署连接字符串?
@Triynko 你可以运行以下命令并告诉我这些命令是否成功? docker run --rm alpine ping host.docker.internal docker run --rm alpine ping docker.for.win.localhost docker run --rm alpine ping gateway.docker.internal
F
Felipe Toledo

非常简单快捷,使用 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”以及运行数据库的主机端口


在 HaProxy 中,此解决方案由于某种原因停止了 acl 的工作,只有默认设置有效。
S
Stephen Ostermiller

适用于 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


Docker for Mac 现在公开 docker.for.mac.host.internal 主机名。
host.docker.internal 在 Mac 上可用。更多信息:docs.docker.com/desktop/networking/…
R
Ralph Willgoss

适用于 Windows 10 的解决方案

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

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

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

默认情况下,Azure Storage Emulator127.0.0.1 上侦听,虽然您也可以更改其绑定的 IP,但我一直在寻找可以使用默认设置的解决方案。

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


B
BMitch

想到了几个解决方案:

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

这不能开箱即用的原因是容器默认使用自己的网络命名空间运行。这意味着 localhost(或指向环回接口的 127.0.0.1)对于每个容器都是唯一的。连接到此将连接到容器本身,而不是在 docker 外部或不同 docker 容器内部运行的服务。

选项 1:如果您的依赖项可以移动到容器中,我会先这样做。它使您的应用程序堆栈可移植,因为其他人尝试在他们自己的环境中运行您的容器。而且您仍然可以在您的主机上发布端口,其他尚未迁移的服务仍然可以访问它。您甚至可以将端口发布到 docker 主机上的 localhost 接口,以避免使用如下语法从外部访问它:-p 127.0.0.1:3306:3306 表示已发布的端口。

选项 2:有多种方法可以从容器内部检测主机 IP 地址,但每种方法的工作场景数量有限(例如,Mac 需要 Docker)。最便携的选项是使用环境变量或配置文件之类的东西将主机 IP 注入容器,例如:

docker run --rm -e "HOST_IP=$(ip route get 1 | sed -n 's/^.*src \([0-9.]*\) .*$/\1/p')" ...

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

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

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

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

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

选项 4:各种服务也允许通过基于文件系统的套接字进行访问。此套接字可以作为绑定安装卷安装到容器中,允许您访问主机服务而无需通过网络。对于 docker 引擎的访问,您经常会看到将 /var/run/docker.sock 挂载到容器中的示例(授予该容器对主机的根访问权限)。使用 mysql,您可以尝试 -v /var/run/mysqld/mysqld.sock:/var/run/mysqld/mysql.sock 之类的操作,然后连接到 mysql 使用套接字转换为的 localhost


对于选项 2(所有答案中唯一有效的方法):以下命令获取 osx 和 linux 上除本地主机以外的接口的 IP 地址:ifconfig | grep 'inet ' | grep -v 127.0.0.1 | awk '{ print $2 }' | head -1 | sed -n 's/[^0-9]*\([0-9\.]*\)/\1/p'
--add-host host.docker.internal:host-gateway 是金色的
P
Praveenkumar Beedanal

对于窗户,

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

然后构建镜像并运行。它对我有用。


对我来说不起作用。我有 mac 并尝试从 php 容器连接到 localhost mysql。任何想法 ?
S
Stephen Ostermiller

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


有用。甚至不需要指定 --network=host。看起来 10.0.2.2 被设置为主机的默认 IP。谢谢。
但是,我可以将这个静态 IP 用于所有版本的 Windows 和 Mac 吗?如何通过脚本在多个平台上处理它?
这对我有用。只需在您的主机(Windows)上运行 ipconfig 并获取 Ethernet adapter vEthernet (DockerNAT) 下的 IP 地址
S
Shirish Hirekodi

这不是对实际问题的回答。这就是我解决类似问题的方法。解决方案完全来自:Define Docker Container Networking so Containers can Communicate。感谢尼克·拉博伊

将这里留给可能想要在一个容器和另一个容器之间进行 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 的网络设置。 “网络”:应该有“我的网络”

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

检查第二个容器 docker inspect second_container 的网络设置。 “网络”:应该有“我的网络”

SSH 到您的第二个容器 docker exec -it second_container shdocker exec -it second_container bash

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


正是我想要的。感谢:D
如果您知道这不是问题的答案(事实并非如此,因为该问题专门询问 localhost),您为什么要发布它?
S
Stephen Ostermiller

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

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

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


你应该得到一枚奖牌!我已经为此工作了整整两天。谢谢你的帮助!
我也为此工作了整整两天。这是我在网上找到的唯一提到的地方。微软在谈到从 Docker 容器内连接到 MSSQL 时完全保持沉默。这让你想知道他们是否曾经让它自己工作!
C
Community

编辑:我最终在 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 的方式。


重要的是要注意主机模式提供更好的性能,因为它使用操作系统网络堆栈。
那里很好。尽管可以使用未使用的 IP 附加 docs.docker.com/docker-for-mac/networking 从容器连接到主机服务。不是一个令人愉快的解决方案......但有效。
好东西在这里。例如,一旦进入带有 --network="host" 的容器,如何连接到主机 mysql?
@Buccleuch 只需使用 localhost。查看我的 GitHub 源代码:github.com/sivabudh/system-in-a-box/blob/master/dj_host_docker/…。搜索 'HOST',你会看到 127.0.0.1 连接到 Postgresql。
我可以通过按照@user833482 挂载卷来访问主机 mysql 数据库,当然,在 docker 容器上安装 mysql-client 和 server 之后。
J
JShorthouse

对于 Linux,您无法更改 localhost 服务绑定到的接口

我们需要解决两个问题

获取主机的 IP 使我们的 localhost 服务对 Docker 可用

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

您需要将此容器添加到与其他容器相同的桥接网络中,以便您可以访问它。在您的容器内打开一个终端并确保您可以 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

首先,我们需要通过 ifconfig 找到 docker 使用的桥接网络的名称。如果您使用的是未命名的网桥,则它只是 docker0。但是,如果您使用的是命名网络,您将拥有一个以 br- 开头的网桥,而 docker 将使用该网桥。我的是br-5cd80298d6f4

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

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

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

为此,我们会将所有对 <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

这与@ray-d 上面提到的相同,但解释得很好。谢谢!此外,当使用 docker-compose 时,我还必须在 PREROUTING 链中为到达 docker0 接口的所有数据包添加 DNAT 规则:因为,docker-compose 似乎在构建期间使用 docker0(令人惊讶的是,不是在运行期间): sysctl -w net.ipv4.conf.docker0.route_localnet=1iptables -t nat -A PREROUTING -p tcp -i docker0 --dport 1025 -j DNAT --to-destination 127.0.0.1:1025
E
Ehsan88

首先查看 this 答案,了解您必须解决此问题的选项。但是如果您使用 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: host
     image: geerlingguy/php-apache:latest
     container_name: php-apache
...

+ 表示我添加的行。

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

 ---
 version: "2.2"

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

是的。它适用于:network_mode:host 现在可以访问指向另一个项目 docker 容器的本地 /etc/hosts 域名。
P
Prince

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

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


简单有效!
B
Bruno Bieri

我不同意 Thomasleveil 的回答。

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

让mysql绑定到0.0.0.0会向外界打开db,这不仅是一件非常糟糕的事情,而且与原始问题作者想要做的相反。他明确表示“MySql 在 localhost 上运行并且没有向外界公开端口,因此它绑定在 localhost 上”

回答 ivant 的评论

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

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

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


从 docker 主机,您仍然可以使用 172.17.42.1 地址连接到 MySQL 服务器。但你的笔记是对的。 Aso,我使用 host 网络模式编辑了我的答案,该模式允许将 MySQL 服务器绑定到 127.0.0.1,同时允许容器连接到它
不,正如我所说,如果 mysql 绑定到 localhost,则无法连接到 172.17.42.1。
“让 mysql 绑定到 172.17.42.1 将阻止其他程序使用主机上的数据库访问它。” - 那不是真的。其他程序可以使用 mysql,它们只需要连接到 172.17.42.1 而不是 localhost/127.0.0.1。
此答案中问题的解决方案通常是让 MySQL 服务器绑定到 0.0.0.0,然后设置防火墙,以便无法从 Internet 访问数据库。
Y
Yılmaz Durmaz

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

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

在您的主机上使用 ipconfig 或 ifconfig 并记下所有 IP 地址。容器至少可以使用其中的两个。我在 WiFi LAN 适配器上有一个固定的本地网络地址:192.168.1.101。这可能是 10.0.1.101。结果会根据你的路由器而改变我在 Windows 上使用 WSL,它有自己的 vEthernet 地址:172.19.192.1

我在 WiFi LAN 适配器上有一个固定的本地网络地址:192.168.1.101。这可能是 10.0.1.101。结果将根据您的路由器而改变

我在 Windows 上使用 WSL,它有自己的 vEthernet 地址:172.19.192.1

使用 host.docker.internal。大多数答案都有这种或另一种形式,具体取决于操作系统。顾名思义,它现在被 docker 全局使用。

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


这对于 wsl 用户来说是巨大的。 host.docker.internal 通常是答案....但是如果您使用的是 wsl,那么 docker 将不得不消除多个 IP 地址的歧义。这可能会导致超级烦人的问题,即您在不同端口上本地运行多个服务器,但您只能使用 host.docker.internal 打几个。
B
Binh Ho

尝试这个:

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

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


这是一个巧妙的小技巧。我还没有尝试过,但这是一个非常方便的快速解决方案
s
storenth

您需要知道 gateway!我使用本地服务器的解决方案是在 0.0.0.0:8000 下公开它,然后使用 subnetrun container 运行 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 访问您的环回


b
balki

连接到网关地址。

❯ 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

s
sopheamak

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

通过 /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;同花顺特权;

创建 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”图像:sopheamak/springboot_docker_mysql 端口:-8080:8080 环境:-DATABASE_HOST=local -DATABASE_USER=root -DATABASE_PASSWORD=root - DATABASE_NAME=demo - DATABASE_PORT=3306


h
hasnat

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

m
mohan08p

CGroups 和命名空间在容器生态系统中扮演着重要角色。

命名空间提供了一层隔离。每个容器都在单独的命名空间中运行,并且它的访问权限仅限于该命名空间。 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,运行和调试您的应用程序。但是,不建议走这条路。希望能帮助到你。


P
Philzen

我通过在 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


这是最简单的解决方案。它对我有用,我推荐了很多
w
wiseCoder

为了使一切正常,您需要为您的服务器(caddy、nginx)创建一个配置,其中主域将是“docker.for.mac.localhost”。为此替换“http://docker.for.mac.localhost/api”上的 baseURL“http://localhost/api”

码头工人-compose.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

球童文件.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 settings.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'
},

D
Duc Trung Mai

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


为我工作。我使用了 ifconfig 命令并获取了第一个 inet ip,它似乎是正确的