随着平台规模的增长,代码托管从业人员也会遇到一些问题难以解决,在我职业生涯中同样如此,解决问题的过程是艰辛的,去年年底,我曾经写过一篇文章:《性能,可扩展性和高可用 - 大型 Git 代码托管平台的关键问题》,文章的内容与本节内容相似,这里带领读者重新回顾一下。
大型存储库的优化目前国内 IT 行业版本控制系统都在往 Git 迁移,一些大型企业,软件源码历史悠久,存储的文件各种各样,在迁移到 Git 时,体积巨大的存储库给代码托管平台带来了压力,首当其冲的问题就是从其他版本控制系统迁移到 Git 耗时太长。
Git 在安装了 SVN 的前提下,支持 git svn 命令访问 SVN 仓库,从 SVN 仓库迁移到 Git 的逻辑很简单,就是从 Rev0 开始,递归的创建 Git 提交,如果这个存储库历史悠久,提交特别多,文件特别多,那么转换耗时将非常长。网络上也有一种优化方案,直接在 SVN 中央存储库,通过解析存储库元数据,直接在上面创建 Git 提交,这种方案的耗时可能是原本的数十分之一。KDE 团队维护的 svn-all-fast-export aka svn2git 就是其中一款。
转移到 Git 后,如果存储库包含很多的二进制文件,存储库体积巨大,那么用户拉取的时间还是会很长,一种解决方案是将不同的数据分离,也就是将体积大的二进制文件,通过 Git 扩展 git lfs 追踪,从源码中排除,通过这种措施存储库的体积减小,平台的压力降低,而这些大文件可以存储到其他的设备上,比如对象存储,利用 CDN 优化,就能提升用户的体验.实现 Git LFS 服务器可以参考我之前的博客《Git LFS 服务器实现杂谈》。
如果存储库小文件特别多,这个时候 Git LFS 的作用反而没有那么大了,Git LFS 并不存在打包机制,也没有压缩,如果大量文件使用 Git LFS 跟踪,那么 HTTP 请求数会变得非常多,传输时间也会特别长。微软在将 Windows 源码迁移到 Git 做技术选型便遇到了问题,Windows 源码数百 GB,引用数量数十万,这些传统方案和 Git LFS 完全不能解决。于是微软的开发者推出了 VFS for Git 用来解决这个问题,简单来说,VFS for Git 的手段是只获得浅表 commit 以及相应的 tree 对象,然后在文件系统建立虚拟文件,也就是用户空间文件系统 Filesystem in Userspace (FUSE) 创建占位符文件,但向这种文件发起 IO 操作时,驱动会触发 VFS for Git 客户端取请求远程服务器,获得这些文件,在 Windows 上 FUSE 使用了 NTFS 重解析点,其 TAG 为 IO_REPARSE_TAG_PROJFS,微软前员工 Saeed Noursalehi(现已加入 Facebook)曾写过一些 VFS for Git 的文章,比如 《Git at Scale》以及《Git Virtual File System Design History》,大家有兴趣可以看一下。VFS for Git 惊艳的架构也吸引了 GitHub 的注意,当时 GitHub 还未被 Microsoft 收购,GitHub 创建了 Linux projected filesystem library 项目试图在 Linux 上创建类似 Windows 平台的 projfs,以支持 VFS for Git 在 Linux 上运行,但该项目一直没有被完成。
VFS for Git 的设计是独树一帜的,也很难推广开来,目前除了 Microsoft 的 Azure,其他平台几乎都没有支持,核心就是 Git 客户端支持难度高。后来 Git 的一些开发者提议在 Git 中实现部分克隆,经过几年的努力,终于支持部分克隆,该方案和 VFS for Git 类似,使用有线传输协议的 filter 机制,实现一个 blob filter 过滤掉 blob,与 VFS for Git 存在差异的是,没有 FUSE 加成,最终使用有限,是否能够有其他手段提升部分克隆的实用性,还得 Git 贡献者们进一步的努力了。
最近,Git 贡献者还增加了 Packfile URIs 设计,该方案旨在将对象通过 CDN 存储,然后客户端根据返回的地址请求到合适的 CDN 下载存储库对象,该方案仍处于早期,还有许多细节要处理,最终能做到什么程度有待观察。
代码托管平台伸缩性大型代码托管平台面临的另一个问题则是系统的伸缩性,在架构上具备良好的伸缩性则意味着平台能做到多大的规模,比如 Gitea/Gogs 这种倾向于单节点的开源代码托管平台要做到大型分布式代码托管平台就麻烦得多,而 GitLab 则更容易搭建分布式可扩展的代码托管平台。
在讨论伸缩性之前,我们要解释一下分布式文件系统为什么不适合大型代码托管平台。
Git 的计算压力并没有随着分布式文件系统的扩展性而分摊。
分布式文件系统很难解决 Git 小文件的问题,特别是小文件带来的系统调用,IO 问题。
分布式文件系统反而会带来平台内部网络数据的消耗,比如文件的元数据,以及文件的数据。
国内外厂商的生产事故历历在目。