
本期作者
彭良友
哔哩哔哩资深测试开发工程师
负责B站基础架构存储/微服务质量保障,一直从事中间件的质量工程建设工作,专注于分布式系统测试方案设计,应用和推广。
01 背景
之前我们介绍了B站分布式KV存储在B站的探索实践(←点击回顾前文)。本文主要介绍对于高可靠、高可用、高性能、高扩展的B站分布式KV存储系统来如何保障其可靠性以及混沌工程的落地实践。
02 大型分布式存储难点
众所周知大型分布式系统很难设计,开发和测试1,这种挑战有多方面因素:意料外的节点故障,不同模块的异步交互逻辑,网络通信故障造成的数据丢失,多核心CPU多线程代码和各种客户端逻辑等,所有这些因素都很容易造成非常严重的线上事故,这些极端情况非常难去发现,判别和修复,这些问题可能是一段并行事务中一行非常简单的代码,很小概率才会触发,但是最终产生的结果可能就是灾难性的。
基于此传统大型分布式系统的可靠性测试需要非常完备的测试用例,以覆盖各种可能的影响因素,通常基于分层的思想,根据开发测试运维经验和历史的线上case来建立可靠性的场景集合并且正交覆盖各种并发和边界值情况,在测试环境逐一验证,可靠性设计是否符合预期并完善,但是难免会遗漏一些预期外或者多因素互相干扰下的故障。

在大型分布式存储系统中更是如此,进一步需要关注数据一致性和持久性,任何的数据错误和丢失对于核心业务来说都无法接受,在已有的分布式系统可靠性场景之外,还需要设计数据一致性和持久性的相关场景以及测试验证方案。分布式存储业界已经发展几十年,各个商用存储团队也有对应的开源测试框架,比较知名的比如P#2和Jepsen3,但是这些框架应用成本高,在非商用存储团队中已有的迭代开发过程中很难有额外的人力应用实施,并且对于操作人员要求高,往往需要特殊的编程语言和框架学习成本。

03 混沌工程的意义
混沌理论4在各个研究领域都有实践,混沌并不是指事务的复杂性超过某个临界点变得无法预测,而是一种思考和量化分析的方法,在探讨一个动态系统必须用整体和连续的关系才能加以解释以及预测其行为,对于一个分布式系统的可靠性也可以利用此方法对其整体和连续性进行思考和分析,对应产生了混沌工程。
混沌工程特别适用于分布式环境,分布式系统是一组通过网络连接并共享资源的节点。对于大型分布式系统,组件通常具有复杂且不可预知的依赖性,很难排除错误或预测何时会发生错误。
分布式系统有许多方式可能会失效,系统越大、越复杂,其行为就越不可预测和混沌。
混沌工程实验故意在分布式系统中产生各种故障异常,以测试系统并找出可靠性优化点,可能发现的一些示例包括:
2008年8月Netfilx因为数据库存储故障造成了长达三天的停机,之后他们开发了对应的测试工具,并在2015年发布了《混沌工程原则》5,通过在复杂系统中采用混沌方式进行实验,增强存储产品可靠性方案处理混乱现象的能力和信心,这些实验遵循四个步骤:
同时提出了5个高级原则,实际在应用实践混沌工程方法时需要结合各个系统的实际使用环境和组织形态会有所区别,这里会补充一些我们实践中的变化:
04 混沌工程实践
4.1 建立稳态假设
B站分布式KV存储是随着业务需求不断迭代开发,功能需求逐渐覆盖到全公司各个业务线,性能和可靠性也是逐步完善优化的过程,在实践中需要对于稳态的标准不断更新和优化。

4.2 真实用户场景
为了更好的测试效果,测试环境必须尽可能的模拟真实环境,逻辑部署和架构图等比例的复制线上真实环境:

4.3 基于真实线上场景设计和持续运行实验
以数据迁移场景来说明混沌实验场景设计的主要组成部分,通过将功能测试和异常测试中识别的真实用户场景用例和不同故障Monkey随机组合之后得到该场景的混沌实验并在测试环境中持续运行,发现尽可能的可靠性故障点,提升组内研发人员对于系统的信心。以下以存储的数据迁移场景为实例进行说明:

4.3.1 稳态指标
通过业务监控采集各种数据操作PUT/GET/DEL的操作时延,请求成功率,检测是否出现数据丢失,以及资源监控等,约定该实验场景下系统各个指标的阈值。对于存储系统来说还需要校验数据的持久性和一致性,以及内部任务和节点状态都可以通过监控采集以及脚本进行检查。



4.3.2 用户场景
用户场景的构造一方面需要模拟线上流量进行,可以使用线上请求的录制和重放或者使用测试自定义规则的流量,另外一个方面模拟各种任务以及运维操作,例如在迁移场景,需要覆盖集群的扩容和缩容,跨区域的迁移以及任务取消等场景并投放在实验过程中,此时完成了一个混沌实验所需要的一个真实的“稳态”用户场景建立。
– 背景流量封装以及检查请求返回值
– 封装数据迁移场景以及检查任务状态
//封装业务请求PUT/GET/DEL并持续检查数据状态go func() { common.PutGetDelLoop(t, true, b.Client, 1000000, 300) close(done1)}() //封装批量请求PUT/GET/DEL并持续检查数据状态go func() { common.PutGetDelBatch(t, true, b.Client) close(done2)}() //封装用户场景:数据迁移并检查任务状态resp := common.RebalanceTable(base.RemoteServer, common.REBALANCE_TABLE, Table, "0:50%,9:50")assert.Contains(t, resp, "OK")log.Info("Rabalance plan: %s", "0:50%,9:50")4.3.3 故障注入封装
在系统故障注入方面,一方面需要在目标节点上安装agent操作各个存储节点,各种存储引擎的数据表对象,比如机器节点,数据表信息,另外一方面需要对各种故障类型Monkey进行封装,比如CPUMonkey,MemMonkey,副本Monkey等,并在实验构建ChaosTest中将实验对象和各种Monkey进行组合,可以采用固定顺序或者随机组合的方式持续运行该实验,实现实验的可重放和可复用。
class Monkeys {public: Monkeys() { // 定义目标节点 m_hosts.push_back("172.22.12.25:8000"); m_hosts.push_back("172.22.12.31:8000"); m_hosts.push_back("172.22.12.37:8000"); srand(time(0)); // 定义各种目标实验对象 m_tables.push_back("test_granite"); m_tables.push_back("test_quartz"); m_tables.push_back("test_pebble"); m_tables.push_back("test_marble_k16"); // ... } // 封装 CPU monkey 注入CPU类型异常 void cpu_monkey() { std::string host = m_hosts[rand() % m_hosts.size()]; cpu_load(host); LOG_INFO "CPU MONKEY:" } // 封装 mem monkey 注入内存类型异常 void mem_monkey() { std::string host = m_hosts[rand() % m_hosts.size()]; mem_load(host); LOG_INFO "MEM MONKEY:" } // 封装 replica monkey 注入副本丢失异常 void replica_monkey() { std::string table = m_tables[rand() % m_tables.size()]; drop_replica(table); LOG_INFO "Replica MONKEY:" } // 封装各种类型 monkey ...}class ChaosTest : public ::testing::Test {protected: ChaosTest() { m_monkeyVec.push_back(std::bind(&Monkeys::cpu_monkey, &m_monkeys)); m_monkeyVec.push_back(std::bind(&Monkeys::mem_monkey, &m_monkeys)); m_monkeyVec.push_back(std::bind(&Monkeys::disk_monkey, &m_monkeys)); m_monkeyVec.push_back(std::bind(&Monkeys::network_monkey, &m_monkeys)); m_monkeyVec.push_back(std::bind(&Monkeys::kill_node_monkey, &m_monkeys)); m_monkeyVec.push_back(std::bind(&Monkeys::stop_node_monkey, &m_monkeys)); m_monkeyVec.push_back(std::bind(&Monkeys::stop_meta_monkey, &m_monkeys)); m_monkeyVec.push_back(std::bind(&Monkeys::replica_monkey, &m_monkeys));...4.4 实验结果的记录和分析
在实验开始前,实验中,实验后采集各种指标和数据,用于最后的数据分析并落地稳定性提升优化。并且通过混沌实验的无人值守和持久运行,持续的发现更多的概率性问题和优化系统稳定性。

05 结果收益

在整套工程实践持续运行的近1年半时间中,拦截了多起场景叠加的严重系统问题,例如在大数据压力下叠加副本丢失场景会出现内部异步线程竞争造成raft节点异常,类似问题传统可靠性故障场景很难发现,同时相比传统分布式测试框架而言其投入产出比具有诸多优势:
06 标准化与服务化
6.1 标准化
2021年中国信通院发布混沌工程实践指南6,可用于评估组织架构实践混沌工程实践的能力,反映混沌工程实践的可行性,有效性和安全性。混沌工程实验需要随着系统架构能力不断提升标准化建设。


6.2 服务化
混沌工程工具种类繁多,大部分故障注入工具已开源,如Chaos Blade和Chaos Mesh。但是不同公司的系统架构不一样,在实践中需要进一步集成和应用各种故障注入工具形成自己的服务化平台。
参考
1 https://www.microsoft.com/en-us/research/wp-content/uploads/2016/04/paper-1.pdf
2 https://github.com/p-org/PSharp
3 https://github.com/jepsen-io/jepsen
4 https://baike.baidu.com/item/混沌理论
5 https://principlesofchaos.org/
6 http://www.caict.ac.cn/kxyj/qwfb/ztbg/202112/P020211223588643401747.pdf
本文为从大数据到人工智能博主「xiaozhch5」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。