Java应用程序无法获取具有静态IP的Docker容器中的主机的IP地址

我使用OpenStack来pipe理我的应用程序。 现在我想将它们作为每个应用程序的容器转移到docker,因为docker更加轻便和高效。

问题几乎与networking相关的每件事都在运行时出错了。

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

这是我的实现。 (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手动设置networking

你现在可以通过启动一个容器

 sudo docker run --rm -it --name repl --dns=8.8.8.8 --net=none clojure bash 

并通过设置networking

 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地址。 首先是通过

 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地址RemoteServer.getClientHost();

那么可能会导致这个问题呢? 我记得java有时会得到错误的networkingconfiguration,但我不知道原因。

InetAddress.getLocalHost()的文档说:

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

由于您没有采取任何措施使您的静态IP地址在容器内可parsing,所以不起作用。

要通过Java查找地址,而不通过主机名,可以通过NetworkInterface.getNetworkInterfaces()枚举所有的networking接口,然后遍历每个接口,检查每个地址以find所需的地址。 使用Java获取当前机器的IP地址时的示例代码

另一个select是在--hostname docker run命令中使用Docker的--add-host--hostname选项来放置你想要的地址的映射,然后getLocalHost()应该像你期望的那样工作。

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

为什么不重新考虑你的原始devise? 我们从两个明显的select开始:

  1. 容器连接
  2. 添加一个服务发现组件

容器连接

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

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

当启动一个容器时,你指定它被链接到另一个。 这导致环境variables被注入包含协作容器的IP地址和端口号细节的链接容器。

应用程序的configuration将停止使用硬编码的IP地址,而是使用在运行时设置的软代码引用。

目前Docker容器链接仅限于单个主机,但我期望这个概念将继续演变成多主机实现。 最糟糕的情况是,您可以在运行时将环境variables注入到容器中。

服务发现

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

  • 动物园pipe理员
  • ETCD
  • 领事
  • ..

使用这样的系统后,您的后端服务组件(例如数据库)将在启动时自行注册,客户端进程将在运行时dynamic发现其位置。 这种分离操作forms非常适合Docker,并且非常好。