本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。
Borg:Kubernetes 的前身
Google 在生产环境中运行容器化工作负载已有十多年。无论是像 Web 前端和有状态服务器这样的服务作业,像 Bigtable 和 Spanner 这样的基础设施系统,还是像 MapReduce 和 Millwheel 这样的批处理框架,Google 的几乎所有内容都以容器形式运行。今天,我们揭开了 Borg 的面纱,Borg 是 Google 长期以来传闻的内部面向容器的集群管理系统,并在学术计算机系统会议 Eurosys 上发布了详细信息。您可以在此处找到该论文。
Kubernetes 直接继承了 Borg 的血统。许多在 Google 从事 Kubernetes 工作的开发人员都曾是 Borg 项目的开发人员。我们将 Borg 的最佳思想融入了 Kubernetes,并试图解决用户多年来在 Borg 中发现的一些痛点。
为了让您对此有所了解,以下是 Kubernetes 的四个功能,它们来自我们使用 Borg 的经验:
- Pod。Pod 是 Kubernetes 中的调度单元。它是一个资源包,其中运行一个或多个容器。属于同一个 Pod 的容器保证会一起调度到同一台机器上,并且可以通过本地卷共享状态。
Borg 有一个类似的抽象,称为 alloc(“资源分配”的缩写)。Borg 中 alloc 的常见用途包括:运行一个 Web 服务器,该服务器生成日志,同时运行一个轻量级日志收集进程,将日志发送到集群文件系统(类似于 fluentd 或 logstash);运行一个 Web 服务器,该服务器从磁盘目录提供数据,该目录由一个进程填充,该进程从集群文件系统读取数据并为 Web 服务器准备/暂存数据(类似于内容管理系统);以及在存储分片旁边运行用户定义的处理函数。Pod 不仅支持这些用例,而且还提供了一个类似于在单个 VM 中运行多个进程的环境——Kubernetes 用户可以在 Pod 中部署多个共存、协作的进程,而无需放弃一个应用程序一个容器部署模型的简单性。
Service。尽管 Borg 的主要作用是管理任务和机器的生命周期,但运行在 Borg 上的应用程序受益于许多其他集群服务,包括命名和负载均衡。Kubernetes 使用 Service 抽象支持命名和负载均衡:Service 有一个名称,并映射到由标签选择器定义的一组动态 Pod(请参阅下一节)。集群中的任何容器都可以使用 Service 名称连接到 Service。在幕后,Kubernetes 会自动在与标签选择器匹配的 Pod 之间对连接到 Service 的连接进行负载均衡,并跟踪 Pod 随着时间因故障重新调度而运行的位置。
标签。Borg 中的容器通常是相同或几乎相同的容器集合中的一个副本,这些容器对应于 Internet 服务的某个层(例如 Google 地图的前端)或批处理作业的工作器(例如 MapReduce)。这个集合被称为 Job,每个副本被称为 Task。虽然 Job 是一个非常有用的抽象,但它可能受到限制。例如,用户通常希望将整个服务(由许多 Job 组成)作为一个单一实体进行管理,或者统一管理其服务的几个相关实例,例如独立的金丝雀和稳定发布轨道。另一方面,用户经常希望推断和控制 Job 中的任务子集——最常见的例子是在滚动更新期间,Job 的不同子集需要具有不同的配置。
Kubernetes 通过使用标签(用户附加到 Pod(实际上是系统中的任何对象)的任意键/值对)来组织 Pod,从而支持比 Borg 更灵活的集合。用户可以通过在其 Pod 上使用“job:<jobname>”标签来创建等同于 Borg Job 的分组,但他们也可以使用附加标签来标记服务名称、服务实例(生产、暂存、测试),以及通常是其 Pod 的任何子集。标签查询(称为“标签选择器”)用于选择应将操作应用于哪些 Pod 集。总而言之,标签和复制控制器允许非常灵活的更新语义,以及跨越 Borg Job 等效的操作。
- 每个 Pod 一个 IP。在 Borg 中,机器上的所有任务都使用该主机的 IP 地址,因此共享主机的端口空间。虽然这意味着 Borg 可以使用普通的网络,但它给基础设施和应用程序开发人员带来了许多负担:Borg 必须将端口调度为资源;任务必须预先声明它们需要多少端口,并将其作为启动参数来使用哪些端口;Borglet(节点代理)必须强制执行端口隔离;命名和 RPC 系统必须处理端口和 IP 地址。
由于诸如 flannel 或内置于公共云中的软件定义覆盖网络的出现,Kubernetes 能够为每个 Pod 和 Service 提供自己的 IP 地址。这消除了管理端口的基础设施复杂性,并允许开发人员选择他们想要的任何端口,而不是要求他们的软件适应基础设施选择的端口。后一点对于简化在 Kubernetes 上运行现成的开源应用程序至关重要——Pod 可以被视为类似于 VM 或物理主机,可以访问完整的端口空间,而无需知道它们可能与同一个物理机器上的其他 Pod 共享。
随着基于容器的微服务架构越来越受欢迎,Google 从内部运行此类系统中学到的经验教训引起了外部 DevOps 社区日益增长的兴趣。通过揭示我们的集群管理器 Borg 的一些内部工作原理,并将我们的下一代集群管理器构建为开源项目 (Kubernetes) 和公共托管服务 (Google Container Engine),我们希望这些经验教训能够惠及 Google 之外的更广泛社区,并推动容器调度和集群管理领域的技术发展。