本文发布已超过一年。较旧的文章可能包含过时的内容。请确认页面中的信息自发布以来是否已不再准确。

Borg:Kubernetes 的前身

十多年来,Google 一直在生产环境中运行容器化工作负载。无论是像 Web 前端和有状态服务器这样的服务作业,还是像 BigtableSpanner 这样的基础设施系统,亦或是像 MapReduceMillwheel 这样的批处理框架,Google 内部几乎所有的东西都作为容器运行。今天,我们在学术计算机系统会议 Eurosys 上公布了 Google 内部长期以来备受传闻的面向容器的集群管理系统 Borg 的详细信息。你可以在这里找到这篇论文。

Kubernetes 直接起源于 Borg。许多曾在 Borg 项目工作的 Google 开发者现在都在参与 Kubernetes 的开发。我们将 Borg 的最佳理念融入 Kubernetes,并尝试解决用户多年来在使用 Borg 时发现的一些痛点。

为了让你有所了解,以下是源自我们使用 Borg 经验的四个 Kubernetes 特性:

  1. Pod。Pod 是 Kubernetes 中调度的最小单元。它是一个资源包络,其中可以运行一个或多个容器。属于同一 Pod 的容器保证被调度到同一机器上,并且可以通过本地存储卷共享状态。

Borg 有一个类似的抽象,称为 alloc(“资源分配”的简称)。Borg 中 alloc 的常见用途包括运行一个生成日志的 Web 服务器,旁边运行一个轻量级日志收集进程,该进程将日志发送到集群文件系统(与 fluentd 或 logstash 类似);运行一个 Web 服务器,该服务器从磁盘目录提供数据,而该目录由一个进程填充,该进程从集群文件系统读取数据并为 Web 服务器准备/暂存数据(与内容管理系统类似);以及在一个存储分片旁边运行用户定义的处理函数。Pod 不仅支持这些用例,还提供了一种类似于在单个 VM 中运行多个进程的环境——Kubernetes 用户可以在一个 Pod 中部署多个共存的、协作的进程,而无需放弃“一个容器一个应用”部署模型的简单性。

  1. Service。尽管 Borg 的主要作用是管理任务和机器的生命周期,但在 Borg 上运行的应用受益于许多其他集群服务,包括命名和负载均衡。Kubernetes 使用 Service 抽象支持命名和负载均衡:一个 Service 有一个名称,并映射到由标签选择器定义的动态 Pod 集合(参见下一节)。集群中的任何容器都可以使用 Service 名称连接到该 Service。在底层,Kubernetes 会自动在匹配标签选择器的 Pod 之间对连接到 Service 的请求进行负载均衡,并跟踪由于故障导致 Pod 随时间被重新调度后的位置。

  2. 标签。Borg 中的容器通常是某个 Internet 服务的一个层级(例如 Google 地图的前端)或批处理作业的工作进程(例如 MapReduce)的一组相同或几乎相同的容器集合中的一个副本。该集合称为 Job,每个副本称为 Task。尽管 Job 是一个非常有用的抽象,但它可能有局限性。例如,用户经常希望将整个服务(由许多 Job 组成)作为一个整体来管理,或者统一管理服务的几个相关实例,例如独立的金丝雀发布和稳定版发布轨道。另一方面,用户经常希望对 Job 内 Task 的子集进行推理和控制——最常见的例子是在滚动更新期间,Job 的不同子集需要不同的配置。

Kubernetes 通过使用标签(用户附加到 Pod 以及系统中的任何对象上的任意键值对)来组织 Pod,从而支持比 Borg 更灵活的集合。用户可以通过在其 Pod 上使用“job:<jobname>”标签创建等同于 Borg Job 的分组,但他们也可以使用额外的标签来标记服务名称、服务实例(生产、预发、测试),以及通常情况下 Pod 的任何子集。标签查询(称为“标签选择器”)用于选择应将操作应用于哪些 Pod 集合。总而言之,标签和Replication Controller 允许非常灵活的更新语义,并支持跨越等同于 Borg Job 的操作。

  1. IP-per-Pod。在 Borg 中,一台机器上的所有 Task 都使用该主机的 IP 地址,因此共享该主机的端口空间。虽然这意味着 Borg 可以使用普通网络,但它给基础设施和应用开发者带来了诸多负担:Borg 必须将端口作为资源进行调度;Task 必须预先声明它们需要多少端口,并将要使用的端口作为启动参数;Borglet(节点代理)必须强制执行端口隔离;命名和 RPC 系统也必须处理端口以及 IP 地址。

由于出现了软件定义的覆盖网络,例如 flannel 或内置于公有云中的网络,Kubernetes 能够为每个 Pod 和 Service 分配自己的 IP 地址。这消除了管理端口的基础设施复杂性,并允许开发者选择他们想要的任何端口,而无需其软件去适应基础设施选择的端口。后一点对于简化在 Kubernetes 上运行现成的开源应用至关重要——Pod 可以被视为与 VM 或物理主机非常相似,拥有完整的端口空间访问权,而无需在意它们可能与其他 Pod 共享同一台物理机器。

随着基于容器的微服务架构日益普及,Google 在内部运行此类系统所获得的经验引起了外部 DevOps 社区日益增长的兴趣。通过揭示我们的集群管理器 Borg 的部分内部工作原理,并将我们的下一代集群管理器构建为一个开源项目 (Kubernetes) 和一个公开可用的托管服务(Google Container Engine),我们希望这些经验能够惠及 Google 之外的更广泛社区,并推动容器调度和集群管理领域的最新技术发展。