关于美团:Code美团代码托管平台的演进与实践

3次阅读

共计 8280 个字符,预计需要花费 21 分钟才能阅读完成。

美团代码托管平台通过长期的打磨,实现了分布式架构的革新落地,托管数以万计的仓库,日均 Git 相干申请达到千万级别。本文次要介绍了美团代码托管平台在迭代演进过程中面临的挑战及解决思路,心愿对大家有所帮忙或启发。

1. 引言

Code 是美团自研的代码托管平台,其中包含了代码版本治理、分支治理及代码评审等性能,协同泛滥研发流程工具平台,撑持外部所有工程师的日常研发工作。通过近 3 年的建设,目前 Code 托管了数以万计的仓库,日常解决千万级的 Git 相干申请,稳固撑持着美团研发流程标准的继续落地。本文次要介绍美团在建设代码托管平台过程中面临的一些挑战和实践经验。

2. 美团代码托管平台建设之路

2.1 代码托管平台的发展史

回顾美团代码托管平台的发展史,整个历程能够划分为三个阶段:单机部署、多机部署以及自研分布式代码托管平台。

第一阶段:单机部署

美团最后的代码托管平台,和绝大多数 Web 零碎一样,单机部署即可运行,所有用户的申请均通过 Web 利用进行响应。因为 Git 应用基于文件组织模式的存储模式,无论是通过页面拜访还是执行 Git 命令操作,最终都会体现为磁盘的文件读写,高 IO 磁盘尤为重要。整体架构如下图 1 所示:

第二阶段:多机部署

在拜访规模不大的状况下,第一阶段这种单机架构能够满足日常的开发需要。但随着研发团队业务需要的一直增长,测试自动化流程的逐步完善,扩展性瓶颈也愈发显著,次要体现为以下 2 个方面:

  • 存储:因为公司资源限度和地区调配不均等因素,代码托管平台部署机器已配置最大容量的可用 SSD 磁盘,使用率仍高达 80%,可用空间严重不足。
  • 负载:随着研发人员的一直增多,在拜访高峰期,CPU 和 IO 负载高达 95% 以上,页面呈现重大的卡顿,仅能通过限流保障系统的继续服务。

因此,单机部署无奈再承载高峰期的访问量,零碎扩容迫不及待。于是,咱们开始设计了一套可能通过多机负载同一仓库 IO 的读写拆散架构计划,以解决较为重大的 IO 负载问题。在读写拆散架构中,最重要的是要保障用户视角的数据一致性(用户随时能够读取提交的最新代码),这里采取了以下措施:

  1. 写操作仅产生在主节点。
  2. 采纳懒汉同步模式,在读取数据时触发从节点同步数据,若失败,则路由到主节点。
  3. 采纳独主兜底模式,遇遇到突发状况时能够迅速禁用从节点,保障数据安全。

如图 2 所示,咱们将仓库拜访模式依照应用层协定辨别为 HTTP 和 SSH,别离由对应的解析代理模块进行读写散发操作后再下发到主从节点(此处采纳了 Round-Bobin 的算法散发读申请),使得读吞吐量整体扩充了 2 倍。对于从节点,咱们部署了 Agent,在用户发动读申请时会触发同步仓库数据的 Fetch 操作,以保证数据的一致性。

第三阶段:自研分布式代码托管平台

在第二阶段,尽管通过多机负载 IO 的读写拆散架构短暂性地解决了扩展性瓶颈问题,但仓库数据仍在继续一直地指数增长。同时,除扩展性问题之外,可用性瓶颈也凸显进去,次要体现在以下 2 个方面:

  • 运维:无论是日常迭代更新版本还是热修复紧急 Bug,都须要停服能力部署零碎,停服期间用户无奈应用代码托管平台。
  • 备份 :零碎采纳冷备份的形式多正本存储 Git 数据,无奈保障外围数据的实时复原,异常情况下存在数据失落危险。
    因而,搭建具备高可用性和程度扩展性的分布式架构火烧眉毛。咱们调研了业界支流代码托管平台的分布式计划,并联合公司外部的业务个性,最终抉择了基于应用层分片的分布式架构,该架构满足了以下 2 个个性:
  • 高可用:采纳三正本多活模式,躲避代码失落危险,且零碎版本更新无需停服,单机断电、宕机均可失常提供服务。
  • 程度扩大:可通过扩容分片集群的形式进行存储和负载扩大,实现狭义下的“有限”容量。

综上所述,Code 基于 GitLab 生态开源组件二次开发,并采纳了应用层分片多活模式的分布式架构计划,简介如下:

  1. 底层存储服务基于 GitLab 生态开源组件二次开发,有良好的生态和丰盛的性能反对。
  2. 各服务间均通过 gRPC 进行交互通信,次要思考点是 Git 大多数为二进制数据通信,gRPC 基于 HTTP 2.0,有良好的传输性能和流式反对。
  3. 通过路由模块实现逻辑层与存储层无效隔离,逻辑层对物理分片无感知,存储层如同一个整体提供服务。
  4. 采纳了多活复制模式的数据保障架构,进步读写吞吐量,满足日均千万级的申请量需要。
  5. 针对于应用层分片的劣势,在架构设计时也做了相应的针对性优化,具体如下:

    • 热点库:提供了自动化的分片迁徙能力,在发现仓库呈现热点时,可进行分片迁徙达到分片平衡。
    • 跨分片数据交互:通过业务层的 Git 事务包装,咱们应用共享 Object 的模式并确保互相关联的仓库均落在同一分片上,既防止了跨分片通信的问题,也缩小了磁盘空间占用和拜访时延。

3. 美团代码托管平台架构演进的落地和挑战

代码托管平台在架构演进过程中,最终实现了以下两个指标:

  • 高可用:缩短停机工夫,进步可用性,零碎稳固牢靠。
  • 高扩大:针对计算和存储资源,能够实现程度扩大。

接下来,针对于每个指标,本文别离从技术挑战、计划选型、设计及解决方案等方面具体介绍咱们的实践经验。

3.1 扩展性指标

3.1.1 技术挑战

在进行程度扩大革新时,次要面临了以下两类挑战:

  • 规模性:在研发流程自动化等背景下,美团代码托管平台须要具备千万级吞吐、低提早及高可用的零碎性能,以进步研发效率。
  • 兼容性:技术改造波及的场景比拟多,次要有两方面的考量:(1)用户低感知,新老零碎保障现有通信形式及平台应用形式不变;(2)兼顾过渡时期底层存储介质多样性、运维体系兼容等问题,并保障上下游零碎的失常运行。

3.1.2 计划选型

通过对支流代码托管平台(GitHub、GitLab、Bitbucket 等)的调研剖析,咱们发现各大平台次要采纳了以下两种架构解决扩展性问题。

通过上述比照能够发现,如果间接接入共享存储,临时无奈满足代码托管平台的稳定性和性能要求(若 Git 机制进行并行优化,且应用更高读写性能的分布式存储系统,或者是一个不错的抉择)。在共享存储优化革新老本较高的前提下,咱们最终采纳了应用层分片的分布式架构,它既满足扩展性的要求,也更加成熟和稳固,并体现出不错的性能。

3.1.3 方案设计

咱们通过代理模块实现了申请散发,通过路由模块实现了仓库分片,通过利用模块的无状态革新实现了弹性伸缩,从而达成了程度扩大的架构指标。上面将对这些模块进行具体的介绍。

代理模块

  • SSH Proxy:提供 Git-SSH 操作代理,提供 Git-SSH 申请代理,通过路由模块获取路由信息,到指标机器执行 SSH 操作。SSH Proxy 组件基于 go-crypto 库开发,实现了公钥辨认用户,流量管制,长连贯超时解决,SSH 转 gRPC 等性能。后续打算引入 signature 校验,以应答不同的应用场景。
  • HTTP Proxy:提供 Git-HTTP/Web 申请代理,通过路由模块存储的仓库分片映射关系,决策仓库路由节点。HTTP Proxy 基于 Go-Gin 开发,实现了申请甄别,流量管制,多层代理等性能。最后 HTTP Proxy 还被作为灰度迁徙的外围组件,通过流量对立收口,反对申请散发到新老 Code 零碎,以确保申请和数据的平滑迁徙。

路由模块

  • Shard:记录仓库与其所在分片之间的映射关系,是应用层分片架构的“中枢系统”。Shard 服务除保护映射关系外,还是容灾模块必不可少的“决策者”,通过获取各个节点以后拜访仓库的最新版本,从而决定读写路由。因为 Golang 杰出的高并发体现,目前路由相干接口的均匀响应工夫在 15ms 以内。该模块的次要个性如下:

    1. 建设仓库和分片的映射关系,为了防止因为仓库门路更新造成文件夹拷贝 / 挪动等行为带来肯定的复杂性,这里采纳了仓库 ID 作为惟一标识。
    2. 利用 Go Routine 获取节点的数据同步状态,并通过超时机制保障用户非有限时期待。
    3. 应用 Key-Value Cache 存储仓库和分片的映射,以升高映射关系的申请时延。

利用模块

利用模块次要包含以下两点性能:

  • 提供 Git 相干的业务逻辑接口解决代码内容信息、代码评审等复杂性业务申请。
  • 监听代码和分支变更音讯,发送事件告诉买通第三方业务零碎和收集度量信息。

整体模块架构如下图 7 所示:

3.1.4 解决思路

规模性解决思路

规模化的次要指标是:具备 撑持千万级申请的零碎能力,并反对计算、存储等资源的程度扩大能力,其中路由平衡是必不可少的一环。

a. 路由平衡

Code 系统对数据源可靠性要求较高,而对性能要求绝对比拟低,因此咱们采纳了 严格仲裁 的路由模式,具体的逻辑配置如下:

  • 应用版本号断定哪个节点提供的代码内容最新:版本号越大,代表数据越新,当版本最大时即为最新的数据。当 W +R > N 时总能读到最新的数据(N:总节点个数,W:判断写入胜利须要的响应节点数,R:读取数据时至多要胜利读取的个数),当 W 越小时,写入的可用性就越高,R 越小,读取的可用性就越高。咱们抉择了 N =3,R=W= 2 的惯例举荐配置,依据概率推算可达到 99.999% 的可用性程度。
  • 采纳读修复模式:当读取数据时,若发现节点数据不统一,此时触发数据同步逻辑,以修复落后节点的数据。

该性能内置于路由模块的 Shard 服务,架构如下图 8 所示:

兼容性解决思路

兼容性指标总结为一句话就是:业务应用无感知。因而,咱们次要从以下三个方面思考兼容性。

a. 与各零碎交互方式及现有基础设施兼容

Code 零碎的泛滥上游零碎(多套前端 UI、业务研发流程工具平台等)依赖零碎提供的凋谢 API、Hook 机制等扩大性能,为了缩小系统升级对业务方造成影响,须要保证系统交互方式兼容。同时还要确保零碎运维监控体系失常运行,维持可监测状态,咱们次要做了以下四件事件:

  • 兼容外围性能:应用频度高的性能平移到新零碎,而应用中低频的性能,与业务沟通应用场景,再评估是否兼容。
  • 从新设计局部性能:提供更为正当的 WebHook 配置能力及簇新的代码评审性能。
  • 边缘性能经营下线:推动废除和历史遗留性能的下线,并提供正当的代替计划。
  • 买通运维体系:放弃现有监控埋点及运维接口接入形式,使零碎处于可保护、可监测的状态。

b. 非分布式版本无缝切换到分布式版本

Code 零碎仓库泛滥,须要有低成本的用户自主切换形式保障数据逐渐迁徙,咱们次要做了以下三件事件:

  • 可视化主动切换:通过页面一键迁徙按钮,低成本实现从非分布式版本切换到分布式版本(迁徙进度可感知,执行过程中仓库可读不可写,确保数据残缺)。
  • Proxy 屏蔽底层存储介质多样性:通过 Proxy 放弃繁多的调用形式不变,可兼顾获取非分布式版本和分布式版本的存储数据。
  • 非凡数据共享存储:用户和 SSH Public Key 等数据与仓库数据没有强制关联关系,可实现数据共享。

c. 历史数据平滑迁徙

Code 零碎存在泛滥的历史代码数据和业务数据,如何无效、残缺地将历史数据平滑迁徙到新的分布式系统,变得尤为重要。为了达成业务应用无感知的指标,次要做了以下两件事件:

  • 优先迁徙“轻量”仓库:先迁徙应用性能繁多的仓库,依据用户反馈逐步完善迁徙能力。
  • 业务维度批次迁徙:依照业务线划分迁徙批次,同类应用模式的仓库同期迁徙,以躲避反馈问题多样性。

3.2 可用性指标

3.2.1 技术挑战

在进行可用性革新时,咱们次要面临 数据安全性 层面的挑战。代码作为公司的重要资产之一,需达到两方面的要求:

  1. 代码单点失落可数据恢复。
  2. 用户视角能够读到正确的代码数据。

3.2.2 计划选型

目前,业界次要有以下三种支流的数据复制模式。

业界大多数分布式版本控制系统采纳的是单主复制模式保障数据安全,随着美团外部研发流程的逐步完善,对于创立正文 Tag、分支治理等需要逐渐减少,读写比从最后的 10:1 缩短到当初的 5:1,因而须要较高的写入性能。

咱们衡量了高吞吐量和数据强一致性的双重指标,在单主复制架构的根底上,采纳以仓库维度单主复制为外围,节点多活为个性的复制模式(下文简称为多活模式),从而保障了数据安全和零碎可用性。

3.2.3 方案设计

咱们次要通过存储模块中,对 Git 的读、写及初始化三类不同的申请别离采取绝对应的数据处理机制,并联合多活复制模式,达成了高可用性的指标。

存储模块

Git Server:次要存储和治理 Git 仓库数据,提供 Git 相干的 gRPC 接口。该服务基于 GitLab 生态开源组件二次开发,次要在数据同步机制、容灾模块、局部底层命令上做了适配性优化,共波及以下 4 个逻辑模块:

  1. Replication Manager:数据复制外围模块,依据不同的申请(读、写或初始化)执行不同的复制逻辑,从而保障数据一致性。
  2. Code Core:Git Server 的外围服务模块,次要提供了 Git 的 gRPC API 供上游模块应用。
  3. Git Core:实现扩展性和高可用性的重要组件,这里通过源码的形式将 GitLab 生态开源组件引入到我的项目中,作为第三方 Git API 供我的项目应用。
  4. Git Command Factory:Git 命令的中枢控制器,提供管制 Git 过程数量、传递参数上下文,隔离执行环境及格式化输入数据等性能。

各个逻辑模块间关联如下图 10 所示:

Git Cluster:又称为分片,它由三个 Git Server 节点组成。三个节点间通过各自的 Replication Manager 模块获取到集群中其余节点的 IP 等信息,应用 gRPC 协定进行数据复制备份,能够保障用户视角的数据一致性,逻辑架构如下图 11 所示:

3.2.4 解决思路

数据安全性解决思路

Code 零碎要解决的问题中,数据安全问题尤为重要,是保障研发流程安全可靠的要害。在思考数据安全性解决思路之前,先要明确数据一致性判断准则,Code 采纳以下准则评判两个仓库数据统一。

数据统一评判准则:若仓库所在两个节点存储的 refs 数据完全一致,则称为这两个节点上的仓库数据统一。

目前零碎数据安全机制次要有以下几个特点:

a. 多活复制

目前 Code 零碎每个分片蕴含 3 个节点,即代码数据保障三正本,即便呈现 1~2 台节点故障导致数据不可复原的状况,也可通过其余节点进行数据恢复。咱们采纳了多活复制模式,即任何一个满足必要条件(以后拜访仓库在该节点的数据均重演至最新版本)的机器节点均能够进行读写操作,与单主模式相比进步了写操作的吞吐量,节俭了主备切换的老本,使部署、节点替换及异样复原更加简略。多活复制模式束缚有以下两点:

  1. “单写”机制:在同一时刻,同一个仓库的写操作须在同一节点进行。
  2. 数据安全锁机制:若某仓库底层 Git 的操作出现异常谬误,则在数据未复原前,其后对该仓库的所有操作均会在该节点进行,会产生部分热点。

多活复制次要由数据存储和数据压缩两个局部组成。

01 数据存储

  1. Git 次要由 objects 和 refs 两类数据组成。objects 数据为不可变数据,创立后为只读模式,以文件的模式存储于本地磁盘中;refs 数据为可变数据,能够进行更新。两类数据别离采纳不同数据源进行存储。
  2. 用户在拜访仓库时,如果某个 objects 没有在任何一个分支的关联链中,那么断定为不可达,对于不可达的 objects,无需保护其一致性。不可达 object 的示例如下:

02 数据压缩

在 Code 零碎中,须要记录 refs 的变更日志以进行数据回放,保证系统的数据一致性。因为每个仓库的 refs 数据变换是比拟频繁的,会产生大量的日志,从而造成存储压力。因此咱们采纳了日志压缩技术,缩小不必要的数据开销,压缩形式如下图 13 所示:

例如上图中的 main 分支,其初始状态为 main -> a,第 4 个 log 为main -> e,第 5 个 log 为main -> f,则这 3 个 log 能够压缩为一个 log,即main -> f 并将其利用于初始状态,与压缩前回放触发的后果是统一的,main 都将指向值为 f 的 commit。

03 相干优化

在实际过程中,咱们发现采纳纯 Git 命令执行数据复制操作无奈无效管制资源分配,因此从通信形式、并发模式及复制粒度等方面做了优化,从而进步了整体的数据复制效率。

b. 跨机房备份

Code 零碎每组分片的 3 个节点至多来自于两个不同的机房(目前依照规范化部署,均革新为 3 机房),若其中一个机房产生故障,仍可提供服务。咱们对该架构做了针对性的容灾演练,通过演练验证了节点掉线对系统的影响较小,联合灵便的节点替换能力,可在 30 分钟内上线新的节点,达到容灾均衡状态。

c. 数据热备

Code 零碎提供数据热备机制,通过数据复制的形式,任何写入的新数据会立刻同步到其余正本节点,根本“0”提早,保障了用户视角的强一致性。数据同步机制是热备的要害,咱们次要通过以下步骤实现。

01 写操作阶段

  1. 通过引入仓库粒度的写锁,保障同一个仓库同时只能在一个节点执行写入操作,而后通过 Git Internal Hook 机制触发 object 数据的同步,并长久化记录 refs 数据。
  2. 正本节点通过读取长久化的 refs 数据,重演操作,从而放弃了 refs 数据与写入节点统一。

02 读操作阶段

  1. 如果以后仓库持有写锁,则间接路由至持有写锁的节点读取数据。
  2. 如果未持有写锁,则用各个节点的版本和数据源存储的版本数据进行比照,将版本大于等于数据源存储的最新版本的所有节点作为候选路由节点,并采纳负载平衡算法进行路由;如果没有符合条件的节点则需进行同步弥补,待弥补胜利后再进行路由抉择。

03 相干优化

在最后实现中,咱们采纳了无状态同步,发现存在同步工作被屡次执行的状况,后续通过工作前置查看等形式防止了不必要的数据同步工作,最终缩小了 50% 的同步工作。

d. 数据巡检

数据巡检是保证系统安稳运行,数据安全可靠必不可少的一个环节,它能够及早地发现零碎中潜在的隐患。巡检服务作为 Code 零碎的外围服务,在升高数据危险,进步零碎服务的稳定性方面起到了关键作用。对于巡检服务,咱们次要从以下几个方面进行思考:

  • 透明性:尽可能地防止对用户的失常申请产生影响,缩小不必要的烦扰,对于零碎拜访能够做到安稳可控。
  • 可靠性:作为数据安全的重要服务,它本身也要做到弹性伸缩,多点容灾,具备高可用的个性。
  • 可维护性:对于数据巡检发现的问题,可能通过无效伎俩进行解决。同时要进步巡检服务的效率,随着零碎架构的迭代出新、模块降级,巡检服务要随之更新,从而做到无效的保障。

综合以上几点,咱们采纳了无状态的服务架构,提供定点巡检、全量巡检、定时巡检等模式保障数据安全。其中巡检的数据次要分为以下两类:

  • refs 数据:依据数据一致性评判准则,refs 数据是 Git 外围数据,因此它的测验是必不可少的。
  • 版本数据:Code 零碎是基于版本进行读写路由的,因此当版本过大时,可能会产生大量的数据同步,为了防止突增同步申请对系统造成肯定的 IO 抖动,监控版本差距是尤为必要的。

巡检服务的整体架构如下图 17 所示:

4. 总结

本文系统性地介绍了美团在 Code 零碎演进过程中面临的扩展性和可用性两大瓶颈,并别离针对上述两类瓶颈和对应的挑战,具体论述了解决方案和落地的实践经验。

基于上述的架构革新实际,目前美团代码托管平台实现了仓库容量程度扩大、负载自主平衡等个性,稳固撑持着研发流程标准的落地。咱们将来会在撑持研发效率,保障研发平安方面持续进行摸索和演进,争取积攒更多贵重的实践经验,后续再跟大家分享。

5. 将来瞻望

  • 自动化运维:目前零碎的运维机制自动化水平低,咱们心愿将来能够主动检出零碎异样并进行复原,其中包含数据修复,主动扩容及热点迁徙等性能。
  • 提供代码畛域最佳实际:依靠研发工具平台,继续推动美团研发流程标准的迭代更新,积淀最佳实际并提供无力的工具撑持。
  • 代码平安:与信息安全团队严密单干,提供更为齐备的平安控制策略,包含代码扫描、破绽主动修复、危险行为预警等性能。

6. 本文作者及团队简介

潘陶、费翔、丹丹、毛强等,来自根底研发平台 - 研发品质与效率团队。

美团研发品质与效率团队,负责公司研发效力畛域平台和工具的建设(包含研发需要管理工具、CI/CD 流水线、分布式代码托管平台、多语言构建工具、公布平台、测试环境治理平台、全链路压测平台等),致力于一直推动优良的研发理念和工程实际,建设一流的工程基础设施。

浏览美团技术团队更多技术文章合集

前端 | 算法 | 后端 | 数据 | 平安 | 运维 | iOS | Android | 测试

| 在公众号菜单栏对话框回复【2021 年货】、【2020 年货】、【2019 年货】、【2018 年货】、【2017 年货】等关键词,可查看美团技术团队历年技术文章合集。

| 本文系美团技术团队出品,著作权归属美团。欢送出于分享和交换等非商业目标转载或应用本文内容,敬请注明“内容转载自美团技术团队”。本文未经许可,不得进行商业性转载或者应用。任何商用行为,请发送邮件至 tech@meituan.com 申请受权。

正文完
 0