后彼
2025-6-4 21:46:50
在过去两年多的时间里,随着 AI 大模型的快速发展,JuiceFS 在携程内部得到了越来越多 AI 用户的关注。目前,携程通过 JuiceFS 管理着 10PB 数据规模,为 AI 训练等多个场景提供存储服务。
本文将介绍携程如何使用 JuiceFS,以及基于 JuiceFS 实现的关键管理能力,包括多租户权限管理、计费功能、故障排查和监控等方面。同时,还将分享 3 个生产环境中的排障案例。最后,我们对比了 JuiceFS 与极速 NAS 的性能与成本,JuiceFS 在大多数业务场景中能提供与极速 NAS 接近的性能,同时成本仅为极速 NAS 的十分之一。
01 JuiceFS 在携程的应用:从冷存到 AI 场景
携程早在 2022 年就已经开始接入 JuiceFS,当时的主要目的是替代 GlusterFS,以提高其列表性能,服务 DBA,处理偏冷的数据。此外,这种解决方案能够与 OSS 的生命周期和运行策略相搭配,有效地降低 DBA 处理冷数据的成本。点击此处查看早期案例。
随着 AI 应用的需求变化,尤其是在 AI 模型训练过程中,存储需求开始转向大带宽读写和频繁的写入操作,如模型的 checkpoint 保存、数据分发及存储等。
AI 这个场景最大的痛点在于,携程的训练和推理系统被分开管理,导致存储架构非常割裂。训练过程中产生的模型需要通过复杂的上传和下载流程来分发到其他平台,这个过程显得非常低效且繁琐。
目前的做法是,训练平台和推理平台通过 JuiceFS CSI 挂载相同的卷,但对权限进行区分:训练平台具备读写权限(ReadWriteMany),而推理平台则仅具备只读权限(ReadOnlyMany)。对于只读负载,预读功能可通过 JuiceFS 中的相关参数进行调优,以提高性能。
此外,AI 应用面临的另一个问题是存储性能的瓶颈,尤其是在读性能方面。AI 推理任务需要较高的带宽,而许多存储产品的带宽表现有限。与 OSS 配合使用时,存储带宽可以根据数据量的增加而自动扩展。例如,OSS 为用户提供的带宽与数据流量成正比,数据使用量越大,分配的带宽也越大,这种设计使得 AI 用户在大规模数据读取时能够获得所需的带宽。
02 JuiceFS 部署架构 & 关键能力
我们搭建的部署架构与社区大部分的推荐方案一致,采用了 TiKV + OSS 的组合。具体来说,架构由以下几个核心组成部分构成:
TiKV & PD 作为元数据引擎 :TiKV 支持分布式架构和事务处理,具备出色的性能。通过跨 IDC 部署,确保系统的高可用性。
Ali OSS 作为存储底座 :结合专线网络提供大带宽传输能力。同时,OSS 的自动转冷功能使得系统在成本控制上具有优势,性价比高。
JuiceFS 客户端的定制化 :对 JuiceFS 客户端 进行了针对性修改,特别是在内部管理功能上,例如自助服务、计费系统、控制限速等。我们还进行了优化,以便用户在 Kubernetes (k8s) 环境中能够方便地使用。
关键能力 1:多租户权限管理与计费
我们要为多个用户群体提供服务,包括 AI、DBA 等不同领域的用户。为了保证资源的合理使用,我们为每个申请用户提供了独立的 token,用户需使用 token 进行挂载,以此实现资源的隔离。
为了控制成本,我们对用户的使用进行严格监控和计费。我们在每个卷(Volume)级别进行实时监控,并按小时生成账单,确保每个用户的使用情况得到清晰记录和计费。
在 Kubernetes 环境中,很多用户选择使用动态存储类来实现存储的自动化调度。但为了实现自助申请并便于计费,我们采用了静态 PVC。这种方式可以方便地关联卷,并与自动化流程相结合,确保每个卷的费用被精确记录。在 JuiceFS 中,每个卷都记录了一个全局变量(totalUsedSpace),该变量用于追踪与计费相关的使用情况。
虽然社区也支持对子目录的计费,但对于我们初期开发而言,按卷级别计费相对简单和高效。
计费原理
携程内部使用的工具 FinOps 可以定期拉取阿里云费用中心的数据,以获取每个云产品的费用信息。通过这个工具,我们能够实现对各项云服务费用的实时监控。
在使用这个工具时,当申请 JuiceFS 卷时,需要进行关联,卷的创建会与相应的用户关联。为了有效管理,我们每小时都会进行一次打点监控,记录空间使用情况、文件引用数量等指标,并将这些数据发送到 FinOps 系统。然后,FinOps 会对每个卷进行费用分摊,将 OSS 的费用按比例分摊到每个卷的所有者。
费用异常:存储泄露
在日常运营中,主要关注的是费用的上升情况和费用占用的趋势。这些费用数据能够反映出是否存在异常问题。在一次费用分析中,我们遇到了一个典型问题:通过 JuiceFS 统计的使用量没有明显变化,但整个 OSS 的成本却有所上升。经过进一步分析,确认阿里云的收费政策和 OSS 单价并未变化。然而,分摊出去的单价却上升了,导致了整体费用的增加。
在检查 JuiceFS 的计费统计和阿里云 OSS 上的文件量统计时,我们发现两者之间存在显著的差异。OSS 提供了一个存储空间清单功能,可以查看当前整个 bucket 下所有文件的使用量。我们通过使用 OSS 清单对数据进行聚合,发现 JuiceFS 统计的用量远低于 OSS 统计的用量。这导致了用户在 JuiceFS 中的存储空间使用量被低估,从而使得 OSS 存储空间的费用被错误地分摊给其他用户,进而导致其他用户的单价上升 。
这一差异的根本原因在于 JuiceFS 删除文件的实现方式。对于大量删除的文件,JuiceFS 使用软删除策略标记文件为已删除,但后台会逐步删除这些数据。由于在使用过程中禁用了所有后台任务,导致用户的删除操作产生了许多待处理(pending)和失败的删除请求。
进一步分析后,我们发现 JuiceFS 存在两种数据泄露情况:一是待处理删除(pending delete),二是回收站(trash)中的数据。为了应对这一问题,我们设置了一个额外的监控服务,每隔 6 小时扫描一次潜在的数据泄露。如果发现泄露数据,我们会启用一个专门的客户端,取消禁用后台任务,并清理这些泄露的数据。
功能禁用
禁用回收站 :回收站功能在很多用户场景下并不需要开启。回收站是后台任务,需要额外的资源进行清理,尤其是在使用 TiKV(如 RocksDB 数据库)的情况下,回收站会对数据库性能造成一定压力,尤其在出现突发的大规模删除时。因此,我们选择不启用回收站功能。
禁用备份元数据(BackupMeta)功能 :JuiceFS 提供了元数据备份功能,但在数据量较大时,逻辑备份速度较慢,无法满足我们的需求。为了提高备份效率,我们更倾向于使用 TiKV 提供的官方备份工具来进行数据库备份,这样可以更好地支持大规模数据的备份需求。
关键能力 2:日志收集与管理,提高排障效率
我们使用内部工具将 TiKV 和 PD 中的日志收集到 ClickHouse,通过 Kafka 传输并最终存储到 ClickHouse 。
通过这样的日志收集,我们能够及时捕捉集群中的错误信息。许多情况下,集群可能会产生大量的错误,但用户并未察觉,且从客户端来看,性能似乎并未受到显著影响。然而,经过多次事故的处理,我们发现很多问题都是通过分析 TiKV 的日志来发现的,从而能在早期阶段及时解决潜在问题。
关键能力 3:监控
在监控方面,我们挑选了 TiKV 官方提供的一些关键指标来构建自有的监控系统。TiKV 和 TiDB 的整体监控体系相对复杂,官方提供的监控看板包含了大量信息,显得过于繁杂。刚接手时,这一部分确实没有很清晰的理解。
在实践过程中,我们最终选取了几个核心的指标,以便更有效地监控 TiKV 的性能:
性能相关指标:包括 CPU、内存使用情况以及热点读写。
PD(相关指标:重点监控 Region leader 分布与调度情况。
GC(垃圾回收)相关指标:包括 GC 时间和 MVCC 删除等信息。
在 JuiceFS 客户端的监控中,我们通过 Prometheus 接口获取 CSI 的指标。然而,对于普通挂载情况,特别是在用户自行部署 JuiceFS 的机器上,我们无法直接控制或访问这些数据。因此,我们使用 JuiceFS 提供的 state 文件来采集简化的监控指标。大部分场景中,state 文件已经能够覆盖我们需要的指标。为了高效采集数据,我们在每台机器上部署了一个 DaemonSet,通过该工具定期读取 state 文件并进行监控。
客户端监控看板包括以下内容:CPU 和内存使用情况、启动时间和启动参数、Golang 性能指标(如堆内存中的活跃对象)、以及读写性能(包括缓冲区和块缓存的使用情况)。除了关注客户端的读写性能外,更多时候我们更侧重于整体带宽情况。
关键能力 4:元数据备份
在 TiKV 生态中,存在两个不同的 br 备份工具。TiKV 文档中提到的 br 工具只能备份通过 rawKV API 写入的数据,无法备份通过 txnKV API 写入的数据。
与此不同,TiDB 仓库中的 br 工具更侧重于备份 TiDB 数据。这个工具提供了 backup txn 子命令,专门用于备份通过 txnKV API 写入的数据。最终,我们采用了 TiDB 的全量快照备份方案,每日进行定时备份 。
在使用 TiKV 集群版本 v5.2 并直接应用 TiDB br 工具的 master 分支代码 时,遇到了一些问题:
虽然可以成功备份数据,但在恢复时出现了错误。
在备份过程中,TiKV 持续尝试执行备份操作,且无法停止。
针对这些问题,我们将其反馈给了 TiKV 社区,并在社区的帮助下成功解决了相关 bug。解决问题后,我们对备份过程进行了优化,通过设置备份限速为 50MB/s,使得备份过程能够在大约 15 分钟 内完成。
TiKV 备份通过 trip-tikv-manager 服务进行管理,该服务负责调度和执行备份任务。备份数据被存储在独立的对象存储 中,目前使用的是 Ceph 存储系统。
目前,在我们的最大集群扩展后的生产环境中,能够在 20 分钟内完成全量备份。备份任务基本上会在每天定时执行,并通过监控实时查看备份状态 。由于 JuiceFS 服务全天没有明显低谷,我们选择在白天对 TiKV 进行备份。
鉴于我们的系统部署为三中心结构,并已对 Region 实施了 Zone 级别的隔离,即便单一中心发生宕机,也不会影响到 TiKV 的可用性。因此,我们将 TiKV 的备份视作一种额外的安全措施,仅执行快照级别的备份与恢复操作。
03 生产环境排障案例
案例 1:TiKV MVCC (Multi-Version Concurrency Control) 堆积
这个问题是在去年 9 月被发现的。当时我们发现,尽管 TiKV 数据库和 JuiceFS 的整体使用量并没有显著增长,但数据库的磁盘空间和引擎大小却急剧下降。奇怪的是,TiKV 的 CPU 使用率和 QPS 并未发生明显变化。进一步分析日志后,发现大量与 region 相关的报错,这些错误是由于 MVCC(多版本并发控制) 堆积引起的 。MVCC 堆积后,region 内的旧版本数据不断累积,导致 region 无法正常分割,从而阻碍了硬盘空间的及时回收。
通过深入排查并使用 tikv-ctl 工具解码报错的 region 中的 key 后,我们发现这些 key 均来自同一个 JuiceFS 卷。由于用户频繁更新文件,JuiceFS 中的 chunk Key 数量不断增加,每次更新都会在 TiKV 中生成新的版本,从而迅速消耗内存和磁盘资源。
进一步分析我们发现,TiKV MVCC 堆积问题与 JuiceFS 的元数据定义和存储方式密切相关。TiKV 作为支持 MVCC 的数据库,能够保证事务的隔离性。每当数据被更新时,TiKV 会为每个写入操作分配一个时间戳(TSO),从而创建一个新的版本,而不是直接修改原有数据。这种机制确保了事务的隔离性,并保证了读操作可以读取到一致的数据。
在实际应用中,JuiceFS 会将文件切分为多个 chunk,每个 chunk 包含若干 slice。当一个 chunk 被频繁更新时,TiKV 会为该 chunk 创建新的版本,导致相同 chunk key 在 TiKV 中产生多个版本。
频繁更新同一 chunk 会使 TiKV 无法及时回收过期的版本,从而迅速消耗存储资源,最终导致 MVCC 堆积 。随着这些未回收的旧版本不断积累,TiKV 的存储压力逐渐增加,可能会导致性能下降,甚至引发磁盘空间不足等问题。
具体来说,高频更新文件导致以下两方面的性能问题:
JuiceFS :由于 chunk 记录的 slice 数量不断增多,JuiceFS 需要更多时间来恢复完整的文件视图。当 slice 数量过多时,JuiceFS 会暂停写入,并强制执行数据压缩(compaction)操作。
TiKV:频繁写入版本会增加 RocksDB 中存储的数据量,导致 LSM 树的读性能下降,从而影响 TiKV 的整体性能。
针对上述问题,我们采取了一种更加激进的垃圾回收(GC)策略。具体做法是设置一个独立服务,每隔 5 分钟通知 TiKV 的 PD 节点,告知它在接下来的 25 分钟内的数据可以被 GC 回收。这样,TiKV 在 GC 时能够通过 compaction 过程高效回收无用数据,减少了 GC 对 CPU 的占用。同时,通过加速 TiKV 的 compaction 过程,能够有效降低 MVCC 堆积的风险,防止版本过度堆积而导致的性能瓶颈。
案例 2:大量容器同时扫盘打爆 TiKV
在进行大量容器同时扫描磁盘时,TiKV 负载超过 70%,甚至出现崩溃的情况。经过排查发现,所有卷都是通过 CSI 形式挂载的。在使用 CSI 挂载 JuiceFS 时,会启动一个单独的 Pod 并在其中挂载 JuiceFS 客户端。由于 JuiceFS 客户端与应用 Pod 完全隔离,无法感知应用中的挂载点操作,导致 TiKV 的 PD(Placement Driver)管理节点的利用率不断上升。
进一步调查后,发现大量 GET 请求,主要是文件查找操作。虽然 OSS 监控数据显示一切正常,但 TiKV 却持续受到来自历史失败请求的压力,导致性能下降。卷中存在大量小文件和超大目录,导致 TiKV 不断处理类似“扫盘”行为的请求。最初我们认为问题出在宿主机上的某些应用,但进一步排查后发现,JuiceFS 应用的 Pod 中有一个定时任务,该任务会定期扫描目录。多个 Pod 同时对一个超大目录进行扫描,造成 TiKV 负载极大。
针对这一问题,采取了以下对策:
消除 updatedb 的影响 :通过使用 ConfigMap 将 /etc/updatedb.conf 挂载到用户 Pod 中,覆盖镜像自带的配置,并在配置中禁止扫描 JuiceFS 挂载点。
限流元数据操作 :为了防止用户不经意间的行为影响 TiKV 集群的性能,我们修改了 JuiceFS 和 TiKV 相关代码,在元数据部分添加了限流机制,避免 TiKV 因过多文件查找请求而过载。
进一步限流代码 :尽管之前对 OSS 带宽进行了限流,但问题仍未完全解决。因此,我们进一步添加了针对元数据操作的限流代码,特别是针对 TiKV 的元数据操作,从而缓解了 TiKV 集群服务质量下降的问题。
案例 3:JuiceFS client OOM
在使用 JuiceFS 的过程中,AI 用户,存在不规范的使用方式。这些用户会在 JuiceFS 中存储训练数据集,这些数据集可能是图片或文档,都是小文件,而且通常都是讲大量小文件集中存储在一个目录中。某些目录中的文件数量甚至达到数百万,甚至数千万个。当多个应用同时发起目录读取请求时,可能会导致内存溢出(OOM)问题,尤其是在目录中包含大量小文件时。
为了解决这一问题,我们对线上 JuiceFS 客户端进行了 pprof dump 分析,确认问题出在 JuiceFS 的目录读取实现。具体而言,在 JuiceFS 中,目录读取是一个阻塞式操作。每次读取目录时,系统会拉取该目录下所有文件的名称和属性。如果目录中包含大量文件,这一过程会消耗大量内存。例如,对于一个包含 500 万个文件的目录,单次目录读取请求会导致内存占用达到 3.7GB,并且这部分内存会被长时间持有。
为了解决这一问题,团队将全量读取目录的实现修改为流式缓冲读取方式,从而有效减少了内存占用,并防止了 OOM 问题的发生。在与社区沟通并反馈该问题后,社区积极参与修复,最终成功解决了这一问题。 meta: support dir stream #5162
04 JuiceFS 的成本优势:十分之一极速 NAS
我们对 JuiceFS 与阿里极速 NAS 的进行了性能对比,结果显示,在大部分读写场景下都不落于极速 NAS,甚至性能更加优秀。
JuiceFS 具有客户端缓存和预取功能,并且采用了 OSS 大带宽、以及数据和元数据分离的设计,这使得它在大部分应用场景中表现出色,尤其是在大模型推理应用中。大模型推理应用通常需要高带宽的顺序读取场景。
通过将 JuiceFS 与 OSS 结合使用,我们实现了一个分布式文件系统方案,能够在大多数业务场景中提供与极速 NAS 相同的功能和接近的性能,而成本仅为极速 NAS 的十分之一 。 这一成本优势是 JuiceFS 方案的最大魅力之一,它能够显著降低我们的运营成本。
Q&A
Q:大模型对于存储的主要需求是什么,还是只关注性价比 ?
A:在大模型场景中,我们最关心的是顺序读写带宽。训练过程涉及训练数据和模型的加载,以及检查点(checkpoint)的写入。推理过程则主要涉及模型的加载。尽管整个流程中涉及一些写操作,但读操作占主导,因此,我们特别重视提升读带宽的性能。
Q:从对象存储拉取数据慢,有什么建议 ?
A:JuiceFS 非常适合 AI 负载场景,能够提供非常高的顺序读写带宽。对于顺序读写,JuiceFS 可以启用预读功能,提前从 OSS 拉取数据,这能有效提升性能。然而,由于 OSS 的延迟一般高于 50ms,这可能会影响随机读写的性能。如果对低延迟有较高要求,NFS-Ex 和 CPFS 都能提供 1ms 以内的 4K 随机读写延迟。此外,JuiceFS 官方也提供了分布式缓存方案来降低延迟。总体来说,这之间是性能与成本之间的权衡。
Q:TiKV元数据规模大概能支撑到多大的量?数据量大的场景,元数据是不是成为瓶颈 ?
A:提供一组携程的数据供参考,我们的一台生产集群,TiKV节点数量为 6 个,每个节点配置为 64 核 256 G 内存,承载着接近 40 亿个文件的负载。在这种配置下,TiKV 的 get p99 延迟仍然保持在 1.5ms 以下。实际生产中,元数据的延迟与 OSS 的延迟不在同一量级,因此 TiKV 并未成为瓶颈。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
相关推荐