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

2021年7月13日失败后,哔哩哔哩的SRE稳定性保障揭晓

时间:2023-03-11 22:40:51 科技观察

哔哩哔哩SRE开发5年。2017年之前,哔哩哔哩没有SRE。当时的主要职责是优先考虑效率和需求响应(例如变更、标准化、告警治理和琐事优化)。2018年引入SRE文化,开始了解业务架构,推动阅读多元主动建设,探索Oncall制度/审阅文化在B站SRE落地,2019年正式进入落地阶段。首先,我们优化了琐事(释放人力),通过平台自行审批工单变更。随后探索了SLO体系,包括平台、基于SLO的服务分级体系、应急响应体系、审核平台、问题管理体系建设。2020年,SRE稳定系统初步完善,B站做了20年的很多活动(S10、跨夜、最美夜等)。有了故障前的应急响应和故障后的恢复,我们开始探索混沌工程和故障前的故障演练;质量之后就是容量,B站是容器化部署(探索PaaS容量管理系统)。经过一系列落地,2020年度稳定体系已初步完善。2021年至今,SRE不断转型,逐步从Oncall体系向BP体系转变,更加注重稳定性、降本增效。之前做过的多活动、服务分类、SLO,今年都会重新优化构建。现在全员都在做SRE改造。稳定性保证下面介绍改造过程中的具体分析。首先是稳定性保障(高可用、多活、容量管理、活动保障)。对于高可用,首先要看整体架构。用户首先从CDN层到接入层。接入层有4层LB/7层SLB,以及逐步推广的APIGW。进入服务层后,服务端会有一个BFF(服务网关),下面是Service、Job、Admin等,中间件主要是MQ、数据同步Canal、DTS、消息通知Notify、缓存;存储层面为关系型数据库、KV、对象存储、ES等;再往下是可观察性和效率系统;底层是稳定性系统,包括服务分类&SLO、混沌工程/故障演练、多活容灾/应急预案管理、问题管理/事件分析、降容/降本和活动保障。接入层这是接入层架构。用户通过DNS或降级HTTPDNS访问CDN。CDN通过机房边缘的POP点将流量聚合到机房。机房有不同的可用区(逻辑概念),每个可用区主要是SLB和APIGW。常见故障有几种:网络故障:边缘节点返回机房(运营商公网故障);机房故障:一般比较少见(主要看概率);组件故障:如SLB节点故障等;服务故障:SLB/APIGW代理服务等;接入层高可用解决方案,如DNS降级HTTPDNS、多CDN节点动态路由、APP边缘节点网络层故障降级第三方CDN重试;多POP点进行流量汇聚,多条线路源站互为备份。如果单个可用区的SLB发生故障,CDN支持从其他可用区跨专线回源。对于后端服务故障,SLB可以发现服务区内的所有节点;单个可用区中的服务故障节点可以自动降级。SLB还支持普通的降级、容灾和限流。终于在7.13上出现了故障。重建SLB花了一个小时。失败后,对负载均衡的初始化和重构做了很多优化。现在可以在5分钟内重建一套SLB。APIGateway的高可用和SLB类似,这里不再赘述。服务层说了南北方向(接入层),现在来看东西方向。对于SRE,必须了解这一层(微服务架构)。如果你不明白,一旦服务失败,你就不能给企业失败止损方案。服务调用是通过内部Discovery服务发现组件进行的。服务采用多活部署(可用区A和B),每个机房服务部署在多个节点上。单机算力不同,取决于K8s有没有规划算力)。这个过程中可能会出现问题,比如容器节点的计算能力不同,服务B流量过载,可用区失效等。还有几个高可用的能力:服务调用有P2C算法,服务A调用服务B,以及服务B将返回它自己的节点。CPU使用率,ServiceA根据本次响应的RT选择一个最优节点(包括ServiceB各节点的CPU使用率/成功率),虽然ServiceB不同节点的CPU算力不同,但最终的CPUusage速率相同;熔断,当服务B发生故障时,服务A进行熔断;降级场景有两种:一种是可用区降级,当服务B在某个可用区发生故障时,服务A可以通过服务调用B发现组件降级到其他可用区;另外,在做多活的时候,如果服务没有部署在这个可用区,就把服务降级到其他可用区;内容质量下降,服务B失败,服务A可以访问本地缓存/降级服务。在我们的场景中,如果播放地址失效,会降级为容灾播放地址。B站首页的APP推荐可能是动态的,推荐系统失效也会给用户降级热门视频。限流有两种,一种是微服务框架端的全局限流,是框架层面的拦截器,B站内部主要是Golang,使用Kratos框架,实现分布式限流基于框架。第二部分是动态限流,不需要预先配置。它是基于谷歌的TCPBBR算法实现的。会自动分析每个容器节点的CPU使用率/成功QPS等,做自适应保护。中间件服务的另一个失败场景是中间件依赖。查看核心服务对中间件的依赖,比如服务写入数据,可以接受评论弹幕数据的最终一致性,将写入的数据抛给MQ,使用Job消费消息队列,写入DB。DB层面会挂一个Canal组件来消费数据库binlog,将消息写回消息队列,然后flush到缓存。我们最常用的组件是缓存/MQ/DB。生产系统缓存有RedisCluster、Redis单节点/分布式、Memcache三种模式(内部推荐按此顺序)。每种语言在生产环境SDK中都有各种错误。比如短连接风波,在拜年活动之前,活动刚开始,收费服务就挂了。后来发现Jedis的并发数超过配置的Total后,后面的连接会变成短链接。Redis单线程机制,长连接和短连接性能相差10倍,一旦遇到短连接,性能会急剧下降。我们使用过包括Redis在内的各种语言,向某个节点发送一些集群节点或者slotstorm请求,甚至可能固定向某个节点发送请求,快速杀死一个节点。当集群风暴发生时,遇到短连接就会发生雪崩,没有办法恢复。恢复的唯一方法是重建缓存或冷重启。后来我们对缓存做了一个统一的proxy代理(sidecar模式,或者通过proxylesssdk)。为了降本增效,我们将单体容器改为sdk方式进行部署和接入。消息队列主要用于临时持久化,异步写入DB。中心化代理,不以sidecar方式部署,我们内部支持redis和gRPC。2020年,B站做了很多扩圈活动,kafka话题数量激增。当时代理的是kafka的SaramaSDK,同样被炸,故障不断。数据库层面,内部提供数据库代理,以sidecar方式部署。业务读写分离,业务透明。拦截异常SQL,比如慢SQL。为了降低成本,提高效率,现在也提供了SDK模式。我在架构层面提到了很多内容。SRE必须了解业务架构。如果他们不了解架构,他们只能做日常所需(这与管道无异)。不懂业务架构的SRE是没有灵魂的。那么有了高可用,B站就不会爆了吗?2021.7.13失败,哔哩哔哩/微博/知乎全网热搜,我们来做个回顾。2021.7.13故障晚上10:522021年7月13日,用户反映B站不可用,大量内部服务和域名接入层告警不可用。10点57分,Oncall发现SLB故障。11点17分,内网登录也受到影响,核心成员17点17分才进入内网系统。11时23分,独活活的生意基本恢复。多活机房SLB容量过载,多活机房业务层正常/接入层宕机(容量增加了4倍左右)。11时后,车流逐渐减少,重启恢复。当时直播业务也很活跃,首页界面没有分流到多个机房(所以没有恢复)。11点17分登录分析问题,开始怀疑是SLB组件的问题。联系到最近在SLB层面对引擎的Lua层做了三处修改,但是没有恢复。这个时候,有两个选择。一是继续排查问题(底层Debug,SLB具体是什么故障?);二是给商家一个可预见的止损方法。当时我选择新建一个SLB来尝试隔离流量,恢复业务。然后验证流量隔离是否有效。1:00左右,新建SLB全部完成,开始业务流量切换。1点40分左右,所有服务已经切换到新集群,核心服务全部恢复。从这里可以看出很多问题:用户的链路和办公网络的链路没有完全隔离,在11点17分陆续进入内网系统。有的办公网后台直接放在公网上供用户访问,办公网放在公网上。也会使用公网SLB(不完全隔离)。多活并没有像预期的那样立即生效。来自多活机房的SLB流量只支持读,不支持写。人手短缺。故障止损和故障排除不能并行。1点左右新建一个,稍后再尝试排错。组件故障预案不完善,处理时间长(创建集群、下发配置、配置公网IP耗时近一个小时)。复杂事故影响范围大,故障定位成本高。同时我也看到,多活是机房级出现故障时容灾止损最快的方案!从7.13故障可以看出:多动座容量不足。机房与业务多活定位关系混乱,流量调度层不支持基于用户特征的细粒度流量分片。它严重依赖CDN平台。多活接入层/用户流量调度分发层在CDN层面实现,没有多活统一管控平台。多活元信息也缺乏平台管理,比如哪些业务有多活(多活就是同城双活/异地容灾),业务有什么规则支持多活。高可用-多活规范了多活元信息的定义,以及业务多活CRG的定义(CRG定义是指蚂蚁的多活模式)。Gzone服务(全局共享服务),可以在用户之间共享数据,在一个可用区写入,在其他可用区读取(数据强一致性)。在B站场景中,视频播放/番剧/电影/视频/稿件信息/直播间信息数据都是平台类数据,这类数据不需要单元化,用户之间共享。Rzone单元化业务(用户管道数据),多可用区切片写入/读取,B站用户维度数据为社区场景(评论/弹幕/动态等适合单元化场景)。Czone有点类似于Gzone。Czone的所有可用区都可以在Gzone和Rzone之间写入/读取(非单元化)。这种情况要求业务接受某些数据延迟和不一致。整理机房。当时上海有很多机房(都在一个区,机房的位置比较混乱)。经过梳理,将上海4个机房划分为两个逻辑可用区,在上海部署同城双活(Gzone类型服务);江苏可用区作为Rzone单元化服务部署,模拟异地;我们还规划了华北/华南可用区,做单元化和Czone服务部署(30ms-40ms网络延迟)。高可用——同城双活组件也进行了优化。流量的路由器在DCDN层面分发到各个机房。每个机房的服务都是读写存储,主要是KV,DB,访问的Proxy。到电脑室。通过GZS做多活状态管理。这些组件的优化主要有:DCDN支持用户Mid和设备ID做一致性哈希,支持多个机房同时动态加权。DCDN是基于Nginx构建的,后来重新开发支持多机房的动态权重。原来的多活服务不支持写入,在多活机房没有本地化部署写链路的感觉。核心环节一??定要写到本地,微服务框架支持写感知,弱依赖服务返回宿主机房。存储层面,最早的多活没有Proxy组件,需要业务决定在哪个机房读/写(管理成本特别高)。后来我们统一了Proxy来进行业务访问管理。KV存储原本不支持机房部署,不支持容灾切换,后来进行了改造优化。包括TIDB也支持多机房容灾切换。GZS使用GZS管理业务多活动元信息、多活动定义、切片编排/可视化。GZS:InvokerGZS内部称为invoker,基于API的元信息管理,结合内部CMDB系统(CMDB存储业务元信息/审批平台),同时链接多个内部多活组件,如DCDN/KV存储/DB存储/APIGW等组件进行流量切换。核心是对以上四部分进行优化:多活规则元信息获取方式优化平台多活和同城双活断电演练多活排布、准入流程优化审批、检查、可观察性加强实际多活主动编排流程是的,编排先选择业务(评论/弹幕/播放),选择业务类型(同城双活/单元化),编排接入层的规则。数据库层的安排是KV和DB。需要整理各个维度的多维度资源,业务可以发起切入。商业和商业领域。在业务域(直播/电商/社区),业务域下会有业务(社区有评论/弹幕/点赞等),分流编排为流量编排或存储编排。切割完成后触发批准。审核通过后就是检查,主要由三部分组成:容量检查,CPU/MEM;连接池,当流量切换到另一个机房时,流量可能翻倍;以及数据库/缓存连接池是否可以处理;限流,微服务框架也有很多限流配置,限流能不能承受。延期检验主要针对仓储层面的检验。现在是同城双活,延迟基本没问题。隔离检查,是否跨业务混存?比如评论业务是否使用动态/弹幕DB和KV。Observable,检查指标是可观察的,连接池和限流怎么变化?业务多活流量规则,流量切换是否达到预期?是否将流量从1:1切换为1:0;业务涉及核心应用SLO指标。除了质量,上面提到的降本增效想必是今年很多互联网公司的目标。让我们谈谈如何通过容量管理来降低成本和提高效率。容量管理在进行容量管理之前,SRE们要思考一个问题,应该管理什么容量?在整个系统架构中,有一个接入层(核心是带宽);应用层的核心是计算资源;DB和storage,也就是存储资源。对于SRE来说,容量管理的核心应该是应用。谁关注容量管理?不同的角色有不同的关注点,比如R&D/SRE/平台/预算组等,目标收益是想清楚再做。下面是每个角色对容量管理的期望:研发的核心是资源扩容,自动扩容,发布/回滚不受资源限制。SRE不仅要关注服务资源的利用率,还要关注弹性伸缩、部门资源水平、降本增效。部门维度,关注部门资源水位、利用率、部门成本、部门TopN服务报表。在平台维度上,容器平台着重Buffer、平台超卖、资源组合、资源利润率、降本增效。成本团队负责资源使用、计费等工作。思考完这些问题后,围绕K8s形成了内部容量管理,架构图如右。底层是K8s平台,上层是基于K8s应用基础数据采集(集群容量/资源池容量/节点容量/应用容量/app基础镜像);上层是VPA/HPA/combinedpool/quota管理。VPA面向SRE平台(研发不可见)。VPA的优点是动态调整服务Request指标,增加K8s可调度资源数量;HPA,水平弹性伸缩,面向研发,解决服务扩展问题;pooling,Buffermultiplexing,增加弹性资源容量。解决机器尺寸差异或Node节点差异;quota管理,不做pooling,每个部门只能看到独立资源的物理资源,不能看到pooling后的物理资源(只有逻辑资源,LIKE云平台),以limit为指标。最上面是容量可视化和报表操作。在底层元数据和容量保障之上,为业务提供统一的可视化平台/运营平台,可以查询部门的资源容量/排序,以及一个组织/业务的容量和数据。容量管理-VPAVPA是降低成本和提高效率的好方法。先来看几个概念。K8s的应用维度有3个指标,一个是CPUUsed(实际用了多少CPU,比如Limit分配8个C,Request可以分配4个C,超卖2倍,业务实际使用2c,Used2c/Request4c/Limit8c;应用CPULimit/CPURequest=业务超卖率)。B站一开始超卖是因为在应用维度超卖,是研发自己配置的。遇到了一些痛点。当Request被分配,Used极低的时候,资源池就没有资源可以调度了。SRE找R&D根据应用维度调整超卖率。对于研发,他们想抢占资源。发布了,会导致共识差,效率低,收益极小。后来VPA改为平台维度。平台维度改造后,研发不再关注超跌率,只关注Limit,不再感知Request。VPA指标放在平台维度,业务实际使用了多少CPU/CPU总量=物理资源CPU使用率。CPULimit/CPUTotal=资源池超卖率。CPURequest/CPUUsed=资源冗余。在内部,如果物理资源的CPU使用率低于40%,VPA策略将调整为动态回收Requests。比如某个业务部门的资源使用率特别低,会直接调整到VPA策略。右边是K8s和VPA联动的架构图,最上面是VPA管理平台:策略管理/数据操作/黑名单/预警。比如有些业务不适合做VPA,比如jobtype/pushservice(一天只跑5-10分钟),这种服务做VPA是没有任何意义的。在做活动的时候,整个资源池应用的CPU使用率会增加。此时开启VPA,第二天资源池分配的CPU请求会增加,导致资源池无可用资源。同时,在做了VPA之后,我们也做了很多VPA的预警策略,发现VPA是否达到预期。中间是链接VPA和K8s的VPA策略引擎。底层是K8s提供的VPAAPI。VPA主要解决两个场景。一种是添加新的Pod。例如,如果一台机器宕机,pod漂移是很常见的。该场景下,通过K8sAPIServer级别的Webhook,对所有新增的Pod进行VPA;第二种是向所有现有Pod发布VPA策略。Updater、stock和新添加的Pod都可以是VPA。规则基于这四个:服务级别/服务配置文件/资源??类型/指标索引。例如内部L0服务使用CPUUsed7-dayP99指标对所有容器CPU资源进行VPA,L2服务需要同时作为CPU和内存的VPA。根据1天的指标来做比较有利。这是在线池中K8s资源的使用率。有两个指标。一种是只看容器的CPU使用率,另一种是看物理机的CPU使用率。8月初,物理机使用率达到50%以上,整个容器液位也能超过40%。在没有VPA之前,我们一个资源池的CPU占用率在高峰期能达到20%-30%已经不错了。今年上半年,我们没有增加资源,所有的新业务需求都是靠VPA来满足的。我们之前谈到了高可用性和多活动。B站每年都会举办很多活动,比如S11、跨网拜年等。2021年,S11当天直播人次将突破1000万。活动保障流程主要是活动了解->能力预估->压力测试演练->审核清单->技术支持能力&方案。我们之前谈到了高可用性和多活动。B站每年都会举办很多活动,比如S11、跨网拜年等。2021年,S11当天直播人次将突破1000万。活动保障流程主要是活动了解->能力预估->压力测试演练->审核清单->技术支持能力&方案。活动重保活动开始前做活动了解活动形式:直播/点播/秒杀/抽奖...重点场景:不同活动侧重点不同,弹幕互动,抽奖,送礼活动外链:全链路保障活动推送:站内推送,弹窗推送后场景活动:直传点数,二级热点时间线容量预估,基础资源切换带宽,源站带宽静态和动态CDN业务资源PaaS、IaASCache、MQ压测&演练内部压测分为三轮:第一轮1:现有资源推倒业务瓶颈;第二轮:资源准备好后,根据之前预估的人数进行容量压力测试;第三轮:所有优化和安全方案上线后的压测演练。演练层面分为计划演练和故障演练两部分:包括上下游依赖、中间件依赖、节点故障、HPA能力。Reviewchecklist和activitychecklist关闭混合部门关闭VPA活动链接服务启用HPA技术支持能力&计划技术支持能力关注的是你有什么高可用能力,你有什么能力在问题出现之前保证业务稳定。比如多活、跨机房降级、断路器等,还有HPA、VPA等。应急预案就是出现问题后应该做的事情。两者侧重点不同。应急预案涵盖攻击、容量、服务故障三个方面。现场重保&重播看好监测行情,定期同步S局等赛事核心指标。一场比赛结束后,会同步比赛的数据,包括上述技术资源、业务资源、服务是否出现。一些高可用问题,比如是否有一些异常,限流等。记录活动过程中问题的变化。回顾的时候,不仅要回顾活动当天的回顾,还要回顾之前准备的活动。SLO实践与反思前面提到的SLO是关于稳定性保证和高可用的。如何衡量服务可用性?我们先看看谷歌的定义:SLO为服务可靠性建立了一个目标水平,这是可靠性决策的关键因素,也是SRE实践的核心。Google甚至说没有SLO就没有SRE。对于具有基于SLO的错误预算的可靠性工程方法,服务利益相关者同意SLO,甚至服务也可以实现SLO。根据谷歌的理论,哔哩哔哩在已有的基础上分为两个部分:服务分类和SLO系统。服务级别分为四个级别,L0-L3。对象包括业务(产品)=>应用=>API。生意就是产品。比如审稿级别是L0,评论是申请。应用级别不能超过产品级别。应用下有API(评论/拉评论),API的层级不会超过应用的层级。等级出来后,用于业务标准化、变更流程控制、告警计量等。不同的服务等级有不同的告警衡量/稳定性要求,比如服务是否具备降级/多活动的能力。SLO系统提供的核心能力是SLO指标的选择/计算/定义。Servicegrading&SLO这是实践SLO的过程。先创建业务&分级&确定SLO;然后创建应用程序和评分;然后创建接口&分级&计算SLI指标;最后根据接口聚合到业务。这个模型不起作用。我们的反思:分级模型过于理想,分级成本很高;各种元信息关联不及时,数据准确率低;当我们开始做SLO计算时,它只算公网业务,而且只是在接入层(SLB层)的指标,单一指标无法覆盖所有业务场景;部分内网服务不对外开放,导致没有SLI数据;SLI数据做报告,没人看新模式:层级模型优化SLI模型优化SLO告警治理先说说目前的玩法:创建业务只对主业务场景打分。比如评论的核心是post/pull评论(打分这个场景)。应用程序不再需要评级,只需要标记。计算SLI计算多个对象/多个SLI指标。API是业务特定波动的体现,也算作其指标数据。对于业务指标,比如在线人数、播放数、评论数等。这是一门生意。我们将计算三个维度的SLI指标。对这些指标做SLO操作和质量操作。单独衡量SLI是没有意义的,核心是SLO告警和质量运营。甚至SLO告警比质量运营更重要,因为无论是质量运营还是SLI指标都不能帮助企业提供什么,但是SLO告警会帮助企业第一时间发现问题,提高告警的准确性。事故分级优化服务分级后,也解决了事故分级中存在的问题。以前事故分级有业务损失&服务级别两个指标:宕机&PV损失,因为之前的分级模型比较复杂,有业务/应用/接口,以哪个级别的故障作为故障级别,我们经常在这里混淆。业务分类优化后,只对主场景进行评级,发生事故时,考虑业务损失&业务评级&业务主场景系数。主场景故障系数是故障功能对主场景的影响程度。这个模型完全包含了服务分类的复杂模型,所以没有必要争论失败。SRE工作的培训和改造是层层递进的:最上面是比较传统的日常问答、变更、告警处理等,再往下是SRE的横向串联、协作、连接,再往下是核心SRE即运维,支撑业务,如业务迁移、重构&改造、中间件推广、技术运维、应急响应等。基于以上三层抽象稳定体系,再将各种稳定实践整合成一个稳定体系系统。SRE的能力要求:运维能力:基础运维能力、网络、OS/kernel、架构能力开发能力:工具开发、运维平台开发、工程能力等合作共赢:项目管理能力、团队合作、同理心、情商和操作意识个人潜力:学习能力、好奇心、逆向思维能力、责任感和责任感。CultivationandTransformationSRE的培养有四个方面:SRE文化:团队必须认可SRE文化,从团队Title和组织名称可以看出对SRE的重视程度;从上到下传达SRE的转变;SRE等级序列;SRE方法论:学习SRE理论知识,主要是《SRE工作解密》《SRE工作手册》;SRE研讨会:SRE方法论分享。以理论为基础的实践。掌握方法论后,我们会邀请大家做一个研讨会,讨论SRE的章节和内容,包括SRE稳定性和基本运维架构的知识;开发转型:开发绕不开SRE,内部也鼓励开发转型。B站基于Golang。SRE也是基于Golang的。鼓励SRE先做工具开发。能力达标后,将指派专业发展导师参与SRE平台的开发实践。最后,开发平台提高了SRE的工作效率,实现了良性循环。作者简介武安创,2016年加入哔哩哔哩,深度参与哔哩哔哩微服务拆分、云原生改造、高可用建设、SRE改造、稳定系统实施等项目。目前主要专注于B站线上业务的SRE稳定体系建设与推广,对SRE的实践有着深入的探索与思考。