摘要:系统高可用是一个宏大的命题,从设计思想、架构原则到工程能力、服务管理等等方方面面,每个视角单拆出来都不是一篇文章可以解决的。本文将从大局上全面系统地梳理高可用系统架构,起到一个提纲挈领的作用。
分享概要
前言:海恩法则和墨菲定律
一、可用性
二、高可用架构设计总体思想
三、代码架构高可用
四、容量评估和规划
五、高可用系统架构设计
六、高可用的数据层架构
七、服务运营
八、高质量的服务管理
九、能力和职责
系统高可用是一个宏大的命题,从设计思想、架构原则到工程能力、服务管理等等方方面面,每个视角单拆出来都不是一篇文章可以解决的。本文将从大局上全面系统地梳理高可用系统架构,起到一个提纲挈领的作用。
前言:海恩法则和墨菲定律
海恩法则:
事故的发生是量的积累的结果。再好的技术、再完美的规章 , 在实际操作层面也无法取代人自身的素质和责任心 。薛定谔的猫:
“薛定谔的猫”告诉我们,事物发展不是确定的,而是量子态的叠加。墨菲定律:
任何事情都没有表面看起来那么简单 。所有事情的发展都会比你预计的时间长 。会出错的事总会出错。如果你担心某种情况发生,那么它更有可能发生 。蝴蝶效应:
世界会因一些微小因素的变动,而发生很大的变化。熵增原理:
“热力学第二定律”(熵增原理)告诉我们,世界总是在变得更加混乱无序。警示我们,在互联网公司里,对生产环境发生的任何怪异现象和问题都不要轻易忽视,对于其背后的原因一定要彻查。同样,海恩法则也强调任何严重事故的背后都是多次小问题的积累,积累到一定的量级后会导致质变,严重的问题就会浮出水面。那么,我们需要对线上服务产生的任何征兆,哪怕是一个小问题,也要刨根问底:这就需要我们有技术攻关的能力,对任何现象都要秉着以下原则:为什么发生?发生了怎么应对?怎么恢复?怎么避免?对问题要彻查,不能因为问题的现象不明显而忽略 。
个人学习实践和总结笔记:
架构设计的愿景就是高可用、高性能、高扩展、高效率。为了实现架构设计四高愿景,需要实现自动化系统目标:
标准化。流程自助化。可视化:可观测系统各项指标、包括全链路跟踪。自动化:ci/cd 自动化部署。精细化:监控平台、数据分析精细化。要实现这些,在中小型公司,架构师可以 hold 住,而在大企业/大厂里面,虾兵蟹将是无法搞定的,至少是 vp 级别来推动。
整个自动化体系需要多平台、系统、工具来解决各类场景的自动化执行,各个平台之间要相互联动形成体系化,而不是相互脱离,各自发展。目前还没看到一站式平台可以串接需求、设计、开发、测试、发布、运维等,而高可用系统架构设计要从产品、开发、运维、硬件等全方位去统筹综合考虑形成一体化。
本文着眼整体,全局规划、分层设计,提供一个稳定性建设总体规划的参考。如果缺什么补什么,没有总体规划的建设,后面做出的系统往往是相互割裂,定位不清、无法持续集成,最后就是沦为一堆零散工具而无法形成体系(个人拙见,仅供参考)。紫色部分是我们最近半年主要关注和建设的部分:
一、可用性
1、业务可用性指标
所谓业务可用性(availability)也即系统正常运行时间的百分比,架构组/SRE 最主要的 KPI (Key Performance Indicators 关键业绩指标)。对于我们提供的服务(web/api)来说,现在业界更倾向用 N 个 9 来量化可用性,最常说的就是类似“4个9(也就是99.99%)”的可用性。
故障时间=故障修复时间点-故障发现(报告)时间点
服务年度可用时间%=(1-故障时间/年度时间)× 100%。
2、故障的度量与考核
对管理者/部门而言:可用性是产品的整体考核指标。对于研发工程师而言:使用故障分来考核:
高危S级事故故障一旦出现故障,可能会导致服务整体不可用100严重A级故障客户明显感知服务异常:错误的回答20中级B级故障客户能够感知服务异常:响应比较慢5一般C级故障服务出现短时间内抖动1考核指标可以使用故障分度量:故障分=故障时间分钟* 故障级别权重。
3、服务分级
如果是一个分布式架构设计,系统由很多微服务组成,所有的服务可用性不可能都是统一的标准。
为了提高我们服务可用性,我们需要对服务进行分类管理并明确每个服务级别的可用性要求。
类别服务可用性要求描述一级核心服务核心产品或者服务99.99%(全年53分钟不可用)系统引擎部分:一旦出现故障,整个系统瘫痪二级重要服务重要的产品功能99.95%(全年260分钟不可用)类比汽车轮子:该服务出现问题,该重要功能不可用。三级一般服务一般功能99.9%(全年8.8小时不可用)类比汽车倒车影像:该部分出现问题,稍微影响用户体验四级工具服务工具类是服务0.99非业务功能:比如爬虫、管理后台、运维工具二、高可用架构设计总体思想
高可用系统的架构设计,要从产品需求、代码开发、运维部署、故障管理等系统全生命周期进行全方位去考量和设计,核心思想就是:
故障事前:故障预防,总结经验,做到有智慧的绕开问题。故障发现:及时发现,通过完善观测平台及时发现问题吧。故障恢复:快速恢复,做好应急预案降低故障影响。故障总结:复盘总结故障问题,层层剖析问题产生的原因,由表及里分析问题发生的本质。高可用系统的架构设计思想包括但不限于:
1、系统设计
产品层面:主要是故障发生后的兜底策略等。例如生成式大模型考虑远程代码执行漏洞,设计时尽量避免将用户输入内容作为代码部分进行执行,如需执行,需要将服务部署在经过安全隔离的环境中。
代码架构:系统都是研发人员设计和编码写出来的,因此首先要对研发层面有一个代码架构规范,例如编码规范、如果代码架构设计不足,就会造成影响全局的架构设计。同时借助代码分析工具,分析代码可能存在的 bug 或者安全漏洞,例如对于业务设计需要将用户输入内容作为代码部分进行执行,需要应用程序做安全防范。
做好容量规划和评估:主要是让开发人员对系统要抗住的 QPS 量级有一个基本认知,方便后续进行合理的架构设计和演进。
2、故障预防
应用层面的高可用预防:主要是负载均衡、弹性扩缩容、异步解耦、故障容错、过载保护等。
数据层面的高可用预防:主要是冗余备份(热备,冷备)、失效转移(确认,转移,恢复)等。
模块变更机制预防:核心模块变更、新数据集、模型上线、流程变更、新模块、核心 sdk 变更等上线的规范化与审批。
模块健康度系统:查询系统健康状态,如调模块调用情况、资源使用情况、进程情况、服务处理状态等,快速判断故障模块是否异常。我们健康检查系统是通过拉取各个运营系统(容量系统、变更系统、监控系统等)的模块各项指标数据,结合历史故障数据,分析模块可能存在的异常。
3、故障发现
运维层面发现:主要是发布测试、监控告警、容灾、故障演练等。
完善报警治理:四五星报警周期治理,优化指标报警方式,完善新指标,提高报警的准确率、可靠性、时效性。
故障大屏:全面了解整个业务的健康状况。
4、故障恢复
制定《故障管理规范》
做好故障应急预案,建设大招平台:针对特定故障建立相应应急预案,在出现故障后使用相应的应急预案进行快速恢复,最大程度降低影响范围。
切流系统工具:可用区/IDC 出现故障问题,可以切流到其他可用区。
5、故障总结
故障复盘:复盘总结每个故障问题,层层剖析问题产生的原因,由表及里分析问题发生的本质。
故障汇总:进行故障分类、定级、影响等,如果系统组够大当然需要故障管理系统来管理。
三、代码架构高可用
系统都是研发人员设计和编码写出来的,因此首先要对研发层面有一个代码架构规范,例如编码规范、如果代码架构设计不足,就会造成影响全局的架构设计。例如我们之前一个系统,很多功能耦合在一个大单体应用里面,某个功能接口出现问题,就导致整个系统崩溃。
通过规范研发流程可以让我们更好地去研发和维护一个高可用的系统:
1、制定研发规范和原则
设计文档评审原则:例如新项目重构项目的设计文档一定要评审,通过评审及时发现问题。
遵守代码架构规范:包括编码规范、项目层次规范、模块规范、依赖规范等。工程的顶层目录结构设计规范,团队内部保持统一,尽量简洁,提高代码的可维护性。遵循团队内部的代码规范 代码规范可以大大减少 bug 并且提高可用性。
2、设计阶段
原则上新功能或者模块要有设计文档,规范好相关方案设计文档的模板和提纲,让团队内部保持统一,例如
架构:数据模型、接口定义;
流程:正常流程、异常场景设计。
交叉影响:配置接口、数据库、可靠性、性能等。
设计文档能够指导整个开发流程,包括编码、接口文档和测试用例,所有出现的问题都可以追溯到设计文档中。
设计文档是否需要评审:新项目、重构项目、大的系统优化或者升级一定要评审。
3、编码阶段
遵循代码规范进行编码。
单元测试:代码编写完需要有一定的单测能力来保证代码的健壮性,同时也能保障我们后续调整逻辑或者优化的时候可以保证代码的稳定。
日志规范:对日志进行分类,错误日志和业务日志尽量分开存放,便于开发人员查看,也便于通过日志对系统进行及时监控。要接入统一日志系统、能够做到分布式链路追踪。
代码安全:借助代码分析工具,分析代码可能存在的 bug 或者安全漏洞。
API 安全建议:
API 数据传输采用随机、不可预测、复杂的 Token 机制;对 API 调用的数据、功能等实施严格访问控制,并严格设置白名单清单;严格定义 API 输入数据类型,并校验、过滤所有传入数据;对 API 的请求采用公开密码算法进行数字签名和校验;加密 API 请求流量,可采用非对称加密算法逐个加密敏感信息字段,加密结果需做 Base64 编码等;设置 API 请求频率限制策略。4、线上发布阶段
系统灰度发布:确保故障不会造成大面积影响。
接口监控完善,确保及时发现发布过程可能存在的问题。
总结一下代码层面可能引发的故障/问题:
代码逻辑 bug。超时配置不合理。新老版本功能兼容性问题。代码缺陷引发 core 或者内存溢出。代码安全漏洞:如 sql 注入等。日志不规范导致无法快速定位问题,小问题演变故障。四、容量评估和规划
1、容量评估
明确系统的业务场景,如果是管理工具平台相关,可能不太关注 QPS 相关指标。如果是应对业务系统,一般都要考虑 QPS 均值和峰值。
如果是新系统,可以先搜集产品和运营同学对业务的大体预估,可以大体估算出 QPS 相关指标。如果是老系统,那么就可以根据历史数据来评估。评估的时候,要从一个整体角度来看全局的量级,然后再细化到每个子业务模块要承载的量级。
2、容量规划
是指系统在设计的时候,就要能够初步规划好系统大致能够维持的量级,比如是十万级还是百万级别的请求量,或者更多。不同量级对应的系统架构设计完全不一样。尤其到了千万、亿级别的量级的时候,架构设计会有更多的考量。
这里值得注意的是,不需要在一开始就设计出远超当前业务真实流量的系统,要根据业务实际情况来设计。同时,容量规划还涉及到:系统上下游的各个模块、依赖的存储、依赖的三方服务分别需要多少资源,需要有一个相对可量化的数据。容量规划阶段更多是要依靠自身和团队的经验,比如要了解系统的 log 的性能、redis 的性能、rpc 接口的性能、服务化框架的性能等等,然后根据各种组件的性能来综合评估已经设计的系统的整体性能情况。
3、性能压测
容量评估和容量规划之后,还需要做就是性能压测。最好是能够做到全链路压测。
性能压测的目的:
一是确保系统的容量规划是否准确。如果系统规划的容量是能够抗千万级别的 QPS。那么实际上真的能够抗住吗 ?在上线之前首先要根据经验来判断,
其次是通过性能压测得暴露系统爆弱环节。性能压测要关注的指标很多,但是重点要关注的是两个指标:一个是 QPS,一个是响应耗时要确保压测的结果符合预期。
我们中心的 SRE 压测任务:建立常态化压测机制,使核心链路在目标 QPS 下保持稳定。
搭建压测平台,定期根据实际现网请求生成源模块到目标流量的放大系数,测出不同业务资源占用率峰值,辅助容量评估。
五、高可用系统架构设计
1、架构分层
为了降低应用服务管理的复杂性,我们把整个系统划分成若干个层次,每一层专注解决某个领域的问题,并向上提供服务。
典型架构分层设计如下:按照功能处理顺序划分应用,这是面向业务深度的划分。同时禁止逆向调用。
每个公司的架构分层可能不一样,但是目的都是为了统一架构术语,方便团队内部沟通。
接入层:主要流量入口,经过简单应用层:直接对外提供产品功能,例如网站、API 接口等。应用层不包含复杂的业务逻辑,只做呈现和转换。服务层:根据业务领域每个子域单独一个服务,分而治之。数据层:数据库和 NoSQL,文件存储等。我们先列出目前我们系统有哪些环节,每个环节是否薄弱. 客户端访问服务器端,经过很多环节,任何环节出问题,都不能访问:
接入层:
dns 被劫持:域名是否使用 https。黑客攻击:是否有弱密,服务器权限,数据库权限。ddos 攻击:是否有必要使用高防 IP 接入流量。CC 攻击:免费和收费版域名分开,网关是否有限流和防刷措施。应用层/服务层:
服务代码 bug。服务引发 core/内存溢出。依赖第三方服务不可用。自身没有做好监控告警,不能及时发现问题。上线变更操作导致故障。流量过载导致服务不可用。服务没有做好性能压测,无法应对流量高峰影响。数据层:
数据一致性问题。数据误操作(如误删除)。数据库损坏,如服务器磁盘损坏导致数据库不可用等。整体架构层:
服务器故障。系统整体架构容量规划不合理,无法应对流量高峰影响。缺少切流工具无法降低故障影响。2、接入高层可用设计
在接入层,这里主要是架构运维的高可用要求的事项:
1)域名高可用
域名规范解析和规范化管理,应该制定《域名规范管理说明》,例如根据产品重要等级,制定使用高防 IP 的策略。域名解析 DNS 防劫持:必须使用 https。ddos 攻击:是否有必要使用高防 IP 接入流量。2)规范 API 管理
3)明确各个 API 限流和防刷策略
接入层架构参考:
当系统对外的接口繁多,同时不同的项目不同的接口,没有一个统一管理的系统,也不方便监控和跟踪 api 的健康状况。需要 api 网关,方便 api 日常管理,包括控版本管理,升级,回滚。更重要的是 api 网关起到限流防刷(CC 攻击)作用,保护后端服务。
3、应用层高可用设计
1)应用层设计主要原则
可以水平扩展:通过接入层的负载均衡,实现故障自动转移。无状态设计:无状态的系统更利于水平扩展,更利于做负载均衡。状态是系统的吞吐量、易用性、可用性、性能和可扩展性的大敌,要尽最大可能避免。
回滚设计 :确保系统可以向后兼容,如果应用服务上线后出现 bug,可以紧急回滚。
灰度发布:结合接入层设计 A/B 功能,实现灰度发布,比如按 ip,请求参数等分发流量。
幂等设计:系统中的多次操作,不管多少次,都应该产生一样的效果,或返回一样的效果。
调用设置超时:调用服务一旦超时,通信框架应该返回异常,调用方根据调度策略选择重试或者请求转移。
异步调用:调用不重要的服务同步变为异步。
降级处理:服务具备降级能力。
运行环境隔离:对于生成式大模型的远程代码执行设计原则:在独立且无敏感数据的环境中执行代码任务,同时限制资源,任务执行完成后将环境销毁。
2)应用服务可能存在异常的情况
服务内部出错、异常。服务响应超时。
服务负载过高;
网络链路延迟或中断;
服务依赖链中部分依赖 SLA 不达标,造成整体服务不可用;
服务链条过长,造成 SLA 整体不可控;
3)提高服务可用性机制
解决的思路:服务分级治理、服务隔离、自我保护、失效转移或恢复、服务降级。
服务分级治理:将服务分级管理,对于核心/重要的,使用更好硬件并隔离部署,避免连锁的故障响应。服务隔离措施:依据服务重要性分级或流量特点、用户画像等,从物理上隔离服 务。将服务使用的资源(CPU、线程、IO 等)隔离,主要使用舱壁模式;比如核心服务单独部署,三级服务可以混合部署。自我保护措施:快速失败(failfast)、限流、调用超时、熔断(Resilience4j);失效转移机制:失效检测、失效重试、失效转移(failover)、失效恢复(failback);服务降级措施:在流量高峰,服务可能由于大量的并发调用导致性能下降,最后引起服务不可用。为了保证网络和核心流程可用,需要服务降级。依据依赖服务的重要性或依赖程度(强、弱),实行相应服务降级手段:
同步变异步:低优先级服务调用同步变异步,比如日志存储等。降级开关,拒绝部分服务:通过流量网关做相应的限流甚至直接拒绝服务。4)具体容错机制说明
①failover:失效转移:失败自动切换”,是一种备份操作模式。
Fail-Over 失效转移是一种备份操作模式,当主要组件异常时,其功能转移到备份组件。其要点在于有主有备,主故障时系统能够自动地切换到备用服务上, 保证服务继续运行。比如内部调用 nginx 代理,不能是单点提供服务,需要有主备模式,通过 keep-alive 机制自动切换到备用服务上。核心和重要服务都需要按 N+1 原则部署。通常用于读操作;
②failfast:快速失败:快速识别,就是只发起一次调用,失败后立即报错。
fail-fast 是“快速失败”,尽可能的发现系统中的错误,使系统能够按照事先设定好的错误的流程执行,对应的方式是“fault-tolerant(错误容忍)”。以 JAVA 集合(Collection)的快速失败为例,当多个线程对同一个集合的内容进行操作时,就可能会产生 fail-fast 事件。例如:当某一个线程 A 通过 iterator 去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程 A 访问集合时,就会抛出 ConcurrentModificationException 异常(发现错误执行设定好的错误的流程),产生 fail-fast 事件。通常用于非幂等性的写操作;
③failback:故障自动恢复:故障转移(Fail-Over)之后,服务能够自动恢复。
Fail-over 之后的自动恢复,在簇网络系统(有两台或多台服务器互联的网络)中,由于要某台服务器进行维修,需要网络资源和服务暂时重定向到备用系统。在此之后将网络资源和服务器恢复为由原始主机提供的过程,称为自动恢复。
④failsafe:失效安全:出现异常时,直接忽略。即使发生了故障,也不会对系统/服务造成伤害,或尽量减少伤害。
Fail-Safe 的含义为“失效安全”,即使在故障的情况下也不会造成伤害或者尽量减少伤害。维基百科上一个形象的例子是红绿灯的“冲突监测模块”当监测到错误或者冲突的信号时会将十字路口的红绿灯变为闪烁错误模式,而不是全部显示为绿灯。通常用于写入审计日志等操作;
5)接口幂等设计
应用调用服务失败后,会将请求重新转发到其他服务上,这个时候要进行幂等性的判断,有可能服务调用失败是网络问题导致的,实际上业务处理成功了。
4、服务分级治理
服务层设计最主要原则:服务分级管理。
线上有很多服务,每个服务的可用性要求不一样,我们需要先这些服务做分级。
各级服务的部署原则:核心服务:独立服务器且 N+1 部署。三级和四级服务可以共享服务器部署。各级服务上线发布原则:核心和重要服务:晚上12点上线。,三级和四级随时可上线。各级服务监控原则。一级核心服务:
定义:
可用性:99.99%,极高可用性,全年53分钟不可用。
条件:
服务自身可用性:99.99%。依赖数据资源服务可用性要求:(应用服务研发方自定义)。依赖第三方服务可用性要求:(应用服务研发方自定义)。需要部署的服务器数:N 台。服务设计满足以下原则:
冗余 N+1 部署:故障自动转移到多部署一个节点,避免单点问题。可监控:服务流量预警、端口存活、进程占用的资源、服务接口功能逻辑是否正常,应用 FGC 等情况。可回滚、灰度:灰度部署服务,部署的服务出现问题可快速回滚。可独立部署:可以直接在运维平台打包部署,而不需要依赖其他服务部署完成后才能部署运行。可独立测试:可以单独测试。水平扩展:流量激增可快速扩容。异步设计:服务需要通知第三方服务,必须通过消息队列进行异步方式完成。幂等设计:服务可以重复调用,不影响结果。可容错:自身有容错和修复能力:隔离手段:服务使用的资源(CPU、线程、IO 等)隔离,使用舱壁模式;
自我保护手段:快速失败(failfast)、流控、超时、熔断;
失效转移或恢复手段:失效检测、重试、转移(failover)、回退恢复(failback);
降级手段:依据依赖服务的重要性或依赖程度(强、弱),同步变异步,降级开关、拒绝部分服务等。
二级重要服务:
定义:
可用性99.95%(故障具备自动恢复的能力,全年260分钟不可用)。
条件:
服务自身可用性:99.95%。依赖数据资源服务可用性要求:(应用服务研发方自定义)。依赖第三方服务可用性要求:(应用服务研发方自定义)。需要部署的服务器数:N 台。服务设计满足以下原则:
冗余 N+1 部署:故障自动转移到多部署一个节点,避免单点问题。可监控:监控进程、端口存活、进程占用的资源,应用 FGC 等。可回滚、灰度:灰度部署服务,部署的服务出现问题可快速回滚。故障隔离:服务器只部署唯一该应用服务,该应用服务出现问题,只影响自身服务问题。可独立部署:可以直接在运维平台打包部署,而不需要依赖其他服务部署完成后才能部署运行。可独立测试:可以单独测试。水平扩展:流量激增可快速扩容。可容错:自身有容错和修复能力。三级一般服务:
定义:
可用性99.9%(全年8.8小时不可用)。
条件:
服务自身可用性:99.95%。依赖数据资源服务可用性要求:(应用服务研发方自定义)。依赖第三方服务可用性要求:(应用服务研发方自定义)。需要部署的服务器数:N台。服务设计满足以下原则:
冗余 N+1 部署:可以单点部署。可监控:可监控服务进程、端口存活是否正常。可回滚、灰度:灰度部署服务,部署的服务出现问题可快速回滚。故障隔离:一个服务器上可以部署多个应用,但保证服务器资源充足。可独立部署:需要独立部署。可独立测试:可以单独测试。水平扩展:流量激增可快速扩容。可容错:需要具备一般的容错能力。四级工具服务:
定义:
条件:
服务自身可用性:99.9%。依赖数据资源服务可用性要求:(应用服务研发方自定义)。依赖第三方服务可用性要求:(应用服务研发方自定义)。需要部署的服务器数:N 台。服务设计满足以下原则:
冗余 N+1 部署:可以单点部署,进程存活就可以。可监控:不需要监控。可回滚、灰度:只要部署成功就可以。故障隔离:哪个服务器有资源就可以部署。可独立部署:不用考虑。可独立测试:不用考虑。水平扩展:不用考虑。可容错:不用考虑。来源:dbaplus社群一点号