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

时间:2014-11-23 10:10:08

标签: docker

我使用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地址。首先是代表:

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有时会得到错误的网络配置,但我不知道原因。

2 个答案:

答案 0 :(得分:6)

InetAddress.getLocalHost()的{​​{3}}说:

  

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

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

要在不使用主机名的情况下在Java中查找地址,您可以通过NetworkInterface.getNetworkInterfaces()枚举所有网络接口,然后遍历每个接口,检查每个地址以找到所需的地址。代码示例 documentation

另一种选择是在--add-host命令上使用Docker的--hostnamedocker run选项来放置所需地址的映射,然后getLocalHost()应该作为你期待。

答案 1 :(得分:3)

  

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

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

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

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

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

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

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

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

    服务发现

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

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