我使用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有时会得到错误的网络配置,但我不知道原因。
答案 0 :(得分:6)
InetAddress.getLocalHost()
的{{3}}说:
返回本地主机的地址。这是通过从系统中检索主机名,然后将该名称解析为InetAddress来实现的。
由于您没有采取任何措施使您的静态IP地址在容器内可解析,因此无效。
要在不使用主机名的情况下在Java中查找地址,您可以通过NetworkInterface.getNetworkInterfaces()枚举所有网络接口,然后遍历每个接口,检查每个地址以找到所需的地址。代码示例 documentation
另一种选择是在--add-host
命令上使用Docker的--hostname
和docker run
选项来放置所需地址的映射,然后getLocalHost()
应该作为你期待。
答案 1 :(得分:3)
在我的设计中,每个应用程序容器都应该有一个静态IP 地址,我可以使用hosts文件找到容器网络。
为什么不重新考虑您的原创设计?让我们从两个明显的选择开始:
Docker文档中描述了这种方法。
https://docs.docker.com/userguide/dockerlinks/
启动容器时,指定它与另一个容器相关联。这会导致将环境变量注入到包含协作容器的IP地址和端口号详细信息的链接容器中。
您的应用程序的配置将停止使用硬编码的IP地址,而是使用在运行时设置的软代码引用。
目前,docker容器链接仅限于一个主机,但我希望这个概念将继续演变为多主机实现。最糟糕的情况是,您可以在运行时将环境变量注入容器中。
这是大型分布式应用程序采用的常用方法。此类系统的示例实现将是:
有了这样的系统,您的后端服务组件(例如数据库)将在启动时注册自己,客户端进程将在运行时动态发现它们的位置。这种解耦操作形式非常适合Docker,并且非常好。