共计 9029 个字符,预计需要花费 23 分钟才能阅读完成。
「社区发起人举荐语」——
1. 分布式系统无奈保障相对可用,置信大家都碰到过软件系统长时间不可用。面对相似问题,美国经济学家⽶歇尔·渥克提出了灰犀牛实践,用灰犀牛⽐喻⼤概率且影响巨⼤的潜在危机。
2. 如果你也面临简单零碎稳定性保障的难题,举荐浏览本文,武老师给你讲述 B 站如何遭逢、盯紧、应答稳定性”灰犀牛“的故事,心愿对你有肯定启发。
作者介绍
B 站在线 SRE 负责人 - 武安闯
「TakinTalks」稳定性技术交流平台特聘讲师,2016 年退出 B 站,深度参加 B 站微服务拆分、云原生革新、高可用建设、SRE 转型和稳定性 体系落地等我的项目,如 Oncall、问题治理 & 复盘、高可用建设、多活容灾、混沌工程、SLO 经营、容量治理等。以后关注 B 站 SRE 的稳定性体系建设和推广,对 SRE 的实际有深刻的摸索与思考。
上周 B 站技术发的“713 事变”复盘文章爆了,很多小伙伴都在关注咱们 B 站的高可用建设,其实始终以来咱们在这个方向做了很多的致力和尝试。看了文章的小伙伴都晓得事变是因为 SLB 层面故障引起的,咱们最终是通过多活进行服务复原的,当然这其中也存在很多其余的问题,明天我想就以下几个方面来跟大家分享咱们是怎么从故障中吸取教训并进行优化的。
1.B 站的高可用计划是怎么做的?
2. 做了高可用计划,就能避免零碎不出故障吗?
3.“713”事变后,针对多活容灾做了哪些优化?
一、B 站的高可用计划是怎么做的?
故障的剖析和复盘是咱们 SRE 永远绕不开的工作,对于 SRE 来说,想要定位问题必须要对系统架构和业务架构有足够的理解,这是所有工作发展的根底。我将分享 B 站的高可用计划相干建设,为了便于大家了解,这边先给大家介绍一下 B 站的零碎架构。
1、先来看看整体零碎架构
B 站的架构次要蕴含用户拜访、接入层、服务层、中间件 / 平台以及基础设施。
- 用户拜访层:用户拜访是多端的,有 APP、Web、多屏,包含 OTT 电视等;
- 接入层:次要有三局部,咱们的动静 DCDN、第三方的商业 CDN 以及七层的 SLB 负载平衡,SLB 上面还有一层 API GW;
- 服务层:第一层是服务的 BFF 网关也是服务的 Interface 层,网关层上面是咱们服务的 Service 层;
- 中间件 / 平台:比方外围的中间件缓存、DB、可观测零碎,还有咱们的 KV 和对象存储、CMDB 和业务流程平台等;
- 基础设施:蕴含 Paas、Iaas、混合云等。
2、每一层架构做了哪些高可用计划?
每一层架构都会有不同类型的常见故障,针对不同层面的问题和架构有不同的高可用计划,上面我会针对接入层、服务层、中间件这 3 个层级的高可用计划开展说一说。
2.1 接入层的高可用
后面有提到咱们接入层有 DCDN、SLB 和 API GW,那在理论的架构外面,用户会基于 DNS 和 HTTP DNS 来拜访咱们的 DCDN 的节点,而后 DCDN 回源时会由边缘的 POP 点来做流量的汇聚并进入咱们多个机房,因为咱们做了多活的架构,所以是多个可用区的模式。
在这外面可能呈现的故障有网络故障、组件故障、服务故障以及机房故障常见 4 种,比如说用户拜访 DCDN 节点时运营商网络故障或者是 CDN 节点回到机房的骨干网故障都属于网络故障,而咱们“713”故障就是组件层面的 SLB 的故障;服务层的故障就比拟多了,服务的代码 bug、性能过载都会导致故障,机房故障就不太常见了。
针对以上这些常见的故障,目前常见的高可用计划大略有这些:
1)针对网络故障的计划
- 在 DNS 故障的时候,很多公司都会想到降级到 HTTP DNS;
- 对于端上来说 APP 申请解析到一个地区返回多个 DCDN 节点,让挪动端来做一个最佳的选路;
- 当咱们 APP 在拜访 DCDN 时呈现网络层面的故障,咱们能够升高到第三方的 CDN,对咱们本人的 DCDN 做容灾;
当 CDN 回源的时候会走咱们的 pop 点进行流量汇聚,咱们的 pop 点有多个能够做线路的互备;
2)针对组件以及服务故障的计划
因为咱们“713 故障”是 SLB 层的问题,所以在针对这个局部的高可用计划会做的多一些。
- SLB 向后段转发的时候是能够发现多个可用区里的服务的,蕴含 API GW 以及没有走 API GW 的其余服务;
- 当单可用区的节点故障了,是能够主动降级到其余可用区的节点的;
- 当 SLB 所代理的服务出现异常的时候,咱们也能够做对应的 API 的降级、熔断和限流等;
- 对 SLB 故障的解决流程做了优化,之前新建一套集群包含配置公网 IP 到测验实现须要花一个小时的工夫,当初咱们能够做到 5 分钟重建一套集群并实现配置与下发。
API GW 层面咱们把很多 SLB 的能力给下放了,API GW 也能够返现多可用区的服务节点,当呈现故障时也能主动降级到其余可用区,同时它也反对 API 的降级、熔断和限流等;
2.2 服务层的高可用
SLB 是南北向的流量架构,但服务层的流量其实是东西向的流量,服务层的高可用计划可能更偏差于微服务侧的治理。对于 SRE 来讲,是必须要晓得本人所负责业务零碎的治理能力的,不然在业务零碎呈现故障的时候,你就只能干等着了,不能给业务一个正当的止损倡议和决策。接下来就具体说说服务东西向流量的高可用应该怎么做。
咱们的服务发现是通过 Discovery 发现组件来实现的,采纳的是多可用区的部署,比方 Service B 在可用区 A 和可用区 B 都有部署,部署是 k8s 多 pod 模式,散布在不同的物理机上。因为洽购的机器批次不一样,机器有新有老就会导致算力不统一。如果流量过载怎么办?两个可用区其中一个可用区故障了又该怎么办呢?咱们这里有一些常见的故障预案跟大家分享一下。
1)P2C 算法
针对算力不统一的问题,咱们外部在做微服务调用时有一个 P2C 的算法,它的外围逻辑是在服务间调用的时候,服务端会返回本人的目前的 cpu 使用率,客户端能够剖析服务的响应 RT、期待的 response、成功率等来抉择最优的 server 节点来进行动静的权重调整。这就能保障即便不同节点的算力不一样,但最在终实例层面或者容器层面的 CPU 使用率是平衡的。咱们服务也是有熔断的策略,熔断比拟常见这边就不开展叙述了。
2)降级
因为咱们的服务是部署多可用区的,当一个可用区的服务故障时咱们会通过服务发现来调用其余可用区的服务,如果这个服务所有可用区都故障了,咱们就会采纳内容品质降级的策略,具体分为服务降级、内容降级 2 种具体场景。
服务降级就是原本申请服务 B 的,不申请服务 B 转而申请服务 C;内容降级就是将数据在本地缓存下来,当服务不可用了就从本地缓存来响应服务。比方咱们 B 站的首页举荐性能,当举荐服务不可用的时候,咱们会降级到一些热门的稿件让用户有视频能够看,尽管这个视频不是用户真正喜爱看的,但至多保障用户有视频可看不会呈现白屏或者空窗的状况。
3)限流
在服务间调用的时候遇到这种流量过载个别会采取限流,B 站微服务间限流有两种模式,一是全局流控,是分布式的全局限流,通过微服务的框架拦挡层面来实现的。它的限流对象是东西向的流量调用,比如说 HTTP、grpc、mysql 数据库的限流。另一种是单节点的动静流控,叫 BBR 的限流,它是基于 Google BBR 拥塞控制算法来实现的,它会基于咱们服务单节点的 cpu 使用率、成功率、RT 去动静决定这个节点当初的申请要不要解决,进行自适应的限流爱护。
2.3 中间件的高可用
除了服务间的调用,服务对中间件的高可用也特地重要,因为 服务间调用和中间件依赖故障,基本上就是服务中最高频率的两种故障了。
图上能够看到咱们外围服务的中间件架构,咱们的服务常常会用到缓存来进行数据减速、读 cachemiss、读 DB,写的场景个别是通过 MQ 来做异步长久化,而后会有专门的 Job 去生产。在更新缓存的时候咱们会有另一个场景,数据库做了主从同步,有一个 canal 的组件会来生产咱们从库的 binlog,再把生产过去的数据写到 MQ 外面,而后再通过 Job 来刷新咱们的缓存。
从这个架构里能够看到咱们用到的外围中间件有 3 个,一个是缓存,咱们外部简直所有的服务都会用到,还有音讯队列和 DB。在这套架构外面咱们的数据是保障最终统一的,因为 B 站的场景很多数据对用户来讲并不需要强统一,比如说弹幕、评论、珍藏这种数据,只有做到最终统一就能够了,当然电商和领取的场景就不适宜这种架构了。
1)缓存中间件的高可用
对于缓存来讲,咱们目前反对 Redis Cluster、Redis、Memcache3 种模式,但在咱们生产的理论应用里边,咱们是倡议先用 Redis Ciuster,而后再用 Redis 的单节点,最初再用 Memcache。因为 Memcache 是不反对集群架构的,同时它也没有数据长久化的模式,日常中应用中也没有方法进行数据迁徙。
B 站在 2017-2018 年的时候,很多业务是直连 Redis Cluster 的,用的不同的 SDK,有 java、Php、C++ 等。每一种语言过后都出过 bug,每个部门应用 Redis Cluster 的业务根本都炸过一次。
Redis 在 3.0 和 4.0 版本的时候是一个单线程的服务模型,降级到 6.0 之后正式引入了 I /O Thread 多线程,在之前单线程的模型下用短连贯的模式去申请 Redis,它的性能降落会特地重大。咱们在物理机上做过测试,单核的场景,长连贯能跑到十几万的 QPS,如果是短连贯的话会降落近 10 倍,只有一两万的 QPS。
过后咱们有个拜年纪的流动,2016、2017 年加入过的同学应该晓得,它是一个点播的流动,流动一开始用户会给视频充电,这个充电的服务是一个 java 的服务用到的是 Jedis SDK,SDK 的连接池有 maxtotal 和 maxidle 两个配置,当连贯数量超过 maxidle 的值当前,后续的连贯就变成短连贯了,导致流动一开始用户大量充电,服务就挂了。
从那之后咱们就开始建设 proxy 代理,通过 sidecar 模式来部署,近两年咱们的 Redis Cluster 就再也没有呈现过那种大规模的故障了。因为 sidrcar 模式部署须要容器,为了降本增效咱们往年又新增了 proxyless sdk 的部署模式。
2)MQ 中间件的高可用
MQ 次要是做数据的长期长久化和异步写 DB 的,它是一个集中式代理部署的模式,提供 redis 以及 gRPC 协定的拜访,过后咱们的代理是应用 go 语言的 kafka 的 sarama sdk 来实现的,也是各种炸。2020 年咱们 B 站常常做一些经营流动所以用户的流量增长的很快,MQ 层面的连接数、topic 增长的都很快,代理方面压力也很大,动不动就故障。起初咱们微服务团队优化了 sarama 的机制,外围服务也做了降级能力,能够绕过 MQ 通过 RPC 形式跟 job 间接通信,实现容灾。
3)Mysql 数据库的高可用
数据库 mysql 这一层是由咱们专门的数据库 DBA 负责的,当初也是用一个 proxy 代理、sidecar 模式去部署的。这种模式上线之后实现了读写拆散,对业务侧是通明的。咱们最罕用的就是对异样 SQL 的拦挡能力,比如说治理后盾有经营同学查了一个数据触发了 mysql 或者未压测上线的慢 sql,而后导致数据库过载,影响到咱们 C 端的服务的时候,能够通过 SQL proxy 来把这个 SQL 做一个拦挡,及时复原咱们的数据库,让 C 端用户能够失常应用。同时通过 proxy 联动外部的 BRM 的高可用组件,当咱们数据库或者某个节点不可用了,它能够主动感知并切换。
二、高可用计划能避免零碎不出故障吗?
B 站有了下面这么多高可用能力,咱们的业务还会不会炸呢?
答案是必定的。
因为 2021 年“713”的时候,咱们就呈现了一个线上大规模的故障。这里给大家再简略的同步一下,咱们这个故障的过程,感兴趣的能够回顾一下具体的文章《2021.07.13 咱们是这样崩的》。
在事变的过程中咱们也发现了许多的问题,从后果来看能够晓得咱们服务疾速止损靠的就是多活,所以说 多活是咱们机房级别故障时容灾止损最快的一个计划了。在这次故障之后呢,咱们具体复盘了咱们的多活架构,发现外面存在一些问题:
- 多活元信息是没有平台治理的。咱们哪个业务做了多活业务、是什么类型的多活、是同城的还是异地单元化、哪些业务是哪些 url 的规定反对多活的、以后多活的流量调度策略是什么、用户是随机回到咱们多个机房的,还是要基于用户的 ip 或者用户的一些设施 ID 来做路由的策略……这些没有中央保护,咱们过后只能用文档来保护,这导致信息割裂的特地重大,信息的更新也不及时。
- 多活切量能力是齐全依赖咱们 CDN 运维。因为咱们 B 站的多活流量调度是在边缘的 DCDN 侧来实现的,常见的流程是,SRE 提出一个切量的需要来通知 CDN 的同学,同时通知他们要切哪个域名、哪一个 URL 规定,而后 CDN 发动变更开始切量,同时同步到 SRE 和研发,让 SRE 和研发同学一起来察看服务的状态和多机房的流量。
如果在过程中漏了一个接口,或者漏了一条规定,那整个流程要再走一遍,导致咱们过后的多活切量效率极差,并且容灾的成果也不好,基于这些背景咱们定了优化两个方向:一是多活的根底能力建设,二是咱们多活的管控能力晋升。
三、“713”事变后,多活做了哪些优化?
1、梳理相干元信息
咱们哪些业务做了多活?利用的是什么多活模式?在多活里每个机房的定位是什么?这些相干的元信息咱们是没有的,所以咱们首先做的事件就是梳理相干的元信息。
1.1 业务信息
后面有提到咱们哪些业务做了多活,这些信息咱们是没有的,所以一开始就着手于业务模型的梳理与定义。咱们之前的业务模型利用的是三级构造,是一级是部门,二级是我的项目,三级是利用。我的项目这个级别有时代表服务的组织,有时代表服务的业务,信息比拟凌乱导致不能反映出业务实在的组织和相干信息。
起初咱们对模型进行了拆分,利用以业务维度做聚合,业务再向上关联的时候,分为两个视角,一个视角是组织,这个组织就是你实在的组织架构,用于咱们的权限、审批、估算和老本相干的场景。另一个视角是业务所属的业务域。对业务来讲它是一个多活架构的治理单元,也是多活的一个治理单元。比如说咱们要推多活笼罩的时候就会按业务维度进行。
1.2 多活模式
CRG 的概念是蚂蚁多活的概念,CRG 代表了 Gzone、Rzone、Czone 三种模式,B 站引入了这个概念,并做了相干业务的多活模式调整。
- Gzone 的模式下用户间的数据都是共享的,B 站的视频播放、番剧播放、稿件信息、直播间,都偏差于平台侧的数据,这种业务场景,都能够做成 Gzone 的模式。
- Rzone 的模式也就是单元化的模式,他更实用于用户侧的流水型的数据,比方评论、弹幕、动静、领取都是流水型的数据。
- Czone 模式介于 Gzone 和 Rzone,也是在用户间做数据共享,但它的可用区是能够做本地读写的,能够承受肯定的数据提早与不一致性。
1.3 机房定位
B 站有多个机房,但定位是有些凌乱,在多活的场景里咱们做了从新的梳理。将物理机房依照逻辑进行划分,映射到不同的可用区,以上海机房为例,四个机房分为两个可用区,这两个可用区就用来做 Gzone 的服务部署。因为两个可用区都在上海,做同城双活时网络延时特地低,个别状况下 1 毫秒左右,根本就不存在服务延时的问题了。
2、组件的多活能力建设
B 站的很多业务场景都是适宜做同城双活的,因为很多数据都是偏差于用户间共享的。下图左边是咱们当初的同城双活模式架构图:由咱们的 DCDN 层面也就是多活的 Router 层面来做咱们的用户侧的申请路由散发(目前是基于用户的 Mid 或者用户的设施 ID 来做路由散发)散发到不同的可用区。不同可用区的服务会拜访咱们的缓存、KV 存储和 DB 存储,这两个存储都会通过咱们 Proxy 的模式去拜访。两头还有一个 GZS 的组件,通过它来进行多活的全局管控。
在 713 故障之后,咱们也对这些组件都做了肯定优化。
- DCDN 层面:咱们曾经反对通过用户的 ID 或者用户设施 ID 来 Hash 路由到不同的机房,同时反对用户流量在多机房的动静权重,1/100 到 1 /99,或者 2 /98 都是能够动静反对的。
- Service 层面:之前咱们做同城双活时没有实现写,那个时候还没有 Proxy,当初有了 Proxy 就能够让业务方来做本地的写革新了,而后通过存储的 Proxy 来路由到主可用区。
- Proxy 层面:通过 proxy 同时反对 KV 和 DB 两种存储模式,并实现读写拆散和 failover。
- GZS 层面:后面有讲到咱们多活是没有全局管控的,那 GZS 就是来做咱们的多活业务、业务利用和 API 的元信息管理的,蕴含业务、多活、切量的编排。
3、Invoker 管控平台的搭建与利用
为了做好 GZS 咱们外部研发了一个 Invoker 管控平台,它会与 B 站外部的业务树、Dashboard、Comet 多个平台联动,从 4 个层面来优化咱们之前的多活流程。多活规定元信息获取形式优化针对平台自身停顿多活和演练多活编排、接入流程的优化审批、巡检、可观测能力的增强
3.1 通过平台标准多活流程
那基于这个平台咱们是怎么落地多活的切量的呢?根本的流程是编排—切量—审批—巡检—可观测,接下来筛选几个重点跟大家具体分享。
1)多活定义编排
多活编排首先要确定这次多活的编排业务范围是一个业务还是一个业务域,评论、弹幕这些都是业务,他是边界比拟明确的产品能够进行编排,而业务域是业务的汇合,比方直播、电商这些就是业务域,它上面由哪些规定、哪些 DB 与 KV 的存储要全副编排下来工作量就会十分大,所以业务域是不反对编排的,咱们采取的形式是业务先做好编排,再基于业务的元信息来聚合业务域。
其次要确认业务它用了哪些 DB,具体的多活模式是什么,Gzone 还是 Rzone。
到了接入层的编排:它应用的多活规定是什么,一个服务 / 产品在对外的时候肯定有多个 url 以及规定,这里须要编排出哪些规定是反对多活的;这个多活规定在 CDN 层的调度它是应用用户的 MID 还是设施 ID,还是基于其余形式来进行多活的流量散发;这些信息都是要明确编排的。
B 站目前音讯层面的编排是暂不反对的,而数据层面的编排次要就是咱们的 KV 和 DB 了,当这些编排都实现之后就会触发一个审批,由研发和 SRE 同学来确认这次的编排是否正确。
2)多活切量编排
编排实现之后咱们就能够发动切量了,切量时候你还是要先抉择你的业务范围,是业务还是业务域。而后你要抉择你切量的权重,每个可用区权重是多少,1:1 甚至是 1:99 都是能够准确管制的。
切量的编排还要明确这一次切量是否要切咱们的存储,存储是否要切 DB 是否要切 KV。大多数都是只切 DCDN 的流量层面就能够了,切 DB 的话可能会产生数据抵触和修数据的问题。
切量编排实现之后会触发切量审批,审批会过 SRE 以及 CDN 的负责人,同时通过工单的模式来明确一些操作,把每一个编排每一个层面的内容有哪些变更都会 Diff 进去,让大家直观地看到咱们这次变更了哪些内容。
3)切量可视化
下面的工作都实现之后就正式进入切量了,那第一步咱们会进行一些危险预检,它次要分为 3 个局部:
- 容量巡检:容量巡检的是咱们利用的 cpu,缓存的 cpu、DB,包含 KV 的 cpu 等。在这里咱们还会进行连接池的巡检,看看咱们利用拜访 DB 的连接池怎么样,利用拜访缓存的连接池怎么样。之前咱们在外部切量的时候踩过很屡次坑,咱们发现服务侧在不同的可用区是有不同的限流阈值的,切量后可用区流量翻倍导致限流阈值被触发影响到用户,所以咱们当初还会做一些限流配置的巡检。
- 提早巡检:因为咱们当初应用了 Gzone 的架构,所以 DB 和 KV 的同步提早都比拟低,像同城双活个别提早就在一毫秒左右,这部分没有遇到什么问题。
- 隔离巡检:隔离巡检次要是看咱们的 DB 和 KV 的切换是否有跨业务的混用,比如说我要切评论的业务,后果我发现弹幕的业务也在援用这套 DB,那我这个切量的影响范畴就不止仅是评论了,那还蕴含弹幕,这就须要告诉到弹幕相干的业务研发同学。
可观测这一块,咱们会监控后面巡检的相干指标、业务多流动流量、业务 / 利用 SLO 指标等内容。巡检相干指标在理论切量过程中也会发生变化,所以须要监控,而实时关注多活流量的状态是为了理解切量是不是合乎预期。业务和利用 SLO 的指标的监控次要是利用的成功率、提早这些利用侧外围指标的一个可视化。
3.2 建设管控平台本身的高可用
切量平台自身咱们也有做高和用,是依照同城双活的模式来部署的。针对切量平台自身的高可用工作分为三局部:
第一局部是多活 ,切量平台依照业务架构的 Gzone 模式做了部署,平台所依赖的外围平台,比方 DCDN、KV 和 DB 平台也做了 Gzone 模式的部署。咱们在进行切量演练的时候也会对 Invoke 平台的数据层来做切换的演练,比方切一下 DB 来验证平台否真正做到同城双活的容灾。
第二局部是降级 ,713 的时候因为登录不了鉴权零碎导致咱们不能及时处理问题,所以当初咱们认证是可降级的,不强依赖咱们的登录态。同样咱们不能因为审批挂了,导致多活切不了,所以当审批平台挂了,审批是能够降级跳过的;同理如果说我预检不通过,是有危险的,但没有更好的应答措施,也是能够降级强制来做一个切量的。咱们也会定期通过对工具平台做这些故障演练,模仿审批挂了或者是预检挂了来看咱们平台的降级是否合乎预期。
第三局部是效率层面的,DCDN 是必定要切的,他是部署在全国各地的边缘节点,公网的稳定性肯定是比不上内网的,所以针对这块场景咱们也做了相干优化。比如说失败能够疾速重试、局部节点失败能够先跳过,先保障能够切的节点全副切掉,以达到咱们的预期,最初再来对立解决失败的节点。在切量的过程中如果发现服务有性能 / 容量问题,或者多机房承载不了流量导致不合乎预期都能够做疾速的一键回滚。
4、优化的成果:多活效率晋升 10 倍
目前咱们全副的多活业务都曾经接入 Invoke 切量平台了,包含咱们的主站、直播、OGV、电商等。咱们在生产做了很屡次演练,因为不在生产进行演练,当故障真正产生的时候,你是不敢切量的。H1 期间咱们就在生产大略切量演练了 60+,过程没有呈现过什么问题,也发现了很多继续优化的点,比如说巡检可观测。
当初的切量流程是 SRE 跟研发确认咱们切量的业务、组件、流量、执行,当初一个切量三到五分钟就实现了,而在此之前咱们的切量都是半个小时起步的,效率大大晋升。
写在最初,故障其实并不可怕,咱们要做的也不仅仅是修复故障,更重要的是要长于从踩过的坑中总结经验,找到优化和晋升的方向,只有这样零碎能力持重倒退。
关注公众号回复关键词【7161】获取讲师 PPT
讲师演讲视频回放链接:https://news.shulie.io/?p=501…