关于云计算:sealfs-一个世界冠军要开始写文件存储了

34次阅读

共计 4695 个字符,预计需要花费 12 分钟才能阅读完成。

背景

sealfs 的作者拿过很多编程较量的世界冠军或者十分好的名次:

  • 2019.12 SSCAIT(国内星际争霸 AI 算法开发大赛)
  • ** 在“排位赛”中世界综合排名第 3 位,以 84% 的胜率在“近 50 场胜率排行榜”中最高排名第二位。
  • 2022.01 RLChina 智能体挑战赛 队长 冠军
  • 2018.08 天池阿里巴巴寰球调度算法大赛 冠军 总名次 1/2116
  • 2017.05 2017 华为软件精英挑战赛 寰球总决赛二等奖,名次 2/10000+ 队长
  • 2022.03 AI 球球大作战:Go-Bigger 多智能体决策智能挑战赛 总名次 10/1434 队长
  • 2020.08 天池首届云原生编程挑战赛 赛道二外部赛道第一名,总名次 7/4031 队长

当初是 sealos 的合伙人,sealfs 也是为 sealos 而生的,筹备做一个高性能分布式文件存储,如果整个 sealos 是云操作系统,那 sealfs 就是这个操作系统的磁盘。

sealfs 还有个平凡指标,就是在一年内拿下 io500 的榜首!心愿能胜利。

sealfs 设计文档

丝滑,高性能的分布式文件存储

为什么要为云原生构建新的分布式文件存储

应用本地存储
  • 单点故障
    云原生利用应用本地长久化存储带来的最大问题是单点故障,因为数据位于某个特定的节点,节点宕机会导致数据不可用,受绑定的利用无奈重启,也难以迁徙到其余节点。此外单节点磁盘故障甚至会呈现数据失落的问题。
    目前对于一些数据可靠性要求高的利用,都在应用层实现了分布式架构,实质上是将复杂性交给了利用,对于简单的利用而言,老本过高。
  • 存储布局与扩容
    利用本地存储意味着须要对每个节点进行容量布局,在节点故障时还须要对本来的分布式架构进行额定的迁徙配置,带来了微小的工作量和出错概率。
    另一方面,本来的容量有余时,进行节点扩容也是本地存储无奈解决的难题。
  • 性能效率低下
    因为磁盘设施 IO,本地存储的性能是存在瓶颈的,多利用共享磁盘时尤其显著。
应用现有分布式存储
  • 性能
    只管实践上分布式存储的数据性能随着节点增多能够不受限制,但实际上还是受到几方面的影响。一是集群一致性带来的瓶颈,应用 raft 等协定的分布式计划存在的问题;二是数据调用流程缩短带来的内存拷贝老本,采纳 fuse 的泛滥文件系统带来的问题;三是元数据申请的瓶颈,典型的是 GFS,大规模集群下目录遍历的老本十分高。
  • 配置
    配置简单,特地的节点扩容,许多限度要求非常刻薄。典型为 GFS 扩容极其简单。cephfs 的稳定性较差。
  • 价格
    高性能的商业分布式存储价格昂贵,开源产品又大多无奈等量齐观。

实用场景

性能极致计划×牢靠数据体验

  • 高性能的网络数据拜访

    • 无分布式元数据的计划
    • intercept
    • 残缺 posix 申请(link、rename、delete 的高效实现)
  • 丝滑

    • 数据可靠性
    • 数据服务高可用性
    • 存储节点可扩大

用户接口与零碎调用流程

对象存储、文件存储、块存储接口

目前对于存储服务所提供的接口次要有三种,对象存储、文件存储和块存储。
日常应用遇到的大多数是文件存储,应用程序大多也是以文件为单位进行文件操作,申请交给文件系统。而块存储则位于文件系统之下,与存储设备间接对接,其拜访指标是存储设备的一段区间,文件存储的申请在解决之后被转换为块存储。
二者在 posix 都提供了残缺的接口标准。在 linux 中零碎中,这两种类型的存储也被纳入了内核中,由内核来处理程序的零碎调用。
对象存储则是一类第三方的存储接口,提供单个文件的读写,典型的规范有 aws-s3 等。这类接口与 linux 零碎无关,拜访则须要独自的客户端。

  • 本地文件(块)存储调用流程
    本地文件存储的一次调用流程包含:
  • 用户态申请
  • VFS
  • 内核文件系统
  • 块设施驱动

如果申请是块设施申请,则间接交给内核的块设施驱动(两头省略了两步,不影响)。能够看出无论哪种申请,都只进行一次内核用户态切换。

网络文件(块)存储调用流程

fuse

网络文件存储有许多不同的实现形式,最容易实现且易于应用的 fuse。

在 fuse 中,通过网络进行文件存储的一次调用流程包含:

  1. 用户态申请
  2. VFS(切换)
  3. FUSE 内核模块
  4. 用户态 client(切换)
  5. 用户态 server
  6. vfs(切换)
  7. 内核文件系统
  8. 块设施驱动

过程中总共波及 3 次用户内核态切换,调用返回后则为 6 次,老本大大增加。

内核文件系统

为了缩小调用次数,另一种实现计划则是间接在内核态实现文件系统,网络申请则在内核态实现。

  1. 用户态申请
  2. VFS(切换)
  3. 内核态 client
  4. 用户态 server
  5. vfs(切换)
  6. 内核文件系统
  7. 块设施驱动

这种计划能够将切换次数缩小至 2 次,但毛病也不言而喻:

  1. 内核编程的调试简单
  2. 须要为客户端装置额定内核模块
零碎调用劫持

此外还有一种计划。在上一节的图中,能够看到,用户申请并不是间接交给 linux 内核的,而是通过了 glibc(或其余 libc 库)来提交零碎调用,这意味着能够在 libc 层替换零碎调用的地址,实现零碎调用劫持。

  1. 用户态申请
  2. 零碎调用劫持 client
  3. 用户态 server
  4. vfs(切换)
  5. 内核文件系统
  6. 块设施驱动
    这种状况下,用户内核态切换次数仅为一次,网络交互也齐全实现在用户态,无论是切换的代价还是编程老本都能够降到最低。

遗憾的是,并不是所有的应用程序都是通过 libc 进行零碎调用的,例如 golang 的二进制程序,其不应用任何动静链接。

对象存储调用流程

因为对象存储无关零碎调用,而是由第三方定义规范,所以无关任何用户内核态切换,没有这一层面上的限度。
这也是为什么开源的对象存储产品如此之多,且性能良好的局部起因了。

论断

fuse 作为实现老本最低的一种形式,是实现 sealfs 的最好抉择,但须要留神 fuse 存在的一些问题,次要须要解决:

  • 双缓存
  • inode 转换
  • 其余 fuse 下冗余的机制

开发内核文件系统不是个好的抉择,用户利用所在的节点装置内核补丁可能存在危险与限度。

零碎调用劫持的实现难度不高,性能更好,能够作为非凡需要场景的可选反对项。

网络架构

客户端

应用 libfuse 构建客户端,用于挂载一个目录,并解决所有该目录下的申请。
一个文件申请会被客户端应用在线算法映射到一个服务器,通过 socket 链接进行传输。

服务器

一个服务器用于组织一部分文件内容,接管任意地位的客户端对于位于该指标服务器的所有文件申请。一个节点上能够存在多个服务器,所有服务器间组织治理的文件内容不存在重合。

数据流连贯

一个数据流连贯用于放弃沉闷的服务器连贯。一个客户端与一个服务器间仅存在一个(或可配置的无限多个)数据流连贯,用于解决客户端所有位于该服务节点的文件申请。
可能的实现形式是间接应用 RPC 框架,或基于 socket 或其余网络协议,亦或是各类 RDMA。目前间接应用 socket 实现,有些简单,具体流程在申请流程中形容并实现。

申请流程
  1. client 接管申请,创立解决线程。创立解决线程的工作是由 libfuse 实现的,sealfs 实现的函数能够认为曾经是独立线程。
  2. 计算文件所在的服务器,内容在元数据管理中,本节不细述。
  3. 向 server 发送文件申请,hold 线程。发送申请的过程要思考多个申请并行处理的状况。为每个申请建设一个 socket 是最简略的实现,但创立连贯的提早过高,网络连接数也可能会过多。放弃多个长连贯保障了创立连贯的提早问题,但在大并发的状况下,仍旧无奈解决网络连接数量过多,同时代码的实现也稍显简单。所以采纳了一个长连贯共享多个文件申请的形式。一个线程发送申请须要蕴含该申请的 id 与数据长度,同时须要实现一个额定的线程平安的队列用于保留发送申请后的线程的锁。
  4. server 解决文件申请,并将申请后果返回给 client。处理过程中始终保持了申请的 id。
  5. client 接收数据,激活申请线程并解决返回值。一个(或无限多个)独立的线程用于接管申请后果,其中蕴含了申请的 id,须要在队列中查问申请 id 所对应的线程锁,写入后果并开释该线程锁,激活原申请线程并将后果返回给利用。

上述流程能够横向扩大,一个 client 能够存在多个长连贯,用于解决一个线程解决 socket 申请带来的 cpu 瓶颈。

内存拷贝

采纳了多个申请共享同一线程的形式,socket 发送申请时,因为数据不定长,须要提前发送一个长度变量,能力防止粘包。这里有两种不同的计划:
一种是应用多个 socket 实现连接池,每次发送一个申请应用一个 socket,该计划不存在数据包连续性的问题,能够屡次发送。
另一种是用同一个 socket,然而要保证数据连续性,须要进行字符串拼接,波及内存拷贝,开销会变大,那为了防止这个问题,每次发送数据须要给线程加锁,这个是第一个阶段的实现计划。

元数据管理

  • 无分布式元数据

在大数据环境下,元数据的体量也十分大,元数据的存取性能是整个分布式文件系统性能的要害。常见的元数据管理能够分为集中式和分布式元数据管理架构。集中式元数据管理架构采纳繁多的元数据服务器,实现简略.然而存在单点故障等问题。分布式元数据管理架构则将元数据扩散在多个结点上.进而解决了元数据服务器的性能瓶颈等问题.并进步了元数据管理架构的可扩展性,但实现较为简单,并引入了元数据一致性的问题。另外,还有一种无元数据服务器的分布式架构,通过在线算法组织数据,不须要专用的元数据服务器。然而该架构对数据一致性的保障很艰难.实现较为简单。文件目录遍历操作效率低下,并且不足文件系统全局监控治理性能。

为了效率采纳无元数据的分布式架构,一种简略的实现是应用文件名进行 hash,获取对应的服务器 id,将所有该文件相干的数据与元数据全副放在该服务器上。其可扩展性也较好,一方面能够实现基于一致性 hash 的节点扩缩容,另一方面也能够实现基于 offset 的文件分段,均衡存储空间。这个办法参考 GFS、gekkofs。

存在的问题在于两点,一是文件目录的遍历问题,因为元数据扩散,须要屡次申请各个服务器来获取元数据目录信息。二是目录存储空间的容量难以监控。可能的解决方案是提早更新所有父目录的元数据,但理论状况可能还有许多简单问题,例如删除某目录的申请仍须要遍历所有数据。这些问题是 GFS 等存储计划还未能解决的,解决这些问题会非常乏味,也会使 sealfs 变得独特。

  • 本地元数据管理
    每个节点由一个 leveldb 本地数据库负责存储元数据,以 kv 的模式保留能够被哈希到该节点的文件名: 文件元数据。申请达到时先向数据库查问元数据,再存取对应地位的文件内容。
  • 集群元数据管理
    对于整个集群来说,除了文件元数据,还须要存储服务器信息,保留有几个节点。因为更新和读取的频率不高(仅在扩缩容时会变更),间接应用 raft 保护即可。

一些其余的扩大

  • 数据可靠性与高可用
    一个文件被 hash 到一个节点上,那么多 hash 几次就能够散布到多个节点上实现 replica。
  • 数据扩缩容
    基于一致性 hash 实现扩缩容,具体细节临时先不讲。须要明确的是增加或删除节点后集群会进行 rebalance,这是一致性 hash 自身须要做的,无需额定设计。rebalance 期间会导致集群性能降落,且可能消耗较长时间,但对于能够继续提供服务。在 rebalance 期间须要做的工作如下:
开始扩容迁徙数据扩容实现
更新集群元数据client 进行二次申请,确认迁徙后数据和迁徙前数据一致性,并将数据写于新节点; 同时迁徙工作进行数据迁徙和同步确认集群元数据
  • 租户治理
    对于不同的 client 申请挂载的磁盘,进行容量限度隔离
  • 内存缓存
    在 server 端实现文件的内存缓存,防止存储瓶颈。但同时要求不能有集群级别的断电。可选实现。

sealos 以 kubernetes 为内核的云操作系统发行版,让云原生简略遍及

laf 写代码像写博客一样简略,什么 docker kubernetes 通通不关怀,我只关怀写业务!

正文完
 0