我正在开发一个应用程序(使用Go或可能使用PHP),用户需要上传照片和图像。
我已经在不同位置设置了两个ZFS(镜像)存储服务器,但是我对如何让用户最好地上传文件存有疑问。 ZFS处理配额和预留。
我在所有服务器上都使用一个复制的Galera数据库运行,这既出于安全性考虑,也便于从每个服务器访问用户帐户。换句话说,每个服务器始终具有数据库的本地副本。所有用户都是仅的虚拟用户。
到目前为止,我已经测试了以下设置选项:
解决方案1
在具有虚拟用户的存储服务器上运行SFTP(带模块的ProFTPD)或FTPS(带TLS的纯FTPs)。
这使人们可以使用Filezilla之类的客户端直接访问存储服务器。同时,用户还可以使用我们的Web GUI从我们的主Web服务器上载。
此设置的一个优点是FTP服务器可以处理虚拟用户。我们的Web应用程序还将通过SFTP或FTPS发送文件。
一个缺点是FTP很麻烦,防火墙很烦人。我也更喜欢FTP而不是SSH(SFTP),而不是TLS之上的FTP(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服务器成为上传文件的瓶颈。
无论如何,我更喜欢保持简单!我不喜欢添加东西。从长远来看,我希望它易于维护。
任何想法,建议和建议将不胜感激。
你如何做到的?
答案 0 :(得分:0)
如果我们正在谈论文件存储,那么Web应用程序应该与底层存储无关。关注点分离。
另一方面,(S)FTP(S)不是存储方法。它是一种通信协议。它并不排除您拥有共享存储。见上文。
ZFS不具备共享存储功能,因此基本上可以选择以下选项:
所以,让我们逐步了解一下。
我知道ZFS令人着迷,但问题在于:例如xfs的最大文件系统大小为8 exb减去一个字节。专门的术语是“负载”。为您提供联系:国会图书馆拥有大约20TB的数字媒体-可以容纳大约40万次。即使是出色的ext4也可以容纳5万个LoC。而且,如果您拥有那么多数据,则FS是您最小的担心。建造下几座发电厂,以使您的设备正常运转。
要点很高兴考虑,但是请使用您认为合适的任何方式。我个人使用xfs(在LVM上)几乎完成所有操作。
当然,为什么不呢?除了安全噩梦(特权升级,还有其他人吗?)。 ProFTPd(内置咖啡机和厨房水槽)是我将用于任何用途的 last FTP服务器。它具有庞大的代码库,非常适合accidentally introducing vulnerabilities。
基本上,它可以归结为项目中存在的技能。你们可以 正确 加固系统和FTP服务器并 monitor 来处理安全事件吗?除非您的答案是自信的“是的,当然,他们有丰富的经验!”您应该最大程度地减少攻击面。
要旨除非您真的知道自己在做什么,否则不要这么做。而且,如果您不得不问,您可能不必问。没有意图冒犯,只是陈述事实。
就我个人而言,我对GlusterFS的体验还不完美。当涉及网络延迟和填充时,复制具有相当大的要求。简而言之:如果我们要谈论多个可用区,例如EMEA,APAC和NCSA,那几乎是不可能的。您会被困在georeplication上,这对于您描述的用例而言并不理想。
另一方面,NFS和CIFS的问题是根本没有复制,并且所有客户端都需要访问同一服务器实例才能访问数据-如果您认为需要基础ZFS,这不是一个好主意。相处。
要点在全球范围内共享文件系统,复制延迟中等,访问时间 非常 ,很难做到,并且可以 非常 昂贵。
规模。慢慢来首先,您应该能够与基于文件的简单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的功能。由于原因:
mongos
查询路由器,用于访问分片数据。我创建了一个repository on GitHub,其中包含界面建议的示例,并实现了基于文件系统的存储库以及MongoDB存储库。您可能想看看它。目前缺少缓存。如果您希望看到它已实施,请在此处打开一个问题。