原文地址
http://mesos.apache.org/documentation/latest/high-availability-framework-guide/
阅读建议:有写过或者看过Mesos Framework的开发者,或者对Mesos有较多了解的使用者,文章的部分其实包含了分布式系统设计的一些通用的思路。对于分布式系统感兴趣的人也适合本文。
我基于本文改进了自己的框架的可用性,非常有效。另外Mesos支持restful接口,基于Restful接口,我们可以采用更加稳定的、开源的一些服务来代替自己设计的框架,比如使用PHP服务器来管理。
一些翻译或未翻译名词
- Mesos master 或者 master Mesos的主节点,一般端口为5050,作用是协调资源分配
- Scheduler 调度器,获取资源并产生实际的任务
- task 任务
agent或者客户端 其实文章中多数情况下指的就是Mesos-Slave
一些读起来有点别扭的词:
- 数据存储:指的是文件或者mysql或者zookeeper等可以保持数据的东西。
网络隔离:或者网络割接描述一种网络不可达或者不可用的情况。
翻译的不专业,敬请谅解。。。会逐步改进。
正文
Mesos框架是用来管理任务的。为了让框架有高的可用性,它必须能够在多种失败的场景下继续管理任务。作为一个框架的开发者你需要关注的常见情况如下:
- Mesos 的master节点可能失效。比如因为失去网络连接而崩溃。如果master使用了高可用模式,这会导致另一个Mesos master副本成为当前的主节点。在这种情况下,scheduler 会重新注册到新的主节点并确保任务状态正常。
- 框架运行的主机可能失效。为了确保框架依旧可用并且可以继续管理新的任务,框架开发者需要确保有多个Scheduler副本在不同的节点中运行。并且在原先主节点失效后,让副本节点成为新的主节点。Mesos本身并不会告诉框架开发者如何处理这个情况,我们会在下面提供一些建议。它们将会在运行像 Apache Aurora或Marathon这样长时间任务的时候非常有用。
- 运行任务的的节点可能失效。另外可能节点本身没有失效,但是Mesos agent 可能无法和主节点通信等等。
请注意,这些问题有可能同时发生。
Mesos 架构
在讨论以上失效场景之前,有必要强调一下Mesos 是如何设计并影响高可用性的:
Mesos在组件之间提供了一种不可靠的消息:消息被最多递送一次(可能被丢掉)。框架设计者应当考虑到发送的消息可能没有被收到并且准备好适当的矫正工作。为了定位这个问题,框架的Scheduler 应该设计一个超时时间用于判断新的task是否被生成。如果在超时后 Scheduler没有看到新任务的状态更新,它应该开始矫正。比如,调节任务状态(为失效),然后如果有必要,重新启动一个任务。
一般来说,分发系统无法区分消息是“丢失”还是“延时”。在上面的例子中,Scheduler可能在超时后收到状态更新信息,并且已经开始进行校正工作了。所以框架设计者应该考虑到这种情况发生的而可能性并且为此在程序上做好适配。
Mesos事实上在进程之间提供一种有序的的(但是不可靠)的消息递送:比如如果框架发送M1 和 M2给master,那么框架将收到:没有任何消息,只有M1 或 M2,M1 然后 M2 ,但是一定不会收到M2 然后 M1。
为了方便框架开发者,Mesos提供了可靠的任务状态更新机制(谢天谢地)。客户端会将状态更新持久化到磁盘上然后发送到master。master将状态发送到合适的Framework Scheduler。当一个Scheduler承认状态更新,master会将这种承认发还给客户端,这样允许保存状态更新的存储进行垃圾回收。如果客户端在一定时间内没有收到状态更新的确认,它会重复发送状态更新给master(这里要小心),同样也会再发给Scheduler。所以,假设客户端和Scheduler都是可用的情况下,任务的状态更新是“至少”发送一次。要想应付任务状态可能发送超过一次的情况,将Framework Scheduler的处理逻辑设计成幂等(每次结果都一样)是非常有用的。
Mesos master 将注册的框架和活动的任务的信息都保存在内存里:并没有持久化到磁盘上或者试图在master失效后依旧保持这些数据。这帮助Mesos master 通过许多的任务和框架,扩容为大型的集群。这个设计的一个负面影响就是在失效后,需要更多的工作来恢复在内存中丢失的数据。
如果Mesos master 不可用(宕机或者断网什么的),集群将继续运作:当前的Mesos 客户端和用户的任务将继续运行。但是,新的任务无法被调度,framework将不会收到资源的offers或者来自于之前启动过的任务的状态更新信息。
Mesos 并不规定framework应该如何实现,也不为框架如何处理失败情况承担任何责任。取而代之,Mesos 尽力提供框架的开发者他们需要的工具去实现这些特性。不同的框架可能在处理失效的问题上采用不同的方案,这取决于他们的实际情况。
高可用框架的一些建议:
一个高可用的框架可以按照下面的一些通用方案来设计:
为了容忍Scheduler失效,框架应该同时运行多个Scheduler 实例(通常是3个)。在任何时候,只有一个实例是他们的头头:这个实例和Mesos master连接着。接受资源分配和任务状态更新,启动新的任务。其他的Scheduler只是随从:他们只在头头挂掉的时候被使用,在这种情况下,其中之一被选为新的头头。
Scheduler们需要有个机制来判断当前的头头挂了并且选举一个新的头头。常见的是使用一些协调服务比如zookeeper。查看你使用的协调服务器的文档来获取更多的关于如何实现头头选举的功能。
当一个Scheduler当选为新的头头,新头头要重新和Mesos master连接。当注册到master的时候,框架应该和之前失效的框架使用同一个ID。这确保master会认识到这个连接并不是一个新的会话,而是继续之前的失效Scheduler的会话。
注意:当旧的scheduler 头头失效的时候,默认情况下master会马上杀掉所有的和framework有关的任务和Executors。一个典型的生产环境框架,这个默认行为是不可容忍的。要避免这个情况,高可用的框架应该设置failover_timeout字段一个非常大的值。为了避免在生产环境中以外破坏任务,许多框架将failover_timeout设置为1个星期甚至更多。
在重新连接Mesos master之后,新的头头应该确保本地环境状态和整个集群保持一致。比如,假设之前的头头正好要启动一个新的任务,然后挂了。这个任务应该能够继续启动成功,并且在这里新选举的头头将会收到这个任务状态更新。为了应对这个情况,framework一般使用一种有健壮的一致性的分布式数据存储来记录这些关于活动或暂停的任务信息。事实上,用于头头选举的工具zookeeper也可以用于这个目的。一些Mesos框架使用Mesos 日志副本来达到这个目的。
- 数据存储应该在执行动作之前,用于记录Scheduler将要进行的动作。比如,如果一个Scheduler决定创建一个新的task,首先将这个意图写入到数据存储中。然后发送launch task 给Mesos master。如果这时Scheduler失效了,而新的scheduler启动,新的头头可以查看到数据存储中所有可能在集群中的业务。这是一个前置写入模型,利用数据库或者文件系统来提升可靠性。这个设计有2个部分需要强调。
Scheduler必须在执行任务前将“意图”落地:否则可能失去了写入的机会(其余不翻译,都是这个情况会有多糟糕)。
Scheduler必须确保“意图”能够确实记录到数据存储中,在继续启动任务之前(一些分布式数据库比如Cassandra 可能在写入一个备份就返回成功了,其他节点可能还没有写入)
Task的生命周期
- 一个Mesos任务要经历一系列的状态。只有任务所在的终端才知道任务真正的状态是什么。一个Framework Scheduler通过和Mesos master 通信来了解任务的状态,确切的说,是监听任务状态更新和performing task state reconciliation.
- Framework 可以通过状态机描述一个状态,一个初始状态和许多种可能的终止状态。
- 一个任务始于TASK_STAGING 状态。一个任务在这个状态时master收到Framework的关于启动这个任务的状态,但是这任务还没有开始跑。在这个状态中,任务的依赖已经获取了-for example, using the Mesos fetcher cache.
TASK_STARTING是可选的,是自定义Executors的首要选择。它用于描述自定义Executor已经知道了任务(可能已经在读取资源了)但是还没有跑起来。
一个任务在确实运行成功后,发送TASK_RUNNING状态(如果失败了,需要发送一些失败的状态,后面会列出)
当一个framework企图要启动一个任务但是在超时后并没有收到状态更新时,framework需要进行适配。它需要主动询问Master这个task现在的状态。针对未知的任务master会回复TASK_LOST。framework可以用这个区分tasks是启动慢了还是master没有递送启动请求(比如请求被丢弃了)
注意这个技术的正确性是基于Scheduler和master直接的消息通信是有序的。
- TASK_KILLING 状态是可选的,它试图指示杀掉这个进程的请求已经被Executor接到了。但是任务还没有被杀掉。这对于优雅的结束进程非常有用。Executor不要发送这种状态,除非框架能够兼容。
成功只有一个,失败的方式却有很多。。。
TASK_FINISHED 任务正常结束或者关闭
TASK_FAILED 任务因为错误被关闭
TASK_KILLED Executor杀掉了任务
TASK_LOST 暗示任务有在跑,但是master监控不到,未来会废弃
TASK_ERROR 启动的时候就挂了
对付分离的或者失效的客户端
Mesos master使用2种不同的机制追踪客户端的可用情况和健康情况
- master 和 agent(客户端)的TCP连接状态
- 监控健康是周期性ping 客户端。master发送“ping” 消息给客户端,并且期待在超时之前有一个“pong”的反馈给master。如果响应确切数量的ping消息,客户会被认为是失效的。这个行为可以由 --slave_ping_timeout 和 --max_slave_ping_timeouts 的master选项控制。
如果和客户端的TCP的长连接断开或者健康检查失败(注意是或)。master就认为节点挂了,并且任务呗移除除了集群。
特别的:
如果tcp连接断开,agent被认为无法连接。The semantics when a registered agent gets disconnected are as follows for each framework running on that agent:
如果tcp断开,客户端断开
如果framework在检查点:没有马上要执行的操作。客户端有机会再超时之前再次连上。
如果framework不在检查点:所有的framework的任务和Executors都被认为丢失了。master会马上为任务发送一个TASK_LOST状态。这个更新将不会可靠的递送给Scheduler(说好状态是可靠的。。。注意下面的提示)。客户有一个机会在健康检查超时前重新连接。如果agent重连,任何发出的TASK_LOST状态更新消息会被kill(原来如此)。
这种行为模式的根本原因是,使用典型的TCP设置,master和angent 之间TCP持久连接的错误更倾向于客户错误(比如slave进程被中断)而不是网络隔离,因为Mesos的健康检查超时比典型的TCP-level的超时要来的小的多。因为不在检查点的framework将不会 survive a restart of the mesos-slave process,master发送TASK_LOST状态更新这样这些任务可以被重新调度。Of course, the heuristic that TCP errors do not correspond to network partitions may not be true in some environments.
如果一个agent在健康检查(health checks)中失败了,它会被计划移除。移除的速度可以有master控制(--slave_removal_rate_limit master 配置)以避免移除一次性移除一系列的子节点(比如在网络切割的时候)。
当移除一个客户端,master将会给客户端打个removed的标记,并记录在持久化状态中(master失效后也在)。master发送slavelost callback 给每一个已注册的scheduler driver;也会发送每一个在这个客户端运行的任务的TASK_LOST 状态更新
注意:master发送的无论是callback还是状态更新,都不是可靠的。比如,master或者Scheduler挂了或者网络连接在发送的时候异常,他们不会重发!
同时,任何一个移除的客户端的任务会继续跑,而且客户端会尝试重新连接master。如果移除的客户端能够重连到master(比如网络连接又恢复了,shit),重新注册的请求会被拒绝,而且客户端会被要求关闭。客户端会关闭所有的正在跑的任务和executor,移除的客户端上面的持久化volumes 和 dynamic reservations 将被保护(不理解)。
一个移除的客户端在启动一个新的mesos-slave进程后可以重新加入集群。当移除的客户端被master关闭时,Mesos确保下一次mesos-slave 启动的时候(使用同一个工作目录和同一个host),客户端会得到一个新的agent ID;结果就是,客户端会被当做一个全新的客户端加入集群。agent将会保留之前创建的持久化volumes 和 dynamic reservations,虽然链接这些资源的angent ID已经变了。典型情况下,框架在应对失效或者隔离的客户端的时候会调度失效客户端上的任务到其他地方。这需要非常小心,尽管失效的客户端可能还活着,但是和master隔离并且无法通讯。依赖自然的网络隔离,客户上的任务可能还是可以和其他客户端或者集群节点通讯。Framework可以做些事情来避免这个情况(比如,使用zookeeper来停止agent工作,如果他们的zk连接还是正常的),但是Mesos将这些细节留给了框架开发者。
处理隔离的或者失效的Master
上面描述的行为在一个新的Mesos master刚被选举完的一段时间中是不会被执行的。上面会注意到,多数的Mesos master状态仅保存在内存中。同时,当当前master失效,新的master 选举后,新的master只有当前集群的极少数信息。取而代之,他重建他的信息通过通知 framework 和 客户端新master被选举,并重新注册到它上面。
框架重新注册
当一个master失效发生,连接在之前的master的framework需要连接到新的master上。MesosSchedulerDriver 持有多数细节of决定什么时候先前的master失效,什么时候连接到新的master上;当framework成功注册到新的master上, “reregistered scheduler driver callback”(一个回调函数) 将被调用。
客户端重新注册
当一个新的master被选举后的一个时期内,在给定的客户端重新注册之前或者slave_reregister_timeout被触发,尝试调试在这个客户端上面的任务状态将不会反回任何信息(因为master还不能确切知道任务状态)
如果一个客户端在一定时间 (controlled by the --slave_reregister_timeout configuration flag),内没有重新注册到新的master上,master会标记这个agent为失效并且和上面说的失效流程处理相同。但是,有一个不一样,默认情况下,客户端被允许重连到失效的master节点,即使在slave_reregister_timeout被触发后。也就是说,framework可能看到TASK_LOST 的任务状态更新,但是过一会发现任务其实在跑(以为这个客户端是允许重连的)。这个行为可以避免,通过允许--registry_strict 这个选项。这个选项在将来的Mesos版本中,会变成默认值。