前言
Mesos被称作是“分布式系统的操作系统”。的确,Mesos封装了很多低层的东西,使得用户开发分布式应用变得简单了很多。但是,毕竟我们是在使用分布式系统,一些failure的情况(如网络连接中断和宕机)难免还是需要考虑的。
比如我们的framework与Mesos之间的连接可能会中断,甚至是framework会crash,在这种情况下,默认的,Mesos会un-register我们的framework,这会导致我们所有的task都会被强行停止。显然,这在很多情况下不是我们想要的结果。所以Mesos在很早的版本就包含了framework failover的功能,即只要在创建frameoworkInfo的时候,将failoverTimeout设置为一个大于零的数,Mesos就不会在framework连接中断之后立刻停止该framework的task,而是等待framework重新连接,直到failover timeout过了为止。这个功能使用起来还是蛮简单的,不用细说。这篇文章主要将我自己在工作中遇到的两个相关的问题记录下来,希望对其他人有所帮助。
问题
Framework ID 应该怎么设置
想要重新register我们的framework而又要让Mesos知道这个和之前中断的那个framework是同一个framework,那么显然我们要用跟之前同样的framework ID。所以,似乎我们只要永远用一个固定不变的framework ID就可以了。但实际上,这样会遇到一个大问题。
从Mesos 0.21版本开始,如果一个framework已经停止(即主动stop或是断开连接的时间超过了failover timeout),那么这个framework就不可以再使用相同的framework ID去register了。因此,我们不能用一个固定不变的ID,而是应该在第一次register的时候让Mesos为我们generate一个ID,然后将这个ID写在persistent storage上并在之后每次重新register的时候都使用这个ID。如果某次连接中断的时间超过了failover timeout,那么就放弃之前的ID,让Mesos再为我们generate一个新ID,并把它写到persistent storage上,以此反复。
根据具体情况的不同,我们可以考虑把framework ID写在不同的persistent storage上,比如local disk, network-attached storage或者是zookeeper。实际上,Mesos的Java API当中提供了一个叫ZooKeepeState的类,使得framework可以直接通过这个类将state写到zookeeper上面,很多著名应用的Mesos framework,如ElasticSearch,Cassandra,都是用这种方法来persist framework ID的。由于ElasticSearch和Cassandra的framework都是开源的,所以在这里也没必要再写一段sample code了,只附上相关链接:
https://github.com/apache/mesos/blob/master/src/java/src/org/apache/mesos/state/ZooKeeperState.java
好了,我们已经将framework ID写在zookeeper上了,那么每次我们framework重新register的时候就可以用这个framework ID了,只有当连接中断的时间超过了failover timeout的情况才让Mesos为我们generated一个新ID。那么问题来了,我们怎么知道framework中断的时间已经超过了failover timeout呢。实际在这个问题的处理上,目前仅靠Mesos提供的API还没有一个比较好的方法:当我们用存储在persistent storage的framework ID去register的时候,如果已经过了failover timeout,那么我们会收到Scheduler.error(...) callback,但是目前这个callback的argument中没有包含可靠的信息可以使我们确切地知道这个error是由failover timeout过了导致的(见MESOS-2522)。所以目前最好的办法只能是通过处理Scheduler.error(..) argument中的error message来判断是否failover timeout过了,比如检查error message中是否包含"Framework removed"字样,如果是,则清除persistent storage上存储的ID,然后在创建frameworkInfo的时候将frameworkId设为一个空的字符串,即让Mesos为我们generate一个新ID。
Failover timeout过了之后framework没有被终止
在framework的连接中断之后,如果该framework设置了failoverTimeout,那么这个framework还会继续显示在Mesos Web UI的Active Frameworks部分,它的tasks也会继续运行。但是在failover timeout过了以后,正常来讲,该framework就会显示在Terminated Frameworks部分,它的tasks也会被强制停止。但是我在工作中发现我的framework程序停止的时间过了failover timeout之后它仍显示在Active Frameworks部分,而且它的tasks也没被强制停止。
原来这是因为Mesos有一个bug,就是在调用了SchedulerDriver.stop(...)之后不会中断framework与zookeeper之间的连接(见MESOS-1634)。而恰好我的framework是一个OSGi框架下的一个插件,所以在我的framework停止了之后,整个JVM并没有停止,所以如果底层没有explicitly去中断连接的话,那么与zookeeper的连接不会中断,所以就出现了上面说到的现象。
Mesos的这个bug对于24x7运行的framework来说并没有太大问题,因为这个bug相当于使得failover timeout变长了,这对于24x7运行的framework来说可能反而更好。而对于其他的framework来说,这导致其停止之后仍然在占用资源,显然是不好的。其中最常引发的问题是framework在多次重复开始和停止之后,zookeeper的连接数被用尽,这是一个需要注意的问题。