-
系统概览
- Borg是谷歌开发的一种高效的集群管理系统,旨在优化资源利用率和提高系统的可靠性及可用性
- 隐藏资源管理细节与故障处理,允许用户专注于应用程序的开发
- 保证非常高的可靠性和可用性,以支持用户应用程序的高可靠性和高可用性
- 支持运行来自众多应用的数十万个作业,并高效运行于数以万计的机器上
- Borg是谷歌开发的一种高效的集群管理系统,旨在优化资源利用率和提高系统的可靠性及可用性
-
用户视角
- 用户通过定义job和task与Borg进行交互
- 一个job由运行相同程序的一个或多个task组成
- 每一个job运行于一个Borg cell(单元)之中,cell是一组机器的集合,是Borg管理的基本单元
- Borg的工作负载
- 长期运行的时延敏感型服务
- 批处理作业
- 运行在实体机上,避免VM的虚拟化开销
- Allocs
- 预留给一项或多项任务的一组资源
- Alloc可以将不同 jobs 的 tasks 聚集到同一台机器上
- 如果一个 alloc 必须重新分配到另外一台主机,属于它的 task(s) 也会同它一起重新被调度
- 一旦创建一个 alloc 集合,就可以提交一个或多个 jobs 运行其中
- 命名服务和监控
- Borg包含一个稳定的 Borg命名服务 (BNS),包括 cell 名,job 名和 task id
- Borg将 task 的主机名和端口写入 Chubby,用于 RPC 系统查找 task endpoint
- Borg还会将 job size与其运行状态写入Chubby,便于load balancer平衡流量
- 用户通过定义job和task与Borg进行交互
-
Borg的架构
- Borgmaster
- 主管理进程
- 逻辑上的“单点”,有5个在线备份,使用Paxos选举master
- 状态存储在内存中,并且备份在高可靠性的Paxos存储中
- 调度进程
- 可行性检查
- 用于找到满足任务约束、具备足够可用资源的一组机器
- 打分(scoring)
- 在“可行机器”中根据用户偏好,为机器打分
- 打分策略
- worst fit(E-PVN的变种)会将任务分散到不同的机器上
- 有余量应对流量的尖峰
- 会导致资源的碎片化,阻碍大型task的部署
- best fit,会尽量“紧凑”的使用机器,以减少资源碎片
- 便于大型task的部署
- 错误的资源估计会被“惩罚”,尤其影响突发的负载
- 影响利于富裕计算资源的batch jobs
- 混合模型,尽量减少“受困资源”,即因为其它资源被完全占用而无法分配出去的资源
- worst fit(E-PVN的变种)会将任务分散到不同的机器上
- 优化
- 启动时间优化
- 中位数启动时间为25s,80%用于安装相关依赖
- 将相关task优先分配到拥有相关依赖的机器上
- 使用 tree-like 或 torrent-like 机制,并发的分发相关依赖
- 计算开销优化
- 使得Borg能管理更多的机器
- 打分缓存:将可行性检查和打分结果缓存
- 等价类:同一 job 中的 task 通常具有类似的约束,因此可以将多个任务视为一个等价类
- 松弛随机化:计算所有机器的可行性和得分代价太高,可以随机取样一批机器,然后选择其中一个“足够好”的机器
- 启动时间优化
- 可行性检查
- 主管理进程
- Borglet
- Borglet 是运行在每台机器上的本地代理,管理本地的任务和资源
- Borgmaster 会周期性地向每一个Borglet拉取当前状态,易于控制通信速度,避免“恢复风暴”
- 为了性能可扩展性,每个Borgmaster副本会运行无状态的 link shard 去处理与部分Borglet通信
- 当 Borgmaster 重新选举时,link shard 会重新划分分区
- link shard 会聚合和压缩信息,仅仅向被Borgmaster报告状态的更新,以此减少更新负载
- 如果 Borglet 多轮没有响应资源查询,则会被标记为down。运行其上的任务会被重新调度到其他机器。如果恢复通信,则 Borgmaster 会通知 Borglet 杀死已经重新调度的任务,以此保证任务的唯一性
- Borglet与Borgmaster失去联系时,仍会继续处理相关任务。以应对 Borgmaster 的暂时失效
- Borgmaster
-
可靠性
- 自动重新调度器被驱逐的任务
- 将任务分散到不同的失败域中
- 限制一个作业中同时失败任务的个数和中断率
- 使用声明式的期望状态表示和幂等的变更操作,以便无害地重新提交请求
- 对于机器级别的失效,限制其重新调度的速率,因为难以区分大规模机器故障和网络分区
- 避免重试引发错误的<任务-机器>匹配对
- 关键数据持久化,写入磁盘
-
资源利用和效率
- 评估方法
- cell compaction:通过移除机器来找出给定工作负载能适应的最小的单元大小,然后反复从头开始重新打包工作负载,以确保不会因错误的配置而陷入困境
- “单元共享”:在同一台机器上运行生产任务和非生产任务,以优化资源使用
- 实验表明,共享资源会影响实际的CPU计算性能
- 但是在节约成本的巨大优势上面,CPU性能的退化是可以容忍的
- “大型单元”:允许超大型计算任务,减少任务的碎片化
- 细粒度资源请求
- 以千分之一的CPU核,和内存、磁盘的字节数为资源请求的最小单元
- 相比预设资源分配(套餐),可以避免额外的资源开销
- 资源回收
- 对于可以容忍低质量资源的工作(例如批处理作业),Borg会评估任务将使用的资源,并回收空闲资源
- 最初的预留值与其资源请求一致,然后300秒之后,会慢慢降低到实际使用率外加一个安全边缘
- 如果利用率超过资源预留值,预留值会快速增长。
- 评估方法
-
隔离与安全性:
- 安全隔离
- 使用Linux chroot jail在共享同一台机器的任务之间确保安全性
- 性能隔离
- 基于cgroup的资源容器,允许详细的资源核算并执行限制,防止任务相互干扰,确保稳定和可预测的性能
- 使用appclass,尽可能保证延迟敏感服务的资源使用
- 区分可压缩资源 和 不可压缩资源
- 可压缩资源(compressiable) - CPU%和Disk IO,可以暂时限流
- 不可压缩资源(non-compressible) - 内存、磁盘占用,需要清除优先级低的线程
- 内核的CPU调度器,允许根据每个资源容器的负载状况来动态决定是否要驱逐低优先级任务,同时避免多个高优先级任务在一个cpu上争抢
- 仍在尝试cpu调度时更好的考虑线程亲和、NUMA亲和等策略
- 安全隔离
-
经验教训:
-
负面经验
- Job作为Task的唯一分组机制的局限性
- 缺乏将整个多Job服务作为单一实体进行管理,或引用服务相关Job(如Canary与Prod滚动更新)的方式
- 用户会在Job名称中编入拓扑,并构建外部管理工具来解析这些名称,这导致了滚动更新和作业调整大小等问题的不灵活语义
- Kubernetes通过使用标签组织其调度单元(Pods),提供了更多灵活性
- 单个IP地址带来的复杂性
- 同一台机器上的所有任务共享该机器的单个IP地址和端口空间
- 导致端口也成为一种资源,在调度时候需要被考虑
- Kubernetes采用了更友好的方法,每个Pod和服务都获取自己的IP地址,从而简化了这些复杂性。
- 偏向于高级用户
- Borg提供了一整套面向“高级用户”的功能,允许他们细致调整程序运行方式
- 这种API的丰富性使得对于“普通”用户更加困难,并限制了其演变
- Google构建了自动化工具,对于允许“失败”的应用程序,通过实验来探测适当配置
- Job作为Task的唯一分组机制的局限性
-
积极经验
- Allocs是有用的
- Kubernetes中的Alloc等效物是Pod,它是一个资源包,用于一个或多个容器,总是被调度到同一台机器上并可以共享资源
- 集群管理不仅是任务管理
- 尽管Borg的主要角色是管理任务和机器的生命周期,但运行在Borg上的应用程序从许多其他集群服务中受益,包括命名和负载均衡
- Kubernetes使用服务抽象支持命名和负载均衡,服务有一个名称和一组由标签选择器定义的动态Pods。
- 自省至关重要
- 尽管Borg几乎总是“运行良好”,但当出现问题时,找到根本原因可能具有挑战性
- Borg的重要设计决策之一是向所有用户展示调试信息
- Kubernetes旨在复制Borg的许多内省技术,例如,它配备了cAdvisor等工具进行资源监控和基于Elasticsearch/Kibana和Fluentd的日志聚合
- 主控节点是分布式系统的核心
- Borgmaster最初被设计为一个单体系统,但随着时间的推移,它变得更像是一个内核,位于协作管理用户作业的一系列服务的中心
- Kubernetes架构更进一步,它有一个核心的API服务器,仅负责处理请求和操纵底层状态对象,集群管理逻辑被构建为小型可组合的微服务,这些服务是这个API服务器的客户端
- Allocs是有用的
-
- 论文
本文部分内容由ChatGPT4生成
Comments
comments powered by Disqus