前言


最近在项目中使用到DockerDubbo,想在Docker中运行一个服务并把该服务自身的信息发布到Dubbo注册中心。

刚开始测试时候将所有容器都放在同一台主机中,测试过程很顺利,但是当进行Docker主机扩展,将容器部署在不同的主机时候,就发现一个奇怪的现象:应用之间调试不通了。

这里对具体问题解释一下:Dubbo提供了一个方便的服务发现机制,每个服务(这里称为提供者)只要向Dubbo注册中心注册过,注册中心就会将服务的地址发送给同样在注册中心注册的服务调用方(这里称为消费者),之后即使Dubbo注册中心挂了也不影响服务的调用。

当服务提供者部署在容器中时,这时候发现其在Dubbo中心注册的是容器的IP地址,而对于另外一台主机上的消费者来说这个IP是不可访问的,当时我在网上找了很多相关的资料,最后得出几种解决方案。

解决方案


  1. 设置容器的IP与主机IP在同一网段内,使容器IP可直接访问(会占用大量的IP地址,且IP会限制在同一网段,在生产环境中往往不可能)。

  2. 通过复杂的iptables路由规则,通过多层桥接方式打通网络(此法是可行的,也是今后要考虑的,但是操作起来略麻烦)。

  3. 对Dubbo进行扩展,扩展dubbo protocol配置,增加配置项publish host、 publish port,对应主机的ip和port,并且在注册服务时将主机的ip和port写到注册中心。(这种方法需要对Dubbo进行扩展,不太建议)

 

以上三种方法都有一定的局限性和复杂性,我就想能否有更加简单便捷的方法可以解决这个问题。

最后查阅资料时发现Dubbo是先通过hostname(通过ping hostname)来得到本机的IP地址的,换句话说也就是dubbo是读取/etc/hosts文件得到主机的IP的,又因为Docker在重启时候IP地址会重置,即/etc/hosts文件在Docker重启时候会被重置,所以考虑在Docker启动的脚本中动态修改/etc/hosts文件,从而改变Docker主机的IP。

实施


在Docker启动时候将运行一个脚本文件(该脚本将通过另外一个脚本运行服务,将服务注册到Dubbo中心),在脚本文件中运行下面命令:

start.sh

1
2
3
4
5
#!/bin/sh
sed -i  "s/.*$(hostname)/$DOCKER_IP $(hostname)/" /etc/hosts
/data/app/bin/start.sh

在启动Docker时候将宿主机的IP作为一个环境变量传递给Docker主机,然后Docker主机中将运行上面的start.sh脚本,该脚本的作用是在启动服务之前,通过查找/etc/hosts文件把现有的Docker主机名对应的IP修改为宿主机的IP,从而使得注册到Dubbo中心的是宿主机的IP和端口。

一切都似乎没问题,然后在运行的时候却出现一个意想不到的错误:

sed: cannot rename /etc/sed8DbrGm: Device or resource busy

原来/etc/hosts文件是在宿主机挂载到Docker中,所以使用sed/awk等命令时候将导致设备或者资源忙的错误。

既然不能直接修改原来的文件,那么就变通一下,把原来的文件复制一份,通过修改复制后的文件,然后通过cat复制文件重定向到/etc/hosts去,问题得到解决!

新的脚本如下:

start.sh

1
2
3
4
5
6
7
8
#!/bin/sh
cp /etc/hosts /etc/hosts.temp
sed -i  "s/.*$(hostname)/$DOCKER_IP $(hostname)/" /etc/hosts.temp
cat /etc/hosts.temp > /etc/hosts
/data/app/bin/start.sh
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐