作者: Stan Peng,微软云计算架构师

本文将和大家聊聊服务器 / 服务的高可用性,特别是在 Azure平台上的高可用性。

高可用是什么?

首先,大家之前应该都听过 gitlab 误删数据库导致的长期业务停滞。现在去聊这个事未免有点太迟钝,但这个事件实在太过典型,我们也正好就这个案例谈谈所谓的高可用性。

我们先来看看可用性的衡量,计算机系统的可用性用平均无故障时间( MTTF )来度量,即计算机系统平均能够正常运行多长时间,才发生一次故障。系统的可用性越高,平均无故障时间越长。可维护性用平均维修时间( MTTR )来度量,即系统发生故障后维修和重新恢复正常运行平均花费的时间。系统的可维护性越好,平均维修时间越短。计算机系统的可用性定义为: MTTF/(MTTF+MTTR) * 100% 。由此可见,计算机系统的可用性定义为系统保持正常运行时间的百分比。

21

通俗的说,亦即,

MTTF = 云平台、后台硬件多久坏一次,

MTTR = 一旦服务挂了,多久能够修好。

了解了这个概念,我们再看一下这个案例:

工程师人为的删除了数据库文件, 这个概率亦即数据库文件坏的可能性。可能是硬件坏了,可能是数据库软件坏了。 而工程师在维修这个数据库时,花了近 8 小时,即修复好直到业务再次可用,经历了这么久的时间。

我们给这次可用性算一下数据:

以一年来算,我们大致模拟一下。 2/1 日前一共 31 天, 31×24/(31×24 +8*100%= 98.9%

所以说 gitlab在短短一个月时间内,勉强达到了29的可用性。挂了8小时,竟然也有29,所以厂商们的SLA是不是骗人的呢?

答案当然是否定的。我们来看看各个 9 的具体含义。

22

4 9 SLA ,一年也要挂 1 小时。某些客户要跳脚了,我们的业务非常重要,这些 downtime 也不能接受!你们公有云的东西太不稳定了!

如果这样就觉得公有云不适合自己的应用,那就未免小题大做了。大家都知道,目前的应用都在云 / 微服务 / 分布式化了,应用和平台理应是互相适应的过程,而不是纠结于底层的结构。

回到正题,既然要针对业务,自然要分析业务的可用性需求( availability request) 。银行和高级金融场景,我相信对于 SLA 的需求和不常用的互联网业务,必然是有所区别的。这里 CAP 理论还是试用的。

然后光可用性需求还不够,还需要分析预算 (cost analysis), 用怎样的预算达成最大的可用性,这既是云架构师的义务价值所在。

那究竟如何达到高可用呢?

这张来自 google io 大会的 slide 非常好的阐述了目前几乎所有高可用方案:

23

  1. 备份 – 一致性弱,需要交互事物 否,延迟低,可能数据丢失,failover要接受downtime
  2. 主备、双主,基本一致,只不过一致性有所保护,数据丢失可能性略低,可以只读failover
  3. 2PC/Paxos 强一致性,数据节点等多事物交互,高延迟和中等的吞吐,没有数据丢失可能,可以做到实时切换

这几个点如若细细说来,都可以说一大篇。这里说个题外话,Azure存储的实现就是用的paxos,所以客户不存在数据丢失可能。

为了大家看得方便,我们用 M ySQL 作为例子解说。

24

源自oracle mysql HA solution

SLA 99% MySQL replication
SLA 99.9% MySQL fabric
SLA 99.99% DRBD+PACEMAKER
SLA 99.999% MySQL cluster

这是来自 oracle 的关于 M ySQL 高可用的方案推荐,大家可以看到,这正好 match 了谷歌之前的高可用描述, 备份和主备技术,正好在 99% 的范围呢。而自动化双主以及更成熟的 cluster 技术,对 SLA 的保证则更好。

采用这些技术,方能实现应用在云平台上的高可用性。

如何适配在我的Azure云应用上?

之前说了一堆概念,我们来说说在 Azure上的一些最佳实践。

这次我们先说说应用设计。

  • 避免任何单点故障。 为避免单点故障影响可用性,应该将所有组件、服务、资源和计算实例部署为多个实例。 这包括身份验证机制。 将应用程序设计为可配置为使用多个实例,自动检测失败,并将请求重定向到未失败的实例(平台并不在其中自动执行此操作)。
  • 根据不同的服务级别协议分解工作负荷。 如果服务由重要和比较不重要的工作负荷组成,请以不同的方式进行管理,并指定服务功能和实例数,以符合其可用性要求。
  • 最小化和了解服务依赖项。 尽可能将使用过的不同服务数降到最低,并确保了解所有存在于系统中的功能和服务依赖项, 包括这些依赖项的性质、失败造成的影响,或整个应用程序上每个服务降低的性能。 Azure 保证大多数服务至少提供 99.9% 的可用性,但这意味着应用程序依赖的其他每个服务可能降低 0.1% 的系统整体可用性 SLA
  • 尽可能将任务和消息设计为幂等(安全重复),使重复的请求不会导致问题。 例如,服务可以充当处理消息的使用者,这些消息由系统中充当生成者的其他部分发送为请求。 如果使用者在处理消息之后、确认消息已处理之前失败,生成者可以提交重复请求,由使用者的另一个实例处理。 出于此原因,使用者与它们执行的操作应该具有幂等性,以便重复以前执行的操作不会使结果无效。 这可能意味着要检测重复消息或使用乐观方法来处理冲突以确保一致性。
  • 使用消息代理来为重要事务实现高可用性。 用于启动任务或访问远程服务的许多方案都使用消息传送在应用程序与目标服务之间传递指令。 为了获得最佳性能,应用程序应该能够发送消息,然后返回以处理更多请求,而无需等待回复。 若要保证传送消息,消息传送系统应提供高可用性。 Azure 服务总线消息队列实施至少一次语义。 这意味着,发布到队列的每个消息都不会丢失,不过在某些情况下可能会发送重复的副本。 如果消息处理是幂等的(请参阅前一项),则重复传送应该不是问题。
  • 将应用程序设计为在达到资源限制时适当降级,并采取适当的措施将对用户的影响降到最低。 在某些情况下,应用程序上的负载可能超出一个或多个部件的容量,导致降低可用性和连接失败。 缩放有助于缓解此问题,但可能会达到资源可用性或成本等其他因素施加的限制。 将应用程序设计为可在此情况下自动适当地降级。 例如,在电子商务系统中,如果订单处理子系统处于高压状态(甚至完全故障),可以暂时禁用该子系统,同时允许其他功能(例如浏览产品目录)继续工作。 适当的做法是延后对故障子系统发出的请求,例如,仍然允许客户提交订单,但同时要保存订单,以便在订单子系统稍后再次可用时进行处理。
  • 适当处理急剧突发事件。 大多数应用程序需要处理不同时间的不同工作负荷,例如业务应用程序中上午出现的第一波高峰,或者电子商务网站中发布新产品时。 自动缩放有助于处理负载,但可能需要一些时间使其他实例联机并处理请求。 通过将应用程序设计为将请求加入其使用的服务队列,并在队列接近完全容量时适当降级,以防止突发和意外的活动高峰导致超出应用程序的处理能力。 确保有足够的性能和容量可供非高峰条件清空队列和处理未完成的请求。 有关详细信息,请参阅 Queue-Based Load Leveling Pattern(基于队列的负载调节模式)。