进可攻退可守的存储解决方案
背景
掐指一算,已经离职创业一个月了。前些天在给创业公司寻找存储的解决方案,花了几天的时间把 MinIO 和 S3 相关文档深入了解了下,总结出一个进可攻退可守的存储解决方案。在此将一些重点列一下,算是给自己备忘,也算是抛砖引玉。
对象存储
说到存储,不得不提到最近非常火热的「对象存储」,英文全称为 Object Storage Service,简称 OSS。你可以把 OSS 简单理解为大型的文件键值对系统,区别于一般的键值对系统,OSS 存储的是文件。
最早做对象存储的应当是 Amazon Cloud,其对象存储服务通常被称为 Amazon S3,或简称 S3。后面做对象存储的基本都是在「山寨」S3,包括阿里、腾讯等云厂商。更普遍的证据在于,基本上所有的 OSS 服务商、以及相关的软件都在努力朝「兼容 S3」方向发展。此处应有一句「S3 NB」。
对象存储相比于块存储、文件存储,封装性更强,也非常满足我司存储图片、视频的需求,因此存储选型的大方向基本敲定了它。至于对象存储的更多解释,以及对象存储和其它存储的详细对比,此处不展开介绍,感兴趣可自行谷歌。
何谓进可攻退可守
看标题中的「进可攻退可守」,你可能会好奇什么是进可攻退可守?为何要进可攻退可守?
做软件开发的应该都知道扩展性的重要性,在选择技术方案的时候尤其要注意,千万不要因为仓促走了一条路,让自己无路可走。
初创企业因为资金有限、设备有限,没有自己的机房,可能也没有多好的服务器。这种情况下,我们多半是要使用第三方云厂商提供的对象存储服务,比如阿里云、腾讯云之类。
企业正常运转之后,考虑到数据的安全性以及长期成本等因素,多半会考虑自建分布式存储服务。说到这里,如果你想到迁移成本的问题,那么你已经在成为架构师的路上了。
为了降低迁移带来的开发成本,在使用第三方 OSS 时,应当尽量解耦,比如用一个「无公害」的第三方服务,对阿里云 OSS 进行代理和中转。那么谁能担当此大任呢?
MinIO
有这么一个团队,他们开发出了一款开源软件,可以用于快速部署私有的对象存储服务,具有高性能、高扩展性、安全可靠、兼容 S3、分布式友好等特点。它就是我们实现「进可攻退可守的关键」—— MinIO。
Build high performance data infrastructure for machine learning, analytics and application data workloads with MinIO
MinIO 有 server
和 gateway
两种运行模式,前者适用于有自己的硬盘等存储硬件设备的情况,后者可以理解为代理模式,目前可以代理 Azure、GCS、NAS、S3 以及其它兼容 S3 的 OSS 服务商。
代理模式下,MinIO 对外部请求进行解析和转发至被代理方(比如阿里云 OSS),间接提供存储服务。当然,MinIO 不只是做了请求转发,还提供了多种额外的功能,比如文件缓存、服务监控等。
使用 MinIO 提供对象存储服务,创业初期运行在 gateway
模式,后期迁移时,不需要改调用方的代码,只需要将数据从第三方云厂商迁移至自有设备即可,由此实现了「进可攻退可守」。关于 MinIO 的更多介绍,可以看 官方文档 以及 官方代码仓库,此处不啰嗦。
文件保护
说到文件存储,就会涉及到文件的上传与下载问题,尤其是其中的权限问题。对于一个非开放式的存储系统而言,不应该让所有人都可以随意访问或上传文件。MinIO 或者说 S3 提供两种方式,对文件进行保护。
一种是创建权限有限的用户,创建时设置其相关权限,创建后会生成相应的 KEY
和 SECRET
,用户使用合法的密钥信息读取和上传文件。一种是根据用户的访问意图,创建预先签名的临时的 URL,用户拿着该 URL 拥有对资源的临时上传下载权。
如果说访问策略简单,不需要创建过多的用户,同时不担心密钥泄露的问题,可以采用第一种方式,MinIO 也提供了完善的 JS SDK,可以方便地实现文件访问、小文件上传、大文件分片上传的功能。
需要注意的是,如果 MinIO 运行在 gateway
模式,需要配合 etcd
的使用,才可以创建多用户。详情可以查看 MinIO Multi-user Quickstart Guide。
如果访问策略无法固化,而且站点用户数量庞大,或者希望尽可能轻量化 MinIO,则应该考虑第二种方式。根据实现不同,可以分为以下几个场景:
- 访问文件:后端根据文件所在的存储桶
Bucket
和文件名Key
,按照 S3 签名过程生成临时访问链接给用户 - 上传文件:
- 小文件:后端根据存储桶和文件名,按照 S3 签名过程生成临时上传链接给用户,用户使用 PUT 方法,将文件上传至相应链接
- 大文件:
- 后端根据存储桶和文件名,向 MinIO 发起创建分片上传的请求,获取到
UploadId
- 根据
UploadId
和分片数量生成分片上传的 URL - 用户对文件分片、计算每片的 Content-MD5 值,通过分片对应的 URL 使用 PUT 方式上传
- 用户告诉后端所有分片上传完成后,后端向 MinIO 发合并分片请求,结束本次分片上传
- 后端根据存储桶和文件名,向 MinIO 发起创建分片上传的请求,获取到
此处有坑
如果你恰好是用 Laravel 开发,或者以下内容会对你有些帮助,否则可以跳过。
Laravel 使用 MinIO
How to use MinIO Server as Laravel Custom File Storage
创建预签名 URL
使用的 aws-dk-php
库,可以参考官方文档。话不多说,直接上示例代码。
1 | /* @var S3Client $storageClient */ |
Content-MD5 小坑
为保证上传的准确性,可以在上传时提供 Content-MD5
头部,MinIO 会对上传内容和提供的头部进行校验,只有校验通过才会保留并返回 200
的状态码。
但是,此处提到的 Content-MD5
与 MD5 值不同,是指将文件的 MD5 值的二进制表示通过 Base64 编码而来。如果不确定自己的实现方式是否有误,可以利用 这个网站 作对比。
小结
嗯,总的来说,如果你想使用对象存储,但又不希望过度依赖阿里云等云服务厂商,希望以后迁移成本较低,那么 MinIO 是一个不错的选择。
参考资料
- 文件系统vs对象存储——选型和趋势
- 什么是云对象存储?
- 对象存储概述
- Amazon S3
- 块存储/文件存储/对象存储的本质差别
- The Content-MD5 Header Field
- 使用 Content-MD5 标头和 MD5 校验和
- How to use MinIO in Laravel
- AWS SDK PHP Document
- Upload Part - Amazon Simple Storage