如何在Docker容器内部获取Docker主机的IP地址

545

正如标题所述,我需要能够在容器内部检索Docker主机的IP地址和主机到容器的端口映射。


1
您能详细说明一下您想如何使用这些信息吗?您说“docker主机”--您在多个主机上运行Docker吗?一旦您的容器知道了主机的IP地址和端口映射,它将会做什么? - Andy
将Docker主机IP地址传递给Docker容器的最简单方法,我认为是在容器内部使用“docker container exec”进行调用。假设您想从busybox容器内部ping主机,请使用以下命令: $ IP = '8.8.8.8' && docker container busybox ping $IP ' 找出主机IP的方法,使用您喜欢的任何方式即可。 - SauloAlessandre
28个回答

418
/sbin/ip route|awk '/default/ { print $3 }'

正如 @MichaelNeale所指出的,除非我们只在构建时需要这个IP地址,否则在Dockerfile中使用此方法没有意义,因为该IP地址将在构建时硬编码。


65
如果你在容器中使用docker bridge(默认设置),它会输出桥接IP地址,比如172.17.42.1,而不是主机的IP地址,比如192.168.1.x(假设你的主机在家庭NAT网络中)。在这种情况下,如果你想要获取192.168.1.x地址,最好采用@Magno Torres提供的方法。 - Programster
4
“RUN”命令不会按你的期望工作,它只会在构建时计算IP地址,并且在那之后永远是静态的,没有用处。它将成为构建主机的IP地址。 - Michael Neale
7
@Programster,我可以假设人们想要docker主机和容器之间的连接,这就是桥接IP可以给你的(当然,在标准的“home”安装中)。如果“真正的”主机IP地址甚至可以在docker桥接之外且不可访问,为什么有人需要它呢?这是所有刚安装docker并想要快速使用它的人的解决方案。 - spinus
2
@MichaelNeale,和以前一样,我认为大多数刚开始使用Docker的人需要主机和容器之间的连接。如果有人正在进行“正式”的部署,那么他可能不会使用Docker桥接,而是使用自定义网络,然后可能已经意识到所有网络问题或者他们已经设置了DNS(或其他发现方式)。 - spinus
1
@spinus - 但这仅在它运行的主机上才有效 - 在这种情况下 - 您可以直接硬编码它 - 或查找它 - 我认为这不是一个有用的答案,会误导很多人 - 建议您将其删除。(RUN位) - Michael Neale
显示剩余6条评论

366

从版本18.03开始,您可以使用host.docker.internal作为主机的IP地址。

Docker for MacDocker for Windows,以及可能还有其他平台中都适用。

这是针对Mac特定的更新,自17.06版本起可用的docker.for.mac.localhost和自17.12版本起可用的docker.for.mac.host.internal,这些选项也可能仍然适用于该平台。

请注意,正如MacWindows文档中提到的一样,此功能仅用于开发目的。

例如,我已在我的主机上设置了环境变量:

MONGO_SERVER=host.docker.internal

在我的docker-compose.yml文件中,我有这个:

version: '3'

services:
  api:
    build: ./api
    volumes:
      - ./api:/usr/src/app:ro
    ports:
      - "8000"
    environment:
      - MONGO_SERVER
    command: /usr/local/bin/gunicorn -c /usr/src/app/gunicorn_config.py -w 1 -b :8000 wsgi

2
很遗憾,Docker的开发人员不愿意为我们提供一种平台无关的方法来做这件事,因为他们不希望支持意外的用例(比如从Docker容器中访问本地机器上的任何服务)。 - Andy
60
我曾经听说过Docker的口号是“构建一次,随处运行”。但实际上这是错误的,因为你总是需要配置主机系统。所以docker.for.mac..在大多数情况下是无用的,因为你的公司中往往不仅有Linux或Mac环境。开发人员使用的是Linux、Mac和Windows混合环境。这个领域没有意义,因为在99%的情况下,这是一个混合Host OS环境。我不会在macOS下开发容器,然后将其部署到macOS服务器上。我会将其部署到Linux上。这是每个人都会做的。那么,docker.for.mac..到底有什么意义呢? - TheFox
4
很遗憾,它不再起作用了。在19.03.2上进行了测试。 - KMC
2
这完全不起作用。 host.docker.internal未解析任何内容。这太复杂了,无法按照文档进行设置。 Docker版本20.10.5。 - Triynko
5
对我而言,“host.docker.internal”是可用的(在Windows 10、WSL2 Ubuntu 20.04和Laravel Sail中)。 - Andrew P.
显示剩余8条评论

113

更新:在Docker for Mac版本18.03及以上,您可以将host.docker.internal用作主机的IP。请参见aljabear的答案。对于早期版本的Docker for Mac,以下答案仍然有用:

在Docker for Mac上,docker0桥不存在,因此这里的其他答案可能无法正常工作。但是,所有的出站流量都将通过您的父主机路由,因此只要您尝试连接到一个被它本身(且docker容器不认为它本身)识别为自己的IP,您就应该能够连接。例如,如果您从父机运行此命令:

ipconfig getifaddr en0

这应该会显示您的Mac在当前网络上的IP地址,您的Docker容器也应该能够连接到此地址。当然,如果IP地址发生变化,这可能会很麻烦,但是您可以在父机上执行类似于以下操作以将自定义环回IP添加到Mac上,从而使容器不认为它是本身:

sudo ifconfig lo0 alias 192.168.46.49
您可以在 Docker 容器中使用 telnet 进行连接测试。在我的情况下,我想要连接到远程的 xdebug 服务器。
telnet 192.168.46.49 9000

现在,当针对 192.168.46.49 的流量进入您的 Mac 时(以及所有离开容器的流量都通过您的 Mac),您的 Mac 将认为该 IP 是自己。完成使用此 IP 后,可以像这样删除环回别名:

sudo ifconfig lo0 -alias 192.168.46.49

需要注意的一点是,如果Docker容器认为流量的目的地是它自己,那么它将不会向父主机发送流量。因此,如果遇到问题,请在容器内检查环回接口:

sudo ip addr show lo
在我的情况下,显示的是inet 127.0.0.1/8,这意味着我不能使用127.*范围内的任何IP。 这就是为什么我在上面的示例中使用了192.168.*。请确保您使用的IP不会与您自己的网络中的某些内容发生冲突。

3
这个主机名的使用没有意义,因为它在Linux的Docker下无法工作。为什么他们没有也为Linux添加它呢? - TheFox
他们正在此时查看它 @TheFox: https://github.com/docker/libnetwork/pull/2348 - Ivo Pereira
telnet 命令在我的容器中不存在。 - Triynko

74
据我所知,在Linux下的Docker(标准发行版)中,主机的IP地址始终为172.17.0.1(在Docker的主网络上,详见评论以了解更多信息)。获取它的最简单方法是通过从主机运行ifconfig (接口docker0)。
ifconfig

从 Docker 容器内部,可以使用以下命令获取 Docker 的 IP 地址:ip -4 route show default | cut -d" " -f3

您可以使用以下命令行快速在 Docker 中运行它:

# 1. Run an ubuntu docker
# 2. Updates dependencies (quietly)
# 3. Install ip package   (quietly)
# 4. Shows (nicely) the ip of the host
# 5. Removes the docker (thanks to `--rm` arg)
docker run -it --rm ubuntu:22.10 bash -c "apt-get update > /dev/null && apt-get install iproute2 -y > /dev/null 2> /dev/null && ip -4 route show default | cut -d' ' -f3"

24
这适用于附加到 docker0 默认桥接接口的容器。但对于使用 Docker Compose 启动的容器则不适用,它们不会在默认的桥接接口上运行。使用 docker network lsdocker network inspect <network_name> 可以帮助你找出该 IP 地址是什么。 - Carl Patenaude Poulin
2
谢谢你的指示。顺便说一下,我在使用 Linux,并且我期望得到 172.17.0.1 的结果,但实际上我正在检查一个容器,它拥有 172.23.0.1。运行的容器数量是否会影响主机机器的映射,从而增加 IP 数字? - funder7
2
@funder7,实际上这取决于你在Docker内使用的网络。您可以使用docker network ls访问网络,如果您没有定义任何网络,则只需知道docker-compose在Docker内自动定义了虚拟网络(您也可以列出它们)。 - Nek
@Nek 可能是我的问题,我看的是容器的内部 IP,而我的 docker0 网络接口是 172.17.0.1,不管怎样,我现在使用 docker.host.internal,它支持 Linux。感谢你的命令,我会尝试一下! - funder7
是的,这在Linux上完美运行。 - Akashgreninja

57

对于在AWS上运行Docker的用户,主机的实例元数据仍可从容器内部获取。

curl http://169.254.169.254/latest/meta-data/local-ipv4
例如:
$ docker run alpine /bin/sh -c "apk update ; apk add curl ; curl -s http://169.254.169.254/latest/meta-data/local-ipv4 ; echo"
fetch http://dl-cdn.alpinelinux.org/alpine/v3.3/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.3/community/x86_64/APKINDEX.tar.gz
v3.3.1-119-gb247c0a [http://dl-cdn.alpinelinux.org/alpine/v3.3/main]
v3.3.1-59-g48b0368 [http://dl-cdn.alpinelinux.org/alpine/v3.3/community]
OK: 5855 distinct packages available
(1/4) Installing openssl (1.0.2g-r0)
(2/4) Installing ca-certificates (20160104-r2)
(3/4) Installing libssh2 (1.6.0-r1)
(4/4) Installing curl (7.47.0-r0)
Executing busybox-1.24.1-r7.trigger
Executing ca-certificates-20160104-r2.trigger
OK: 7 MiB in 15 packages
172.31.27.238

$ ifconfig eth0 | grep -oP 'inet addr:\K\S+'
172.31.27.238

这是对于使用AWS的人来说非常方便的方法。我使用它来配置Consul代理的客户端和绑定地址。当你处于无法使用主机网络的情况下(例如在Elastic Beanstalk和ECS中部署容器)时,它非常理想。 - Richard Clayton
这真是救命稻草,谢谢。我一直在苦恼如何处理我ECS集群中容器之间的通信问题。 - Brennan
在基于Ubuntu的Phusion basimage上,我需要稍微修改您的命令:ifconfig eth0 | grep -oP 'inet \\K\\S+' - Jean Claveau
如果Docker有类似这样的东西,那就太棒了。 - Marat Mkhitaryan
对于Fargate,您将不得不在http://169.254.170.2/v2/metadata/的JSON响应中搜索“IPv4Addresses”。 - user10699646
同样在GCP中:curl -s -H 'Metadata-Flavor: Google' http://metadata/computeMetadata/v1/instance/network-interfaces/0/ip 用于获取第一个网络接口的IP地址。 - Rondo

40
docker network inspect bridge -f '{{range .IPAM.Config}}{{.Gateway}}{{end}}'

可以使用docker network inspect来检索它。


4
迄今为止最佳答案,考虑到它不依赖于容器中存在任何二进制文件。 - Francesco Casula

28

创建容器时,唯一的方法是将主机信息作为环境变量传递

run --env <key>=<value>

22
更具体地说,桥接的 IP 地址可以通过命令行选项进行传递,例如:-e "DOCKER_HOST=$(ip -4 addr show docker0 | grep -Po 'inet \K[\d.]+')"(使用 http://unix.stackexchange.com/questions/87468/is-there-an-easy-way-to-programmatically-extract-ip-address 的被接受答案)。 - ncoghlan
8
我认为它在 Docker for Mac / Windows 中不起作用。(指网桥IP) - zx1986

27

--add-host 可能是一个更加干净的解决方案(但没有端口部分,只能处理主机部分)。因此,在您的 docker run 命令中,可以这样做:

docker run --add-host dockerhost:`/sbin/ip route|awk '/default/ { print  $3}'` [my container]

(来自https://dev59.com/v18d5IYBdhLWcg3waxgp#26864854)


当有人想在/etc/hosts中添加条目时,我说“为此而制作”,但在这个问题中,这并不完全正确。此外,OP要求“主机和端口映射”,而您只涵盖了主机。 - Bryan
好的。我有点困惑,因为被接受的解决方案只涵盖了主机部分。而且我认为你的解决方案比spinus的更优秀。 - Augunrik
如果您想使用 dind - docker-in-docker,这种方法不起作用。内部的 Docker 将会有不同的 IP 地址。 - noisy

23

对于大多数希望自动执行此操作的应用程序,标准最佳实践是:不要这样做。相反,您可以让运行容器的人将外部主机名/IP地址作为配置注入,例如作为环境变量或配置文件。允许用户注入此信息可提供最具可移植性的设计。

为什么会这么困难呢?因为容器的设计目的是隔离应用程序与主机环境。默认情况下,网络被命名空间化到该容器中,主机的详细信息受到来自容器内运行的进程的保护,这些进程可能并不是完全可信的。


根据您的具体情况,有不同的选择:

如果您的容器正在运行主机网络,则可以直接查看主机上的路由表以查看默认路由。从此问题中,以下内容适用于我:

ip route get 1 | sed -n 's/^.*src \([0-9.]*\) .*$/\1/p'

容器中使用主机网络的示例如下:

docker run --rm --net host busybox /bin/sh -c \
  "ip route get 1 | sed -n 's/^.*src \([0-9.]*\) .*$/\1/p'"

对于当前版本的Docker桌面版,它们将DNS条目注入到嵌入式虚拟机中:

getent hosts host.docker.internal | awk '{print $1}'

20.10版本发布后,如果您使用额外的选项运行容器,则host.docker.internal别名也可以在Linux上工作:

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

如果您正在云环境中运行,您可以从云提供商(例如AWS)检查元数据服务:

curl http://169.254.169.254/latest/meta-data/local-ipv4

如果你想获得你的外部/互联网地址,你可以查询远程服务,例如:

curl ifconfig.co

每种方法都有其局限性,并且只适用于特定场景。最便携的选择仍然是将IP地址作为配置注入并运行容器,例如以下选项在主机上运行早期的ip命令并将其注入为环境变量:

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

对我有用 -> ip route get 1 | sed -n 's/^.*src ([0-9.]) .$/\1/p' - Vijay Dohare
我个人认为这应该是被接受的答案。 - Riki

19

Mac和Windows的TLDR

docker run -it --rm alpine nslookup host.docker.internal

...打印主机的IP地址...

nslookup: can't resolve '(null)': Name does not resolve

Name:      host.docker.internal
Address 1: 192.168.65.2

细节

MacWindows上,您可以使用特殊的DNS名称host.docker.internal

主机具有不断变化的IP地址(如果您没有网络访问,则没有IP地址)。从18.03版本开始,我们建议连接到特殊的DNS名称host.docker.internal,该名称解析为主机使用的内部IP地址。这仅用于开发目的,在Docker Desktop for Mac之外的生产环境中将无法正常工作。


1
请不要在多个问题中添加相同的答案。请回答最好的一个,并标记其他重复的答案,一旦您获得足够的声望。如果它不是重复的,那么请根据问题调整帖子并标记为未删除。 - Bhargav Rao

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接