跳转到内容

Borg 2015 — Google 把一万台机器假装成一台

待复核

日常类比:Borg 像一座超大机场的调度中心。乘客只说”我要从上海到北京”,不用知道飞机停在哪个机库、哪个登机口还有空位、哪架飞机刚检修完;调度中心把这些细节吃掉,只把”航班能不能准时飞”交给你。

Borg 是 Google 内部的集群管理系统:开发者提交一个 job,里面有很多 task;Borg 决定每个 task 放到哪台机器上、怎么限制 CPU 和内存、失败后怎么重启、什么时候可以被更高优先级任务抢走。

论文的价值不在一个新公式,而在一份十多年生产经验:Google 公开了”大规模机器池应该怎么被组织、调度、隔离和运维”。后来的 Kubernetes、Omega、Mesos、Nomad 都在回答同一组问题。

用户说:请跑 100 份搜索服务,每份 4 CPU、8 GB 内存
Borg 做:选机器、检查 quota、启动 task、监控健康、失败重调度
用户看:100 份服务维持在期望状态,不需要手工登录机器

类比边界也要说清:机场调度通常调人和飞机,Borg 调的是进程、资源和故障;它不是简单排队系统,而是持续维护一个会变化的机器世界。

不理解 Borg,下面这些事都解释不了:

  • 为什么 Kubernetes 的核心是”提交期望状态,然后控制器持续修正现实”
  • 为什么云厂商要把在线服务和离线 batch 混跑,而不是一类业务一批机器
  • 为什么资源隔离只靠”别超 CPU / 内存”还不够,还要考虑故障域、抢占和重启
  • 为什么大规模系统论文常常不证明新算法,却能改变整个行业的工程习惯

Borg 可以拆成 三件事

  1. 把机器池变成 cell:一个 cell 是 Borg 的基本调度边界,通常包含很多机器。类比:一座城市分成多个片区,每个片区有自己的物业中心,避免所有事都挤到总公司。

  2. 用 BorgMaster 和 Borglet 分工:BorgMaster 是大脑,保存 cell 状态、接受 job、做调度;Borglet 跑在每台机器上,负责启动、停止、上报本机 task。类比:总调度室下命令,楼层管理员执行并回报。

  3. 用 priority、quota 和 packing 做取舍:priority 决定谁能抢谁,quota 决定谁有资格提交,packing 决定资源怎么装箱。类比:医院急诊先看危重病人,但每个科室也有床位预算。

这三件事串起来,就是 Borg 的核心:让很多团队共享同一批机器,同时尽量保证高优先级服务可靠、低优先级任务能填满空闲资源。

开发者写一份类似这样的配置:

job search_frontend {
task_count = 100
priority = "prod"
resources = { cpu = 4, memory = "8GB" }
binary = "/srv/search_frontend"
}

逐部分解释:

  • task_count = 100 表示同一个服务要跑 100 份副本,不是手工启动 100 次
  • priority = "prod" 表示这是线上服务,比普通 batch 更不应该被抢
  • resources 是向 Borg 说明”每份任务大概要多少空间”,调度器才知道能不能塞进某台机器

Borg 收到后先做准入检查,再找可行机器,最后让对应机器上的 Borglet 启动进程。用户不需要 SSH 到机器上操作。

低优先级 batch 正在跑,高优先级服务突然扩容:

machine-23 当前状态:
prod: 24 CPU
batch: 8 CPU
free: 0 CPU
新 prod task 需要 8 CPU
Borg 决策:kill batch task,释放 8 CPU,启动 prod task

逐部分解释:

  • prod 是用户可见服务,延迟和可用性更重要
  • batch 通常可以重跑,损失的是完成时间,不是用户请求
  • kill batch task 不是故障,而是调度策略:用低优先级任务吸收资源波动

这就是混部的关键:batch 吃掉 prod 平时不用的资源,但 prod 真需要时可以拿回来。

案例 3:用 alloc 表达”一组任务要同机”

Section titled “案例 3:用 alloc 表达”一组任务要同机””

有些任务不是孤立的,比如主服务和日志收集进程要在同一台机器:

alloc loggable_service {
resources = { cpu = 8, memory = "16GB" }
task main_server { cpu = 6, memory = "12GB" }
task log_shipper { cpu = 2, memory = "4GB" }
}

逐部分解释:

  • alloc 先在某台机器上预留一块资源,就像先订一间会议室
  • main_server 是真正服务用户请求的进程
  • log_shipper 跟主服务同机,方便读取本地日志并送到后端系统

Kubernetes 的 pod 可以看作这种思想的开源简化版:把需要同生命周期、同网络环境的容器放到一起。

  1. 把 Borg 当成 Kubernetes 的同义词:Borg 是内部系统,Kubernetes 是开源产品;两者继承关系很强,但 API、存储和生态取舍不同。
  2. 只看调度算法,不看准入和 quota:没有 quota,用户会无限提交任务;调度器再聪明也挡不住资源被许诺过头。
  3. 以为混部只会省机器:混部也带来干扰、抢占和调试成本,论文用 cell compaction 和 CPI 数据说明收益大于成本。
  4. 把 cgroups 当成完整安全边界:Borg 面向内部可信环境,资源隔离不等于敌对租户隔离,公有云还需要更强沙箱。

适用

  • 一个组织内部有大量服务和 batch,希望统一管理机器池
  • 业务能接受 task 被重启、迁移、抢占,并且应用本身做了重试
  • 机器规模大到手工分配已经不可维护,需要集中调度和监控
  • 资源利用率很重要,愿意用复杂运维换更少机器

不适用

  • 小团队只有几十台机器,BorgMaster 这种复杂控制面会过度设计
  • 强敌对多租户环境,必须优先考虑硬隔离和安全审计
  • 极低延迟业务不能接受邻居干扰,也不能接受被调度系统随时打断
  • 跨地域强一致调度,Borg 的 cell 边界主要解决单区域机器池问题
  • 2003 年前后:Google 内部开始用 Borg 管理大规模服务和 batch,替代更分散的脚本化运维方式。
  • 2011 年:Mesos 公开,把”集群资源管理”带到更多学术和工业讨论里。
  • 2013 年:Omega 论文发布,尝试把 Borg 的单体控制面拆成共享状态和多个调度器。
  • 2014 年:Kubernetes 开源,把 Borg / Omega 的经验翻译成外部开发者能用的 API。
  • 2015 年:Borg 论文在 EuroSys 发表,Google 第一次系统公开这套内部调度经验。
  1. 大规模运维先是产品问题,再是算法问题:用户要的是提交服务、看状态、失败恢复,而不是直接操作机器。
  2. 共享资源池能省钱,但必须有规则:priority、quota、抢占、故障域分散,都是共享之后的必要制度。
  3. 中心化控制面不等于不能扩展:Borg 通过缓存、等价类、随机采样和分片通信,把中心化做到很大。
  4. 工程论文的价值在取舍细节:哪些地方故意简单、哪些地方复杂化,往往比单个机制更值得学。
  • borg —— 同一篇论文的旧版笔记,可作为概念速查。
  • omega-2013 —— Omega 是 Borg 的后继实验,重点在共享状态和多调度器。
  • borg-omega-kube-2016 —— 把 Borg、Omega、Kubernetes 放在一条演化线上看。
  • kubernetes —— Kubernetes 继承了 Borg 的很多思想,但改成开放生态能接受的 API。
  • paxos-simple-2001 —— BorgMaster 的副本一致性离不开 Paxos 这类共识思想。
  • mapreduce —— MapReduce 这类 batch 负载是 Borg 混部和抢占策略的重要对象。
  • apollo-2014 —— Apollo — 让两万台机器自己决定谁跑哪个任务
  • twine-2020 —— Twine — Facebook 把整个数据中心当一台机器调度