当前位置: 首页 > 科技观察

安全生产体系稳定建设

时间:2023-03-17 18:58:28 科技观察

作者|彭文清(马可)序安全是产品之本,体验之本,是企业的核心竞争力。安全生产是一项系统工作,也是一件比较琐碎的事情。需要通盘考虑,千方百计保证系统安全稳定运行。之前一直负责商品的稳定性,在这方面有很多经验和实践。记得2018年我们在做产品发布组件化改造的时候,刚好网站刚开始调整类目。一时间,连续3个月每个月都有故障。当时维稳压力很大。当然,这也是一个机会。产品的稳定性也是从那个时候开始,然后逐渐提高。概要安全生产建设大致可以分为这三个阶段:事前:故障预防,这里需要考虑的是如何通过事前设计最大程度地保证质量,降低风险,提高稳定性。进行中:应急响应,出现问题如何处理,快速恢复的方法是什么,流程是什么。事后:在检讨改进之后,事后一定要总结经验,举一反三,解决类似的问题,不要一石二鸟。预防失败魏文王问扁鹊:你为什么医术这么好?扁鹊说:我的医术在我兄弟三人中算是差的。最好的是我大哥,没有任何症状就熄灭了,第二好是我二哥,稍微消退了一点症状。又是我,我才开始治大病,所以虽然名扬天下,但医术却远不如大哥二哥。打个比方,大哥擅长防错,二哥擅长应急,扁鹊最擅长总结和检讨。因此,预防生产事故是最重要、最基本的。架构设计的单一技术点大部分时间都可以正常工作,但当规模和复杂度达到一定程度时,故障其实无处不在。--《安全生产指南》失效设计是安全生产最重要的方法论和准则。只有针对各种故障场景提前部署防御,才能在出现问题时有效处理问题。那么针对故障进行设计的具体方法有哪些呢?避免单点冗余的冗余设计是最早的飞行器常用的技术术语。飞机设计最突出的特点之一是冗余度。例如,一架大型客机通常有两套甚至四套发动机。同样,也有两套基本相同的主副驾驶系统。这样的设计会产生额外的成本。其根本原因在于冗余设计可以保证在一台发生故障时系统的正常运行。在ICBU场景中,中美容灾是我们主要的冗余设计,也是保障稳定的核心手段。Commodity是ICBU中第一个在中美异地搭建容灾能力的业务场景。正是因为这种能力,Commodity避免了很多失败。服务划分和雪崩回避其实是很好的隔离。中国古代的木船设计中有一项非常重要的发明叫做“水密舱”,也被很多军舰所借鉴。一艘典型的航空母舰据说有2000多个独立的密闭舱室。作用是在船舶遇到事故或破损进水时,通过隔离破损舱室来减缓沉没。是对安全生产的设计,对我们具有借鉴意义。系统应采用模块化设计,薄弱点或风险点可单独隔离降级,保证整体可用性。之前我们有一个问题,是因为ES集群中某台物理机故障导致上层应用的线程池被耗尽,导致服务不可用。根本原因是隔离没做好。服务幂等性保证一致服务或接口应保证幂等性,多次调用和一次调用的结果应一致,避免重复调用导致数据或逻辑异常。可以漂移的无状态服务服务或接口最好以无状态模式设计。从可扩展性的角度来看,无状态模式可以很容易地根据流量情况进行弹性伸缩。精确监控运行状态将我们的应用系统比作飞机。我们的系统还需要各种传感器和仪表板来实时监控系统的运行状态。对于运维人员来说,系统应该是一个白盒而不是黑盒。核心业务或系统监控有助于提前发现问题,细粒度、多维度的监控有助于快速定位问题。自动化运维管控最好的处理方式就是不用处理。核心思想是标准化、简化和自动化所有操作,减少人为干预,提高运营效率。产品发布/产品管理有一些自动容灾计划,当出现问题时可以自动快速恢复。以下案例是周末发生的风险提示,通过自动快速恢复避免了上线故障的发生。此外,旺铺今年发布了风险提示。当出现容量和性能问题时,通过自动弹性扩容快速解决问题。编码开发并发控制在服务能力有限的情况下,我们需要主动控制客户端并发数。特别是在基于任务的场景中,通常有两种选择:静态限流和动态限流。静态限流比较简单,但有资源利用率不高的局限性。动态限流的原理是根据服务器的响应动态调整客户端的速率。简单来说就是计算服务超时/异常的次数。如果有更多的超时,请求将变慢。数据控制数据是信息系统的血液,只有通过数据的流动才能串联起一个业务或功能模块。一方面,数据的生产要经过严格验证,确保数据的合法性和准确性。另一方面,数据消费需要评估一次处理的数据量和数据处理的频率。不要因为高并发和海量数据而对系统内存和负载造成压力。商品因此经历了最惨痛的教训:防御性编程批量容错:比如一个列表不能因为某一行有问题导致整个列表失效。同一个同步任务不能因为某个任务异常而阻塞整个任务队列。因此,有必要在这些场景下做好容错工作。其实还是在强调隔离风险。商品、质量分计算任务等引擎同步任务有独立的在线实时队列、实时重试队列、离线队列。目的是不影响实时增量任务的正常执行。数据兼容性:另外,一些现有的业务可能积累了大量的历史数据。以产品为例。在我们今天的理解中,moq是指最小起订量必须是一个数字,包装重量也必须是一个数字。在功能升级的迭代过程中,需要考虑与历史数据的兼容性。简化代码越简单的东西越可靠,越复杂的东西容错性越差。把简单的事情想复杂,把复杂的事情简单化。少用同步锁:在系统设计和编码中尽量使用无锁实现,避免不必要的死锁。产品也因为同步锁出现上线问题:慎用线程池:其实和同步锁类似,是比较危险的操作,一定要测试验证。容灾保护过载保护过载保护是保证系统整体可用性的重要手段之一。设计过载保护可以有效避免因流量问题导致系统不可用。所以我们的应用一定要有自我保护的能力,Web类应用接入流量清洗系统,服务类应用做好服务流量的限制。依赖降级目前的业务系统大多比较复杂,尤其是在分布式架构中,一个请求可能需要依赖多个系统的支持才能完成。调用链中的任何一个节点都可能影响整体稳定性。所以一方面要保证自身服务的稳定性,另一方面还要考虑依赖服务出现问题后如何保证主链路的可用性。依赖降级的前提是先明确依赖关系:1、弱依赖自动降级,比如校验。2.有些强依赖但是可以异步处理,降级后再异步重试。3、有些强依赖,不能异步,所以需要产品化,降级能力应该是产品设计的一部分。下图是产品发布环节的部分容灾和降级点:95%以上从未使用过。安全生产建设是这样的。很多维稳措施,大部分时候是不为人知的,真正用到的时候,都是关键时刻。监视、预警、监视和标准化一堆毫无意义的“系统错误”是无济于事的。标准错误代码和SPM监控目前是一种更好的做法。预警有效性和召回优先级:预警的有效性其实是比较难达到的。监测覆盖面越广,预警机制越灵敏,可能出现的警报越多,误报就越多。一开始先召回产品无人值守的样板房,逐步减少误报。这是一个持续治理的过程。报警分层:另外可以根据监控场景对报警进行分层,通过报警分层可以在一定程度上考虑召回和请勿打扰。重要业务系统监控推送至核心预警组,非故障场景或辅助定位的监控告警可推送至其他普通预警组。还需要对告警通知方式进行分层。比如成本率下降2%,可能是钉钉通知,下降5%需要打电话。测试回归测试回归是质量保证中非常重要的一部分。相信有些人会怀疑它的价值,一开始确实是费时费力,但又是一件困难而正确的事情。这里分享两点:单元测试是代码的魔镜,我们会发现通常情况下,很难跑出单个测试是因为代码设计不合理,耦合度太大。单一测试可以帮助您优化代码。单次测试是质量的保证。前端时间和我连线的前端同学突然对我说,“文清,没想到这个项目这么顺利。”这个项目也是我第一次在项目中亲自编写完整的单测代码。从我个人的实践经验来看,单体测试还是很有价值的,尤其是对于喜欢重构的人来说。巡检/流量回放我们目前的业务大多比较复杂,分支流程较多。比如产品的叶子类目有5000多个,每个类目的产品列表都不一样。这么多场景是人肉覆盖不了的,只能靠机器和自动化来回馈。前台页面场景检查是一种比较好的方式,它可以模拟用户操作。对于后台服务流量播放也是一种可行的方案。无人值守的产品样板房也采用巡检和流量回放的方式来保证发布质量。压力测试新服务上线前,需要对流量和性能进行评估,根据预估对服务进行压力测试,验证是否能够满足需求。同时,整个链路所能支持的QPS上限也需要通过压测来确定,以帮助我们合理评估系统水位。变更发布回过头来分析,我们会发现我们的大部分问题其实都是变更引入的。商技部三规九条规定了变更发布“可灰化、可监控、可回滚”的三大原则。发布前必须做好发布规划和审核,检查是否满足这三个要素。灰度分为功能灰度和发布灰度。功能灰度可以根据你团队的情况来设计。释放灰度,首先建议从进程中添加一个低流量的灰度环境。可以将部分流量劫持到低流量环境中,在低流量环境中可以提前发现一些重大问题。此外,对于符合条件的应用,也建议按单位/区域分别部署,在短时间内模拟蓝绿部署,为容灾和流量切换创造条件。回滚记得做回滚验证,不要没有暂停就完成回滚。因为之前遇到过一个案例,回滚越多,问题越严重。原因是当时的问题是远程缓存异常导致应用启动后数据异常。应急处理故障预防是减少问题发生的概率,而不是消除问题。根据墨菲定律,哪里有可能,哪里就会发生。所以,应急响应是我们的底线,也是保证高可用的重要一环。一旦容灾倒换出现故障,我们首先要做的不是定位原因,而是及时止血,快速恢复。容灾切换往往是快速恢复的首选,时效性最高。基于Web的应用:目前比较成熟的方案是vipserver容灾机制。原理也很简单,多台机器在vipserverkey下挂载,一般情况下,因为同机房的规则生效,请求都是在本机内调用。当需要容灾时,阻塞异常单元下的机器,将同一机房的无效流量分流到其他单元,达到跨单元容灾的效果。基于服务的应用:目前没有专门的标准方案。商品方式是使用代理服务在多地注册,在容灾切换时通过调用不同单位的代理服务来达到跨单位流量切换的效果。当线上出现变更回滚问题,如果不能通过容灾和流切换的方式解决,需要查看是否有变更发布,如果判断可能与变更相关,回滚先回来。最重要的是做好回滚验证。扩容重启遇到突发流量、下游系统缓慢、资源容量不足等情况,都可能导致应用本身负载不足。遇到资源瓶颈时,快速扩容是首选。这个时候就考验系统的灵活性了。目前有一些可行的方案:VPA:基于CPU/内存垂直弹性,调整CPU核心数和内存大小,但有上限。HPA:基于CPU/内存级别的弹性,缺点是扩展速度不够快,仍然需要经过应用启动的过程,应对突发的流量高峰可能不太可行。KPA:基于流量/响应级别的弹性,据说可以替代CSE。猜测应该可以通过重放内存镜像实现快速扩容。当容量无法扩展时,重启是解决系统性能问题的另一个重要手段。据不科学统计,90%的暂时未知的性能问题都可以通过重启得到暂时解决或一定程度的缓解。限流降级面对容量问题,扩容之外的另一种选择是限流降级。可以根据优先级限制边缘应用程序和非核心场景。分层和子场景:我们支持的业务必须有优先级,包括核心业务和边缘场景。当需要限流降级时,不能一刀切,先根据重要程度限制不重要的应用。分用户粒度:同样,我们服务的用户也有优先级。以商品为例,有付费商户和免费商户,所以在整体资源有限的情况下,可以先降级免费商户。基于资源占用的层次结构:比如商品场景中存在商品数量较多的商户。这些用户的一些操作可能会占用较大的系统资源。比较常见的是数据热点或者数据偏移,影响性能。因此,对于资源盗用者必须有相应的措施,比如禁止读写。如果紧急广播不能在短时间内恢复(参考1-5-10标准),则需要挂起紧急广播以减少用户访问。预编译各业务场景的紧急公告,必要时一键执行。应急参考应急预案有很多,但是在处理线上问题时,要分清轻重缓急,先做什么,再做什么,最后做什么。下图是一份编制好的应急响应SOP参考文件。它不是操作手册,出现问题时无法查阅,但每个人的脑海中应该都有这样的画面,熟悉在线应急处理流程。故障演练所有的安全生产措施都做好了,那么如何验证它是否能正常工作,故障演练是一个很好的手段。一方面可以检验稳定保障措施的有效性,另一方面也可以锻炼技术人员的应急处置能力。我不止一次看到有人在排除故障时感到紧张并且手在颤抖。因此,常态化的故障演练,尤其是突击演练,可以让更多人参与到应急处理过程中,在实际出现问题时多练习才能有条不紊。总结和总结检讨和改进中的问题并不可怕。重要的是分析流程中存在哪些问题,流程机制存在哪些漏洞,从而帮助我们做得更好。商品稳定措施是在一次次面对各种问题的过程中总结实践出来的。经验分享和总结是利己之举,以免重蹈覆辙。分享经验是无私的,帮助别人不落入同一个坑。另外,技术风险防控平台的故障大部分是公开的,可以查看自己的应用是否存在同样的风险。风险意识总结:维稳最重要的是要有风险意识,保持对生产的敬畏之心。只有有风险意识的人,才会注重产学研全过程的稳定。比如引入一个新的依赖,它会考虑如果异常对自己有什么影响,有没有容灾方案,能不能降级等等。另外,风险意识会逐渐养成一些良好的习惯,比如定期检查系统的水位。每天早上刚到公司做的第一件事就是查看监控和报警。减少损失:应急处置的首要原则是尽量减少损失,先恢复,再定位。风险自愈:安全生产建设的最终目标应该是具备自愈能力,能够在系统出现问题时及时介入并自动恢复。学习人体,他有三重免疫系统,一百年还算稳定的系统。——鲁肃因此,要事前有风险意识,事中及时止损,事后查漏补缺,构建风险自愈能力。更重要的是要秉持长期主义精神,相信我们终能做好安全生产工作。