如何处理视频和图像上传到存储服务器



我正在开发一个应用程序(在Go中或可能在PHP中),用户需要上传照片和图像。

我已经在不同的位置设置了几个ZFS(镜像)存储服务器,但我对如何让用户最好地上传文件持怀疑态度。ZFS处理配额和预订。

我在所有服务器上运行一个复制的Galera数据库,这既是为了安全,也是为了方便从每台服务器访问用户帐户。换句话说,每个服务器始终都有数据库的本地副本。所有用户都是仅限的虚拟用户。

到目前为止,我已经测试了以下设置选项:

解决方案1

在具有虚拟用户的存储服务器上运行SFTP(带模块的ProFTPD)或FTPS(带TLS的纯FTP)。

这使人们可以使用像Filezilla这样的客户端直接访问存储服务器。同时,用户还可以使用我们的web GUI从我们的主web服务器上传。

这种设置的一个优点是FTP服务器可以处理虚拟用户。我们的网络应用程序还将通过SFTP或FTPS发送文件。

一个缺点是FTP是meeh,烦人的防火墙。此外,我更喜欢FTP而不是SSH(SFTP),而不是FTP而不是TLS(FTPS)。然而,只有ProFTPD有SSH模块,但与PureFTPd相比,使用它确实很痛苦(许多配置选项不起作用和文件权限错误的问题),但PureFTPd只支持TLS。

使用真实的SSH/SCP帐户运行并使用PAM是而不是选项。

解决方案2

使用NFS或CIFS在web服务器上本地装载存储服务器(Samba非常擅长在出现故障时自动恢复)。

在此设置中,用户只能通过我们的主web服务器上传。然后,web服务器应用程序和在存储服务器上运行的应用程序需要支持可恢复的上载。我一直在研究使用tus协议。

以上两种设置的缺点是需要以某种方式管理存储容量。当存储服务器1达到其最大用户数时,应用程序需要知道这一点,然后只为存储服务器2、3等创建虚拟用户。

我计算了每个存储服务器可以容纳多少用户,然后让web应用程序与虚拟用户一起检查数据库,看看何时需要将新创建的用户移动到下一个存储服务器。

这是一所相当古老的学校,但它很管用。

解决方案3

与解决方案2相同(无FTP),但将我们的web应用程序上传内容克隆到每个存储服务器,然后重定向用户(或为他们提供到存储服务器的物理链接,s1.example.com、s2.example.com等)

这种设置的可能优势在于,用户可以直接上传到他们分配到的存储服务器,而不是通过我们的主web服务器(防止它成为可能的瓶颈)。

解决方案4

在存储服务器上使用GlusterFS,构建一个可以轻松扩展的集群。我已经测试了GlusterFS,它在这个目的上运行得很好。

这种设置的优点是,我实际上不需要关心文件在哪些存储服务器上的物理位置,而且我可以通过向集群中添加更多服务器来轻松扩展存储。

然而,这里的缺点是,我们的主web服务器可能会成为一个瓶颈。

我还考虑添加一个负载均衡器,然后使用多个web服务器,以防我们的主web服务器成为上传文件的瓶颈。

无论如何,我更喜欢保持简单!我不喜欢添加东西。从长远来看,我希望它易于维护。

任何想法、建议和建议都将不胜感激。

你是怎么做到的?

如果我们谈论的是文件存储,web应用程序应该与底层存储无关;关注点分离。

(S) 另一方面,FTP(S)不是一种存储方法。它是一种通信协议。这并不妨碍您拥有共享存储。请参见上文。

ZFS不具备共享存储功能,因此您基本上只能选择以下选项:

  1. 哪个底层文件系统
  2. 我想通过(S)FTP(S)提供额外的访问模式吗
  3. 如何使我的文件系统跨多个服务器可用?GlusterFS、CIFS还是NFS

所以,让我们来了解一下。

文件系统

我知道ZFS很有趣,但事情是这样的:例如,xfs的最大文件系统大小已经是8个字节减去一个字节。这方面的专业术语是"s…load"。给你一个关系:国会图书馆拥有大约20TB的数字媒体,大约可以容纳40万次。即使是好的ol’ext4也能容纳5万个LoC。如果你持有那么多数据,你的FS是你最关心的问题。建造接下来的几座发电厂,让你的东西继续运转。

Gist考虑起来很好,但使用任何你觉得舒服的东西。我个人几乎所有的事情都使用xfs(在LVM上)。

其他访问方法

当然,为什么不呢?除了安全噩梦(特权升级,有人吗?)。ProFTPd,内置咖啡机和厨房水槽,是我最后一台FTP服务器。它有庞大的代码库,这有助于意外引入漏洞。

基本上,它可以归结为项目中存在的技能。你们能正确地加固系统和FTP服务器,并监控是否发生安全事件吗?除非你的回答是自信的"是的,ofc,有丰富的经验!"否则你应该尽量减少你的攻击面。

Gist不要,除非你真的知道自己在做什么。如果你必须问,你可能不会。无意冒犯,只是陈述事实。

共享文件系统

就我个人而言,我已经。。。使用GlusterFS的体验并不完美。当涉及到网络延迟等问题时,复制有相当多的要求。简而言之:如果我们谈论的是多个可用性区域,比如EMEA、APAC和NCSA,这几乎是不可能的。你会被地理复制所束缚,这对于你描述的用例来说并不理想。

另一方面,NFS和CIFS存在根本没有复制的问题,并且所有客户端都需要访问同一个服务器实例才能访问数据——如果您认为需要底层ZFS来处理,这不是一个好主意。

Gist具有适度复制滞后和访问时间的全局范围内的共享文件系统非常很难做到,并且可能会使非常非常昂贵。

哈哈,聪明人,你有什么建议

缩放。慢速地一开始,您应该能够使用一个简单的基于FS的文件存储库。然后检查大规模共享存储的各种其他方式并迁移到它。

转向实现,我甚至会更进一步,你应该让你的存储成为一个接口:

// Storer takes the source and stores its contents under path for further reading via
// Retriever.
type Storer interface {
StreamTo(path string, source io.Reader) (err error)
}
// Retriever takes a path and streams the file it has stored under path to w.
type Retriever interface {
StreamFrom(path string, w io.Writer) (err error)
}
// Repository is a composite interface. It requires a
// repository to accept andf provide streams of files
type Repository interface {
Storer
Retriever
Close() error
}

现在,您可以很容易地实现各种存储方法:

// FileStore represents a filesystem based file Repository.
type FileStore struct {
basepath string
}
// StreamFrom statisfies the Retriever interface.
func (s *FileStore) StreamFrom(path string, w io.Writer) (err error) {
f, err := os.OpenFile(filepath.Join(s.basepath, path), os.O_RDONLY|os.O_EXCL, 0640)
if err != nil {
return handleErr(path, err)
}
defer f.Close()
_, err = io.Copy(w, f)
return err
}

就我个人而言,我认为这将是GridFS的一个很好的用例,尽管它的名称不是文件系统,而是MongoDB的一个特性。至于原因:

  1. MongoDB提供了一个称为副本集的概念,通过服务器之间透明的自动故障切换来确保可用性
  2. 它提供了一种相当简单的自动数据分区机制,称为分片集群
  3. 它配备了无限数量的访问网关,称为mongos查询路由器,用于访问您的碎片数据
  4. 对于客户端来说,除了连接URL之外,所有这些都是透明的。因此,它的存储后端是由单个服务器组成,还是由具有600个节点的全局复制的分片集群组成,这并没有什么区别(几乎除了读取偏好和写入问题)
  5. 如果操作得当,不会出现单点故障,您可以跨可用性区域进行复制,同时将"热门"数据保持在各自用户附近

我在GitHub上创建了一个存储库,其中包含一个接口建议示例,并实现了一个基于文件系统的存储库和一个MongoDB存储库。你可能想看看它。它目前缺乏缓存。如果你想看到它的实现,请在那里打开一个问题。

最新更新