paint-brush
为什么不应该将文件系统放在对象存储之上经过@minio
6,908 讀數
6,908 讀數

为什么不应该将文件系统放在对象存储之上

经过 MinIO7m2023/11/14
Read on Terminal Reader

太長; 讀書

当大型组织需要存储和访问海量数据以进行深度学习、人工智能和其他数据密集型用例时,POSIX 不具备满足这些需求的能力或可扩展性。
featured image - 为什么不应该将文件系统放在对象存储之上
MinIO HackerNoon profile picture
0-item
1-item

购买存储时,重点通常是媒体,但考虑访问方法可能更为重要。在设计和采购基础设施时,您需要考虑存储协议,尤其是当您放弃旧存储以迁移到云原生对象存储时。然而,对象存储依赖于 S3 API 进行通信,而遗留工作负载则依赖于 POSIX(可移植操作系统接口),它提供了 20 世纪 80 年代开发的一组标准,允许应用程序在 Unix 操作系统之间移植。大多数企业的应用程序很可能已经投入使用了数十年,这些应用程序是为在 POSIX 上运行而开发的。工程师也很可能已经意识到 POSIX 的糟糕性能。


话虽这么说,当您的遗留系统只能以某种格式或从某个来源获取数据时,您的选择可能会受到限制,您可能别无选择,只能实现过时的协议或重写代码。例如,如果您只能通过文件系统从本地磁盘获取数据,而不能通过网络访问 RESTful API,那么您必须首先在磁盘上提供该数据,然后应用程序才能使用它。然而,使用对象存储作为文件系统在性能、兼容性、数据完整性和安全性方面会产生许多严重的负面影响。


让我们使用名为s3fs-fuse的小实用程序通过一些实际测试来演示这一点。该实用程序允许您将 S3 存储桶挂载为本地文件系统。它代表 S3(简单存储服务)文件系统 - FUSE(用户空间中的文件系统)。它是一个开源项目,利用 FUSE(用户空间中的文件系统)接口向 S3 提供类似文件系统的接口。


使用s3fs-fuse安装 S3 存储桶后,您可以与该存储桶进行交互,就像它是本地文件系统一样。这意味着您可以对存储桶中的文件使用常规文件操作(例如读取、写入、移动等)。这听起来非常方便,我们可以说它简化了应用程序开发。但对象存储和文件系统本质上存在根本差异,这将影响作为文件系统挂载的 s3 存储桶。


让我们花点时间从s3fs-fuse实用程序退后一步,讨论将对象存储视为文件系统远非最佳的真正原因。这个问题比s3fs-fuse要大得多,还包括其他实用程序,例如基于 Rust 的Mountpoint for Amazon S3 ,这是一个将本地文件系统 API 调用转换为 S3 对象 API 调用的文件客户端。第一个原因是所有这些实用程序都依赖 POSIX 进行文件系统操作。 POSIX 效率低下,而且它从来就不是为了通过网络处理非常大的文件而设计的。


随着需求(尤其是并发需求)的增加,基于 POSIX 的系统的速度会降低。当大型组织需要存储和访问海量数据以进行深度学习、人工智能和其他数据密集型用例时,POSIX 不具备满足这些需求的能力或可扩展性。虽然全闪存阵列保留了 POSIX 的优势,但可扩展性和 RESTful API(云的标志)就像氪石一样。


因此,在对象存储之上运行 POSIX 并不是最佳选择。让我们看一下其中的一些原因:


  1. 性能: POSIX FS 接口本质上是以 IOPS 为中心的。它们很健谈、昂贵且难以扩展。 RESTful S3 API 通过将 IOPS 转化为吞吐量问题来解决这个问题。扩展吞吐量更容易且成本更低。这就是对象存储在大规模下具有高性能的原因。在 S3 上分层 POSIX 将无法扩展,因为 POSIX 过于繁琐,无法通过 HTTP RESTful 接口执行。


  2. 语义:因为对象操作是原子的、不可变的,所以没有办法保证一致性的正确性。这意味着您可能会在崩溃时丢失未提交的数据,或者在共享挂载时遇到损坏问题。


  3. 数据完整性:在提交之前,对文件的写入或任何更改都不会出现在命名空间中。这意味着跨共享安装的并发访问将看不到修改。它对于共享访问没有用。


  4. 访问控制: POSIX 权限和 ACL 很原始,与 S3 API 处理身份和访问管理策略的方式不兼容。无法在顶级 S3 API 上安全地实施 POSIX 访问管理。


POSIX 还缺乏开发人员喜欢的 S3 的大部分功能,例如对象级加密、版本控制、不变性——这些在 POSIX 世界中根本没有等效的功能,而且没有任何东西能够翻译它们。

POSIX 痛点

这些例子说明了这个问题及其影响。首先,我们将使用这个大约 10GB、有 112 行的 CSV 文件。


注意:我们假设s3fs-fuse已安装,并且您已将对象存储中的存储桶之一安装到文件系统中。如果没有,请按照此处的说明进行操作。


在这些示例中,我们假设存储桶名称为 test-bucket,文件名为taxi-data.csv,位于 /home/user/ 目录中,并且s3fs-fuse bucket安装在 /home/user/test-bucket/

复制操作

我们将首先尝试一些简单的操作,然后尝试使用 mc 命令将 CSV 文件复制到我们的测试存储桶并记录所花费的时间

time mc cp /home/user/taxi-data.csv minio/test-bucket/taxi-data.csv


这不会花费很多时间,并且文件应该复制到我们的存储桶中。现在让我们尝试对s3fs-fuse执行相同的操作

time cp /home/user/taxi-data.csv /home/user/test-bucket/taxi-data.csv


测试期间花费的时间


real 1m36.056s user 0m14.507s sys 0m31.891s


就我而言,我只能将文件部分复制到存储桶,并且操作失败并出现以下错误


cp: error writing '/home/user/test-bucket/taxi-data.csv': Input/output error cp: failed to close '/home/user/test-bucket/taxi-data.csv': Input/output error


经过多次尝试成功了


real 5m3.661s user 0m0.053s sys 2m35.234s


正如您所看到的,由于该实用程序需要进行大量的 API 调用以及操作的一般开销,该实用程序变得不稳定,并且大多数操作甚至无法完成。

熊猫示例

我们向您展示了一个简单的cp示例,该示例可能令人信服,也可能不令人信服,因为让我们面对现实吧,您可能认为time cp相当初级。


因此,对于需要更多经验证据的人来说,让我们编写一个 Python 片段来测试这一点。我们将使用s3fs-fusepython s3fs包做一个简单的 Pandas 示例,并查看性能影响


import timeit import os import fsspec import s3fs import pandas as pd # Write a dummy CSV file to test-bucket df = pd.DataFrame({"column1": ["new_value1"], "column2": ["new_value2"]}) df.to_csv("s3://test-bucket/data/test-data.csv", index=False) def process_s3(): # add fsspec for pandas to use `s3://` path style and access S3 buckets directly fsspec.config.conf = { "s3": { "key": os.getenv("AWS_ACCESS_KEY_ID", "minioadmin"), "secret": os.getenv("AWS_SECRET_ACCESS_KEY", "minioadmin"), "client_kwargs": { "endpoint_url": os.getenv("S3_ENDPOINT", "https://play.min.io") } } } s3 = s3fs.S3FileSystem() for i in range(100): # Read the existing data print(i) df = pd.read_csv('s3://test-bucket/data/test-data.csv') # Append a new row new_df = pd.concat([df, pd.DataFrame([{"column1": f"value{i}", "column2": f"value{i}"}])], ignore_index=True) # Write the data back to the file new_df.to_csv('s3://test-bucket/data/test-data.csv', index=False) execution_time = timeit.timeit(process_s3, number=1) print(f"Execution time: {execution_time:.2f} seconds")


测试期间花费的时间

Execution time: 8.54 seconds


现在让我们对s3fs-fuse尝试同样的操作


import timeit import pandas as pd # Write a dummy CSV file to test-bucket df = pd.DataFrame({"column1": ["new_value1"], "column2": ["new_value2"]}) df.to_csv("s3://test-bucket/data/test-data.csv", index=False) def process_s3fs(): for i in range(100): # Read the existing data print(i) df = pd.read_csv('/home/user/test-bucket/data/test-data.csv') # Append a new row new_df = pd.concat([df, pd.DataFrame([{"column1": f"value{i}", "column2": f"value{i}"}])], ignore_index=True) # Write the data back to the file new_df.to_csv('/home/user/test-bucket/data/test-data.csv', index=False) execution_time = timeit.timeit(process_s3fs, number=1) print(f"Execution time: {execution_time:.2f} seconds")



测试期间花费的时间

Execution time: 9.59 seconds


这些示例演示了对 S3 文件的持续读取和写入。想象一下,多个客户端同时执行此操作 - 延迟会急剧增加。

删除开销!

正如您所看到的,使用 POSIX 转换将对象视为文件与使用直接 API 处理对象之间的区别是天壤之别。在安全性、性能、数据完整性和兼容性方面根本没有可比性。 MinIO 拥有可与几乎所有流行编程语言集成的SDK ,并且它可以在几乎所有平台上运行,例如 Kubernetes、裸机 Linux、Docker 容器等等。


MinIO 通过静态和传输中的加密来保护对象,并结合 PBAC 来调节访问和擦除编码以保护数据完整性。无论您在何处运行 MinIO,您都将获得尽可能最佳的性能,因为它利用底层硬件(请参阅为您的 MinIO 部署选择最佳硬件)来提供尽可能最佳的性能。我们对 MinIO进行了基准测试,GET 上的速度为 325 GiB/s (349 GB/s),PUT 上的速度为 165 GiB/s (177 GB/s),仅使用 32 个现成 NVMe SSD 节点。


MinIO 和您的应用程序之间根本不需要文件系统实用程序!遗留应用程序可能获得的任何优势都将被 POSIX 的痛苦所抵消。


如果您对在应用程序中使用 POSIX 翻译有任何疑问,请务必通过Slack联系我们!


也发布在这里