随着公司业务的快速发展,同程旅行的非结构化的数据突破 10 亿,在 2022 年,同程首先完成了对象存储服务的建设。当时,分布式文件系统方面,同程使用的是 CephFS,随着数据量的持续增长,CephFS 的高复杂性和运维难度逐渐成为瓶颈。考虑到可观测性、稳定性和管理效率等维度,同程最终决定转向 JuiceFS。

目前,同程已在 JuiceFS 上构建了一个企业级存储平台,平台规模涵盖了超过 20 个文件系统和 2000 多个客户端挂载点,能够高效管理亿级文件和百 TiB 级别的数据量。值得一提的是,整个存储平台的日常运维工作仅需一人。该平台主要应用于多个场景,包括 AI 应用、容器云环境以及应用级共享存储需求。

01 文件系统选型:从 CephFS 到 JuiceFS

在使用 JuiceFS 之前,同程内部使用的是 Ceph 来提供对象存储和分布式文件存储服务。然而,Ceph 的技术栈复杂度较高,掌握难度大,对使用经验和运维经验都有较高要求。同时,在可运维性和生态建设方面也存在一定不足,对日常稳定性保障构成了较大挑战。

相比之下,JuiceFS 具有诸多优势。JuiceFS 设计上实现了元数据和数据分离,这与我们内部已有的成熟对象存储系统和分布式数据库系统高度契合。凭借已有的技术经验,能够自主进行问题排查和性能分析。此外,JuiceFS 的工具链和生态建设相对成熟,具备良好的 POSIX 兼容性和云原生支持。特别是其 CSI 功能,提供了多种挂载模式,使我们能够灵活选择。中文的用户社区也使得我们在使用过程中的沟通更为顺畅。

选择 JuiceFS 的另一个原因是它能够与我们现有的技术栈良好融合。简要介绍一下我们现有的基础系统:我们自建了一个基于开源 Seaweed 构建的 S3 集群,并搭建了 S3 代理,兼容 Seaweed、Ceph 以及腾讯 COS 等公有云S3服务。S3 集群具备主从机制,可以在代理层实现从主集群切换到从集群。此外,我们的 DCDB 是一个内部使用的分布式数据库系统,语法兼容 MySQL,基于百度的 BaikalDB 构建。

02 JuiceFS 在同程旅行的平台化建设

在平台化建设过程中,系统的可观测性、应用接入与部署以及数据安全性是至关重要的要素。为了确保平台的高效运行,我们在多个维度上进行了精心设计和优化,以实现全面的监控和高效的服务管理。

在可观测方面,我们构建了一系列监控大盘,以全面监控关键指标。同时,我们接入了公司内部的监控告警系统,为容量、接口耗时等重要指标配置了告警规则。为了更高效地接入内部监控系统,我们开发了一个挂载点自动发现程序。该程序从元数据引擎中获取当前的客户端列表,并实时将客户端列表的更改信息推送给我们内部的监控采集系统。

在应用接入与部署方面,我们提供了一系列易用工具,以支持应用的快速接入和部署。这些工具不仅简化了操作流程,还降低了运维难度。此外,我们高度重视数据安全性,对重要的文件系统都实现了全面备份,以确保数据的完整性和可用性。

在监控告警的具体内容方面,我们主要关注服务端的情况,特别是元数据与 S3 服务的平均时延、请求成功率以及异常情况等关键指标。这些指标对于评估系统性能和稳定性至关重要。

高可用 JuiceFS 服务集群:单中心闭环

它解决的一个典型场景是 Kubernets 单中心集群。 Kubernets 本身并非跨中心集群,而是每个中心都部署独立集群,即单中心闭环架构。在这种架构下 Kubernets 的各类资源和服务依赖通常限制在同一数据中心内,以避免跨数据中心通信带来的延迟、带宽消耗和网络复杂性。JuiceFS 在 Kubernets 中,主要解决的是持久化的需求,而不是数据共享的需求。

另外,一些对于性能要求较高的应用,流量也需要保持在同一数据中心内,以避免跨中心传输带来的延迟和带宽消耗。因此,在这些场景中,会采用单中心闭环的方案,将 JuiceFS 相关服务在每个 IDC 部署为独立集群,以确保数据存储和计算任务在同一中心内进行,最大化性能并降低延迟。

这种方式适用于内部 Kubernets 集群等场景,主要解决有状态应用的持久化存储问题。同时,通过将流量闭环在同一 IDC 内,避免了跨机房传输带来的性能瓶颈,从而保证系统的稳定性和性能。

高可用 JuiceFS 服务集群:跨中心闭环

这种部署方式主要应对的是那些本身跨机房部署且存在共享数据需求的应用场景。这类应用不仅需要访问同一个文件系统,而且对高可用性的要求也相对较高。若某个机房的服务出现故障,不可能要求所有应用都切换到其他中心,因此,我们采用了跨中心部署的后端服务集群方案。

在此方案中,对象存储,如 S3 集群以及 DCDB 等关键组件均实现了跨中心部署。具体而言,S3 的 Master 节点会在每个 IDC 都部署一个,以确保服务的全局可达性。同时,数据副本也会存储在多个中心,以提高数据的可靠性和容错性。DCDB 同样采用了跨中心部署策略,其服务在每个中心都有部署,数据副本则通过其内置的复制机制在多个中心间同步。

为了优化流量路径并减少跨中心传输带来的延迟和成本,我们在正常情况下将客户端请求限制在本地机房,流量通过负载均衡转发到本机房的 S3 服务节点。这不仅保证了性能,还减少了不必要的跨机房流量。由于跨中心集群内部的数据同步和复制需求,集群内部仍然会有跨中心流量。

在出现故障的情况下,例如某个中心的 S3 服务出现问题,我们可以将流量入口切换到其他中心。这一故障切换机制进一步保障了集群的高可用性。

03 落地 JuiceFS 收益

JuiceFS 的架构与我们内部已有的对象存储系统和分布式数据库系统高度兼容,使得集成过程非常顺利,整个项目仅投入了 2 人力,从选型到原理研究,再到最终落地的实施,仅用了半年的时间完成了从 CephFS 到 JuiceFS 的切换。基于 JuiceFS,我们成功构建了一个企业级存储平台,显著提升了存储系统的可观测性、稳定性与管理效率。

从 CephFS 切换到 JuiceFS 后的主要收益:

  • 扩展性和灵活性

    • 可以无缝扩展存储容量,并轻松应对数据量的快速增长,无需停机或影响现有业务。
    • 更好地适配云计算和容器化环境,便于企业在多云或混合云环境中运行。
  • 简化运维

    • 完善的可观测性功能,方便集成到企业内部系统。
    • 运维简单,能更好的支持稳定性保障工作。
  • 数据安全和可靠性

    • 更强的数据容错能力,能够自动进行故障恢复,确保数据的高可用性。
    • 提供强大的备份和灾难恢复能力,保证数据长期安全可靠。

目前,JuiceFS 已在多个场景中提供了强大的存储解决方案,满足了不同应用的需求:

  • 容器云平台:作为基础架构,JuiceFS 有效支持了云中心的持久化存储,尤其是通过容器存储接口(CSI)实现了有状态应用的数据持久化功能,解决了容器化环境中对持久存储的核心需求。
  • 大数据与 AI 平台:在大数据和 AI 应用中,JuiceFS 为海量数据存储提供了高效的支持,特别是在模型训练和数据处理过程中,显著提升了存储性能,解决了对大规模数据存储的需求。
  • 应用共享文件场景:JuiceFS 使多个应用实例能够便捷地共享文件资源,替代了传统的数据传输方案(如 SFTP),优化了应用间的数据交换和资源共享。
  • 数据冷备:尽管对象存储被广泛采用,但一些用户仍然偏好文件系统接口,JuiceFS 正是为这些场景提供了可靠的数据备份解决方案,确保了数据的长期可靠性与可访问性。

04 JuiceFS 使用中的挑战与优化实践

读写性能优化

首要挑战来自于一个关键业务场景——商品库业务。在该场景中,写服务负责数据的全量和增量更新,而读服务需要在更新完成后(通常为 10 分钟内)迅速加载数据。

在此过程中,我们观察到了一系列性能上的挑战。特别是在数据加载阶段,会产生大量的目录列表读取和文件操作,峰值带宽需求高达 20Gbps,同时元数据操作量也达到了 4 万次的高峰。为了应对这些挑战,我们对后端服务进行了针对性的优化,涵盖了资源存储和元数据管理两大方面。

在 S3 存储方面,我们进行了大量的链路对比测试。通过对比经过四层负载均衡(tvs)与直接连接 S3 的性能差异,我们发现了一些隐藏的链路节点存在较大的延时损耗。特别是在高带宽场景下,四层负载均衡的性能瓶颈尤为明显。为了解决这个问题,我们将负载均衡器升级到了采用高性能 DPDK 技术的版本,从而显著提升了链路性能。

元数据方面,我们深入调研了部署方案和元数据引擎的性能边界。尽管我们对 DCDB 进行了多项优化,如事务处理等,但由于其基于 Raft 协议,存在多次 RPC 请求的问题,即使经过极致优化,也只能达到毫秒级响应。对于跨中心集群部署而言,网络消耗和成本也是不可忽视的问题。因此,我们最终确定了将流量闭环在单个中心的方案,并选择了 Redis 作为元数据引擎,以确保元数据操作在 1 毫秒以内完成。

此外,通过将原始文件系统按照顶层目录拆分为多个子文件系统,我们成功地将整体流量分散到各个JuiceFS服务中。在进行了上述优化后,我们已经能够满足业务的性能要求。

我们还深入业务逻辑,协助业务方优化了代码流程,进一步降低了请求量,为系统创造了更多的性能提升空间。

低版本 FUSE 缓存同步 bug

这个bug的现象是当我们在一台机器上删除目录后再创建同名目录时,其他机器上看到的却是之前删除的目录内容。经过深入分析,我们发现这是 Linux 内核 2.6 版本中的一个已知 bug。该 bug 导致在删除目录后,Linux 的目录项缓存和 inode 缓存未能及时更新,从而在其他机器上产生了错误的目录视图。幸运的是,在 Linux 内核 3.10 版本及更高版本中,这个 bug 已经被修复。因此,我们建议大家在使用 JuiceFS 时,尽量使用 3.10 版本及以上的 Linux 内核,以避免类似的性能问题和 bug。

写入阻塞问题的排查与解决方案

在进行写入压测过程中发现偶尔应用写入会卡住,一段时间后应用写入报错 input/output error,查看挂载进程日志是访问 S3 403 鉴权失败

首先,由于问题偶发,我们可以排除密钥错误的可能性。通过直连 S3 测试,我们确认问题与中间链路节点有关,特别是与 Nginx 的交互存在问题。

为了深入了解问题根源,我们在 S3 端增加了日志记录,并发现 S3 在进行签名时使用了“Expect: 100-continue”请求头。这里需要解释一下,HTTP 协议中,“Expect: 100-continue”是一种机制,用于在上传大文件时,先发送 HTTP 请求头给服务器,服务器若接受,则返回 100 状态码,客户端再发送HTTP请求体。然而,在我们的案例中,打印日志发现 403 的请求不带有 Expect 头,但 S3 签名串用到了 “Expect:100-continue” 这引发了我们的进一步调查。

通过排查 JuiceFS 代码及 S3 SDK 发现是 S3 SDK 处理重试时,如果上传请求体大于 2M 时,会默认加上 “Expect:100-Continue”。然而,Nginx 在处理此类请求时,虽然遵循 HTTP 规范返回了 100 状态码,但在后续向 S3 转发请求时,却未保留该头部,S3 在收到请求后校验签名,由于没有收到 Expect 请求头,会使用默认的 “Expect:100-continue”,最终由于 Expect 请求头的值大小写不一致,导致签名校验失败,导致 juicefs mount 进程会重试阻塞。

针对此问题,我们提出了两种修复方案:

  • 一是将 SDK 中的“Expect: 100-Continue”头部修改为规范的小写形式;
  • 二是为 Nginx 添加一个不启用“Expect: 100-continue”的选项,这个选项是很必要的,可以减少一次网络交互。

这两种方案均能有效解决问题,我们向 S3 SDK 社区 JuiceFS 社区提交了 pr。

其他 Tips

文件权限

经常会出现在一批机器中,存在用户名相同但 uid 不相同的情况。针对这种情况,建议如果要做隔离,就在挂载时做好隔离,比如使用挂载子目录的方式来做隔离。同时,文件写入方负责文件权限的正确分配,确保其他文件使用方能有正确的权限。

元数据备份

当元数据数量庞大时,可能会遇到备份失败的情况。如果你的元数据引擎已经具备副本机制或者你已经实施了定时备份策略,那么可以考虑关闭元数据备份功能,以节省资源。

k8s-CSI

在使用 k8s-CSI 时,开可以选择禁用 format 选项。具体操作是,给 k8s-CSI只提供name和metaUrl两个参数即可。这样一来,在 k8s-CSI 运行过程中,就不会实际执行 format过程。这一做法能够带来两大好处:

首先,它能够保护我们的安全信息,如 S3 密钥等敏感数据不被泄露。由于format 过程中可能涉及文件系统的关键信息,禁用该功能能够减少信息暴露的风险。

其次,它允许存储提供方来管理文件系统的配置。在将JuiceFS交付给容器云平台之前,我们已经完成了文件系统的配置工作。这样一来,容器云平台就可以直接使用已经配置好的文件系统,无需再进行额外的配置或调整。

05 未来展望

分布式元数据引擎

我们注意到当前在某些场景中,对元数据性能的要求较高。针对这些场景,我们可能会考虑使用 Redis。然而,目前的 Redis 存在容量瓶颈问题,因为它为了保证事务的一致性,只使用了集群中的一个节点进行数据存储,无论集群中部署了多少个节点,实际上只有一个节点在运行,这导致了容量不能水平扩展。

此外,Redis 在运维方面也存在不便之处。因此,我们部门内部正在开发一个分布式的 KV 存储系统。在系统的调研阶段,我们已经与相关部门进行了多轮的沟通。

分布式缓存

通过引入分布式缓存,我们可以更有效地处理大数据场景下的数据存储和访问需求,进一步提升系统的整体性能和稳定性。

希望这篇内容能够对你有一些帮助,如果有其他疑问欢迎加入 JuiceFS 社区与大家共同交流。


JuiceFS
183 声望9 粉丝

JuiceFS 是一款面向云环境设计的高性能共享文件系统。提供完备的 POSIX 兼容性,可将海量低价的云存储作为本地磁盘使用,亦可同时被多台主机同时挂载读写。