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开始:
- 容器连接
- 添加一个服务发现组件
容器连接
Docker文档中描述了这种方法。
https://docs.docker.com/userguide/dockerlinks/
当启动一个容器时,你指定它被链接到另一个。 这导致环境variables被注入包含协作容器的IP地址和端口号细节的链接容器。
应用程序的configuration将停止使用硬编码的IP地址,而是使用在运行时设置的软代码引用。
目前Docker容器链接仅限于单个主机,但我期望这个概念将继续演变成多主机实现。 最糟糕的情况是,您可以在运行时将环境variables注入到容器中。
服务发现
这是大型分布式应用程序采取的常用方法。 这样的系统的示例实现将是:
- 动物园pipe理员
- ETCD
- 领事
- ..
使用这样的系统后,您的后端服务组件(例如数据库)将在启动时自行注册,客户端进程将在运行时dynamic发现其位置。 这种分离操作forms非常适合Docker,并且非常好。