空包网站做红章底单,长春网站建设多少钱,朝天门户网,公司设计图图片简笔画作者#xff1a;石超 一、前言
Master-Worker 架构是成熟的分布式系统设计模式#xff0c;具有集中控制、资源利用率高、容错简单等优点。我们数据中心内的几乎所有分布式系统都采用了这样的架构。
#xfeff;
我们曾经发生过级联故障#xff0c;造成了整个集群范围的服…作者石超 一、前言
Master-Worker 架构是成熟的分布式系统设计模式具有集中控制、资源利用率高、容错简单等优点。我们数据中心内的几乎所有分布式系统都采用了这样的架构。 我们曾经发生过级联故障造成了整个集群范围的服务中断。这让我们反思到 Master-Worker 架构难以有效的分批灰度发布的问题。本文试图分析其中原因并尝试提出几种解决方案。 二、Master-Worker 架构
Master-Worker 架构有时也被称为 Master-Server 架构 或 Master-Slave 架构。 在实践中为了避免 Master 成为单点故障Master 通常由多个节点用 Raft 组成的服务以一主多从的形式出现。有时在一个 Raft 服务中我们也用 Master 和 Slave 述语指代主和从节点。为了避免混淆这里我们称为 Master 的“主节点”和“从节点”主从节点统称为“一组 Master 节点”。 据系统的规模的不同Master 节点通常能够管理数台至数万台 Worker 节点。 下面介绍几种常见的 Master-Worker 架构。第一种是经典的分布式系统架构后两种是常见变种。其实它们都披着外衣而本质相的架构。
2.1 经典架构 根在有些系统中用户总是需要访问 Master 才能得到服务。若 Master 故障则整个集群服务中断。需要指出的是这里说的故障不一定是程序崩溃还包括性能下降、死锁等其他形式的功能失效。Raft 的高可用机制能很好处理如宕机、程序崩溃等明确的问题却难以处理这些“半死不活”的问题。我们曾经遇到过部分线程失效而心跳线程仍然好好的活着的问题。墨菲定律说只要可能发生的事最终都会发生。在我们这样云计算数据中心规模和长时间的部署中各种稀奇古怪的问题都会被撞见。
2.2 数据面和控制面分离 在数据面和控制面分离的设计中用户直接和数据面的节点交互。有中心化部署的管控节点负责负载均衡、宕机恢复、限流等集中工作。用户的请求路径不经过中心管控节点。中心管控节点和数据面节点构成 Master-Worker 架构。尽管数据面划分了多个集群但中心管控同时连接了多个集群的数据面同样有级联故障隐患。设想这样的场景中心管控升级后发送了新格式请求到数据节点。后者无法正确处理新格式的请求产生 coredump。
2.3 有网络功能的基础组件库 程序倚赖的公共基础组件如安全、运维、监控等很多以 SDK 库的形式嵌入在服务进程中。SDK 作为客户端和公共组件服务的服务端通讯或上报信息或拉取配置。这样SDK 和服务端也构建经典的 Master-Worker 架构。这些 SDK 尽管是旁路系统但仍然有机会“兴风作浪”像上面“数据面和控制面分离”一节那样引发数据节点的级联故障。公共服务通常被众多服务使用一旦发生这样的问题影响面积将更大。 三、灰度发布的难题
灰度发布是防范故障的最重要手段。典型的灰度发布是这样工作的在一个集群中依次升级每个节点同时观察整个服务有单机异常或整体服务受损。一旦观测到服务受损则立即中止发布。由于分布式系统自身具有自动恢复failover能力除非是如 coredump 之类的明显问题从服务整体角度观测需要一定时间。
3.1 分片架构
在普通服务中这样的灰度发布机制能很好工作。考虑一个由多个节点组成的服务分批发布每批一个节点。每批之间观察请求成功率若跌破阈值则中止发布。现在我们发布一个带 bug 的新版本。在完成第二批发布后我们观察到系统请求成功率下跌超过阈值因此中止发布。 这个模型比较简单但足够说明问题。在实际中自动恢复机制能掩盖错误。例如网关服务能够将失败的请求发送至另外的节点重试。但我们仍然能通过蛛丝马迹发现故障。在前述例子中我们能够通过网关重试率指标来识别到问题。 3.2 Master-Worker 架构
然而在 Master-Worker 架构中灰度发布机制却不是总是有效。让我们看看在这样的系统中如何进行灰度发布并分析它无效的原因。 假如现在有 A、B、C 三个 master 节点其中 B 是主节点。灰度发布步骤如下
1.升级 A 到新版本。
2.升级 C 到新版本。
3.主从切换由新升级的 A 作为主节点。
4.升级 B 到新版本。 在执行每步时我们同样要观察现有服务是否受损。一旦服务受损就立即中止发布。用这样的方法看似做了灰度其实第三步主从切换的有很大的风险。考虑假想的极端情况A 的新版本增加了新的功能在 A-Worker 心跳等广播类报文中增加新的请求字段。而 Worker 恰好无法处理这个新增字段导致了死锁或程序崩溃。这样就会导致整个集群内大范围的服务中断。 灰度发布机制对防范此类问题无能为力。Master 的主节点的新代码只有在第三步主从节换后才能运行起来。一旦新代码被运行起来在 Master 这样重要的节点对整个集群可能产生重大影响。这些影响是 0 或 1 的影响而不是渐近式的影响。因此在这样的架构无法有效实现灰度发布。 四、解决方案
解法一Master 分片 在经典的 Master-Worker 架构中Master 主节点是单体式的。我们可以将它的功能拆分成到多个分片让每个分片能够单独地升级。这样整个系统的发布变成渐近式的给我们创造出了观察窗口。 这种方法能够防止 Master 服务自身出现重大故障。但如果 Master 和 Worker 仍然是高度互联的无法避免故障沿网络传播至 Worker 所有节点最终造成服务中断。此外Master 承担的某些功能天然是集中式的也就无法用这个方法拆分成多个分片。 解法二分批推送 另一个思路是切断故障的快速传播途径。级联故障之所以发生是因为 master 在升级到新版本后立即运行了新的代码路径发送了新的 RPC 给 worker 节点从而造成 worker 节点运行到了未经测试的代码路径。这个过程是立时发生没有分批灰度的。我们可以在这里也引入分批灰度的机制。 这个方案也有短板。在复杂系统中 Master-Server 交互众多很难针对每个交互都增加这样分批灰度的限制。实际中分批机制一般只能覆盖到几个重点功能。
解法三小而稳的 SDK 库 最后一个解法是为公共服务 SDK 库量身定制的。DNS 是最简单的带有网络功能的 SDK 库它却很少出问题。究其原因是因为它的功能足够简单。借鉴这个思路我们可以把 SDK 库做得简单控制代码量在一千行以内经过充分测试能做到近乎 bug-free。 据我所知能做到这个标准最复杂的库是 sqlite它有 15 万行代码但分支覆盖率达到了恐怖的 100%。考虑到我们实际的工程水平一千行代码是我们能做到的极限。如果 SDK 的功能复杂无法精减那么可以将部分逻辑拆分到本机部署的 agent。而 agent 总是比 SDK 更容易做分批灰度发布。 五、结语
防范集群范围的级联故联是分布式系统中的难题。本文提出了三种方法但它们都有各自的局限。在写作本文时我找了很多同事讨论也在互联网上搜索以及问 ChatGPT但是都没有得到令人满意的答案。本文抛砖引玉如果读者想到更好的方法欢迎一起讨论。