说到保障系统的稳定性上,从预防的层面上看,总是有无数的事情可以去做。我觉得人们经常陷入的一个误区是,总是假设系统的某些方面会出问题,然后想办法针对这些特定的问题去做预防,认为预防好了这些问题自己的系统就万无一失了。这就导致很多时间花费在穷举系统可能出现的错误上、针对特定的错误做预防措施上。
最常见的错误就是增加流程。比如上线的流程、修改某个参数的流程。很可能一开始所有的流程都是简洁并快速的,每个人都可以将精力集中在自己的工作上。直到有一天出现了故障,大家在复盘的时候,发现“哦!假如说我们在发布之前增加一个流程,可以确认一下某个东西没有错误,那这个错误就可以避免了!” 这样的故障复盘越来越多,流程也就开始堆积的越来越多了,比如发布之前要求满足多少的覆盖率测试才能发布,某个变更做出之前必须经过某某审批才能执行。
其实添加一个流程是复盘最容易做出的决策——如果什么都不做的话,就等于问题发生了,作为领导或者负责人什么作为都没有,这个事情后面再发生或者再说起来就会很尴尬。事实上,但很多时候这么做其实是没有什么用的,首先,发生的故障一般都是新的故障,对已经发生的故障进行预防性价比太低;然后增加的很多流程其实并没有什么实际作用,最后会变成一种形式主义。比如强制要求测试的覆盖率,会导致程序员去写一些除了增加覆盖率并没有实际测试功能的代码;增加审批流程会徒增同事之间的沟通成本和审批时间,审批人大部分情况下可能也不知道他审批的是什么东西,背后有什么风险,最后也变成了纯粹的“走流程”。
另一种错误是试图穷举出系统中所有可能发生的故障,对这些故障设定自动化的处理方法或做好这个特定故障的应对方案。在一个很复杂的大型的分布式系统中,穷举出所有的故障几乎是不可能的。针对已经发生的故障提供针对的应对策略或者自动化的解决方式也帮助不大,虽然看起来给人一种安全感——我们每次的故障都不会再发生啦!但实际上就算什么都不做,相同的故障再发生一遍的概率又有多少呢?这又犯了形式主义了,我们从错误中学到的不是预防这一个错误,而是想想这个错误为什么会发生,是不是我们现有的机制出了问题(不要盲目修改机制,不然就犯了上面说的“流程”类的错误了。增加一个流程应该是慎重的,要考虑到这对将来每次的流程都会增加成本。)问题发生之后我们用了多长时间恢复,能否提高恢复的速度?还有一个方面,这种强调特定错误的“自愈”一般是和业务强耦合的,业务在发展,“自愈”的测试成本又很高,很难保持这种“自愈”长期有效。
将大部分的成本花在这部分上面,可能看起来让人很有安全感:“看,我们能应对这么多的情况了。”但是这些穷举出的情况并没有多大的意义,故障依旧会发生,并且总是以我们没有想到的方式。
所以我觉得将精力放在补救措施上会更有意义。寻找那些应对场景广泛的补救方法,不去针对特定的场景,而是针对特定的表现。比如部署在多个 Available Zone,如果监控显示一个 AZ 的流量有问题,无脑切换到其他 AZ 就可以;比如对非关键的服务提供降级措施,日常可以快速发布和迭代,如果出现故障立即降级即可。
以前软件行业那种开发与运维职能分开的模式,典型的矛盾就是开发想快速迭代,运维为了稳定性不想做出任何改变。现在流程的 devops/SRE 文化其实并没有从根本的解决这个矛盾。我觉得答案可能就是 Facebook 那句话“Move fast and breaking things.” 错误终究会发生,不要试图完全预防错误,应该尝试提供快速补救、简单可靠的方案(简单很重要,只有简单的东西才是值得依靠的)。并且还有要 blame free 的文化——建立学习和责任的平衡,不带有惩罚性、责备性的报告。不要再使用不专业的(恐吓性质)的故障责任机制。
相关阅读:
讲得好……有些大公司很喜欢加流程,最后流程又臭又长又解决不了问题