韩国国民搜索 NAVER:为 AI 平台引入存储方案 Juice

2023-12-26  本文已影响0人  JuiceFS

NAVER 是一家多元化的互联网公司,拥有韩国最大的搜索引擎并在人工智能、自动驾驶等高科技领域积极投入。

在搭建 AI 平台时,NAVER 评估了公有云平台的存储产品、Alluxio 以及高性能专用存储产品等多种选项后,最终决定采用 JuiceFS。通过使用JuiceFS,NAVER 成功地将内部存储资源升级为高性能、适应 AI 工作负载的存储解决方案。

AiSuite 是 NAVER 开发者所使用的人工智能平台,它支持 NAVER 的各种服务的开发和运维。

AiSuite 提供基于 Kubernetes 的容器环境,用于高效管理成本高昂的 GPU 资源。它支持 Kubeflow,不仅便于 AI 模型的开发,还能整合模型训练和部署服务的完整 AI 工作流程。此外,AiSuite 还支持使用集成了公司内部数据平台 Kubeflow 工作流组件。

在建设 AI 平台时,最大的挑战就是提供适合 AI 工作负载的存储。随着大型语言模型(LLM)的普及,为了生成优质的 AI 模型,所需数据的规模越来越大,且分布式学习需要多个节点能够同时访问数据。此外,还应能够轻松应用像 Llama 2MPT 等迅速出现的各种大型语言模型开源项目。
适用于 AI 平台的存储需求如下:

寻找能够满足所有这些要求的存储解决方案并非易事。云平台如 AWS EFSGoogle Filestore 等服务与这些要求相似。但是,这些服务与 AWS S3 或 Google Cloud Storage 等对象存储服务相比,它们的成本要高得多(标准费率下 EFS和 AWS S3 有10倍的差异)。此外,由于 AiSuite 是在 NAVER 内部部署的,因此无法使用 AWS、GCP 等外部云存储服务。我们也可以引入一些专用的存储解决方案,如 DDN EXAScaler,但这会带来高昂的成本。本文将介绍为解决这些问题所进行的探讨以及引入新存储解决方案的经验。

01 存储方案选型

我们曾考虑引入像 GlusterFSCephFS 这样的开源解决方案,但由此会带来比较大的运维负担。

我们希望能够使用 NAVER 内部现有资源可支持的存储解决方案。 NAVER 内部现有的存储方案情况如下:

初步方案:引入 Alluxio

为了在 AiSuite 中快速且轻松地使用在 Hadoop 集群中处理并存储在 HDFS 上的数据,我们引入了 Alluxio。但 Alluxio 在我们的场景中存在以下问题:

不完全的 POSIX 兼容性

虽然可以将 Alluxio 用作 Kubernetes 持久卷,但它不支持某些 POSIX API,例如符号链接、截断、fallocate、追加、xattr 等。例如,在挂载 Alluxio 的路径 /data 后,追加操作将失败,如下所示:

$ cd /data/
$ echo "appended" >> myfile.txt
bash: echo: write error: File exists  

许多 AI 开源软件和库默认实现为假设数据位于本地文件系统上。如果不支持某些 POSIX API,可能无法正常工作。因此,在使用 Alluxio 的情况下,有时需要将数据复制到 ephemeral storage 后再使用。这样做会导致 AI 开发变得不便和低效。

数据不一致

Alluxio 更类似于现有存储系统上的一个缓存层,并非一个独立的存储解决方案。当底层存储采用 HDFS 时,直接对 HDFS 进行更改而没有经过 Alluxio,可能会引起 Alluxio 与HDFS 数据的不同步。

在 Alluxio 中,可以设置与原始存储数据同步的时间间隔。更多详细信息,请参考 UFS Metadata Sync。但是,如果同步过于频繁,会对原始存储产生过多的元数据请求。AiSuite 运行了一个以 HDFS 作为原始存储的 Alluxio 实例,以便与基于 Hadoop 的数据平台进行交互。但是,频繁同步导致管理 HDFS 元数据的 NameNode 负载增加。

运维压力

Alluxio 需要运行一个由 master 和 worker 服务器组成的单独集群,这也带来了一定的运维压力。不仅如此,由于 AiSuite 的所有用户都共享这个系统,一旦出现问题,可能会影响到所有用户。

为什么选择使用 JuiceFS

JuiceFS 是一种分布式文件系统,采用“数据”与“元数据”分离存储的架构,文件数据本身会被切分保存在对象存储(例如 Amazon S3),而元数据则可以保存在 Redis、MySQL、TiKV、SQLite 等多种数据库中,这使得企业能够利用现有存储和数据库。

接下来,我会详细介绍 JuiceFS,并解释为什么选择应用 JuiceFS。

配置

元数据引擎(Metadata Engine):负责管理文件的元数据(文件名、大小等)。可以使用多种数据库,如 Redis、TiKV、MySQL/MariaDB、PostgreSQL 等(文档:如何设置元数据)。

数据存储(Data Storage):实际存储数据的地方。可以使用多种存储,包括 S3、OpenStack Swift、Ceph、MinIO、HDFS 等(文档:如何设置对象存储);客户端(Client):与元数据引擎、数据存储进行交互,执行文件 I/O 操作。支持多种接口,适用于不同的环境;JuiceFS 的元数据和数据存储能够使用现有存储和数据库,并且可适配 Kubernetes 环境。例如,只要准备好了 S3 对象存储和 Redis,就可以通过 JuiceFS 创建一个高性能且功能丰富的存储解决方案。这也是 JuiceFS 吸引我们的原因。使用 NAVER 内部支持的存储和数据库,可以方便地搭建存储系统。

特性

JuiceFS 支持并发访问,同时支持 POSIX 和 Kubernetes 环境。满足前面提到的用于 AI 平台的存储要求,具体特性如下:

存储原理

JuiceFS 引入了以下概念来处理文件,目的是为了弥补分布式存储在物理上的分散性和对象存储中对象难以修改的缺点。

元数据引擎管理着诸如文件名、文件大小等元数据。此外,它还包含了文件与实际存储数据之间的映射信息。

缓存

JuiceFS 为了提高性能,采用了多个层级的缓存。在读取请求时,首先尝试从内核页缓存、客户端进程缓存和本地磁盘缓存中读取数据。若这些缓存未命中,则会从远端存储中读取所需数据。从远端存储中获取的数据随后会被异步地存储在各级缓存中,以便未来能更快速地访问同样的数据。

02 Alluxio vs JuiceFS

早期引入的 Alluxio 并没有满足我们所需的存储要求。那么 JuiceFS 呢?以下是对 Alluxio 和 JuiceFS 进行比较的表格。

JuiceFS 和 Alluxio 都支持多种接口,并可以通过缓存来提高性能。与 Alluxio 相比,JuiceFS 具有以下优点:

完全兼容 POSIX

Alluxio 在某些 POSIX API 上提供有限支持。而 JuiceFS 能够完全支持 POSIX 标准,因此可以像本地文件系统一样使用。这意味着,无需修改存储在 JuiceFS 中的训练数据和代码,就可以使用各种 AI 开源工具和库。下面是 POSIX 兼容性测试 pjdfstest 的结果。与AWS EFS 和 Google Filestore 相比,JuiceFS 在支持 POSIX 方面表现更佳。[图片上传失败...(image-6155c6-1703647097942)]

强一致性

Alluxio 更接近于对原始存储进行缓存,而 JuiceFS 是一个独立的存储系统。在 JuiceFS 中,元数据由元数据引擎管理,不依赖于外部系统。数据存储仅用于存放 Block 数据。因此,不会像 Alluxio 那样出现与原始存储不同步的问题。

减轻运维负担

Alluxio 需要运行和维护 master 和 worker 服务器,这增加了一定的运维负担。此外,因为Alluxio被所有用户共享,一旦发生故障,可能会对所有用户造成影响。JuiceFS 可以直接利用现有熟悉的存储和数据库作为元数据引擎和数据存储,仅需 JuiceFS 客户端即可运行,无需部署独立服务器。此外,每个用户都可以独立配置自己的元数据引擎和数据存储,从而避免相互干扰。

03 如何使用 JuiceFS 构建存储方案

使用 JuiceFS,需要准备一个用作元数据引擎的数据库和对象存储。如前所述,JuiceFS 支持多种数据库和对象存储。为了减轻运维负担,我们使用了 NAVER 现有的平台。

如下图所示,AiSuite 通过以下内部平台使用 JuiceFS。

元数据引擎

在 NAVER,可以使用 nBase-ARC Redis 服务或通过 MySQL 支持来设置元数据引擎。如果是进行开发和测试,也可以通过 Helm chart 直接安装并使用 Redis、PostgreSQL 等。JuiceFS 默认每小时将元数据自动备份到数据存储中,且备份周期是可配置的。因此,即使元数据引擎的数据丢失,也可以进行恢复,但由于元数据备份周期的设定,仍有可能会有部分数据丢失。有关详细信息,请参考元数据备份和恢复

数据存储

可以使用 NAVER 内部的 HDFS 或 nubes Object Storage 存储大规模数据。利用这些资源,可以进行大容量且稳定的数据存储。

nubes

nubes 是 NAVER 的对象存储。JuiceFS 本身不直接支持 nubes,但可以通过使用具有 MinIO 接口的 nubes-s3-proxy 来访问。

HDFS

HDFS 是 JuiceFS 默认支持的存储系统。但是,为了在大规模、多租户的 HDFS 中应用 Kerberos,需要进行以下改进:

支持 Kerberos keytab 文件

NAVER 内部的 HDFS 应用了 Kerberos。因此,当 JuiceFS 使用 HDFS 时,需要进行 Kerberos 认证。原先的 JuiceFS 是通过设置 KRB5CCNAME 环境变量为 credential cache 进行 HDFS 认证。但这个方式会在一定时间后过期失效。为解决这一问题,进行了改进,可以通过设置 KRB5KEYTAB、KRB5PRINCIPAL 环境变量使用 keytab 文件。(参见 JuiceFS issue #3283

支持 base64 编码的 keytab 文件

AiSuite 是一个多租户 Kubernetes 集群,共享给多个用户,目标是允许每个用户使用自己选择的元数据引擎和数据存储来运行 JuiceFS。用户需要自己编写 Kubernetes Secret,设置访问元数据引擎和数据存储的路径和认证信息。但是,KRB5KEYTAB 仅是一个文件路径,并不能让用户传递实际的 keytab 文件。为解决这个问题,进行了改进,允许通过设置 KRB5KEYTAB_BASE64 环境变量使用 base64 编码的 keytab 文件字符串。(参见 JuiceFS issue #3817

支持用户指定的 HDFS 路径

NAVER 内部的 HDFS 由多个用户共享,每个用户仅在分配给他们的 HDFS 路径上拥有权限。但是,原来的 JuiceFS 无法指定用于数据存储的 HDFS 路径,因此总是必须将数据存储在 root 目录下,这导致用户遇到了没有权限访问的路径问题。为解决这个问题,进行了改进,允许用户设置自己的 HDFS 路径。(参见 JuiceFS issue #3526

支持 HDFS 路径 hdfs://nameservice

NAVER 内部的 HDFS 为了大规模运营,采用了 HDFS Federation,由多个 NameNode 和 Namespace 组成。原本的 JuiceFS 需要直接指定 NameNode 路径,如 nn1.example.com:8020,这对用户来说确认和设置很不方便。为了解决这个问题,进行了改进,现在可以像 hdfs://nameservice 这样设置 Namespace。(参见 JuiceFS issue #3576

CSI Driver

AiSuite 是一个多租户 Kubernetes 集群,每个用户通过 Kubernetes namespace 进行区分。如果用户间共享 JuiceFS,可能会相互影响,降低稳定性并增加运维难度。因此,我们的目标是使每个用户都能单独准备自己的元数据引擎和数据存储,并独立使用 JuiceFS。此外,为了减轻运维负担并提高用户便利性,支持 Dynamic Volume Provisioning,使用户无需管理员介入,就能直接定义和使用 PVC(PersistentVolumeClaim)。为此,需要进行以下改进:

支持模板 Secret

用户需要创建 Secret,以设置各自准备的元数据引擎和数据存储的访问路径和认证信息。然后,需要在 StorageClass 中设置以引用这些 Secret。这些设置的 Secret 会被用于 Dynamic Volume Provisioning。但是,原有的 JuiceFS CSI Driver 只能在 StorageClass 中设置一个固定的 Secret。为解决这一问题,进行了改进,允许通过 {pvc.name},{pvc.namespace}, ${pvc.annotations['<annotation style="margin: 0px; padding: 0px; outline: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">']} 等方式,从用户创建的 PVC 中引用 Secret。(参见 JuiceFS CSI Driver issue #698)</annotation>

支持 Secret Finalizer

用户的 Secret 不仅在创建 PVC 时使用,还在删除 PVC 时用于删除 JuiceFS 数据。如果在删除 PVC 之前关联的 Secret 被移除,JuiceFS 数据将不会被清理,而会持续留存。为了防止这个问题,在 PVC 被删除之前,设置 Finalizer 以防止关联的 Secret 被移除。在 StorageClass 的 parameters 中设置 secretFinalizer: "true" 可以启用此功能。(参见 JuiceFS CSI Driver issue #707

支持根据 PVC 元数据设置 mountOptions

AiSuite 中存在多种 AI 工作负载,如 AI 学习、服务和数据处理等。为了实现最优性能,根据工作类型可能需要单独配置 JuiceFS。例如,在使用只读数据进行 AI 训练的情况下,可以通过添加 --open-cache 设置来提高读取性能。有关详细信息,请参考客户端内存中的元数据缓存。原先的 JuiceFS 只能应用 StorageClass 中固定的配置。现在进行了改进,允许根据用户创建的 PVC 进行设置,例如使用 {.PVC.namespace},{.PVC.name}, {.PVC.labels.foo},{.PVC.annotations.bar} 这样的方法。(参见 JuiceFS CSI Driver issue

04 JuiceFS 应用方案

为了在 Kubernetes 中支持 JuiceFS,管理员需要部署 JuiceFS CSI Driver,而用户则需要定义自己的 Secret 和 PVC。在多租户 Kubernetes 环境 AiSuite 中,将详细说明如何部署和提供 JuiceFS,包括具体的示例。

部署方法

安装 JuiceFS CSI Driver 后,可以按照标准 Kubernetes 卷的使用方式进行操作,支持通过 Helm 或 kubectl 进行安装。有关详细信息,请参考 JuiceFS CSI Driver 安装指南。进行部署需要 Kubernetes 管理员权限。为了让每个用户使用自己的元数据引擎和数据存储,StorageClass 设置如下。

apiVersion: storage.k8s.io/v1  
kind: StorageClass  
metadata:  
  name: juicefs
provisioner: csi.juicefs.com  
parameters:  
  #配置系统以便用户可以引用自己创建的 Secret
  #用户需要在 PVC 'csi.juicefs.com/secret-name'注释中设置Secret的名称
  csi.storage.k8s.io/provisioner-secret-name: ${pvc.annotations['csi.juicefs.com/secret-name']}
  csi.storage.k8s.io/provisioner-secret-namespace: ${pvc.namespace}
  csi.storage.k8s.io/node-publish-secret-name: ${pvc.annotations['csi.juicefs.com/secret-name']}
  csi.storage.k8s.io/node-publish-secret-namespace: ${pvc.namespace}
  csi.storage.k8s.io/controller-expand-secret-name: ${pvc.annotations['csi.juicefs.com/secret-name']}
  csi.storage.k8s.io/controller-expand-secret-namespace: ${pvc.namespace}
  juicefs/clean-cache: "true"
  #激活 secretFinalizer,以防止用户定义的 Secret 被随意删除
  secretFinalizer: "true"
  #通过指定 pathPattern来设置路径,可以挂载所需的路径
  #用户可以在 PVC 'csi.juicefs.com/subdir'注释中设置所需的路径
  pathPattern: "${.PVC.annotations.csi.juicefs.com/subdir}"
allowVolumeExpansion: true  
reclaimPolicy: Delete  
mountOptions:  
  #允许用户根据需要设置各自的选项
  #用户可以在PVC的'csi.juicefs.com/additional-mount-options'注释中设置所需的JuiceFS选项。
  - ${.PVC.annotations.csi.juicefs.com/additional-mount-options}

使用方法

用户需要为自己准备的元数据引擎和数据存储创建 Secret,包括路径和认证信息等。

apiVersion: v1  
kind: Secret  
metadata:  
 name: myjfs
type: Opaque  
stringData:  
 #JuiceFS文件系统名称。
 name: myjfs
 #MINIO_ROOT_USER
 access-key: user
 #MINIO_ROOT_PASSWORD
 secret-key: password
 #元数据引擎 Redis 路径
 metaurl: redis://:@redis.user1.svc.cluster.local:6379/0
 #minio
 storage: minio
 #bucket1
 bucket: http://nubes-s3-proxy.user1.svc.cluster.local:10000/bucket1
 #https://juicefs.com/docs/community/command_reference/#format
 format-options: trash-days=0,block-size=16384

定义 PVC。此外,需要设置以下注释(annotation):

apiVersion: v1  
kind: PersistentVolumeClaim  
metadata:  
 name: myjfs
 annotations:
   csi.juicefs.com/secret-name: myjfs # 之前创建的Secret名称
   csi.juicefs.com/subdir: myjfs # 在JuiceFS文件系统中的路径
   csi.juicefs.com/additional-mount-options: "writeback,upload-delay=1m" # 如果需要,可以添加JuiceFS的配置
spec:  
 accessModes:
 - ReadWriteMany
 resources:
   requests:
     storage: 100Gi
 storageClassName: juicefs

创建 Secret 和 PVC 后,可以像使用普通卷一样使用它们。下面是一个将之前创建的 myjfs PVC 挂载到 /data 上的 Pod 示例。

apiVersion: v1  
kind: Pod  
metadata:  
 name: example
spec:  
 containers:
 - name: app
...
   volumeMounts:
   - mountPath: /data
     name: juicefs-pv
 volumes:
 - name: juicefs-pv
   persistentVolumeClaim:
     claimName: myjfs

05 性能测试

JuiceFS 性能基准测试如下所示,与 EFS, S3FS 相比,其性能更高。

我们需要验证当使用 nubes 对象存储和 HDFS 作为数据存储时的性能表现。由于 JuiceFS 的性能可能会根据所使用的数据存储类型而有所差异,我们还需关注由于使用 UserSpace 中的 Fuse 而可能出现的性能降低。特别是需要检验因 JuiceFS Fuse 引起的性能下降情况。测试的主要目的是确定,相比直接使用数据存储,JuiceFS 是否会导致性能下降。如果性能差异不显著,那么我们可以在保持性能的同时支持 POSIX 兼容性、并发访问等多项功能。

顺序读写

参考 Fio Standalone Performance Test,按照以下方式进行了测试:

测试结果如下:

文件创建

此次测试比较了创建 1 万个小文件所需的时间。测量使用 nubes 作为数据存储时处理元数据的性能,并与 JuiceFS 进行比较。

测试结果如下:

测试结论

JuiceFS 的性能基本上取决于存储数据设备的性能。它能够支持 POSIX 兼容性且无性能损失、还能支持并发访问等功能。

在某些工作负载和使用情况下,JuiceFS 的性能有时甚至可能优于数据存储设备的原始性能。尽管本文未进行测试,但读取缓存数据时,因为是从本地磁盘读取,因此有可能提高性能。写入临时数据时,应用 writeback 选项也可以提升性能。

JuiceFS 支持多种可根据工作负载进行调整的缓存选项。更多详细信息,请参考 cache

06 小结

企业内部存储 + JuiceFS

在 AiSuite 中,我们利用公司内部支持的 HDFS 和 nubes 对象存储来构建 JuiceFS。这样既能为 AI 工作负载提供适合的存储,又能减轻运维负担。对于前文中评估的企业内部存储,可以总结如下:

在 AiSuite 中,相比于 HDFS,更推荐使用 nubes Object Storage 用作数据存储。这是因为在 HDFS 中,如果文件数量众多,会对管理 HDFS 元数据的 NameNode 造成负担。而 JuiceFS 会将文件分割成小的 Block 来存储,从而在 HDFS 中产生大量文件。即使是最大可设置的 Block 大小 16MB,为了存储 1TB 数据,也需要创建超过 62,500 个文件。虽然考虑过将最大 Block 大小提升至 64MB,但 Block 变大可能会导致效率低下。更多详细信息,请参考 Increase the maximum of blockSize to 64MB

优势

在 AI 平台 AiSuite 中,我们评估了 JuiceFS 作为 AI 工作负载的存储解决方案的可行性。JuiceFS 具有以下多种优势:

通过使用 JuiceFS,能够将企业内部传统的存储转换为具有高性能和多功能的、适合 AI 工作负载的存储。JuiceFS 支持多种存储和多种数据库。这篇文章主要介绍了在 NAVER 内部的 on-premise 环境中的应用案例,但它也可以应用于 AWS、Google Cloud 等公共云环境。希望这篇文章能对面临类似问题的用户提供帮助。

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

上一篇下一篇

猜你喜欢

热点阅读