DGit 介绍

boblu 8年前
   <p>GitHub的数百台服务器中存储着超过3500万仓库以及超过3000万的Gists 代码片段。去年以来,我们创建了 DGit,DGit是一个分布式存储系统,DGit显著地提高了Git存储的可用性,可靠性。</p>    <p>DGit是“Distributed Git”的简写,即分布式Git。众所周知,Git本身就是分布式的,任何的Git仓库备份都是包含该项目所有历史版本的所有的文件,分支,以及提交记录。DGit利用Git的这个特性为每个仓库在三个服务器中保存着三份备份。DGit的的设计初衷是为了实现Git存储没有单点故障的可用性要求。甚至其中的两个备份都不可用,仓库仍能保持可读状态;类似 fetches,clones,以及大部分的web操作依然可用。</p>    <p>DGit 的备份机制是在应用层的,而不是硬件层的磁盘。要知道DGit的备份是通过Git协议同步的三份松耦合的完整仓库,而不是完整的磁盘镜像。这样的设计让我们可以很灵活地决策备份存储在哪里,以及哪个备份用于读操作。</p>    <p>假设一个文件服务器需要下线,DGit可以自动地判断哪些仓库的备份少于3份,并且自动创建一个新的备份到其他可用的服务器上。这个”自愈“的程序使用集群中剩下所有服务器作为操作源和目的。这个自愈程序的吞吐量是多对多并行的,所以性能上会很快,而且这个过程不会引起服务中断。</p>    <h2>DGit 使用原生的Git技术</h2>    <p>大多数的终端用户都是在 .git 文件夹中存储Git仓库的对象,打包文件以及引用。他们利用Git命令行工具或者GitHub Desktop这样的图形化客户端,或者Git的IDE插件去访问仓库。也许这会很让人惊讶,GitHub的仓库存储 DGit,也是使用同样的Git原生的技术。那么为什么不使用 SAN?(Storage Area Network 网络存储),为什么不用分布式的文件系统?为什么不用云技术去抽象这个问题?</p>    <p>答案很简单:因为原生的Git技术足够快并且很可靠。</p>    <p>Git对于延迟是很敏感的,一个简单的Git命令,如 git log 或者 git blame,就有可能需要加载上千个Git对象然后遍历完这些对象。如果其中有任何底层的延迟,系统的性能将急剧下降。因此,利用分布式文件系统存储 Git仓库是不可取的(这点GIT@OSC码云2014年曾经以活生生的反面例子证明过了,详情欢迎找@yashin 开喷)。Git的最佳性能取决于磁盘的访问性能,因此,DGit的文件存储使用的是SSD。</p>    <p>在服务的高层来说,Git 仓库之间的数据交换也通过协议做了最大的优化(例如push,fetch操作)。所以我们直接使用Git的这些协议做DGit的备份同步。</p>    <p>Git是成熟的久经考验的技术。所以我们已经有一辆F1赛车了干嘛还要重复造轮子呢?</p>    <p>GitHub的哲学就是我们的服务器使用Git的习惯要尽可能像我们的用户使用Git一样。DGit继续着这个传统。我们的职员中有几个Git以及 <a href="/misc/goto?guid=4959670626153500154" rel="nofollow">libgit2</a> 项目的核心贡献者,如果我们发现了一个性能瓶颈或者别的问题,我们修复了这些问题的同时也会贡献到这几个开源项目中让所有人都可以受益。以我们对Git技术对经验水平和专业知识看来,使用Git原生技术作为DGit的备份方案是一个明智的选择。</p>    <h2>GitHub 架构, 之前和之后</h2>    <p>一直到最近,我们仓库的备份机制都是使用现成的磁盘级别的备份技术-也就是 RAID 和 <a href="/misc/goto?guid=4959670626268373967" rel="nofollow">DRBD</a>。我们的文件系统都是主重备份的。每一个在线等文件服务器都有一个通过专用网线实时备份的替身。每一个在线的文件都有四个备份:两个备份在主服务器上,使用的是RAID,另外两个备份在热备的服务器上,使用 DRBD。当文件服务器发生任何状况的时候(类似:硬件故障,软件崩溃,机器超负载),需要运维人员去确定故障原因并且切换到热备的服务器。因此,这个方案由于高冗余,数据可靠性级别还是很高的,但是这个故障恢复的程序需要人工干预,不可避免地会出现仓库访问失效,即无法保证可用性。为了让意外尽可能少发生,我们都是使用专业的,高可靠性的服务器存储仓库。</p>    <p>现在,我们使用DGit,每一个仓库分别独立地存储在我们的文件服务器集群中的三个服务器上。DGit自动地为每个仓库选择宿主服务器,同步备份到各个宿主服务器,并选择一个最佳到服务器来响应每个读请求。写操作时同步地写入到三个备份中,保证至少两个备份写入成果才确认提交这个写操作。</p>    <p><img alt="Windows Kernel-Mode Drivers Written in Rust" src="https://simg.open-open.com/show/92bfef33855f24a8d4854e4189b7a344.png"></p>    <p>GitHub的仓库现在存储在一个叫 github-dfs 的集群中,dfs 是 “DGit file server”的简称。这些仓库是利用Git和libgit2存储在每个服务器的本地磁盘中的。这个集群的客户端包括web前端以及响应用户的Git客户端的代理。</p>    <p><img alt="Windows Kernel-Mode Drivers Written in Rust" src="https://simg.open-open.com/show/54fd116664ac2a438986806db28fa157.png"></p>    <h2>DGit 的优势</h2>    <p>DGit带来了很多优势无论对GitHub用户而言还是对GitHub内部对基础设施团队而言。这也是容纳未来更多创新的关键性的基础建设。</p>    <ul>     <li> <p>文件服务器不再需要部署完全一致的主从热备,需要物理距离接近可以用网线连接起来。现在我们可以使用一个异构的文件服务器集群,无论配置是不是最好的。</p> </li>     <li> <p>以前的方案当一个服务器故障了,更换它通常是紧急的,因为它的备份服务器是没有剩余的。两个热备服务器同时断电会导致上千个仓库下线。现在当服务器故障,DGit将自动地快速地创建一个新的备份到集群中。</p> </li>     <li> <p>故障对路由功能的影响更小了。我们只是在故障的服务器恢复之前停止路由到该服务器,不像之前我们需要重启并同步整个服务器。现在的方式重启生产环境的机器也是安全的并且服务不中断没有过渡期。因为使用DGit单个服务器的故障对整体服务的破坏力是很小的,我们不再需要等待人工处理故障,DGit可以立即绕过故障机器进行路由。</p> </li>     <li> <p>我们不需要热备一个服务器,却让这个服务器一直是空闲的。在DGit中,所有服务器的CPU和内存都在活跃地处理用户的请求。写操作像pushes 必须写到该仓库的每个备份,而读操作可以从该仓库的任意一个备份读取。因为读操作远远多于写操作,而每个仓库备份于三个服务器上,所以使用DGit每个仓库可以处理多于以前方式接近3倍的吞吐量。以下图表展示了Git进程的CPU 负载,蓝色曲线代表旧的文件服务,绿色代表DGit服务。蓝色曲线仅代表旧文件服务器主从热备的主服务器,并不包含从服务器。从图表我们可以看出DGit服务器的负载更低,在波峰大概比旧系统低3倍的负载,在波谷大约低两倍。从整体没有看出3倍性能的提升是因为所有的文件服务器必须承担备份维护的后台任务。</p> </li>    </ul>    <p><img alt="Windows Kernel-Mode Drivers Written in Rust" src="https://simg.open-open.com/show/d8cff817ba0dcd4632c9aafc5bc0013a.png"></p>    <ul>     <li> <p>DGit 会自动地平衡各个服务器上的磁盘和CPU利用率。增加新机器完全不需要提前准备:DGit会随机地移动仓库到新的机器上,直到集群的CPU和磁盘空间达到一个平衡。对于已有仓库的扩展或者收缩,DGit也会移动这些仓库保持磁盘空间的平衡。对于有写仓库变得更活跃或者不再活跃,DGit也会移动这些仓库从而保持CPU和内存的平衡。以下图表中,表示三个DGit服务集群,红色代表磁盘接近用满的状态;蓝色代表为了缓解磁盘空间压力新增的服务集群;绿色代表第三个集群,这个集群内一个服务器中的一些仓库被移动到另外两个服务器上。仓库在所有的DGit文件服务器中移动,直到所有服务器的磁盘空间利用率到达一个相当的水平。</p> </li>    </ul>    <p><img alt="Windows Kernel-Mode Drivers Written in Rust" src="https://simg.open-open.com/show/2313bae8ead13af363cf9a171fc7d802.png"></p>    <ul>     <li> <p>DGit降低了仓库共享一个服务器的成本。在DGit之前,一个集合的仓库存储在同一个服务器上。如果其中一个仓库太大了,或者太活跃了,或者太受欢迎了,那么这个服务器上的其他仓库也会变的很慢。在DGit中,同一个服务器上的其他仓库可以直接访问放在其他的备份,这个其他的备份不太可能和这个很忙的仓库的其他备份放在同一个服务器。</p> </li>     <li> <p>仓库备份的去耦合意外着我们可以将备份存储在不同的区域,甚至不同的数据中心。可用性的到提升,并且我们可以按地理位置就近原则给用户提供服务。</p> </li>    </ul>    <h2>首发上线</h2>    <p>DGit是一个很大的系统更改,我们必须平滑地切换过来。DGit最复杂的部分是备份机制不再像以前那么容易理解:每份仓库都明确地存储在三个服务器上,而不是像以前只存在一组主从热备服务器中。因此,DGit必须要负责自身的串行化处理,加锁,故障处理,以及再同步,而不是像之前使用 DRBD,RAID来控制备份同步。以上这些丰富的主题就是我们今后的文章需要去解析的。一言以蔽之,我们在使用DGit来存储用户的数据之前必须要彻底地测试它。我们的部署过程包括很多步骤:</p>    <ul>     <li> <p>首先,我们先把DGit开发团队成员的私人仓库迁移到DGit中。</p> </li>     <li> <p>其次迁移GitHub的一些私有的,并且不在生产环境运行的仓库。当然,事前我们都会开个issue征求我们的同事允许。这是一个礼貌的警告,也是一个向GitHub其他员工介绍DGit的方式。</p> </li>     <li> <p>再迁移剩下的GitHub的私有仓库。</p> </li>     <li> <p>接下来三个月我们不再迁移新的仓库,而是进行大量的自动化测试,证明DGit满足生产环境运营要求,以及修复一些偶然的bug。</p> </li>     <li> <p>三个月的稳定运行后,我们迁移GitHub自己的开源项目仓库到DGit中。接着是用户fork我们的仓库。例如, <a href="/misc/goto?guid=4958530923879886979" rel="nofollow">Linguist</a> 是GitHub所有的,但这个仓库大概被fork了1500次到别的用户名下。存储一些开源项目可以测试DGit高网络负载的能力和吞吐量。</p> </li>     <li> <p>然后我们开始迁移非GitHub所有的开源项目。我们根据GitHub的 s<a href="/misc/goto?guid=4958530805500090444" rel="nofollow">showcases</a> 以及 <a href="/misc/goto?guid=4958822644988629774" rel="nofollow">trendingrepositories</a>, 优先着眼于被fork了很多的开源项目,如:<a href="/misc/goto?guid=4959670626467953875" rel="nofollow">Ruby</a>,<a href="/misc/goto?guid=4958523252942271455" rel="nofollow">Rails</a>,<a href="/misc/goto?guid=4958825667511177724" rel="nofollow">Bootstrap</a>,<a href="/misc/goto?guid=4958523255334079688" rel="nofollow">D3</a>, 等等。在DGit还只是存储少量仓库的时候,我们尽可能压榨DGit的吞吐量,尽可能尝试各种运用场景。</p> </li>     <li> <p>六个月之后,我们对DGit的性能比较满意,足够支撑GitHub的存储了,最后我们才开始逐步把大部分仓库迁移到DGit。</p> </li>    </ul>    <p><img alt="Windows Kernel-Mode Drivers Written in Rust" src="https://simg.open-open.com/show/bc193092dbe4a2b9fe69b20a26d86740.png"></p>    <p>在首发上线期间,我们惯例地关掉一些服务器,有时候一次关闭多个,用户的操作没有被中断。</p>    <p>截止于发布这篇文章的时候,已经有58%的仓库和96% 的Gists代码片段存储在DGit中,这表示GitHub上67% 的Git操作都是基于DGit之上的。接下来我们会尽快把DGit前时代的主从热服务器变成DGit服务器。</p>    <p>github总是力求获取、推送、查看项目仓库能够更快和更可靠。DGit是在仓库存储层多年来满足这些目标同时允许进行水平扩展和提供容错能力。</p>    <p>在下个月我们将会跟进深入理解DGit技术内幕的帖子。</p>    <p>本文地址:<a href="/misc/goto?guid=4959670626660628565">http://www.oschina.net/translate/introducing-dgit</a></p>    <p>原文地址:<a href="/misc/goto?guid=4959670626757980215">http://githubengineering.com/introducing-dgit/</a></p>