最薄弱的环节:链式依赖如何影响系统风险
在评估系统风险场景时,人们极易忽视“链式”依赖。我们所受的训练是在“节点”层面看待风险,追问“这一个东西出故障的可能性有多大”。但系统风险远比这复杂得多。
在大多数系统中,存在一些依赖于其他组件的组件。我们最常审视这一点的场合是服务器存储的设计,但它实际上出现在任何系统设计中。另一个很好的例子是 Web 应用如何需要同时具备应用主机和数据库主机才能运行。
用一个例子来解释链式依赖最为简便。我们将考察一个采用 SAN 存储的标准虚拟化设计,以理解故障域的边界存在于何处、链式依赖存在于何处,以及冗余在系统级风险缓解中扮演何种角色。
在一个用于虚拟化的标准 SAN(存储区域网络)设计中,你拥有虚拟化主机(为简便起见,我们将其称为“服务器”)、SAN 交换机(专用于存储网络的交换机)以及磁盘阵列本身。这三个“层”中的每一层都依赖于其他各层,整个系统才能作为一个整体运行。如果我们采用尽可能最简单的一套配置——一台服务器、一台交换机和一个磁盘阵列——那么我们显然就有三台设备,代表着三个不同的故障点。这三者中任何一个出故障,都会导致整个系统瘫痪。没有哪一个部件能够单独发挥作用。这就是一条链式依赖,而一条链条的强度取决于其最薄弱的环节。
在我们这个极简的例子中,每台设备都代表一个故障域。我们可以通过提升每个故障域的可靠性来缓解风险。我们可以增加第二台服务器,并实施虚拟化层的高可用或容错策略,以降低服务器故障的风险。这提升了其中一个故障域的可靠性,但另外两个仍未触及,风险一如从前。接着,我们可以处理交换层,方法是增加一台冗余交换机,并配置多路径策略来应对单条交换路径的丢失,从而降低该层的风险。至此,已经处理了两个故障域。最后,我们必须处理存储故障域,做法与之类似,即通过增加第二个磁盘阵列来引入冗余,该阵列与第一个互为镜像,并能够在发生故障时透明地进行故障转移。
既然我们已经强化了系统,那么我们在一条依赖链中仍然有三个故障域。我们所做的,是让链条中的每一个“环节”、每一个故障域,各自变得格外有韧性。但链条依然存在。这意味着,整个系统作为一个整体,其可靠性远低于链条中任何单一故障域独自的可靠性。我们做出的成果比起点要好得多,但我们仍然有许多故障域。这些风险会累加起来。
在确定整体风险时,困难之处在于:我们必须评估每个部件的风险,然后确定缓解之后(通过增加冗余)的新风险,再求出链条中各个故障域共同作用下的累积风险,以确定整个系统的总风险。要确定每个故障域内部的风险极其困难,因为风险缓解的方式起着重要作用。举例来说,一组故障转移过慢的存储磁盘阵列集群,即便存储集群本身看起来运转正常,也可能导致整个系统瘫痪。因此,即便是给“故障”下一个清晰的定义,也可能颇具挑战性。
人们往往容易采取一种“自上而下”的风险评估视角,这非常危险,但对于那些并非经常从事风险评估的人来说却非常常见。这里的倾向是,只着眼于“最顶层”的故障域来看待风险——在这样的案例中通常就是服务器——而忽略位于该点之下的任何风险,把那些风险视为“引擎盖之下”的东西,而非风险评估的一部分。人们很容易忽略网络和存储这类技术性更强、更不外显、也更难被理解的组件,转而聚焦于顶层那些相对易于理解、又被大力营销的可靠性方面。这种“顶层视角”意味着顶层之下的风险被遮蔽,并通常被忽视,从而导致高风险,却又没人能很好地理解其缘由。
理解链式依赖这一概念,可以解释为何复杂的系统——即便配有复杂的风险缓解策略——往往最终比更简单的系统脆弱得多。在我们上面的例子中,我们可以做几件事来“压缩”这条链条,从而使整个系统作为一个整体变得更可靠。
最显而易见可被压缩的组件就是网络故障域。如果我们彻底移除交换机,将存储直接连接到服务器(当然,这并非总是可行),我们就切实地消除了一整个故障域,从我们的链条中去掉了一个环节。如今,我们不再有三条各自都存在某种故障可能性的链条,而只剩下两条了。在其他条件都相同的情况下,越简单越好。
理论上,我们还可以把存储故障域也压缩掉,方法是从外部存储改为使用服务器自身的本地存储,这实质上把我们从两个故障域降到了单个故障域——当然,剩下的这一个故障域比压缩之前承载了更多的复杂性,但系统的整体复杂性却大大降低了。同样,这也是在其他所有因素都保持相同的前提下而言的。
另一个值得考虑的思路是让单个节点自身变得更可靠。如今流行的做法是着眼于更大规模的系统,并以那种方式来缓解风险,即通过增加冗余的、低成本的节点来为故障域增添可靠性。但在传统上,这并非通往可靠性的默认路径。在过去更为常见的做法是把高度的可靠性内建到单个节点之中,大型主机及同类系统昔日的盛行便是明证。例如,大型主机和高端存储系统时至今日仍在这样做。这其实可以是一种极其有效的方法,但它无法应对许多场景,而且通常极其昂贵,往往还会因系统需要由厂商部分甚至完全维护而成本倍增。这种做法往往只在特殊的小众情形下才行得通,在更普遍的范围内并不实际。
因此,在任何此类性质的系统中,我们都有三种关键的风险缓解策略可供考量:提升单个节点的可靠性、提升单个故障域的可靠性,或减少依赖链中故障域(环节)的数量。审慎地把这些策略结合起来运用,能够帮助我们达到适合自身业务场景的风险缓解水平。
真正的困难所在——并将一直存在——是对不同风险缓解策略的比较。单个节点的风险通常能够以某种程度的把握来加以估算。而单个故障域内部的冗余策略,其可估算的能力则要差得多——某些冗余策略极为有效,能够造就极其可靠的故障域,而另一些却可能事与愿违,反而降低某个故障域的可靠性!冗余策略往往随之而来的复杂性从来都不是没有附带条件的,尽管它通常会带来回报,但它所承载的可靠性收益的程度,却很少能达到当初的预期。因此,估算一条依赖链的风险就更加困难,因为这既需要清晰理解与每个故障域各自相关的风险,也需要理解存在于故障域边界处的故障可能性(比如前面提到的存储故障转移延迟故障)。
让我们在前面所讨论内容的基础上,探讨在面对同一场景的两种非常常见的处理方式时,确定风险所涉及的种种问题。
对于我们一直在讨论的同一情形,有两个极端的例子:一个是用一台配备内部存储的单台服务器来托管虚拟机;另一个是一条由六台设备构成的“链条”——配有两台服务器并在服务器层采用高可用解决方案、两台交换机在交换层提供冗余、两个磁盘阵列在存储层提供高可用。如果我们改变此处的任何一个重大因素,我们通常都能对相对风险给出相当清晰的估算——比如说,如果任何一个故障域缺乏可靠的冗余——我们就能相当清晰地判定,单台服务器才是整体上更可靠的系统,除非给某个单一节点赋予了极高程度的单节点可靠性,而那在财务上通常是一种不切实际的策略。但是,在每个故障域都保有冗余的情况下,我们就不得不去比较域内可靠性(冗余链条)与域间可靠性(被压缩的链条,即单台服务器)各自的相对风险了。
面对这两种截然不同的处理方式,并没有合理的办法去评估这两种风险缓解手段的比较风险。人们普遍认为,配有广泛域内风险缓解措施的六(或更多)节点方案是两种方式中更可靠的那一种,而这几乎可以肯定,通常也确实如此。但它并非总是如此,而且这种方式很少能以一个真正显著的幅度胜过单节点策略,却常常要付出比单台服务器策略高出四到十倍的成本。对于一项很可能只是可靠性上的小幅提升、外加一份可靠性可能下降的小小风险而言,这潜在地是一项非常高昂的代价。每增加一份冗余,都会增添必须由人来实施、监控和维护的复杂性,而随着复杂性和人为介入的增加,风险也会越来越大。避免人为失误往往比避免机械故障更为重要。
我们还必须考虑恢复的成本。如果故障注定要发生,那么从一个简单系统的故障中恢复通常轻而易举。而一个极其复杂的系统一旦发生故障,可能需要耗费极大的努力才能恢复到可工作状态。复杂的系统还需要广博得多、深厚得多的经验与信心才能加以维护。
要确定系统的可靠性,并没有什么简单的答案。现代信息交付系统实在太过庞大、太过复杂,存在太多无法确定的因素,以至于无法在所有情况下都加以评估。然而,凭借对链式依赖的良好理解,以及对各种风险缓解策略的理解,我们可以采取切实可行的步骤,来大致确定相对的风险水平,看清相似的风险场景在成本上如何比较,识别出脆弱之处,认清故障域和依赖链,并体会到系统设计的变更将如何明确地把我们推向或推离可靠性。
