Skip to content

Latest commit

 

History

History
127 lines (58 loc) · 11.9 KB

repair.md

File metadata and controls

127 lines (58 loc) · 11.9 KB

背景

之前的文章中介绍过,目前 seaweedfs 副本修复、集群容量均衡 均是通过外围 shell 工具定期检查并执行,在生产环境中 shell 的方式比较 track,对系统的可用性有较大影响(shell 性能、调用周期、自动化程度角度-需要人工介入等)。另,从分布式存储系统的角度出发,系统需要有自身的协调自愈能力。

同时,根据之前介绍对社区版本在架构层面的优化尽量对源码侵入较小(方便rebase/cherry-pick 社区优秀的 feature)的原则,将 Rpair 模块从 Master 分离作为独立模块,单独设计、开发和部署。

容量负载均衡在系统内部应该是长期且较慢的过程(机器故障下架常有,机器上架替换故障机器常有,业务增长扩容常有,甚至业务有从混部集群剥离的需求)。集群规模在 1k -5k 节点(单节点 100个 volume),容量负载均衡的任务会很多,单点不仅存在可靠性的风险,执行任务的速度也是达不到要求的,为此提出 Repair-Cluster 小型集群负责整个系统的 副本修复、集群容量均衡 任务。

目标

  1. 功能目标:负责集群副本修复、容量负载均衡。
  2. 架构目标:Repair 模块是无状态的,既 Master 不需要维护 Repair 的状态,Repair-Cluster 可以根据需要任意扩缩容。

在负载均衡部分,目前社区版只针对容量,而没有用户流量维度的负载均衡。Repair 部分也没有针对流量进行负载均衡 ,有以下两方面原因的考虑:第一,OS系统普遍用户流量不高,热点数据靠CDN缓存,回源比例保守估计小于5%(后期可以通过分级限流保证用户流量优先相应,系统内请求在资源充足时再相应)。第二,流量均衡需要 VolumeServer 统计相关流量信息(读写带宽、读写QPS等),目前社区版还没有统计,对 VolumeServer 代码侵入较大。

Repair 设计成无状态服务,主要考虑尽量降低线上部署和运维的复杂度,同时对Master几乎无改动(Master 不需要维护 Repair-Cluster 的元信息)。难点是如何保证多个 Repair 执行的任务不重复(详细设计部分介绍)。

只读阶段

现状

目前社区版只读用于 EC,通过设置 Volume 只读(在上报给 Master),这不是本部分讨论的情况,本部分针对是 Volume 拷贝的过程中是否有必要进行只读 和 如何利用只读完成 Repair 的任务 与 Compact 任务。

Repair 的 Rebalance 、ReplicateFix 和 Compact 都涉及到了 volume 的Copy(前者是远程 copy,后者是本地 copy),下面看一下这些流程中 Copy 的具体逻辑:

  1. 全量Copy:目标节点发起copy 之前会先交互一次获取源 volum 信息,然后发起文件拷贝,在dat、idx、vif文件 copy 完毕之后校验之前的交互信息是否一致(只校验了文件大小),判断文件是否有变化,有的话则 copy 失败。
  2. 增量Copy:目标节点发起copy,带有时间戳(表示从这个时间之后开始拷贝),源节点从该时间戳到文件结束之间的内容,发送给目标节点。
  3. 本地Copy:同机的 全量 copy + 增量copy(逻辑大致相当,代码没有复用)。

全量 copy 配合 tail(观察一段时间没有追加写才结束请求) 用于 volume move(Rebalance)。增量copy 主要用于 副本修复。本地Copy 主要用于 Compact,当删除数据超过闸值进行压缩防止空间浪费。

问题

上述三种方式 volume 级别的 copy 都没有彻底的解决增量数据问题,即使是 tail 和 增量 copy 也不是很严谨。

  1. 全量 Copy 一般是配合 tail 一起使用,但是 tail 需要设置一个超时时间,在超时时间内没有追加数据则认为 tail 结束,在 tail 结束 和 目标节点接收以及源节点删除 volume 再到 master 收到路由更新,有一个时间差,期间源节点有追加写也是会写成功的。
  2. 增量 Copy 同样需要告诉源节点一个上次拷贝结束的偏移量,从偏移量开始到当前文件的结束位置进行 copy,但是结束位置是在请求一进入事获取的,如果差距较大传输数据时间较长,这期间的增量数据也会丢失。
  3. 本地 Copy 在全量 copy 之后,对比新旧两个 volume 最后一条数据的时间戳判断是否有新数据,然后进行追加,如果追加期间有增量数据,那么也会丢失。
解决

社区作者应该是把系统的可用性放在了第一位,少部分数据量的丢失可以容忍。对于大规模集群(1k-5k节点)volume 应该是比较充裕的(通过预分配保持一直有一定数量的 volume 处于待用状态),当对Volume 进行 Copy 之前先进行只读设置,可以完全避免上述问题,并且不影响集群的可用性。

单纯的只读也不能解决所有问题,极端情况下所有的 volume 都在压缩或者搬迁(批量删除之后),此时相对(全量volume)只有少部分预分配可写可能会造成局部过热,为此需要控制只读的比例(比如不能超过全量的 30%)。

只读接口需要返回三种结果:成功、失败、已经只读(既只读接口不是幂等的)。成功、失败是自然结果不赘述,在这里着重说一下已经只读的结果,它用于只读互斥。每个Repair之间任务的互斥、Compact(Master 侧逻辑)与 Repair 上任务之间的任务互斥、Rebalance 与ReplicateFix 之间任务的互斥,在上述任务之前都会先设置只读,如果返回已经设置只读表示该volume 有其他任务在执行,则当前任务会自动放弃,达到互斥的作用。只读互斥保证了 Repair 的无状态,对简化 Rpair-Cluster 的整体设计至关重要

注意:此处的已经只读,并不包括因写满而只读的volume,二者是并行且不互斥的两套逻辑。

只读操作返回失败,一种是设置失败(比如只读的volume 超过了设定的比例),还有一种是网络失败,此时可能 Master 侧设置只读成功,但是返回的网络问题导致失败,此时只读状态将没有逻辑进行取消只读(正常是任务执行完毕后取消只读)。针对这种网络问题或者是取消只读失败(取消只读也可能因为网络问题造成失败),对只读的 volume 设置一个超时时间,超时之后报警人工介入(查询是否有涉及这个 voliume 的只读操作,没有则取消),网络问题发生概率较小,人工运维成本相对较低,该方案也是可以接收的。

详细设计

任务生成

Repair 负责两种任务,Rebalance 和 ReplicatFix 。Repair 都是先从 Master 获取拓扑信息,针对拓扑信息生成量任务,但是两种任务,其生成过程是不一样的。

Rebalance 需要判断是否均衡,如果均衡则 sleep 一段随机时间(分钟级) ,因为 volume 写满使用新的 volume 是有一段时间间隔的,所以一旦均衡再到不均衡相对时间较长(可能是小时级别),sleep 随机事件是因为,多个 Repair 都是无状态的彼此之间不知道互相的状态,引入随机性避免任务的重复(新机器上架可能只需要搬迁几个甚至1个 Volume,多个 Repair 一定会执行重复任务)。

ReplicatFix 在所有副本不符合的 volume 中随机选取一个 volume (同样尽量降低不同的 Repair 挑选相同 Volume 的概率),如果副本多于配置值(则直接删除,社区版支持最少副本数,在这里禁止了这种情况)。对于副本少于配置值进行 pick-up 目标节点(后面介绍)。

如何判断集群是否处于均衡?

在这里做了一个假设:由于业务与collection一一对应,则认为collection 在全集群处于均衡,则整个集群处于均衡。

理想情况下,collection 在全部节点是分散的,并且每个节点上的volume 值都接近均值,但是在混部集群中这种均衡结果是比较难做到的,主要是集群上全部 volume 均衡 和 每个 collection 的 volume 均衡很可能是矛盾的。全部 volume 均衡,可能某个或者某些 collection 是不均衡的,当进过均衡之后 collection是均衡的,但是全局的Volume 是不均衡的,想达到立项状态中间逻辑复杂并且或有大量的无用搬迁影响业务请求。

基于上述假设可以简化均衡策略,上述假设在一些场景中是不理想的,比如在已经均衡的集群中加入少量机器,可能需要业务写入一定量的数据才能做到全局均衡。但是从长期和用户有大量数据存储的角度出发,系统全局终将是均衡的。

每个 Repair 随机选一个 collection,计算其在全局节点上的平均volume值(大于等于1),如果具有最大collection_volume 的数量大于均值的 1.1 倍,最小collection_volume的节点小于 均值的 0.9倍,则任务节点是不均衡的,然后选取源节点、源 volume 和目标节点进行搬迁。

注意:在均值上下浮动10%,认为是均衡的。

选取策略
选取源节点

源节点选取一定是在大于均值 1.1倍以上的节点(备选集),选取的过程:

  1. 对备选集节点的容量累加,然后在累计和的范围内选取一个随机值。
  2. 从小到大按容量排列所有的节点,从小开始累加,知道第一个大于等于随机值的节点,选为目标节点。

使用上述策略有两点考虑,第一,选取节点的概率与容量相关,经过一段时间的 Rebalance ,集群会处于均衡状态。第二,引入了随机性,使得不同 Repair 之间会选择大概率不同的节点(在集群内分散资源消耗),并且从全局来看所有的 Repai 具备一样的随机性,最终也将趋于均衡(通过相同的概率算法,让所有的 Repair 节点自趋向一个目标为系统服务)。

选取源 Volume

选定节点之后在节点上指定 collection 的 volume 中随机选取。这里集群容量均衡主要考虑的是 Volume 的个数,这是在 Volume 大小固定(默认 32G)并且数据量一直在增加终将 Volume 写满的基础上。这部分不过分细化(设计一定的策略选取 Volume,策略与容量相关)。

选取目标节点

选取目标节点的策略与选取源节点的策略基本一致,只不过不适用容量作为选取标准,而是使用剩余容量。

实现
共同抽象

Rebalacnce 和 ReplicateFix 两种任务的核心操作都是 Volume 级别的 Copy,所以成员变量、调度流程、状态机都是可以复用的,因此提炼出 Task 和 Scheduler 两个接口 负责成员变量管理与流程调度以及回调函数的管理。两种任务在任务生成部分逻辑是有差异的,可以通过设置各自定制化的回调实现差异化逻辑。

协程模型与并发度控制

两种任务可以有两个全局队列(单例)管理,通过接口设置其长度进一步控制单 Repair 节点上两种任务的并发度。一个任务可以与一个协程绑定,任务执行完之后会重启来取最新的路由生成新的任务去执行,故任务的系统资源是复用的,每次拉取新的任务重新 reset 执行。

数据修复

社区版使用单纯的强一致同步模型,一旦有一个 VolumeServer 写入失败,则请求失败,但是并没有删除该条数据的逻辑,这就会导致 Volume 内用户级别数据不一致的问题。针对这个问题有个维度去解决:

  1. 一旦有写失败,返回客户端写入失败,之后立即删除已经写入成功的节点数据(删除也有失败,是解决不了的)。
  2. 通过其他模块做 volume 内用户级别数据多副本间的比对,发现不一致的情况查询业务存储 fid->key 的 KV 系统判断业务是否已经删除该条数据,是的话进行删除。