John Wikes 讲 Google Borg 的集群管理

Borg 是 Kubernetes 的前身,也是 Google 内部使用的容器管理工具。它比 Kubernetes 更加强大,代表着人类容器管理的最高水平。本文主要基于 John Wikes 在 2016 GOTO Conf 的演讲


Borg 管理了 Google 里面的服务器集群。Borg 是通过运行 job 来使用集群。一个 job 的描述包含:在什么样的系统环境下运行,运行哪些 tasks,运行在哪个 cell(可以理解为一组服务器),需要多少个 replica 同时运行,每个 replica 需要多少资源。剩下的事情交给 Borg。Borg 会按照这个需求在服务器上运行 job。以下是 Borg 的一段 job 的代码描述:

一个 cluster 一般有一个大型 cell,已经很多小 cell,有些小 cell 是做测试用的。大的 cell 一般 10k 台服务器。

在 Borg 的内部,每一台机器上都运行了一个 Borglet 的客户端,负责收集机器的状态信息,并反馈给 BorgMaster。以上面图为例,当收到这个 job 之后,BorgMaster 与 scheduler 合作,计划将在什么时候,在哪些机器上运行这10000个 replica。

对于 Replica,我们并不关心它是在 VM 还是在 container 里运行。 Borg 会根据当前 cell 里面机器剩余资源的情况决定在哪台机器上运行多少个 replica。之后,就能在 Borg 的 UI 界面上看到,多少个 replica 正在运行等等状态了。

Google 内部 replica 运行在 container 里面,而 GCP 上的 replica 运行在 VM 里面,因为需要更高的安全策略。

根据 job 的优先度,可以被分为 prod 和 non-prod 两类。prod 会被优先运行。而 non-prod 通常是一些后台任务或者不太紧急的任务。当机器资源不足时,non-prod 要为 prod 让位(preemption)。让位的方式就是, non-prod job (很可能以容器的形式在运行)会被 kill,然后在另一台机器上重新启动运行。上面这幅图里展示了,prod 和 non-prod job 在一整周的运行时间里,所遇到的问题的平均次数。灰色部分表示 preemption 的情况。蓝色部分表示例行服务器系统升级。可以看到,prod 和 non-prod 同样时间里为系统升级被 kill 的次数差不多,但 non-prod 的 preemption 次数就多很多了。

另外,在 Google 的生产环境中,每天都有很多机器故障停机。此时 Borg 会在其他机器上重新运行被停掉的程序。

上面这幅图中,用每一列表示一台机器的资源使用情况(黑色小方框)。上半部分是 CPU 使用率,下面是内存使用率。每一个颜色小方块表示一个 job 所使用的资源。所以一台机器上会运行很多 replica。当 Borg 在为 job 分配机器的时候,是需要 CPU 和内存的剩余量都满足 job 的要求才可以分配。如果只有部分要求满足,这就叫做 stranded resource。这个情况是要尽量避免的,因为会造成机器资源浪费。

而关于如何提高一个 cell 的利用效率,又用什么指标来评价一个 cell 利用效率呢?有两个测量办法:

  1. 在 cell 上,增大工作量,直到不能正常运行
  2. 给定一个工作量,缩小 cell ,直到不能正常运行

相对来说,第一个办法会遇到如何增大工作量的问题,是靠增加程序数量还是增大数据量等等。所以 Google 选择了第二种办法。他们从的 server cell 中,抛去了很小的 cell (5000 台以下的 cell),从中选择了不同大小的一共 15 个 cell 做了实验。

通过实验发现了一些结论:

  1. 在一个 cell 上即做 prod ,又做 non-prod 的工作,对于机器的需求量小于强制在不同的 cell 分别做 prod 和 non-prod。
  2. cell 如果太小,会产生过多的资源碎片,影响 cell 使用效率。所以 cell 大一点比较好,cell 大小的上限是根据数据中心的供电状况,还有 SRE 团队的管理能力

Michael Jackson 去世的时候, Google 的 SRE 工程师以为网站遭到 DDos 攻击。Wikes 说,如果他又回来了怎么办?

Google 为了更进一步高效利用服务器资源,支持了更细粒度的服务器资源管理。比如说,一个 job 声明自己所需资源时,核数的最小单位是毫核(千分之一核),而内存和硬盘空间的最小单位是字节。这和 Google cloud platform 对外提供资源时所给出选项是完全不同的。

上图是 Google 的 job 所需要的CPU核数的累积概率分布。可以看出,绝大多数的 prod job 所需要的核数在10核以下。很多 job 所需的核数就是单核。

但是,很多时候人们在描述 job 的时候会要求比 job 所需更多的资源。在 Borg 里,如果出现这种情况,那么 Borg 会把超出合理范围的预留资源分配给其他 job,连 prod 的高优先级 job 也不能例外。

那么,下一个问题就是,多少预留资源是合理的?

经过观察发现,大部分用都只使用了声明资源的 1/3 左右。当然这是为突发状况准备的。上图是一段时间内一台服务器的资源使用情况。最顶上灰色是 job 声明需要的资源,红色线是实际使用率。Google 做了一个算法,计算出了一个合理的预留资源数量,在图中用蓝色线表示。红蓝线之间黄色区域是给 job 的实际的预留资源。而绿色区域是可以用作其他 job 的资源。这条蓝色的线会根据 job 使用资源的变化而调整。我们可以看出,当流量猛增的时候,黄色区域变小,Borg 会自动 scale。

绿色区域经常被用作 non-prod 的 job 使用。要记得,当 prod 需要更多资源时, 这些 non-prod job 会被清掉,在另一个 cell 上重启。所以这个操作当然是越少越好。

当工程师在开发一个产品的时候,在服务器端,他们如果只关心他们 app 的服务器,只关注他们的业务逻辑,那么对他们是最有利的。这个 app 服务器在整个 app 所需的服务器里面只是很小的一部分。还需要有其他服务器来做辅助工作,比如存储、保存配置信息、监测、计算服务器使用账单、系统升级等等。而很多 app 可以共用这些辅助服务器。

在提供了这些配套服务以后,这样就有了面向 Google 以外的 Google Cloud Platform。而一个轻量版的 Borg ,结合 Docker 作为容器打包工具,就成为了 Kubernetes。

这篇文章是一个非常入门的 Borg 介绍。在 Borg 的实际实现中处理了很多细节问题,推荐阅读 Google SRE 白皮书。

参考:

微信公众号:网站架构札记

最近发布