【问题标题】:Java application cannot get IP address of the host in docker container with static IPJava 应用程序无法使用静态 IP 获取 docker 容器中主机的 IP 地址
【发布时间】:2014-11-23 10:10:07
【问题描述】:

我有一段时间使用 OpenStack 来管理我的应用程序。现在我想将它们作为每个应用程序的容器转移到 docker,因为 docker 更轻量级和高效。

问题是几乎所有与网络相关的事情在运行时都出错了。

在我的设计中,每个应用程序容器都应该有一个静态 IP 地址,我可以使用 hosts 文件来定位容器网络。

这是我的实现。 (bash 文件名是 docker_addnet.sh)

# Useages
# docker_addnet.sh container_name IP
# interface name: veth_(containername)
# gateway 172.17.42.1

if [ $# != 2 ]; then
    echo -e "ERROR! Wrong args"
    exit 1
fi

container_netmask=16
container_gw=172.17.42.1

container_name=$1
bridge_if=veth_`echo ${container_name} | cut -c 1-10`
container_ip=$2/${container_netmask}
container_id=`docker ps | grep $1 | awk '{print \$1}'`
pid=`docker inspect -f '{{.State.Pid}}' ${container_name}`

echo "Contaner: " $container_name "pid: " $pid

mkdir -p /var/run/netns
ln -s /proc/$pid/ns/net /var/run/netns/$pid

brctl delif docker0 $bridge_if

ip link add A type veth peer name B
ip link set A name $bridge_if
brctl addif docker0 $bridge_if
ip link set $bridge_if up
ip link set B netns $pid
ip netns exec $pid ip link set dev B name eth0
ip netns exec $pid ip link set eth0 up
ip netns exec $pid ip addr add $container_ip dev eth0
ip netns exec $pid ip route add default via $container_gw

该脚本用于设置容器的静态ip地址,然后运行容器,必须附加--net=none手动设置网络

您现在可以通过

启动容器
sudo docker run --rm -it --name repl --dns=8.8.8.8 --net=none clojure bash

并通过

设置网络
sudo zsh docker_addnet.sh repl 172.17.15.1

在容器bash中,你可以通过ip addr看到IP地址,输出类似于

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
67: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 2e:7b:7e:5a:b5:d6 brd ff:ff:ff:ff:ff:ff
    inet 172.17.15.1/16 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::2c7b:7eff:fe5a:b5d6/64 scope link 
       valid_lft forever preferred_lft forever

到目前为止一切顺利。

让我们尝试使用clojure repl获取容器主机ip地址。首先是repl:

lein repl

接下来评估下面的代码

(. java.net.InetAddress getLocalHost)

clojure 代码等于

System.out.println(Inet4Address.getLocalHost());

你得到的是一个例外

UnknownHostException 5a8efbf89c79:名称或服务未知 java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:-2)

其他奇怪的事情是 RMI 服务器无法通过RemoteServer.getClientHost(); 获取客户端 IP 地址。

那么可能导致此问题的原因是什么?我记得java有时会得到错误的网络配置,但我不知道原因。

【问题讨论】:

    标签: docker


    【解决方案1】:

    documentation 代表 InetAddress.getLocalHost() 说:

    返回本地主机的地址。这是通过从系统中检索主机的名称,然后将该名称解析为 InetAddress 来实现的。

    由于您没有采取任何措施使您的静态 IP 地址在容器内可解析,因此它不起作用。

    要在不通过主机名的情况下在 Java 中查找地址,您可以通过 NetworkInterface.getNetworkInterfaces() 枚举所有网络接口,然后遍历每个接口,检查每个地址以找到您想要的。示例代码在 Getting the IP address of the current machine using Java

    另一种选择是在docker run 命令上使用Docker 的--add-host--hostname 选项来为您想要的地址输入一个映射,然后getLocalHost() 应该可以正常工作。

    【讨论】:

      【解决方案2】:

      在我的设计中,每个应用程序容器都应该有一个静态 IP 地址,我可以使用hosts文件来定位容器网络。

      为什么不重新考虑您的原始设计?让我们从两个明显的选项开始:

      1. 容器链接
      2. 添加服务发现组件

      容器链接

      Docker 文档中描述了这种方法。

      https://docs.docker.com/userguide/dockerlinks/

      当启动一个容器时,您指定它链接到另一个容器。这会导致环境变量被注入到包含协作容器的 IP 地址和端口号详细信息的链接容器中。

      您的应用程序配置不再使用硬编码 IP 地址,而是使用在运行时设置的软代码引用。

      目前 docker 容器链接仅限于单个主机,但我预计这个概念将继续演变为多主机实现。在最坏的情况下,您可以在运行时将环境变量注入到容器中。

      服务发现

      这是大型分布式应用程序采用的常用方法。此类系统的示例实现是:

      有了这样的系统,您的后端服务组件(例如数据库)将在启动时注册自己,客户端进程将在运行时动态发现它们的位置。这种形式的解耦操作对 Docker 非常友好,并且可以很好地扩展。

      【讨论】:

      • 感谢您的提示。我正在尝试动物园管理员。
      猜你喜欢
      • 2014-05-21
      • 2013-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-06-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多