High Availability启示录

最近对HA(High Availability,以下简称HA)有些感想,在此以一个理工男的视角将其记录下来,希望能以飨读者。

很多年以前,HA是个高大上的东西,只有一些大公司或关键业务才会使用。随着技术的演进,HA已变成了现代网络服务的必备。

相信很多人一定了解HA的基本原理,但似乎也讲不出道道来,那就可以继续读下去。如果看官你对HA已经很精通了,那可直接跳到后记看启示录。

在探讨HA之前需要搞清下面几个概念或术语。

术语

RAS

RAS是三个词的缩写,即:

  • 可靠性(Reliability)

  • 可用性(Availability)

  • 可服务性(Serviceability/Maintainability)

RAS起源于IBM对其主机质量的定义。详细定义可以参考wiki词条:http://en.wikipedia.org/wiki/Reliability,_availability_and_serviceability_(computing))。

可靠性用来定义指定时间内系统可靠程度,用MTBF(即平均故障间隔时间)来度量,MTBF表示平均无故障间隔时间,是指两次故障修复之间的平均时间。

介绍可用性之前先看几个相关度量:

MTTF, MTTR

MTTF(mean-time-to-failure) 为平均无故障时间,即系统正常运行的时间。

MTTR(mean-time-to-repair) 为平均故障恢复时间,即系统修复故障的平均时间。

MTBF = MTTF + MTTR,对不能修复的故障用MTTF,对可修复的故障用MTBF,由于MTTR都远小于MTTF,所以MTBF约等于MTTF。

可用性定义为: MTTF/(MTTF+MTTR)

可用性用来描述系统在指定时间内可以正常工作的时间比例,通常用几个9来表示,如下图所示:

一般来说,HA就是指5个9,要做到6个9及以上的要求,所花费的代价会大幅增长。

可服务性用来描述系统出问题时被修复的速度。由于这个时间会影响可用性,所以越快修复则系统可用性越大。

可靠性和可用性在字面上理解感觉很相似,但是有区别的,可靠性一般与安全有关,可用性一般与用户体验有关。系统或许是可用的,但并不一定是可靠的。举例来说,一个系统可能从不宕机,但运行时数据经常发生损坏。

不同系统对二者要求是不同的,例如对那些航空航天产品,可靠性要求就很高,可用性肯定次之,相反对于手机等民用产品可用性要求就比可靠性高些,飞机不可靠的话,道理你懂的。可靠性的增加会显著增加费用,如果手机做成象飞机那么可靠,费用就不是一般高了, 用户肯定也接受不了,再说也没有必要,大不了换个手机,而手机可用性不好会更影响用户情绪。

现在RAS概念被用来评估一个系统,或一个软件层面,单纯强调一个特性没有意义。一个实际的系统应该根据应用场景在RAS和成本方面进行折中。

HA

HA即 High Availability,顾名思义是高可用性。相应词条可见wiki: http://en.wikipedia.org/wiki/High_availability

HA是一个系统层面的概念,不是指单个节点。

HA只是一个指标,即达到几个9以上才能称为高可用性,并没有规定具体系统的实现模板,细节等,但我这篇文章不只是谈HA这个指标,而是从概念,层次等各个方面来阐述HA系统。

这里高可用性中的“高”是个相对概念,既然有高,那也应该有低可用性,很高可用性,极高可用性,有专家将这些等级做了个划分,如下图所示:

需要指出的是,目前HA一般指5个9,但这里的级别也是随着技术进步,软/硬件稳定性提高不断提高的,在未来或许5个9以上都是标配了。

既然有HA,那肯定也有HR(High Reliability),即高可靠性,在涉及安全的领域也有很多相关标准,我这里不打算详细介绍(主要是没深入研究),只提几点:

  1. 串行部件组成的系统的可靠性是由可靠性最低的部件决定的。

  2. 并行部件组成的系统的可靠性是所有部件的可靠性乘积。

  3. 系统的可靠性是可以通过子系统的冗余来提高。

  4. 软件的可靠性是可以通过加强测试来提高。

  5. 硬件的可靠性的提高会显著提高成本。

在现实生活中,HA系统也是比比皆是,比如有4个引擎发动机的大型飞机,就是一个HA系统,一个发动机坏了也不至于飞机失去动力而坠毁。当前各种分布式计算架构,存储架构也都是HA系统,包括我们人自身更是一个复杂的HA系统,单个细胞可以随便死去,我们人体可以生成新细胞来替换老细胞。

所以HA系统的主要思想是通过对不可靠的单节点冗余来换取整体系统的稳定可用。

世界本是虚实之间,虚的东西比如理想,比如精神都是很完美的,无故障,无错误的,实的东西就刚好相反,不完美,不可靠,有很多问题。针对于HA的节点来说也是一样不完美,不可靠,HA的意义就在于通过运行一套拥有冗余节点的系统,来对抗单个节点的不可靠,对外提供一个看起来比较完美的虚的接口,从而来提高整个系统的可靠性,可用性和可服务性(或叫可维护性)。

不要说你发明了HA系统,这些都是大自然给我们的提示,也不要说你发现了HA系统,你有没有发现,它都在那里,不增不减。我们所做的都是在模仿大自然杰作而已。

HA三原则

  • 消除单节点故障

  • 互联可靠

  • 故障检测

一个HA系统必须满足上面三个基本原则,否则就不可能被称为一个HA系统。

第一条,消除单节点故障是指,系统必须提供节点冗余,这也意味做单节点不可能是HA系统。

第二条,互联可靠是指,节点之间,节点和外界之间的连接必须是可靠的,这些连接本身不能存在单点故障。

第三条,故障检测是指,系统必须能够检测到节点的故障,从而做出必要处理。

Fault Tolerance

Fault Tolerance系统是指系统在处理故障方面的能力程度。从系统层面来说,HA系统就是一个Fault Tolerance系统,但Fault Tolerance系统则不一定是一个HA系统,因为它更强调的是系统对故障的预估及处理能力。比如一个正在运行的进程,正常运行没有问题,但可能会遇到很多意外事件,比如磁盘满,网络断,用户输入buffer越界等,如果没有预估到这些事件,则可能会导致故障,那么这个程序就不是一个Fault Folerance很好的程序。

另外Fault Tolerance系统早期常多用于硬件设计,我们这篇文章主要是探讨现代网络服务系统多着眼于系统。

Fault Tolerance字面可译作故障容忍,可以说其HA系统成败之关键,鉴于其重要性,早在1985年,IEEE的IFIP专家组就对Fault Tolerance做了深入研究,这些专家们就“Dependable Computing and Fault Tolerance”提出了一系列概念,这些概念不断演进和扩充,如今便有了下面这些所谓概念框架:

这些专家将Fault Tolerance概念整体纳入可信赖计算(Dependable Computing)范畴,并从三个角度来定义/度量可信赖计算,这三个角度分别是:

  1. 可信赖计算面临的威胁

这些威胁可进一步定义为:缺陷(fault),错误(error),故障(failure,我觉得翻译成故障比失败更贴近中文一点)

这些概念就是这样一环套一环的: 缺陷 –> 错误 –> 故障

这些东东放到下面这个图中就比较容易理解了:

图中可以看出,当一个缺陷导致了错误,这个错误又导致故障发生,从而中断了正常的服务状态,而这个故障经过了一个延时后又被检测到了,并被修复了,最后服务又回到正常状态。

  • 缺陷比较好理解,就是模块,部件,人员等本身的问题,或由这些导致的问题,例如硬件缺陷,设计缺陷,人工操作,环境问题。

  • 错误难理解一点,错误被定义为上述缺陷导致的结果,是系统的一个状态。比如软件中有个野指针,这是一个缺陷,如果用这个野指针访问了一个内存便会导致一个错误。

  • 故障被定义为错误导致的结果,接上面例子,如果有个野指针访问了某个内存,而又没有通过异常处理机制捕获,则程序会出错导致非正常退出,这个错误最后造成一个软件服务故障。当然如果你能在其他模块中检测到这个故障,并且能够修理就是一个基本的Fault Tolerance系统了。

构建可信赖计算的方法

故障可以通过两种方法来消除,这两种方法分别是: 构建(Contruction,之前也被称为Procurement), 验证(Validation)。这两种方法也分别对应于模块/组件的两个阶段:

  • 构建对应于模块/组件的构建阶段,在这个阶段又可以通过两种方法来处理故障,即故障避免(Fault avoidance)和故障容忍(Fault tolerance)。前者比较好理解,就是在软/硬件实现阶段将各种故障原因都考虑进去,并进行规避。后者则涉及到软/硬件的兼容性问题,比如后续版本对以前版本的兼容就可以避免此类故障。

  • 验证对应于模块/组件的运行阶段,在这个阶段可以做的事件有两种,第一种是故障消除(Fault Removal),即出了问题后怎样想办法消除它,基本上属于事后补救。第二种是故障预测(Fault forecasting),即将故障扼杀在摇篮中,基本上属于事前预防。

可信赖计算的的属性

IFIP专家最早只提到两个属性,即Availability和Reliability,但到现在为止已经扩充到6种属性,即:

可以看出,这些属性已经覆盖了之前提的RAS,这些属性都有相应公式进行度量,我在此就不展开了,有兴趣的可以谷哥或度娘。搞懂后就可以出去Zhuangbility了。

  • Availability

  • Reliability

  • Safety

  • Confidentiality

  • Integrity

  • Maintainability

以上提到概念的只是可信赖计算概念的一小部分,也是很基础的一部分,里面还有很多门道,这里抛个砖,有兴趣的可以进一步学习,抛块玉给我。

解释了这么多Fault Tolerance,可以看出专家与普通人的区别了吧:能对我们都知道的东西归个类,下个定义,用公式描述一下就是专家,否则就是路人甲。呵呵,只是逗比一下,专家也是真心不那么容易的,想象一下著名的CAP理论不也是在三个词之间玩文字游戏么?你心里就会说,why didn’t I think of that? 就是啊,why? 因为你还没有打通任督二脉,还不是专家嘛。

Failover

Failover字面意思是失效转移,这是HA的一项简单的容错功能,在早期简单的HA系统一般是AS(A: Active,S: Slave)双节点结构,即一主一备,两个节点之间通过一个串口或网线作为心跳线互相连接,平时只有Active节点工作,心跳断了后,主节点不管正不正常,都自动关闭,备份节点自动接管,并且升级为主节点,从而保持系统正常运行。当失效节点修好重新集成进来后,仍然保持为从节点。这样的切换过程被称为Failover。

当然也有AA模式的双节点结构,这种情况下两个节点都可以同时工作,并且还可以做load Balance,但一般都用在轻量级状态同步,Failover系统主要是以容错功能为主。

LB

LB是Load Balance,即负载均衡。这个其实HA系统的一个性能方面扩充,有些系统没有LB功能,但仍然可以做到5个9。LB的作用主要是提高系统的性能,LB本身也是一个值得大费笔墨的东西,这里先不展开,后面会阐述各种HA + LB的模式。

HA系统组成

前面说了,HA系统种类很多,比如硬件类,像处理器HA,物理网络镜像,RAID0,cluster computing等等,实现形式也各不相同,这里我只讨论网络服务的HA系统(后面所说的HA系统都是指网络服务HA系统),并总结一下其基本组件及各种实现模式。

现代HA系统几乎都有LB功能,一个重要原因是单个模块的可靠性相比以前有很大提高,如果仅做为备份则是很大的浪费。

为了更好的描述HA系统,我画了一个2节点的HA网络服务架构图:

图中可以看出,每个节点都有通信模块,控制模块,状态管理模块。而每个节点的同样模块构成了HA系统的一个层,按照功能,HA系统可以简单的分为四个层:

  • 节点层

  • 网络层

  • 控制层

  • 业务层

节点层是一个独立的节点,可以是物理节点,比如一个PC,也可以是逻辑节点,比如一个进程。但从HA实施来说,最好是物理上独立的节点,这样不会产生依赖,从而导致单点故障。

网络层主要实现对外的虚接口,比如VIP。同时完成节点之间通信。很多现代的HA网络服务不单会提供HA服务,同时也兼顾性能,一般是HA + LB的混合体,即在HA基础之上会提供LB功能,LB功能一般是在HA组件的网络层提供,在实现方式上可能会有一个单独模块来进行流量调度,也可能是由节点之间互相协调流量调度,后面会对这部分详细介绍。

控制层主要实现节点状态探测,失效接管,集成等功能,即Fault Tolerance。

业务层主要是维护业务方面的状态一致性,业务状态是指节点上运行的服务本身的状态,这部份状态只跟这个节点上运行的服务相关,跟其他节点没有关系,即具有局部性,就像是个局部变量一样。业务层需要维护所有节点的业务状态的一致性,以便当某节点失效时,其他节点能从这个失效时的状态开始顺利的接管业务。这个如果节点没有业务状态,则这层可以不用实现。

HA分层

上面提到HA系统一般会分为四层,但根据实际情况,有些层可能不需要或者会大幅简化,比如,如果是无状态系统,则业务层基本上就只需要关注自身业务就可以了。

同时每个层的部署位置也可能不尽相同,比如,有些系统的业务层中的状态可能会下沉到一个独立的系统里,而不是分布在每个节点上,例如一些数据库系统可能将数据库放在SAN网络上,这样每个节点就不需要业务状态维护了。

还有些系统需要更好的性能,可能会单独设立一个流量调度单元来完成。再比如,有些系统的控制层会将大部分逻辑放到另外一个子HA系统进行处理,而各节点的控制层作为客户端来与这个子HA系统进行交互。

节点层

节点是HA服务的容器,HA服务对外是虚拟的,但最终会在某个节点上落地。节点必须为运行该服务提供合适的环境。

节点之间是物理独立的,独立的目的就是要提供物理层面的冗余。上面说过了,节点也可以虚拟节点,比如在同一物理机上运行多个VM,然后在这些VM中运行服务,那么这些VM就是虚拟节点。但由于这些VM共同运行在一个物理机上,物理机down掉后所有这些VM节点会一起down,这样就违背了HA的第一条原则,即节点之间不存在单点故障。所以如果虚拟节点之间存在共同依赖关系时,这样就不可能成为一个完备的HA系统。但如果这些VM都运行在不同的物理机上时,便没有依赖关系,这样的HA系统便是完备的。

此外节点之间的网络互联也是必须要考虑的,这里的互联是指物理层,包括逻辑层的互联。我将其划归节点层的目的是,这层是提供物理上的冗余,单个节点的冗余还不够,网络层面也必须有多路径冗余。

网络层

这里网络层是指软件层面的网络服务。网络层主要有如下责任:

  • 实现虚接口

  • 节点通讯

  • 流量调度

虚接口

虚接口是HA系统非常重要的组成部分,虚接口主要作用是向外部提供一个访问接口,从而将内部实现与外部环境分隔开来,避免暴露内部实现细节,这也是软件设计的一个基本原则。其实现方式可以直接决定HA系统可用性。

从概念上讲,接口一般是数据流集中汇聚之地,如果为HA设计一个集中的接口来转发外部请求,对软件设计来说是很方便的,貌似也可以。但别忘了,这种设计与HA系统基本原则相矛盾,如果这个接口挂掉了,那么整个HA系统就崩溃了,所以这种设计就不能称为HA系统,即使后端的处理节点可能是物理独立的。所以这里之所以称其为虚接口,就是因为对外部来说,用户访问的逻辑形式是集中的,但在内部实现时却是分散的,即最后实现时会在某个具体节点上落地。这样的虚接口才能满足HA系统要求。

常见的虚接口形式有VIP(virtual ip), DNS, RPC,虚拟网络文件系统等。具体采用哪种形式应根据业务需求。现实实现时,由于有些功能是所有节点共有的,所以有时也会将这个虚接口单独作为一层来处理,此时,这层本身也可以是一个HA系统,所有外部请求将由这层来转发到实际处理的节点。例如lvs,分布式文件系统的meta节点等。

VIP

VIP是一种很常见的虚接口,在针对ip服务时几乎是标配,实现方式也有多种:

  • 基于VRRP协议 VRRP协议通过为一个IP设置一个或几个备份下一跳路由的方法来构建VIP,当某个拥有VIP节点失效后,备份节点被接管,VIP被添加,下一跳路由会替换,从而新节点可以通过VIP进行通信。使用VRRP实现的VIP每次只会在一个节点落地,这就限制了其作为LB的功能,即如果业务是跟IP绑定的,则该业务同一时刻只能在一个节点处理,无法在其他不拥有这个IP的其他节点之间进行负载均衡。但VRRP比较简单可靠,同时实现了HA系统的节点探测,接管,迁移等事件,在HA系统要求不高的情况下,简化了HA系统的设计。

  • 基于OSPF协议 基于OSPF协议的VIP需要在每个节点上预先设置好同样的VIP,并且在每个节点上运行quagga的ospfd进程,其将邻居关系报告给ospf三层交换机,同时可以在三层交换机上设置到这些节点ospf cost值,如果这些节点的cost值相同,则三层交换将会对所有发达这些VIP的包进行负载均衡,否则只发到cost最低的节点。ospf三层交换机通过hello包来探测与之相连的节点,如果在所设的dead时间内,没有回应则会断掉路由,将流量切换到另一个备用节点。这种方案的优点是如果服务是无状态的,则做负载均衡很方便,但做HA系统的话,仍然需要各个节点自己来探测节点状态做故障容忍,否则其他节点无法知道这个节点失效信息。还要注意的是,由于ospf的节点失效消息跟HA系统自己的故障探测可能会有时差,并且接管节点也可能不一致,这样会导致这这种情况的发生: 1) ospf路由器已经将失效节点的流量信息切换到有效节点,但HA的故障探测并没有将该有效节点作为takeover节点,这就会导致严重问题。所以必须要求ospf的takeover 节点和HA的takeover节点必须一致。 2) ospf路由器先将流量切换到了有效的takeover节点,但由于HA系统的故障探测有延迟,HA系统还没有检测到故障节点,这样实际的HA系统的takeover节点还没有准备好处理新切换过来的流量,从而导致出错。所以为了处理这个问题,需要HA系统的takeover节点能够容忍这个时差。

  • 基于clusterip clusterip是通过iptables的模块实现的。这个方案通过为每个节点设置同一个clusterip作为VIP,并且在每个节点上为这个VIP设置同样的MAC地址(所有节点回包以这个MAC作为源地址),同时为每个节点设置一个节点号(node_number),发到这个VIP的包所有节点都能收到,每个节点根据算法:hash(sourceip)%node_number来判断是否是自己的包,是就处理,否则丢弃。这个方案的优点是简单,无需更改路由。缺点是仍然要自己维护HA节点状态,同时由于每个包都同时发到所有节点,对系统整体性能有很大影响。

  • 基于LVS 这个想必阿里人都很熟,优点多多不再费口舌,我要说的是虽然其LB功能强,但其Director服务器本身就是单点故障,所以要做HA系统的话,首先要对Director进行HA改造。

DNS

DNS是另一种基于名字解析的虚接口,应用非常广泛,几乎所有大型网站都有基于DNS的HA实现,这里就不展开解释了。

其他虚接口

有些服务比如中间件,会用RPC作为虚接口,客户端不直接与后端服务打交道,而是先查询RPC服务器来列举后端有哪些可可供调用的服务,然后直接来调用。

虚拟网络文件系统也是一种虚接口,通过元数据服务器来提供虚拟文件系统,文件系统空间由元数据服务器来管理,真实服务器不管理文件系统空间,客户端通过元数据服务器来操作文件系统,而由元数据服务器来提供真正的数据服务器,数据服务器之间互相备份,单个数据服务器宕机不会导致真个文件系统崩溃。典型的例子是google 的GFS。

节点通讯

对HA节点来说,相互之间通讯是必须的,无论有无LB功能,有无状态。这个功能可以很大也可以很小。最简单的通讯可以通过心跳实现,两个节点之间通过心跳协议交换状态。复杂系统可能会针对通讯构建更复杂的子模块。值得注意的是,通讯部分有很多故障源,就像前面提到过的一样,这些子模块也必须要提高故障容忍性。

对于心跳协议这里再多说几句,心跳协议多用在双机热备上,即上面提到的failover模式。心跳协议主要用来在两个节点之间相互探测对方状态,可以通过串口线互联,也可以通过网线互联,值得注意的是,无论是串口线还是网线互联,如果只是单线连接很容易会产生脑裂问题,所谓脑裂问题就是,当心跳线断开后,双方节点无法知晓对方状态,都以为对方出了故障,从而出现对资源的争抢,导致系统混乱。

脑裂问题发生的主要原因是,这个节点之间单线互联的心跳线违背的HA三原则,存在单点故障问题,所以要解决这个问题,可以采用双线互联,或采用另外一条辅助心跳线。

流量调度

就像之前提到的一样,我这里所提到的HA系统是指网络服务HA系统,涉及到网络服务一般都需要流量调度功能,这也基本上是标配。

这里流量调度可以简单分为正常调度和异常调度两类:

先说异常调度,其是指当某节点异常时,系统需要将流量调度到正常节点上继续处理,这是故障容忍功能的一部分。不同虚接口,不同架构会有不同的实现方式。对于vrrp或ospf实现的vip接口,流量调度主要是通过改变路由器和节点的路由表来实现异常流量调度,而lvs + NAT方式则通过一个控制节点来实现异常流量调度。

对于正常调度,它主要是指Load Balance。Load Balance与HA结合是很自然的事情,一是单节点可用性比较高,如果仅用来作为备份节点实在是浪费。二则LB的很多事件跟HA相重叠,比如当进行流量调度时,需要探测节点状态,三则HA性能的横向扩展离不开LB。而影响LB性能指标的一个重要因素就是流量调度。具体到不同业务可以采用N多调度算法,这些算法有些也被广泛用在操作系统中对线程的调度,简单点的有直接对五元组hash(很多交换机甚至网卡也都支持,也有人将这种hash算法不叫LB,而叫LS(load sharing)),复杂点的有FIFO,时间片轮转,公平队列等,更复杂的有对QoS等进行评估,对流量进行统计等。所有这些算法都围绕一个主题:公平。但公平这事大家都懂的,你所追求的正是你所缺少的,流量调度也难有绝对公平,所以算法无非都是在时间和空间两个方面玩平衡。

具体有哪些算法请大家自行脑补吧,在此不一一列举。

控制层

控制层是HA系统最重要的组件,它直接关系到HA的可用性问题。

就像Fault Tolerance节谈到的一样,控制层需要在构建和验证两个环节来消除/避免故障。这层可以做成Failover的简单方式,也可做成象hadoop的zookeeper那样的复杂系统。但无论实现简单还是复杂都是为了解决故障处理问题,性能问题。跟上节讲的一样,现在的HA系统实现都将LB集成在一起,但逻辑上讲控制层只负责故障处理,即Fault Tolarence。

控制层之所以难搞要归结于HA系统的组成,前面提到过HA系统三原则要求之一是,节点之间需要互相独立,独立就是要民主嘛,而失败的民主就是一盘散沙,更难管理了。大家都知道,对软件问题来说,专制方式是最容易处理的,用一个皇帝节点来统管其他草民节点,简单粗暴也很有效。但专制的问题是这个皇帝节点如果出问题了,江山就倒了。所以矛盾就产生了,牛人的解决方案就粉墨登场了,道理也很简单,基本上是在民主方案基础上在多搞几个皇帝节点来个组团成一个皇帝委员会,同时委员会内皇帝节点之间也搞民主来选一个真正的领导,所有委员会内皇帝节点对外只呈现一个虚拟皇帝,所有草民节点都听这个虚拟皇帝的话,这样任何一个皇帝死掉,委员会内部立马民主选一个新皇帝来顶上,而草民节点看不到这个皇宫内部争斗大戏,仍然在听从那个虚拟节点的领导。草民节点死掉更简单了,皇帝节点会首先知晓,然后按实现事先设定好的规则来处理,该替换就替换,该公告就公告,如此这般江山社稷就稳固了。所以搞到最后,诸位可以看到,控制层本身就是一个HA系统了。

但无论怎么实现,一个完备控制层要处理以下几个事情:

  • 故障检测

    这是必须的,任何节点的故障需要被尽快被发现,然后被处理,不能让单节点故障在系统中扩散,也不能让其中断业务。

  • 节点接管

    接管属于故障处理部分,这个步骤也需要尽快完成,以避免业务中断。

  • 状态同步

    节点之间的状态应该互为备份,当某个节点出故障时,其他节点应该有其状态信息的拷贝。

  • 状态迁移

    当接管节点准备好后,失效节点的状态能够顺利迁移到接管节点。

  • 节点集成

    当新节点加入HA系统时,其他节点能够感知其存在,并且将自身状态信息同步到新节点。

需要指出的是,以上这些步骤虽然属于控制层,但有时业务层也需要感知,比如业务是数据库,当节点失效时,数据库数据有可能出现不一致情况,此时控制层要将以上这些事件传递给数据库系统,数据库系统然后可以根据故障信息将数据恢复到以前的checkpoint.

业务层

业务层的任务是运行具体的服务,有两种形式的服务,一种是带状态的,另一种是不带状态的。所谓状态是指服务运行过程中产生的能够影响服务运行的一些数据。比如tcp会话中的各种会话状态,数据库的session等等。由于这些数据是由业务来定义,所以需要业务层来处理。对不带状态的业务来说,处理起来就容易多了,这种情况下,无需状态同步,错误处理,节点集成,接管都很容易,每个节点都变成来料加工工厂,招之即来,挥之即去。一些AS/AA结构交换机正是通过这样的failover形式构造HA系统。

对带状态的业务,业务层除了关注自身业务外,还需要关注状态同步,状态数据的一致性问题。有些复杂系统还会有作业调度系统,这个不是普通的LB,而是包括计算和数据在内的综合调度,比LB更优化了。

依赖具体的业务不同,状态同步有时会变得很麻烦,甚至难以处理,比如ipsec HA中的包序号同步问题,这个序号是ipsec协议用来抗重放攻击的,每发送一个加密包这个序号会加1,这个序号就是一个状态数据,必须要同步到其他节点,以防这个节点失效后,其他节点能够从这个序号开始继续发送加密包,否则对端ipsec vpn服务器可能会将收到的包丢弃。现在的问题是,如果每次序号变化都同步到其他节点,隧道多的话,这会导致状态同步代价太高,性能大打折扣。不过幸好ipsec rfc提供了一个补救措施,即ipsec抗重放窗口可以接收比窗口右边沿大的序号,这样就可以不用连续同步序号,而是以一个步长的方式来同步了。但即使是这样,ipsec的ike协商阶段状态仍然没有被同步,因为这个阶段状态太多,变化太迅速,而且涉及内部太多数据结构,目前的方案是,由于这些状态只发生在一个独立节点上,如果该节点失效,其他节点接管时重新初始化这些连接即可。这里只给出ipsec HA作为例子说明状态同步问题,其他业务在实施HA之前必须要考虑到状态同步问题,可以采用的方案也比较多,像checkpoint,重新初始化等都可以。

状态同步的另一个要考虑的问题是数据一致性问题。虽然可信赖计算没有将数据一致性纳入其中,但在实施HA时必须加以考虑。著名的CAP理论已经揭示了当数据被分割后,你就只能在数据的可用性和一致性二者中选其一了,而不能二者兼得。当然这也依赖HA的实施架构,如果采用sharing-disk方式,数据一致性就不会有大问题了。否则就要像各种分布式cluster方式一样要考虑当数据被分区后,怎样在数据一致性和可用性之前平衡了。这里不展开了,有兴趣再去互联网垃圾场翻几遍,肯定能挖到宝。

HA系统模式

前面提到现在的HA系统一般都结合了LB,HA系统主要解决单点故障问题,LB主要解决热节点问题,这两方面问题有很多共性,节点过热的话会导致过载最终可能造成节点失效,而节点失效后又可能会导致其他节点更热,从而形成雪崩效应,最终导致整个系统崩溃。如果把热节点看成一个缺陷的话,必须在构造阶段想办法避免,而这就是Fault Tolerance范畴了。LB是解决热节点的有效方法,所以从这点可以看出,LB也是一个有效的Fault Tolerance方法。而同时HA用来做Fault Tolerance的一些事件也可以被用来处理LB,所以可以看出它们是一对好基友。

从HA系统实施架构来看,各个分层不一定象上面那么清晰,而是根据具体业务需求来做最优调整,下面罗列了一些常见的HA系统架构:

Failover架构

Failover架构是一种简单架构,前面也提到过,是一种双机热备,只有两个节点,一主一备。以前也有冷备,但可以想象,冷备极大的降低了可用性,一些硬件HA系统可能还有使用,但在网络服务方面没人使用。对热备来说也有共享存储和不共享存储两种:

不共享存储的failover架构如下图所示:

这种结构一般用在无节点状态的系统中,比如交换机,也有通过VRRP之类协议来同步路由的AA/AS failover路由器采用这种架构。

共享外置存储的failover架构如下图所示:

很多数据failover系统采用这种共享外置存储的架构,主要是数据库系统有很多状态,这种架构将状态保存到共享的外置存储后,主节点本身状态就无需同步到备用节点,简化了故障容忍性设计。

平等节点架构

平等节点架构中所有节点地位都是平等的,也就是所谓的sharing-nothing结构。就象前面阐述的一样,平等意味着民主,民主意味着权利下放,而权利也同时意味着责任,这也就说单个节点需要承担更多责任,这些责任便是如何来共同维护HA系统的可用性。有一些成熟的协议已经在HA系统的控制层面帮我们做了这些事情,比如VRRP协议。但业务状态同步问题还是需要业务服务自己来解决。我们的ipsec vpn网关便是采用这种架构。平等节点架构如下图所示:

共享存储架构

也就是sharing-disk结构。共享外存架构的有点前面也提到过了,就是节点的业务状态同步可以大幅简化了,顶多是在节点失效或集成时进行状态回滚。这种架构一般要依赖一个具有高可用性的外置存储网络,比如SAN,FB,这些外置存储I/O的速度可以跟DAS设备(即本机磁盘)比拟。这样一来数据一致性问题变得不那么关键了,但I/O却成了系统的热点。架构如下图所示:

多HA系统结构

这样的HA系统里可能存在多个子HA系统,前面提到的hadoop zookeeper便是一个例子。这种系统的优点是,针对具体问题各个击破,谁热就给其降温(LB),谁危险就对其做HA。

架构如下图所示:

可以看出,图中由多zookeeper server组成一个HA子系统为所有client节点服务,整个系统又是一个大的HA系统。

HA系统指标

对于HA系统来说,可用性是其最重要的一项指标,虽然可信赖计算组列出了六大属性,但我觉得从实用角度来说,可维护性,数据一致性,性能指标应该要重点关注。

其中可维护性又可以分为如下几个指标:

  • 故障恢复时间

  • 故障隔离能力

  • 升级能力

性能指标又可分为如下两点:

  • 负载均衡能力

  • 横向扩充能力

我不打算列一大堆公式来分析这些指标,因为每一种都有人做了详尽的研究,如果有人想分析,网上很容易找到相关资料,我这里只列出我们需要的关注点。

后记

絮絮叨叨说了那么多,好像也没新东西啊,现实就是这样,很多道理就像白开水,大家都懂,如果直接在课堂上喂你,你估计不喝,但如果调和一下,加点料,做成心灵鸡汤,你就觉得还蛮有滋味的,然后喝得很开心。所以我这篇文章也希望能将这些已有的东西整理加工一下,同时加了一些自己的料,做成料理,如果口味不好的话,还望看官见谅啊。

copy这词已经被重新发明了很多版本,不同人有不同叫法,律师叫剽窃,普通人叫模仿,打工皇帝叫复制,深圳华强北叫山寨,文人叫拾人牙慧,教授叫研究,学渣叫抄袭,发明家叫借鉴,牛顿叫站在巨人肩膀上,导演叫向xx致敬。如果说我这篇文章有些价值,那是因为站在了牛人的肩膀上,研究了很有价值的文章,比如wiki, lvs,vrrp, ibm等, 在此一并向他们致敬。

说了这里,各位看官可能会问,你标题是HA启示录,那你的启示呢?放心,我不是标题党,还记得HA系统的三原则么?第一条是不能有单点故障,我们每个人作为独立的节点,无论在项目中,还是在公司里也不能出现单点故障问题,就是说,不要因为你的问题导致公司或项目玩不转,这个时侯就是你被替换,备份节点上线的时候了。第二条是可靠互联,我们自己要与上下游节点之间形成互联互通的可靠通信,否则封闭的节点无法组成完整的系统。第三条是故障检测机制,就是说我们要及时更新自己的状态,早请示晚汇报,要让自己问题被各节点及时知晓,不要积累问题,和扩散问题,导致系统架构的不可靠,因为前面说过了,单个节点本不可靠,出问题是正常的,但如果不检测和报告自己的问题,就是你自己的问题了,也就是你这个节点该修理的时候了。

参考文献

  • Sam Siewert (Sam.Siewert@Colorado.edu), Adjunct Professor, University of Colorado: Big iron lessons, Part 2: Reliability and availability: What’s the difference?
  • Jean-Claude Laprie: Dependable Computing: Concepts, Limits, Challenges
  • Sangeetha Seshadri, Ling Liu, Brian F. Cooper ({sangeeta,lingliu,cooperb}@cc.gatech.edu), Lawrence Chiu, Karan Gupta, Paul Muench({lchiu,guptaka,pmuench}@us.ibm.com): A Fault-Tolerant Middleware Architecture for High-Availability Storage Services
  • Magnus Gammelgård, Mathias Ekstedt, Per Närman : Architecture Scenario Analysis – Estimating the Credibility of the Results
  • Paulo Vera-Ssimo,Luís Rodrigues: Distributed Systems for System Architects
  • http://cailin.iteye.com/blog/2014486: zookeeper原理
  • wikipedia