本片文章翻译自google员工的博文:
https://queue.acm.org/detail.cfm?id=2898444
文章深入探讨了 Google 在过去十年中设计和实施三代容器管理系统的经验与洞见。尽管文章发表于七年前,但其内容依然具有极高的时效性和参考价值。文章详细阐述了这些系统的演变过程,为我们揭示了当前广泛使用的 Kubernetes(k8s)平台背后的架构理念、功能特性以及设计原则的起源。
通过这篇文章,我们可以深刻理解 Kubernetes 的设计思想,包括其架构的合理性、功能的实用性以及设计的前瞻性。文章不仅回顾了容器技术早期的发展,还对现代容器编排技术的基石进行了深入分析,帮助我们认识到 Kubernetes 之所以成为云原生技术领域的事实标准,是因为它汲取了 Google 及其他贡献者在大规模生产环境中多年积累的宝贵经验。
google三代容器管理系统
尽管对软件容器的广泛兴趣是一个相对较新的现象,但在 Google,我们已经大规模管理 Linux 容器十多年了,并在那时构建了三个不同的容器管理系统。每个系统都深受其前身的影响,尽管它们是出于不同的原因而开发的。本文介绍了我们从开发和运营它们中学到的经验教训。
▌Borg
Google 开发的第一个统一容器管理系统是我们内部称为 Borg 的系统。它旨在管理长时间运行的服务和批处理作业,这些作业以前由两个独立的系统处理:
- Babysitter
- Global Work Queue
后者的架构强烈影响了 Borg,但专注于批处理作业;两者都早于Linux cgroup。Borg 在这两类应用程序之间共享机器,作为提高资源利用率从而降低成本的一种方式。这种共享之所以成为可能,是因为Linux 内核中新出现的容器技术(事实上,Google 向 Linux 内核贡献了大部分 Linux 容器技术代码),从而可以更好地隔离面延迟敏感型应用和 CPU 密集型批处理任务。
随着越来越多的应用程序被开发出来在 Borg 上运行,我们的应用程序和基础设施团队为其开发了广泛的工具和服务生态系统。这些系统提供了:
配置和更新作业的机制;
预测资源需求;
动态地将配置文件推送到正在运行的作业;
服务发现和负载均衡;
自动扩缩容;
机器生命周期管理;
配额管理;
以及更多。
这个生态系统的开发是由 Google 内部不同团队的需求驱动的,其结果是产生了一些异构的、临时的系统集合,Borg 的用户必须使用几种不同的配置语言和流程来配置这些系统并与之交互。由于其规模、功能广泛性和极高的稳健性,Borg 仍然是 Google 内部主要的容器管理系统。
▌Omega
Omega 是 Borg 的后代,致力于改进 Borg 生态系统的软件工程。它应用了许多在 Borg 中被证明是成功的模式,但它是从头开始构建的,以拥有更一致、更有原则的架构。Omega 将集群的状态存储在基于 Paxos 的集中式面向事务的存储中,该存储由集群控制平面的不同部分(例如调度程序)访问,使用乐观并发控制来处理偶尔的冲突。这种解耦允许 Borgmaster 的功能被分解为充当对等体的单独组件,而不是通过单一的集中式主控来汇集每个更改。Omega 的许多创新(包括多个调度程序)已被纳入 Borg 中。
▌Kubernetes
谷歌开发的第三个容器管理系统是 Kubernetes。它是在外部开发人员对 Linux 容器越来越感兴趣的背景下构思和开发的,而 Google 也开发了一项不断增长的销售公共云基础设施的业务。
Kubernetes 是开源的——与 Borg 和 Omega 形成鲜明对比,它们是纯粹作为 Google 内部系统开发的。
与 Omega 一样,Kubernetes 的核心也是一个共享持久数据仓库(store), 几个组件会监听这个 store 里的 object 变化;
与 Omega 不同,Omega 将存储直接暴露给受信任的控制平面组件,而 Kubernetes 中的状态只能通过特定于域的 REST API 进行访问,该 API 应用更高级别的版本控制、验证、语义和策略,以支持多种不同类型的客户端;
更重要的是,Kubernetes 的开发更加注重开发人员编写在集群中运行的应用程序的体验:其主要设计目标是使复杂的分布式系统的部署和管理变得容易,同时仍然受益于容器带来的更高的利用率。
下面我们将介绍 Google 在从 Borg 到 Kubernetes 的历程中获得的一些知识和经验教训。
底层 Linux 内核容器技术
容器管理系统主要负责上层的管理和调度任务,而整个容器生态的基石,是 Linux 内核提供的容器技术。
▌从 chroot 到 cgroups 的演进
- chroot 的起步:最初,容器技术仅通过 chroot 实现了文件系统根目录的隔离。
- FreeBSD jails 的扩展:FreeBSD jails 在此基础上进一步扩展,实现了包括 PID 命名空间在内的更多隔离功能。
- Solaris 的创新:Solaris 对容器技术进行了深入的研究和创新,推动了其发展。
- cgroups 的集大成:Linux 的控制组(cgroups)综合了前述技术,成为了 Linux 内核中用于资源管理和进程隔离的基石。cgroups 子系统至今仍在积极开发中,是容器技术发展的核心。
▌资源隔离
容器提供的资源隔离使 Google 的资源利用率显着高于行业标准。例如,Borg 使用容器将批处理作业与延迟敏感、面向用户的作业放在同一物理机器上。面向用户的作业保留比通常需要更多的资源,使它们能够处理负载峰值和故障转移,并且可以回收这些大部分未使用的资源来运行批处理作业。
容器提供了使这成为可能的资源管理工具,以及强大的内核级资源隔离以防止进程相互干扰。我们通过在 Borg 开发的同时增强 Linux 容器来实现这一目标。
不过,这种隔离并不完美:容器无法防止对操作系统内核不管理的资源的干扰,例如三级缓存(L3 cache)、 内存带宽,并且容器需要额外的安全层(例如虚拟机)的支持,以防范公有云上各种各样的恶意攻击。
▌容器镜像
现代容器不仅仅是一种隔离机制:它还包括一个映像,即构成在容器内运行的应用程序的文件。在 Google 内部,MPM(Midas 包管理器)用于构建和部署容器映像。在 Docker 守护进程和 Docker 镜像注册中心之间也可以找到隔离机制和 MPM 包之间相同的共生关系。在本文的其余部分中,我们使用容器一词来涵盖这两个方面:运行时隔离和映像。
面向应用的基础设施
随着时间的推移,我们发现容器化的好处不仅仅是实现更高水平的利用率。容器化将数据中心从面向机器转变为面向应用。本节讨论两个示例:
• 容器封装了应用程序环境,从应用程序开发人员和部署基础设施中抽象出机器和操作系统的许多细节。
• 由于精心设计的容器和容器映像的范围仅限于单个应用程序,因此管理容器意味着管理应用程序而不是机器。管理 API 从面向机器到面向应用程序的转变极大地改进了应用程序部署和自省。
▌应用环境
内核中cgroup、chroot和命名空间设施的最初目的是保护应用程序免受嘈杂、嘈杂和混乱的邻居的侵害。将这些与容器映像相结合,创建了一个抽象,该抽象也将应用程序与它们运行的(异构)操作系统隔离开来。这种映像和操作系统的脱钩使得在开发和生产中提供相同的部署环境成为可能,这反过来又提高了部署可靠性,并通过减少不一致和摩擦来加快开发。
使这种抽象工作的关键是有一个密封的容器映像,该映像可以将应用程序的几乎所有依赖项封装到一个可以部署到容器中的软件包中。如果这样做正确,唯一的本地外部依赖项将位于Linux内核系统调用接口上。虽然这种有限的接口极大地提高了图像的可移植性,但它并不完美:应用程序仍然可以暴露在操作系统界面中流失,特别是在套接字选项、/proc和ioctl调用的参数暴露的宽表面区域中。我们希望,开放容器倡议(https://www.opencontainers.org/)等持续努力将进一步澄清容器抽象的表面积。
尽管如此,容器提供的隔离和依赖性最小化在谷歌被证明非常有效,容器已成为谷歌基础设施支持的唯一可运行实体。一个后果是,谷歌在任何时候都在其整个机器上部署了少量操作系统版本,它只需要一小部分人员来维护它们并推出新版本。
有很多方法可以实现这些密封的图像。在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导出的自定义指标进行扩展,如前所述。这些数据可以开发自动缩放器或cAdvisor3等通用工具,这些工具可以在不了解每个应用程序细节的情况下记录和使用指标。由于容器是应用程序,因此无需(去)来自物理或虚拟机内运行的多个应用程序的(去)多路复用信号。这更简单、更强大,并允许更细粒度地报告和控制指标和日志。将此与必须ssh到机器中才能运行top。虽然开发人员有可能将ssh放入他们的容器中,但他们很少需要。
监控只是一个例子。面向应用程序的转变对整个管理基础设施产生了连锁反应。我们的负载均衡器不会平衡跨机器的流量;它们会跨应用程序实例平衡。日志由应用程序而不是机器键入,因此它们可以很容易地跨实例收集和聚合,而不会受到多个应用程序或系统操作的污染。我们可以检测应用程序故障,并更轻松地归因于故障原因,而无需将它们从机器级信号中解开。从根本上说,由于容器管理器管理的实例的身份与应用程序开发人员期望的实例的身份完全一致,因此构建、管理和调试应用程序更容易。
最后,尽管到目前为止,我们专注于应用程序与容器的1:1,但实际上我们使用嵌套容器,这些容器在同一台机器上共同安排:最外层的容器提供资源池;内部容器提供部署隔离。在Borg中,最外层的容器被称为资源分配或分配;在Kubernetes中,它被称为pod。Borg还允许顶级应用程序容器在allocs外部运行;这造成了很大的不便,因此Kubernetes将事情规范化,并始终在顶级pod内运行应用程序容器,即使pod包含单个容器。
一个常见的使用模式是pod保存复杂应用程序的实例。应用程序的主要部分位于其中一个子容器中,其他容器运行支持功能,如日志旋转或单击日志卸载到分布式文件系统。与将功能合并到单个二进制文件相比,这使得不同的团队开发不同的功能变得容易,并提高了鲁棒性(即使主应用程序被夹起,卸载也会继续),可组合性(很容易添加新的小型支持服务,因为它在自己的容器提供的私有执行环境中运行)和细粒度资源隔离(每个都在自己的资源中运行,因此日志系统不能饿死主应用程序,反之亦然)。
▌编排是开始,不是结束
最初的Borg系统使得在共享机器上运行不同的工作负载成为可能,以提高资源利用率。然而,Borg生态系统中支持服务的快速发展表明,容器管理本身只是开发和管理可靠分布式系统环境的开始。在Borg上和周围构建了许多不同的系统,以改进Borg提供的基本容器管理服务。以下部分列表给出了它们的范围和种类:
•命名和服务发现(Borg名称服务或BNS)。
•主选举,使用Chubby.2
•应用程序感知负载平衡。
•水平(实例数量)和垂直(实例大小)自动缩放。
• 部署工具,用于管理新二进制文件和配置数据的仔细部署。
•工作流工具(例如,允许运行阶段之间相互依赖的多作业分析管道)。
•监控工具来收集有关容器的信息,聚合它,在仪表板上显示它,并使用它来触发警报。
这些服务是有机构建的,以解决应用程序团队遇到的问题。成功的被采纳,被广泛采用,并使其他开发人员的生活更轻松。不幸的是,这些工具通常选择独特的API、约定(如文件位置)和Borg集成的深度。一个不想要的副作用是增加了在Borg生态系统中部署应用程序的复杂性。
Kubernetes试图通过对其API采用一致的方法来避免这种增加的复杂性。例如,每个Kubernetes对象的描述中都有三个基本字段:Object Metadata、Specification(或Spec)和Status。
Object Metadata对系统中的所有对象都是相同的;它包含对象名称、UID(唯一标识符)、对象版本号(用于乐观并发控制)和标签(键值对,见下文)等信息。Spec和Status的内容因对象类型而异,但其概念不为:Spec用于描述对象的所需状态,而Status则提供有关对象当前状态的只读信息。
这种统一的API提供了许多好处。具体来说,学习系统更简单:类似的信息适用于所有对象。此外,编写跨所有对象的通用工具更简单,这反过来又可以开发一致的用户体验。从Borg和Omega学习,Kubernetes是由一组可组合的构建块构建的,这些构建块可以很容易地被用户扩展。通用的API和对象元数据结构使这变得容易得多。例如,pod API可供人员、内部Kubernetes组件和外部自动化工具使用。为了进一步提高这种一致性,Kubernetes正在扩展,使用户能够动态添加自己的API,以及Kubernetes的核心功能。
一致性也通过在Kubernetes API中解耦来实现。API组件之间的关注点分离意味着更高级别的服务都共享相同的共同基本构建块。一个很好的例子是Kubernetes副本控制器与其水平自动缩放系统之间的分离。复制控制器确保给定角色(例如“前端”)存在所需数量的pod。反过来,自动缩放器依赖于此功能,只需调整所需的豆荚数量,而不必担心这些豆荚是如何创建或删除的。自动缩放器的实现可以专注于需求和使用预测,而忽略如何实施其决策的细节。
脱钩确保了多个相关但不同的组件具有相似的外观和感觉。例如,Kubernetes有三种不同形式的复制pod:
- ReplicationController:run-forever复制容器(例如,Web服务器)。
- DaemonSet:确保集群中每个节点上的单个实例(例如,日志代理)。
- Job:一个运行到完成的控制器,知道如何从头到尾运行(可能并行)批处理作业。
无论策略上的差异如何,这三个控制器都依赖于公共pod对象来指定他们希望运行的容器。
一致性也通过不同Kubernetes组件的通用设计模式来实现。调节控制器循环的想法在整个Borg、Omega和Kubernetes中共享,以提高系统的弹性:它将所需状态(例如,标签选择器查询应匹配多少个pod)与观察到的状态(它可以找到的此类pod的数量)进行比较,并采取行动收敛观察到的状态和所需状态。由于所有操作都基于观察而不是状态图,因此对账循环对故障和扰动是可靠的:当控制器失败或重新启动时,它只是从中断的地方继续。
Kubernetes作为微服务和小型控制循环的组合设计是通过编排进行控制的一个例子——通过结合协作的独立自主实体的效果来实现所需的紧急行为。与集中编排系统形成鲜明对比,这是一个有意识的设计选择,后者起初可能更容易构建,但随着时间的推移往往会变得脆弱和僵化,特别是在存在意外错误或状态变化的情况下。
避坑指南
这里列举一些经验教训,希望大家要犯错也是去犯新错,而不是重复踩我们已经踩过的坑。
▌ 创建 Pod 时应该分配唯一 IP,而不是唯一端口(port)
在Borg机器上运行的所有容器共享主机的IP地址,因此Borg在调度过程中分配容器唯一的端口号。容器在移动到新机器时,以及(有时)在同一台机器上重新启动时,将获得新的端口号。这意味着DNS(域名系统)等传统网络服务必须被自制版本取代;服务客户端不知道预先分配给服务的端口号,必须被告知;端口号不能嵌入URL中,需要基于名称的重定向机制;依赖简单IP地址的工具需要重写以处理IP:port对。
从我们对Borg的经验中学习,我们决定Kubernetes将为每个pod分配一个IP地址,从而使网络身份(IP地址)与应用程序身份保持一致。这使得在Kubernetes上运行现成软件变得更加容易:应用程序可以自由使用静态的知名端口(例如,HTTP流量为80端口),现有的熟悉工具可用于网络分割、带宽节流和管理等。所有流行的云平台都提供网络底层,支持每pod的IP;在裸机上,可以使用SDN(软件定义网络)覆盖或配置L3路由来处理每台机器的多个IP。
▌容器索引不要用数字 index,用 labels
如果您允许用户轻松创建容器,他们往往会创建很多容器,并且很快就需要一种对它们进行分组和组织的方法。Borg提供作业来分组相同任务(其容器的名称)。作业是一个或多个相同任务的紧凑向量,从零开始按顺序索引。这提供了很大的力量,简单明了,但随着时间的推移,我们开始后悔它的僵化。例如,当一个任务死亡并必须在另一台机器上重新启动时,任务向量中的同一插槽必须执行双重任务:识别新副本并指向旧副本,以防需要调试。当矢量中间的任务退出时,矢量最终会出现孔。该矢量使得很难支持在Borg上方的层中跨越多个集群的工作。Borg的作业更新语义(在进行滚动升级时通常按索引顺序重新启动任务)和应用程序使用任务索引(例如,跨任务对数据集进行分片或分区)之间也存在阴险的意外的交互:如果应用程序使用基于任务索引的范围分片,Borg的重新启动策略可能会导致数据不可用,因为它会删除相邻的任务。Borg也没有提供向作业添加与应用程序相关的元数据的简单方法,例如角色(例如“前端”)或推出状态(例如“金丝雀”),因此人们将此信息编码为使用正则表达式解码的作业名称。
相比之下,Kubernetes主要使用标签来识别容器组。标签是包含有助于识别对象的信息的键/值对。pod可能有标签role=frontend和stage=production,表明此容器正在作为生产前端实例。标签可以由自动化工具或用户动态添加、删除和修改,不同的团队可以在很大程度上独立管理自己的标签。对象集由标签选择器定义(例如,stage==production && role==frontend)。集合可以重叠,一个对象可以在多个集合中,因此标签本质上比显式对象列表或简单的静态属性更灵活。由于集合是由动态查询定义的,因此可以随时创建新集合。标签选择器是Kubernetes中的分组机制,并定义了可以跨越多个实体的所有管理操作的范围。
即使在了解集合中任务身份的情况下(例如,对于静态角色分配和工作分区或分片),也可以使用适当的每pod标签来重现任务索引的效果,尽管提供此类标签是应用程序(或Kubernetes外部的一些其他管理系统)的责任。标签和标签选择器提供了一个通用机制,可以两全其美。
▌Ownership 设计要格外小心
在Borg,任务不是独立于工作而存在的。创建作业会创建其任务;这些任务永远与该特定作业相关联,删除该作业会删除任务。这很方便,但它有一个主要缺点:因为只有一个分组机制,它需要处理所有用例。例如,作业必须存储仅对服务或批处理作业有意义的参数,但不能同时存储两者,当作业抽象不处理用例时,用户必须开发变通方法(例如,将单个pod复制到集群中所有节点的DaemonSet)。
在Kubernetes中,复制控制器等pod生命周期管理组件确定他们负责使用标签选择器的pod,因此多个控制器可能认为他们对单个pod有管辖权。通过适当的配置选择来防止此类冲突很重要。但标签的灵活性具有补偿优势——例如,控制器和吊舱的分离意味着可以“孤立”和“采用”容器。考虑一种负载平衡服务,它使用标签选择器来识别要发送流量的pod集。如果其中一个pod开始行为不端,则可以通过删除导致其成为Kubernetes服务负载平衡器目标的一个或多个标签来隔离该pod与服务请求的隔离。吊舱不再为流量服务,但它将保持正常,并且可以原地调试。与此同时,管理实现服务的pod的复制控制器会自动为行为不端的pod创建一个替换pod。
▌不要暴露原始状态
Borg、Omega和Kubernetes之间的一个关键区别在于他们的API架构。Borgmaster是一个整体组件,知道每个API操作的语义。它包含集群管理逻辑,如作业、任务和机器的状态机;它运行基于Paxos的复制存储系统,用于记录主服务器的状态。相比之下,Omega没有集中式组件,除了存储,它只是保存被动状态信息,并强制执行乐观并发控制:所有逻辑和语义都被推送到存储的客户端,这些客户端直接读取和写入存储内容。在实践中,每个Omega组件都使用相同的客户端库用于存储,该库对数据结构进行打包/解压,重试并强制执行语义一致性。
Kubernetes选择了一个中间地带,提供Omega组件化架构的灵活性和可扩展性,同时强制执行系统范围的不变量、策略和数据转换。它通过一个集中的API服务器强制所有存储访问来做到这一点,该服务器隐藏存储实现的细节,并提供对象验证、默认和版本管理服务。与Omega一样,客户端组件彼此解耦,可以独立进化或替换(这在开源环境中尤为重要),但集中化使执行通用语义、不变量和策略变得容易。
开放问题讨论
虽然我们已经了十几年的大规模容器管理经验,但仍然有些问题还没有很好的解决办法。本节介绍几个供讨论,集思广益。
▌应用配置管理
在我们面临的所有问题中,最聪明的脑力、墨水和代码都与管理配置有关——提供给应用程序的一组值,而不是硬编码到它们中。事实上,我们本可以把整篇文章都放在这个主题上,但仍然有更多话要说。以下是一些亮点。
首先,应用程序配置成为实现容器管理系统(尚未)做的所有事情的包罗万象的位置。在Borg的历史中,这包括:
•减少锅炉板(例如,默认适合工作负载的任务重新启动策略,例如服务或批处理作业)。
•调整和验证应用程序参数和命令行标志。
•实施缺少API抽象(如软件包(图像)管理)的变通方法。
•应用程序配置模板库。
• 发布管理工具。
•图像版本规范。
为了应对此类需求,配置管理系统倾向于发明一种特定于域的配置语言,该语言(最终)使图灵完成,从对配置中的数据进行计算的愿望开始(例如,调整内存量,使服务器作为服务中碎片数量的函数)。结果是人们试图通过消除应用程序源代码中的硬编码参数来避免这种难以捉摸的“配置就是代码”。它不会降低操作复杂性,也不会使配置更容易调试或更改;它只是将计算从真正的编程语言移动到特定于域的语言,该语言通常具有较弱的开发工具(例如调试器、单元测试框架等)。
我们认为最有效的方法是接受这种需求,接受编程配置的必然性,并在计算和数据之间保持干净的分离。表示数据的语言应该是简单的、仅限数据的格式,如JSON或YAML,这些数据的编程修改应该用真正的编程语言完成,其中有理解良好的语义和良好的工具。有趣的是,在前端开发的不同领域可以看到计算和数据的这种分离,Angular等框架在标记(数据)和JavaScript(计算)世界之间保持着清晰的分离。
▌依赖管理
站立服务通常也意味着站立一系列相关服务(监控、存储、CI/CD等)。如果一个应用程序依赖其他应用程序,如果这些依赖项(以及它们可能具有的任何传递依赖项)由集群管理系统自动实例化,那不是很好吗?
使事情复杂化的是,实例化依赖项很少像开始新副本那样简单——例如,它可能需要注册为现有服务(例如,Bigtable作为服务)的消费者,并在这些传递依赖项之间传递身份验证、授权和计费信息。然而,几乎没有一个系统捕获、维护或公开这种依赖信息,因此即使在基础设施层面实现常见情况的自动化也几乎是不可能的。启动新应用程序对用户来说仍然很复杂,使开发人员更难构建新服务,并且通常导致最新的最佳实践没有得到遵循,这会影响结果服务的可靠性。
一个标准问题是,如果手动提供依赖信息,则很难保持最新状态,同时尝试自动确定它(例如,通过跟踪访问)无法捕获理解结果所需的语义信息。(该访问权限必须转到该实例,还是任何实例都足够了?)取得进展的一种可能方法是要求应用程序列举其所依赖的服务,并让基础设施拒绝允许访问任何其他服务。(我们这样做是为了构建系统中的编译器导入。1)激励措施是使基础设施能够做有用的事情作为回报,例如自动设置、身份验证和连接。
不幸的是,表达、分析和使用系统依赖性的系统感知复杂性太高,因此它们尚未被添加到主流容器管理系统中。我们仍然希望Kubernetes可以构建此类工具的平台,但这样做仍然是一个公开的挑战。
总结
十年来构建容器管理系统的经验教会了我们很多东西,我们已将其中的许多经验教训嵌入到 Kubernetes(Google 最新的容器管理系统)中。其目标是构建容器的功能,以显着提高程序员的工作效率并简化手动和自动系统管理。我们希望您能与我们一起扩展和改进它。
参考
- Bazel: {fast, correct}—choose two; http://bazel.io.
- Burrows, M. 2006. The Chubby lock service for loosely coupled distributed systems. Symposium on Operating System Design and Implementation (OSDI), Seattle, WA.
- cAdvisor; https://github.com/google/cadvisor.
- Kubernetes; http://kubernetes.io/.
- Metz, C. 2015. Google is 2 billion lines of code—and it’s all in one place. Wired (September); http://www.wired.com/2015/09/google-2-billion-lines-codeand-one-place/.
- Schwarzkopf, M., Konwinski, A., Abd-el-Malek, M., Wilkes, J. 2013. Omega: flexible, scalable schedulers for large compute clusters. European Conference on Computer Systems (EuroSys), Prague, Czech Republic.
- Verma, A., Pedrosa, L., Korupolu, M. R., Oppenheimer, D., Tune, E., Wilkes, J. 2015. Large-scale cluster management at Google with Borg. European Conference on Computer Systems (EuroSys), Bordeaux, France.
我正在参与2024腾讯技术创作特训营最新征文,快来和我瓜分大奖!