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