下载本文的PDF版本 PDF

Borg、Omega 和 Kubernetes

从十年间三个容器管理系统中学到的经验教训


Brendan Burns、Brian Grant、David Oppenheimer、Eric Brewer 和 John Wilkes,Google 公司

尽管对软件容器的广泛兴趣是相对较新的现象,但在 Google,我们管理 Linux 容器的规模已超过十年,并在此期间构建了三个不同的容器管理系统。每个系统都深受其前代产品的影响,即使它们是出于不同的原因而开发的。本文描述了我们在开发和运营这些系统过程中学到的经验教训。

Google 开发的第一个统一容器管理系统是我们内部称之为 Borg 的系统。7 它旨在管理长期运行的服务和批处理作业,而这两者之前分别由两个独立的系统处理:Babysitter 和 Global Work Queue。后者的架构对 Borg 产生了强烈的影响,但专注于批处理作业;两者都早于 Linux 控制组。Borg 在这两种类型的应用程序之间共享机器,以此来提高资源利用率,从而降低成本。这种共享之所以成为可能,是因为 Linux 内核中的容器支持变得可用(实际上,Google 为 Linux 内核贡献了大部分容器代码),这使得延迟敏感的用户界面服务和 CPU 密集型批处理进程之间能够实现更好的隔离。

随着越来越多的应用程序被开发以在 Borg 之上运行,我们的应用程序和基础设施团队为其开发了广泛的工具和服务生态系统。这些系统提供了用于配置和更新作业的机制;预测资源需求;动态地将配置文件推送到正在运行的作业;服务发现和负载均衡;自动伸缩;机器生命周期管理;配额管理等等。这个生态系统的发展是由 Google 内部不同团队的需求驱动的,结果是一个有些异构、临时的系统集合,Borg 的用户必须使用几种不同的配置语言和流程来配置和交互。由于其规模、广泛的功能和极高的鲁棒性,Borg 仍然是 Google 内部主要的容器管理系统。

Omega6 是 Borg 的衍生产品,其驱动力是改进 Borg 生态系统的软件工程。它应用了许多在 Borg 中被证明成功的模式,但从头开始构建,以拥有更一致、更原则的架构。Omega 将集群状态存储在集中的、基于 Paxos 的事务导向存储中,集群控制平面的不同部分(例如调度器)通过乐观并发控制访问该存储,以处理偶尔的冲突。这种解耦允许将 Borgmaster 的功能分解为充当对等方的独立组件,而不是将每个更改都通过一个单体式的、中心化的 master 进行传递。Omega 的许多创新(包括多个调度器)后来都被融入了 Borg。

Google 开发的第三个容器管理系统是 Kubernetes。4 它的构思和开发是在外部开发人员对 Linux 容器越来越感兴趣的世界中进行的,并且 Google 已经发展了不断增长的公共云基础设施销售业务。Kubernetes 是开源的——这与 Borg 和 Omega 形成对比,后者是作为纯粹的 Google 内部系统开发的。与 Omega 类似,Kubernetes 的核心也有一个共享的持久化存储,组件会监视相关对象的更改。与 Omega 不同的是,Omega 将存储直接暴露给受信任的控制平面组件,而 Kubernetes 中的状态则完全通过特定领域的 REST API 访问,该 API 应用了更高级别的版本控制、验证、语义和策略,以支持更多样化的客户端。更重要的是,Kubernetes 的开发更加注重开发人员编写在集群中运行的应用程序的体验:其主要设计目标是使部署和管理复杂的分布式系统变得容易,同时仍然受益于容器实现的更高的利用率。

本文描述了 Google 从 Borg 到 Kubernetes 的历程中所获得的一些知识和经验教训。

容器

从历史上看,第一个容器仅提供根文件系统的隔离(通过 chroot),FreeBSD jails 将其扩展到其他命名空间,例如进程 ID。Solaris 随后率先并探索了许多增强功能。Linux 控制组 (cgroups) 采用了许多这些想法,并且该领域的开发至今仍在继续。

容器提供的资源隔离使 Google 能够将利用率提高到远高于行业标准的水平。例如,Borg 使用容器将批处理作业与延迟敏感的、面向用户的作业在同一物理机器上进行共置。面向用户的作业预留的资源多于它们通常需要的资源——允许它们处理负载峰值和故障转移——而这些大部分未使用的资源可以被回收来运行批处理作业。容器提供了使这成为可能的资源管理工具,以及强大的内核级资源隔离,以防止进程相互干扰。我们通过在 Borg 开发的同时增强 Linux 容器来实现这一点。然而,隔离并不完美:容器无法防止操作系统内核不管理的资源(例如 L3 处理器缓存和内存带宽)中的干扰,并且容器需要额外的安全层(例如虚拟机)来防止云中发现的恶意行为者。

现代容器不仅仅是一种隔离机制:它还包括一个镜像——构成容器内运行的应用程序的文件。在 Google 内部,MPM(Midas Package Manager)用于构建和部署容器镜像。隔离机制和 MPM 包之间的这种共生关系也可以在 Docker 守护程序和 Docker 镜像仓库之间找到。在本文的其余部分,我们使用容器一词来涵盖这两个方面:运行时隔离和镜像。

面向应用的架构

随着时间的推移,越来越清楚的是,容器化的好处不仅仅在于提高利用率。容器化将数据中心从面向机器转变为面向应用。本节讨论两个示例:

• 容器封装了应用程序环境,从应用程序开发人员和部署基础设施中抽象出机器和操作系统的许多细节。

• 因为设计良好的容器和容器镜像的作用域限定为单个应用程序,所以管理容器意味着管理应用程序而不是机器。这种管理 API 从面向机器到面向应用的转变极大地改进了应用程序的部署和内省。

应用程序环境

内核中 cgroup、chroot 和命名空间设施的最初目的是保护应用程序免受嘈杂、窥探和混乱的邻居的影响。将这些与容器镜像结合使用创建了一个抽象,该抽象还将应用程序与它们运行所在的(异构)操作系统隔离开来。镜像和操作系统的这种解耦使得在开发和生产中提供相同的部署环境成为可能,这反过来又通过减少不一致和摩擦来提高部署可靠性并加快开发速度。

使这种抽象起作用的关键是拥有一个密封的容器镜像,该镜像可以将应用程序的几乎所有依赖项封装到一个可以部署到容器中的包中。如果正确地完成此操作,则唯一的本地外部依赖项将是 Linux 内核系统调用接口。虽然这个有限的接口极大地提高了镜像的可移植性,但它并不完美:应用程序仍然可能暴露于操作系统接口的动荡,尤其是在套接字选项、/proc 和 ioctl 调用的参数所暴露的广泛表面积中。我们希望像开放容器倡议 (Open Container Initiative) (https://www.opencontainers.org/) 这样的持续努力将进一步明确容器抽象的表面积。

尽管如此,容器提供的隔离和依赖项最小化在 Google 已被证明非常有效,并且容器已成为 Google 基础设施支持的唯一可运行实体。一个结果是,Google 在其整个机器集群中在任何给定时间仅部署少量操作系统版本,并且只需要少量人员来维护它们并推出新版本。

有许多方法可以实现这些密封的镜像。在 Borg 中,程序二进制文件在构建时静态链接到公司范围存储库中托管的已知良好库版本。5 即便如此,Borg 容器镜像也没有它本可以达到的那么密封:应用程序共享一个所谓的基础镜像,该镜像在机器上安装一次,而不是打包在每个容器中。此基础镜像包含 tar 和 libc 库等实用程序,因此对基础镜像的升级可能会影响正在运行的应用程序,并且偶尔会成为重要的麻烦来源。

更现代的容器镜像格式(如 Docker 和 ACI)进一步强化了这种抽象,并通过消除隐式的宿主机操作系统依赖项并要求显式的用户命令来在容器之间共享镜像数据,从而更接近密封的理想状态。

容器作为管理单元

围绕容器而不是机器构建管理 API 将数据中心的“主键”从机器转移到应用程序。这有很多好处:(1)它使应用程序开发人员和运营团队无需担心机器和操作系统的具体细节;(2)它为基础设施团队提供了灵活性,可以推出新硬件和升级操作系统,而对正在运行的应用程序及其开发人员的影响最小;(3)它将管理系统收集的遥测数据(例如,CPU 和内存使用率等指标)与应用程序而不是机器联系起来,这极大地改进了应用程序的监控和内省,尤其是在规模扩大、机器故障或维护导致应用程序实例移动时。

容器提供了方便的点来注册通用 API,这些 API 可以在管理系统和应用程序之间实现信息流,而无需彼此了解对方实现的细节。在 Borg 中,此 API 是一系列附加到每个容器的 HTTP 端点。例如,/healthz 端点向编排器报告应用程序运行状况。当检测到不健康的应用程序时,它会自动终止并重新启动。这种自我修复是可靠分布式系统的关键构建块。(Kubernetes 提供类似的功能;运行状况检查使用用户指定的 HTTP 端点或在容器内部运行的 exec 命令。)

可以由容器提供或为其提供其他信息,并在各种用户界面中显示。例如,Borg 应用程序可以提供一个简单的文本状态消息,该消息可以动态更新,而 Kubernetes 提供存储在每个对象元数据中的键值注释,这些注释可用于传达应用程序结构。此类注释可以由容器本身或管理系统中的其他参与者(例如,推出容器更新版本的进程)设置。

在另一个方向上,容器管理系统可以将信息传递到容器中,例如资源限制、容器元数据(用于传播到日志记录和监控,例如用户名、作业名称、身份)以及在节点维护之前提供优雅终止警告的通知。

容器还可以通过其他方式提供面向应用程序的监控:例如,Linux 内核 cgroup 提供有关应用程序的资源利用率数据,并且可以使用 HTTP API 扩展这些数据,并使用 HTTP API 导出自定义指标,如前所述。此数据使通用工具(如自动伸缩器或 cAdvisor3)的开发成为可能,这些工具可以记录和使用指标,而无需了解每个应用程序的具体细节。由于容器是应用程序,因此无需(解)复用来自在物理或虚拟机中运行的多个应用程序的信号。这更简单、更可靠,并允许对指标和日志进行更细粒度的报告和控制。将其与必须 ssh 到机器上运行 top 进行比较。尽管开发人员可以 ssh 到他们的容器中,但他们很少需要这样做。

监控只是一个例子。面向应用程序的转变对整个管理基础设施产生了连锁反应。我们的负载均衡器不会跨机器平衡流量;它们在应用程序实例之间进行平衡。日志按应用程序而不是机器进行键控,因此可以轻松地跨实例收集和聚合日志,而不会受到来自多个应用程序或系统操作的污染。我们可以检测应用程序故障,并更容易地确定故障原因,而无需将它们与机器级别的信号分离。从根本上说,由于容器管理器管理的实例的身份与应用程序开发人员期望的实例的身份完全一致,因此构建、管理和调试应用程序更容易。

最后,尽管到目前为止我们一直专注于应用程序与容器是 1:1 的关系,但实际上我们使用嵌套容器,这些容器在同一台机器上共同调度:最外层的容器提供资源池;内部容器提供部署隔离。在 Borg 中,最外层的容器称为资源分配,或 alloc;在 Kubernetes 中,它称为 pod。Borg 还允许顶级应用程序容器在 alloc 之外运行;这已成为许多不便的根源,因此 Kubernetes 使事情变得规范化,并且始终在顶级 pod 内运行应用程序容器,即使 pod 包含单个容器。

常见的使用模式是 pod 容纳复杂应用程序的实例。应用程序的主要部分位于子容器之一中,而其他容器运行支持功能,例如日志轮换或单击日志卸载到分布式文件系统。与将功能组合到单个二进制文件中相比,这使得不同的团队可以轻松地开发不同的功能部分,并且提高了鲁棒性(即使主应用程序被楔住,卸载也会继续进行)、可组合性(很容易添加新的小型支持服务,因为它在其自己的容器提供的私有执行环境中运行)和细粒度的资源隔离(每个都在其自己的资源中运行,因此日志记录系统不会使主应用程序匮乏,反之亦然)。

编排是开始,而不是结束

最初的 Borg 系统使在共享机器上运行不同的工作负载成为可能,以提高资源利用率。然而,Borg 生态系统中支持服务的快速发展表明,容器管理本身仅仅是开发和管理可靠分布式系统的环境的开始。许多不同的系统已经在 Borg 中、之上和周围构建,以改进 Borg 提供的基本容器管理服务。以下部分列表给出了一些关于它们的范围和种类的想法:

• 命名和服务发现(Borg 名称服务,或 BNS)。

• Master 选举,使用 Chubby。2

• 应用程序感知负载均衡。

• 水平(实例数量)和垂直(实例大小)自动伸缩。

• 管理新二进制文件和配置数据谨慎部署的推出工具。

• 工作流工具(例如,允许运行多作业分析管道,这些管道在阶段之间具有相互依赖性)。

• 监控工具,用于收集有关容器的信息、聚合信息、在仪表板上呈现信息以及使用信息触发警报。

这些服务是有机地构建的,以解决应用程序团队遇到的问题。成功的服务被采纳、广泛采用,并使其他开发人员的生活更轻松。不幸的是,这些工具通常选择特有的 API、约定(例如文件位置)和 Borg 集成深度。一个不希望看到的副作用是增加了在 Borg 生态系统中部署应用程序的复杂性。

Kubernetes 尝试通过对其 API 采用一致的方法来避免这种复杂性的增加。例如,每个 Kubernetes 对象在其描述中都有三个基本字段:Object MetadataSpecification(或 Spec)和 Status

Object Metadata 对于系统中的所有对象都是相同的;它包含诸如对象的名称、UID(唯一标识符)、对象版本号(用于乐观并发控制)和标签(键值对,见下文)等信息。SpecStatus 的内容因对象类型而异,但其概念不变:Spec 用于描述对象的期望状态,而 Status 提供有关对象当前状态的只读信息。

这种统一的 API 提供了许多好处。具体来说,学习系统更简单:类似的信息适用于所有对象。此外,编写跨所有对象的通用工具更简单,这反过来又实现了开发一致的用户体验。从 Borg 和 Omega 中学习,Kubernetes 是由一组可组合的构建块构建的,这些构建块可以很容易地被其用户扩展。通用的 API 和对象元数据结构使这变得更加容易。例如,pod API 可供人员、内部 Kubernetes 组件和外部自动化工具使用。为了进一步提高这种一致性,Kubernetes 正在扩展,以使用户能够动态地添加自己的 API,以及核心 Kubernetes 功能。

一致性也通过 Kubernetes API 中的解耦来实现。API 组件之间的关注点分离意味着更高级别的服务都共享相同的基本构建块。这方面的一个很好的例子是 Kubernetes 副本控制器与其水平自动伸缩系统之间的分离。副本控制器确保给定角色(例如,“前端”)的所需数量的 pod 的存在。反过来,自动伸缩器依赖于此功能,并且只是调整所需的 pod 数量,而无需担心如何创建或删除这些 pod。自动伸缩器实现可以专注于需求和使用预测,而忽略如何实现其决策的细节。

解耦确保多个相关但不同的组件共享相似的外观和感觉。例如,Kubernetes 有三种不同形式的复制 pod:

ReplicationController:永久运行的复制容器(例如,Web 服务器)。

DaemonSet:确保集群中每个节点上都有一个实例(例如,日志记录代理)。

Job:一个运行到完成的控制器,它知道如何从头到尾运行(可能是并行化的)批处理作业。

无论策略上的差异如何,所有这三个控制器都依赖于通用的 pod 对象来指定它们希望运行的容器。

一致性也是通过不同 Kubernetes 组件的通用设计模式实现的。协调控制器循环的想法在 Borg、Omega 和 Kubernetes 中共享,以提高系统的弹性:它将期望状态(例如,应有多少 pod 与标签选择器查询匹配)与观察到的状态(它可以找到的此类 pod 的数量)进行比较,并采取措施使观察到的状态和期望状态收敛。由于所有操作都基于观察而不是状态图,因此协调循环对于故障和扰动具有鲁棒性:当控制器发生故障或重新启动时,它只是从上次中断的地方继续。

Kubernetes 作为微服务和小控制循环的组合的设计是通过编排进行控制的一个例子——通过结合协作的独立实体的效果来实现期望的涌现行为。这是一种有意识的设计选择,与中心化的编排系统形成对比,后者最初可能更容易构建,但随着时间的推移往往会变得脆弱和僵化,尤其是在出现意外错误或状态更改的情况下。

避免事项

在开发这些系统的过程中,我们学到的该做的事情几乎与值得做的事情一样多。我们在此介绍其中一些,希望其他人可以专注于犯新的错误,而不是重复我们的错误。

不要让容器系统管理端口号

在 Borg 机器上运行的所有容器共享主机的 IP 地址,因此 Borg 在调度过程中为容器分配唯一的端口号。当容器移动到新机器时,以及(有时)在同一机器上重新启动时,容器将获得新的端口号。这意味着传统的网络服务(例如 DNS(域名系统))必须由自制版本替换;服务客户端事先不知道分配给服务的端口号,必须被告知;端口号不能嵌入 URL 中,需要基于名称的重定向机制;并且依赖于简单 IP 地址的工具需要重写以处理 IP:端口对。

从我们在 Borg 中的经验中学习,我们决定 Kubernetes 将为每个 pod 分配一个 IP 地址,从而使网络身份(IP 地址)与应用程序身份对齐。这使得在 Kubernetes 上运行现成的软件变得容易得多:应用程序可以自由使用静态的众所周知的端口(例如,用于 HTTP 流量的 80 端口),并且现有的、熟悉的工具可以用于诸如网络分段、带宽限制和管理之类的事情。所有流行的云平台都提供了支持每个 pod 的 IP 的网络底层;在裸机上,可以使用 SDN(软件定义网络)覆盖或配置 L3 路由来处理每台机器多个 IP。

不要仅仅编号容器:给它们标签

如果你允许用户轻松创建容器,他们倾向于创建大量容器,并且很快就需要一种方法来分组和组织它们。Borg 提供作业来对相同的任务(它对容器的名称)进行分组。作业是一个或多个相同任务的紧凑向量,从零开始顺序索引。这提供了强大的功能,并且简单明了,但随着时间的推移,我们开始后悔它的僵化。例如,当任务死亡并且必须在另一台机器上重新启动时,任务向量中的同一个槽必须承担双重职责:识别新副本并指向旧副本,以防需要调试。当向量中间的任务退出时,向量最终会出现空洞。向量使得非常难以支持在 Borg 之上的一层中跨多个集群的作业。Borg 的作业更新语义(在执行滚动升级时通常按索引顺序重新启动任务)和应用程序对任务索引的使用(例如,在跨任务对数据集进行分片或分区)之间也存在潜在的、意想不到的交互:如果应用程序使用基于任务索引的范围分片,Borg 的重新启动策略可能会导致数据不可用,因为它会关闭相邻的任务。Borg 也没有提供一种简单的方法来向作业添加与应用程序相关的元数据,例如角色(例如,“前端”)或推出状态(例如,“金丝雀”),因此人们将此信息编码到他们使用正则表达式解码的作业名称中。

相比之下,Kubernetes 主要使用标签来标识容器组。标签是一个键/值对,其中包含有助于标识对象的信息。pod 可能具有标签 role=frontendstage=production,指示此容器充当生产前端实例。标签可以由自动化工具或用户动态添加、删除和修改,不同的团队可以在很大程度上独立地管理自己的标签。对象集由标签选择器定义(例如,stage==production && role==frontend)。集合可以重叠,并且一个对象可以属于多个集合,因此标签本质上比显式对象列表或简单的静态属性更灵活。由于集合由动态查询定义,因此可以随时创建新集合。标签选择器是 Kubernetes 中的分组机制,并定义可以跨多个实体的所有管理操作的范围。

即使在那些知道集合中任务的身份有帮助的情况下(例如,对于静态角色分配和工作分区或分片),也可以使用适当的每个 pod 标签来重现任务索引的效果,但这由应用程序(或 Kubernetes 外部的某些其他管理系统)负责提供此类标签。标签和标签选择器提供了一种通用机制,该机制提供了两全其美的效果。

注意所有权

在 Borg 中,任务不独立于作业而存在。创建作业会创建其任务;这些任务永远与该特定作业相关联,并且删除作业会删除任务。这很方便,但有一个主要缺点:因为只有一个分组机制,所以它需要处理所有用例。例如,作业必须存储仅对服务或批处理作业有意义的参数,但不能两者都存储,并且当作业抽象不处理用例时,用户必须开发解决方法(例如,将单个 pod 复制到集群中所有节点的 DaemonSet)。

在 Kubernetes 中,pod 生命周期管理组件(例如副本控制器)使用标签选择器确定它们负责哪些 pod,因此多个控制器可能认为它们对单个 pod 具有管辖权。重要的是通过适当的配置选择来防止此类冲突。但是标签的灵活性具有补偿优势——例如,控制器和 pod 的分离意味着可以“孤立”和“采用”容器。考虑一个负载均衡服务,该服务使用标签选择器来标识要向其发送流量的 pod 集。如果其中一个 pod 开始行为不端,则可以通过删除导致 Kubernetes 服务负载均衡器将其作为目标的标签来将该 pod 与服务请求隔离。pod 不再服务流量,但它将保持启动状态,并且可以就地进行调试。与此同时,管理实现服务的 pod 的副本控制器会自动为行为不端的 pod 创建一个替换 pod。

不要暴露原始状态

Borg、Omega 和 Kubernetes 之间的一个关键区别在于它们的 API 架构。Borgmaster 是一个单体组件,它知道每个 API 操作的语义。它包含集群管理逻辑,例如作业、任务和机器的状态机;并且它运行基于 Paxos 的复制存储系统,用于记录 master 的状态。相比之下,Omega 除了存储之外没有中心化组件,存储仅保存被动状态信息并强制执行乐观并发控制:所有逻辑和语义都被推送到存储的客户端中,客户端直接读取和写入存储内容。在实践中,每个 Omega 组件都使用相同的客户端库来访问存储,该库执行数据结构的打包/解包、重试并强制执行语义一致性。

Kubernetes 选择了一个中间立场,该立场在提供 Omega 的组件化架构的灵活性和可伸缩性的同时,强制执行系统范围的不变性、策略和数据转换。它通过强制所有存储访问都通过中心化的 API 服务器来实现这一点,该服务器隐藏了存储实现的细节,并为对象验证、默认设置和版本控制提供服务。与 Omega 中一样,客户端组件彼此解耦,并且可以独立发展或替换(这在开源环境中尤其重要),但中心化使得强制执行通用语义、不变性和策略变得容易。

一些开放的、困难的问题

即使拥有多年的容器管理经验,我们仍然觉得有很多问题我们仍然没有很好的答案。本节描述了几个特别棘手的问题,希望能促进讨论和解决方案。

配置

在我们面临的所有问题中,花费最多脑力、笔墨和代码的问题都与管理配置有关——提供给应用程序而不是硬编码到应用程序中的一组值。事实上,我们可以将整篇文章都专门讨论这个主题,但仍然有更多的话要说。以下是一些亮点。

首先,应用程序配置成为实现容器管理系统尚未(尚未)做的所有事情的通用位置。在 Borg 的历史中,这包括:

• 样板代码减少(例如,默认任务重启策略,该策略适用于工作负载,例如服务或批处理作业)。

• 调整和验证应用程序参数和命令行标志。

• 为缺少 API 抽象(例如包(镜像)管理)实现解决方法。

• 应用程序的配置模板库。

• 发布管理工具。

• 镜像版本规范。

为了应对这些类型的需求,配置管理系统倾向于发明一种特定领域的配置语言,该语言(最终)变得图灵完备,从对配置中的数据执行计算的愿望开始(例如,调整要给服务器的内存量,作为服务中分片数量的函数)。结果是那种难以理解的“配置即代码”,人们试图通过消除应用程序源代码中的硬编码参数来避免这种情况。它不会降低操作复杂性或使配置更容易调试或更改;它只是将计算从真正的编程语言转移到特定领域的语言,后者通常具有较弱的开发工具(例如,调试器、单元测试框架等)。

我们认为最有效的方法是接受这种需求,拥抱程序化配置的必然性,并在计算和数据之间保持清晰的分离。表示数据的语言应该是一种简单的、仅限数据的格式,例如 JSON 或 YAML,而对此数据的程序化修改应该在真正的编程语言中完成,在真正的编程语言中,有明确定义的语义以及良好的工具。有趣的是,在前端开发的不同领域也可以看到这种计算和数据的分离,例如 Angular 等框架,它们在标记(数据)和 JavaScript(计算)的世界之间保持着清晰的分离。

依赖管理

启动一项服务通常也意味着启动一系列相关服务(监控、存储、CI/CD 等)。如果应用程序依赖于其他应用程序,那么如果这些依赖项(以及它们可能具有的任何传递依赖项)由集群管理系统自动实例化,岂不是很好?

使事情复杂化的是,实例化依赖项很少像启动新副本那样简单——例如,它可能需要注册为现有服务(例如,Bigtable 即服务)的消费者,并在这些传递依赖项之间传递身份验证、授权和计费信息。然而,几乎没有系统捕获、维护或公开这种依赖项信息,因此在基础设施级别自动化即使是常见的情况也几乎是不可能的。启动新应用程序对于用户来说仍然很复杂,这使得开发人员更难构建新服务,并且通常会导致不遵循最新的最佳实践,这会影响最终服务的可靠性。

一个标准问题是,如果手动提供依赖项信息,则很难保持其最新状态,同时自动确定它的尝试(例如,通过跟踪访问)无法捕获理解结果所需的语义信息。(该访问必须转到实例吗,还是任何实例都足够?)一种可能的进展方法是要求应用程序枚举它所依赖的服务,并让基础设施拒绝允许访问任何其他服务。(我们在构建系统中使用编译器导入来做到这一点。1)激励将是使基础设施能够反过来做有用的事情,例如自动设置、身份验证和连接。

不幸的是,表达、分析和使用系统依赖项的系统的感知复杂性一直太高,因此它们尚未添加到主流容器管理系统中。我们仍然希望 Kubernetes 可能成为可以构建此类工具的平台,但这样做仍然是一个开放的挑战。

结论

十年来构建容器管理系统的经验教会了我们很多东西,我们将许多经验教训融入了 Kubernetes,Google 最新的容器管理系统。其目标是在容器的功能基础上构建,以显着提高程序员的生产力,并简化手动和自动系统管理。我们希望您加入我们,共同扩展和改进它。

参考文献

1. Bazel:{快速,正确} - 选择其二; http://bazel.io

2. Burrows, M. 2006. 用于松耦合分布式系统的 Chubby 锁服务。《操作系统设计与实现研讨会》(OSDI),西雅图,华盛顿州。

3. cAdvisor; https://github.com/google/cadvisor

4. Kubernetes; https://kubernetes.ac.cn/

5. Metz, C. 2015. Google 拥有 20 亿行代码——并且全部集中在一处。Wired (September); http://www.wired.com/2015/09/google-2-billion-lines-codeand-one-place/

6. Schwarzkopf, M., Konwinski, A., Abd-el-Malek, M., Wilkes, J. 2013. Omega:适用于大型计算集群的灵活、可扩展的调度器。《欧洲计算机系统会议》(EuroSys),捷克共和国布拉格。

7. Verma, A., Pedrosa, L., Korupolu, M. R., Oppenheimer, D., Tune, E., Wilkes, J. 2015. Google 使用 Borg 进行大规模集群管理。《欧洲计算机系统会议》(EuroSys),法国波尔多。

Brendan Burns (@brendandburns) 是 Google 的一名软件工程师,他在 Google 与人共同创立了 Kubernetes 项目。他于 2007 年在马萨诸塞大学阿默斯特分校获得博士学位。 在从事 Kubernetes 和云方面的工作之前,他从事 Google 网络搜索基础设施的低延迟索引工作。

Brian Grant 是 Google 的一名软件工程师。他之前曾是 Borg 的技术主管和 Omega 项目的创始人,现在是 Kubernetes 的设计主管。

David Oppenheimer 是 Google 的一名软件工程师,也是 Kubernetes 项目的技术主管。他于 2005 年在加州大学伯克利分校获得博士学位,并于 2007 年加入 Google,在加入 Kubernetes 之前,他曾担任 Borg 和 Omega 集群管理系统的技术主管。

Eric Brewer 是 Google 的基础设施副总裁,也是加州大学伯克利分校的教授,他在那里率先开发了可扩展的服务器和弹性基础设施。

John Wilkes 自 2008 年以来一直在 Google 从事集群管理和基础设施服务方面的工作。在此之前,他曾在惠普实验室工作,并于 2002 年成为惠普和 会士。他对分布式系统的许多方面都非常感兴趣,但一个反复出现的主题是允许系统自我管理的技术。在他的业余时间,他仍然固执地尝试学习如何吹制玻璃。

Copyright © 2016 。保留所有权利。

acmqueue

最初发表于 Queue 杂志第 14 卷,第 1 期——
数字图书馆中评论这篇文章





更多相关文章

Rishiyur S. Nikhil - 硬件系统设计中的抽象
软件工程的历史是不断发展抽象机制以应对日益增长的复杂性的历史。然而,硬件设计并没有那么先进。例如,两种最常用的 HDL 可以追溯到 20 世纪 80 年代。在类型、封装和参数化等结构抽象方面,标准的更新落后于现代编程语言。它们的行为语义甚至更加落后。它们是根据在单处理器冯·诺依曼机上运行的事件驱动模拟器指定的。


John R. Mashey - 通往 64 位的漫长道路
莎士比亚的话语常常涵盖超出他最狂野梦想的情境。即使人们提前计划,辛勤工作和麻烦也伴随着重大的计算转型。“为了校准“今天的明日遗产”,我们应该研究“昨天的明日遗产”。明天的许多软件仍然将由数十年前的决策驱动。过去的决策会产生意想不到的副作用,这些副作用会持续数十年,并且难以撤销。





© 保留所有权利。

© . All rights reserved.