正如标题所示,我需要能够检索docker托管的IP地址以及从主机到容器的portmap,并在容器内部完成这些工作。


当前回答

如果你想要真实IP地址(不是网桥IP),并且你有docker 18.03(或最新的),执行以下操作:

在镜像名为nginx的主机上运行bash(适用于Alpine Linux发行版):

 docker run -it nginx /bin/ash

然后在容器内运行

/ # nslookup host.docker.internal

Name:      host.docker.internal
Address 1: 192.168.65.2

192.168.65.2是主机的IP,而不是spinus接受的应答中的网桥IP。

我在这里使用host.docker.internal:

主机有一个不断变化的IP地址(如果没有网络访问,则没有)。从18.03开始,我们的建议是连接到特殊的DNS名称host.docker.internal,它将解析为主机使用的内部IP地址。这是为了开发目的,不能在Docker for Windows之外的生产环境中工作。

其他回答

对于大多数希望自动执行此操作的应用程序来说,标准的最佳实践是:你不需要这样做。相反,你让运行容器的人注入一个外部主机名/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 Desktop,他们在嵌入式虚拟机中注入了一个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

如果你启用了docker远程API(例如通过-Htcp://0.0.0.0:4243),并且知道主机的主机名或IP地址,这可以通过大量的bash来完成。

在容器的用户bashrc中:

export hostIP=$(ip r | awk '/default/{print $3}')
export containerID=$(awk -F/ '/docker/{print $NF;exit;}' /proc/self/cgroup)
export proxyPort=$(
  curl -s http://$hostIP:4243/containers/$containerID/json |
  node -pe 'JSON.parse(require("fs").readFileSync("/dev/stdin").toString()).NetworkSettings.Ports["DESIRED_PORT/tcp"][0].HostPort'
)

第二行从本地/proc/self/cgroup文件中获取容器ID。

第三行卷曲到主机(假设您使用4243作为docker的端口),然后使用node解析返回的JSON为DESIRED_PORT。

AFAIK,在Docker for Linux(标准发行版)的情况下,主机的IP地址将始终是172.17.0.1(在Docker的主网络上,请参阅评论了解更多)。

最简单的方法是从主机上通过ifconfig(接口docker0)获取:

ifconfig

在docker内部,docker可以执行以下命令: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.04 bash -c "apt-get update > /dev/null && apt-get install iproute2 -y > /dev/null && ip -4 route show default | cut -d' ' -f3"

我使用的解决方案是基于一个“服务器”,它在接收到http请求时返回Docker主机的外部地址。

在“服务器”上:

1)启动jwilder/nginx-proxy

# docker run -d -p <external server port>:80 -v /var/run/docker.sock:/tmp/docker.sock:ro jwilder/nginx-proxy

2)启动ipify container

# docker run -e VIRTUAL_HOST=<external server name/address> --detach --name ipify osixia/ipify-api:0.1.0

现在,当容器向服务器发送http请求时,例如。

# curl http://<external server name/address>:<external server port>

Docker主机的IP地址由ipify通过http报头"X-Forwarded-For"返回

示例(ipify服务器名称为“ipify.example.com”,端口为80,docker主机IP为10.20.30.40):

# docker run -d -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock:ro jwilder/nginx-proxy
# docker run -e VIRTUAL_HOST=ipify.example.com --detach --name ipify osixia/ipify-api:0.1.0

在容器内部,你现在可以调用:

# curl http://ipify.example.com
10.20.30.40

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

细节

在Mac和Windows上,可以使用特殊的DNS名称host.docker.internal。

主机有一个不断变化的IP地址(如果没有网络访问,则没有)。从18.03开始,我们的建议是连接到特殊的DNS名称host.docker.internal,它将解析为主机使用的内部IP地址。这是为了开发目的,不能在Mac Docker Desktop以外的生产环境中工作。