跳转到内容

Twine — Facebook 把整个数据中心当一台机器调度

待复核

Twine 是 Facebook(现 Meta)2020 年公开的集群管理系统,管着公司内部超过 100 万台机器。它的目标只有一句:把分布在十几个数据中心的所有机器,当成一台超大计算机来调度

日常类比:把整个公司的会议室连成一个共享池,员工不再说 我要 3 楼的 207 室,只说 我下午 2 点要一间能坐 10 人的房间,哪个楼都行,由总调度系统自动分配。Twine 干的就是这件事——只是房间换成了 CPU、内存、GPU。

它不是「再做一个更大的 K8s 集群」,而是换了一层抽象:应用先拿跨地域配额,再谈具体落点;宿主机也可以按应用画像重装。理解这层抽象,后面五个设计才说得通。

不理解 Twine,下面这些事会想不通:

  • 为什么 Facebook、Google 内部都自研调度,而不是直接跑 Kubernetes(K8s)——K8s 更像 Borg 面向通用场景的开源简化
  • 为什么 K8s 一个集群当时常见支持上限约 5000 节点,Twine 论文语境下却能调度十万台量级
  • 为什么 把 K8s 集群继续做大 这条路在某个规模会撞墙,需要换思路
  • 为什么数据中心调度论文 2020 年还在产生新结构,没有 终极答案

K8s/Borg 是教科书答案,Twine 是 超大规模下教科书怎么演化 的真实样本。

Twine 和 Borg/K8s 拉开差距的设计有 5 个

  1. Entitlement(配额承诺):应用拿到的不是 某个集群里的 100 个 pod,而是 跨地域的资源承诺。类比:先批你「亚洲区 50 间会议室额度」,具体哪间稍后排。

  2. Host Profile(宿主机定制):应用能声明内核版本、HugePages(把内存页从 4KB 合成更大块,少翻页表)、sysctl(系统旋钮)。类比:开会前把房间重装成「放映厅」再交付;K8s 的 DaemonSet 改不动这一层。

  3. TaskControl 双向协商:重启/迁移前先问应用「现在能动你吗」。类比:物业要检修,先问住户能不能停水,而不是直接关闸。K8s 的 preStop 只是单向通知。

  4. Power Capping(功耗调度):数据中心总功耗有上限,功率和 CPU、内存平级调度。类比:整栋楼电表有上限,调度器要决定哪几间先开灯。

  5. 单插槽小机器:Facebook 偏好 single-socket(一块 CPU)小机器——故障域小、粒度细、功耗好管。这是和 Borg 常见 dual-socket 大机器哲学差异最大的硬件选型。

传统做法(K8s):

应用方: 我要在 us-east 集群启 100 个 pod
集群: 我这只剩 60 个槽位
应用方: 那我去 us-west 自己再申请一遍

Twine 做法:

应用方: 我要 us-east 5000 vCPU + us-west 3000 vCPU
Twine: 收到,配额已就绪
应用方: 现在我想跑 100 个实例
Twine: 自动分配——80 个去 us-east、20 个去 us-west

逐部分解释:配额和「具体放哪」解耦后,调度器才有空间做全局优化(功耗、网络、故障域)。应用不再自己当跨集群调度员。

ML 训练要 HugePages(大块内存页)+ 特定内核;Web 前端要普通 4KB 页 + 长期支持内核。K8s 里这两类任务很难共享同一台宿主机。

  1. 应用声明 host profile = ml-training-v3
  2. 调度器找一台空闲机器
  3. 重装这台机器成该 profile(分钟级,不是秒级)
  4. 把容器交付上去

代价是交付变慢,收益是同一批硬件能服务差异化需求。

Twine: 我想重启你这个实例,可以吗?
数据库: 现在不行,我有 3 笔事务在飞(正在 fsync=把数据刷到磁盘),给我 90 秒
Twine: 好,90 秒后再问
数据库: 可以了,我已经把 leader(主副本)切给同伴
Twine: 开始重启

若应用一直拒绝、超过强制超时:Twine 仍会迁移,应用侧应把「被强制」当成故障演练——副本接管、连接重试,不能假设协商永远成功。

维度Borg (2015)KubernetesTwine (2020)
单集群规模约 1 万台/cell约 5000 节点跨 cell,十万台量级
配额单元cell 内namespace quota跨 region entitlement
宿主机定制有限不支持重装级host profile
重启协商单向通知preStop(单向)TaskControl(双向)
功耗调度一等公民
硬件偏好dual-socket不限single-socket 小机器
  1. 跨 region 调度的延迟成本:entitlement 跨域听起来美,placement 决策会被网络放大;Twine 用多级缓存和异步预分配。
  2. host profile 切换是分钟级:必须预测需求提前切换,否则交付延迟会让应用方抓狂。
  3. TaskControl 拒绝过多:早期滥用「拒绝重启」让资源收不回;后来加了强制超时和 SLO 兜底。
  4. profile 碎片化:太多定制 profile 会让空闲机器对不上号,看起来有机器却派不出去。

适用

  • 单家公司、内部基建、机器规模 5 万+ 台
  • 工作负载差异大(前端 + ML + 数据库 + Cache 共存)
  • 愿意付出 不通用 的代价换 极致优化

不适用

  • 通用云服务(要卖给外部,host profile 这种侵入式设计不能做)
  • 千台规模以下(K8s 已经够,自造系统不划算)
  • 工作负载单一(比如全是无状态 Web,K8s 简单且足够)
  • 2015 年:Google 公开 Borg——cell/job/task 成了行业共同语言。
  • 2014–2016 年:K8s 开源并快速成为外部默认答案;内部超大规模玩家仍自研。
  • 2020 年:Facebook 在 OSDI 发表 Twine,把 entitlement、host profile、TaskControl、功耗调度写成一套统一系统。
  • 之后:行业继续在「通用编排」和「内部极致优化」两条路上分叉,没有单一终局。
  1. 一个集群 不是必然抽象:规模到十万台量级,cell/cluster 边界要打开,资源要跨域流动。
  2. 教科书答案有边界:K8s 在中等规模是好答案,再往上往往要换思路。
  3. 不通用 是一种武器:不卖云就可以做侵入式设计——这是 Twine 和 K8s 的根本区别。
  4. 功耗是新维度:硬件红利变薄后,功率要和 CPU、内存一起进调度器。
  5. 双向协商 > 单向通知:状态服务要平稳调度,必须给应用「说不」的权力,也要有超时兜底。
  • 论文 PDF:Twine OSDI 2020(18 页,可读)
  • borg-2015 —— Borg — Google 2015 公开的集群管理原型
  • kubernetes-2014 —— Kubernetes — Borg 思路的开源通用化
  • mesos-2011 —— Mesos — 双层调度的另一种解法
  • omega-2013 —— Omega — Google 并行共享状态调度
  • borg-2015 —— Borg 提供 cell/job/task 基本概念,Twine 把 cell 边界打开
  • kubernetes-2014 —— K8s 是面向通用场景的开源路径,Twine 是内部极致优化路径
  • mesos-2011 —— Mesos 的双层调度思路在 Twine 里以 entitlement 形式回归
  • firmament-2016 —— Firmament — 把调度建模成最小费用流的另一种思路
  • omega-2013 —— Omega — Google 并行调度器,和 Twine 同属「打破单集群墙」一族

(暂无反向链接)