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 可以拆成 三件事:
-
把机器池变成 cell:一个 cell 是 Borg 的基本调度边界,通常包含很多机器。类比:一座城市分成多个片区,每个片区有自己的物业中心,避免所有事都挤到总公司。
-
用 BorgMaster 和 Borglet 分工:BorgMaster 是大脑,保存 cell 状态、接受 job、做调度;Borglet 跑在每台机器上,负责启动、停止、上报本机 task。类比:总调度室下命令,楼层管理员执行并回报。
-
用 priority、quota 和 packing 做取舍:priority 决定谁能抢谁,quota 决定谁有资格提交,packing 决定资源怎么装箱。类比:医院急诊先看危重病人,但每个科室也有床位预算。
这三件事串起来,就是 Borg 的核心:让很多团队共享同一批机器,同时尽量保证高优先级服务可靠、低优先级任务能填满空闲资源。
案例 1:提交一个在线服务
Section titled “案例 1:提交一个在线服务”开发者写一份类似这样的配置:
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 到机器上操作。
案例 2:batch 被抢占给 prod 让路
Section titled “案例 2:batch 被抢占给 prod 让路”低优先级 batch 正在跑,高优先级服务突然扩容:
machine-23 当前状态: prod: 24 CPU batch: 8 CPU free: 0 CPU
新 prod task 需要 8 CPUBorg 决策: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 可以看作这种思想的开源简化版:把需要同生命周期、同网络环境的容器放到一起。
- 把 Borg 当成 Kubernetes 的同义词:Borg 是内部系统,Kubernetes 是开源产品;两者继承关系很强,但 API、存储和生态取舍不同。
- 只看调度算法,不看准入和 quota:没有 quota,用户会无限提交任务;调度器再聪明也挡不住资源被许诺过头。
- 以为混部只会省机器:混部也带来干扰、抢占和调试成本,论文用 cell compaction 和 CPI 数据说明收益大于成本。
- 把 cgroups 当成完整安全边界:Borg 面向内部可信环境,资源隔离不等于敌对租户隔离,公有云还需要更强沙箱。
适用 vs 不适用场景
Section titled “适用 vs 不适用场景”适用:
- 一个组织内部有大量服务和 batch,希望统一管理机器池
- 业务能接受 task 被重启、迁移、抢占,并且应用本身做了重试
- 机器规模大到手工分配已经不可维护,需要集中调度和监控
- 资源利用率很重要,愿意用复杂运维换更少机器
不适用:
- 小团队只有几十台机器,BorgMaster 这种复杂控制面会过度设计
- 强敌对多租户环境,必须优先考虑硬隔离和安全审计
- 极低延迟业务不能接受邻居干扰,也不能接受被调度系统随时打断
- 跨地域强一致调度,Borg 的 cell 边界主要解决单区域机器池问题
历史小故事(可跳过)
Section titled “历史小故事(可跳过)”- 2003 年前后:Google 内部开始用 Borg 管理大规模服务和 batch,替代更分散的脚本化运维方式。
- 2011 年:Mesos 公开,把”集群资源管理”带到更多学术和工业讨论里。
- 2013 年:Omega 论文发布,尝试把 Borg 的单体控制面拆成共享状态和多个调度器。
- 2014 年:Kubernetes 开源,把 Borg / Omega 的经验翻译成外部开发者能用的 API。
- 2015 年:Borg 论文在 EuroSys 发表,Google 第一次系统公开这套内部调度经验。
- 大规模运维先是产品问题,再是算法问题:用户要的是提交服务、看状态、失败恢复,而不是直接操作机器。
- 共享资源池能省钱,但必须有规则:priority、quota、抢占、故障域分散,都是共享之后的必要制度。
- 中心化控制面不等于不能扩展:Borg 通过缓存、等价类、随机采样和分片通信,把中心化做到很大。
- 工程论文的价值在取舍细节:哪些地方故意简单、哪些地方复杂化,往往比单个机制更值得学。
- 论文页面:Large-scale cluster management at Google with Borg
- 论文 DOI:10.1145/2741948.2741964
- 三代系统回顾:Borg, Omega, and Kubernetes
- omega-2013 —— 继续看 Google 怎样试图拆开 Borg 的单体调度器
- mesos-2011 —— 对照另一条”资源 offer”式集群管理路线
- kubernetes —— 看 Borg 经验如何变成开源容器编排平台
- 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 把整个数据中心当一台机器调度