Pinterest平台团队开发实现了名为PinCompute的高性能通用计算平台,支持Pinterest的大量异构用例和服务。本文介绍了团队在开发这一平台过程中的经验和实践,对于其他平台团队来说,具有很好的参考意义。原文: PinCompute: A Kubernetes Backed General Purpose Compute Platform for Pinterest
概述
现代计算平台是加速创新和高效运行应用程序的基础。我们正在Pinterest发展自己的计算平台,为90%的用例提供以应用为中心、完全托管的计算API,通过平台敏捷性、扩缩容性以及更低的系统更新成本来加速创新,并在基于Kubernetes的计算平台上运行用户应用程序来提高效率。我们将这个下一代计算平台称为PinCompute,愿景是让PinCompute运行Pinterest最关键的应用程序和服务。
PinCompute与平台即服务(PaaS)云计算模型保持一致,抽象了管理基础设施和Kubernetes的繁重工作,使用户能够专注于其应用。PinCompute利用云原生原理(包括容器、微服务和服务网格)改进了Pinterest架构,通过提供和管理不可变基础设施、操作系统升级和Graviton实例,降低了维护系统更新的成本,并通过将增强的调度功能应用于大型多租户Kubernetes集群(包括超额订阅、bin压缩、资源分层)来节省成本。
本文将讨论PinCompute的原语、架构、控制平面和数据平面功能,并展示PinCompute为Pinterest的创新和效率所带来的价值。
架构
PinCompute是建立在Kubernetes之上的区域性平台即服务(PaaS),由一个主机Kubernetes集群(主机集群)和多个成员Kubernetes集群(成员集群)组成。主机集群运行区域联合控制平面,跟踪该区域的工作负载。成员集群是分区的,用于实际的工作负载运行。每个区域可以有多个成员集群,严格遵循云提供商定义的故障域,明确了平台的故障隔离和运行边界,确保可用性以及控制爆炸半径。所有成员集群在控制平面和数据平面功能之间共享标准的Kubernetes设置,并且支持异构功能(例如不同的工作负载类型和硬件选择)。PinCompute支持多租户,运行不同团队和组织的各种类型的工作负载共享同一个平台,因此需要提供必要的隔离,以确保可以在租户之间安全有效的共享。
用户通过Compute API访问平台,对其工作负载执行操作。我们利用自定义资源(CR)来定义平台支持的工作负载类型,提供一系列工作负载编排功能,以各种形式支持批处理作业和长时间运行的服务。当工作负载被提交到平台时,首先通过主机集群的Kubernetes API进行持久化。然后,联邦控制平面将开始执行区域级别所需的工作负载管理任务,包括配额执行、工作负载分片和成员集群选择。然后,将工作负载分片分发到成员集群执行。成员集群控制平面由负责编排不同类型工作负载的内部和开源operator组合而成。联邦控制平面还从相应的成员集群中收集工作负载执行状态并聚合,以便通过PinCompute API消费。
PinCompute原语
PinCompute原语服务于Pinterest上的异构工作负载,类型包括long running(长时间运行)、run-to-finish(从运行到完成)、ML training(机器学习训练)、scheduled run(运行计划)等。这些用例本质上分为三类:(1)通用计算和服务部署,(2)运行到完成作业,以及(3)基础设施服务。Pinterest运行到完成任务和基础设施服务由现有的Kubernetes原生资源和Pinterest特定资源支持[1]。基于我们关于如何定义简单、直观和可扩展的计算原语的最新想法,PinCompute引入了一组用于通用计算和服务部署的新原语,包括PinPod、PinApp和PinScaler。
PinPod是Pinterest通用计算的基本构建块。像原生Kubernetes Pod一样,PinPod继承了Pod作为基础构建块的本质,同时提供了额外的特定于Pinterest的功能,包括容器更新、管理边车、数据持久化、故障转移等功能,这些功能使PinPod可以轻松用作Pinterest各种生产场景下的构建块。PinPod的目的是在应用程序和基础设施团队之间建立明确的界限,同时仍然保持运行容器的轻量级性质。它解决了许多痛点,例如,每个容器更新可以加速应用程序滚动更新,减少资源消耗,消除在基础设施边车升级期间对用户容器的干扰。
PinApp是一个抽象,提供了在Pinterest上运行和管理长期运行应用程序的最佳方式。通过利用PinPod作为应用程序副本,PinApp继承了PinPod的所有集成和软件交付最佳实践。多亏了联邦控制平面,PinApp提供了一组内置的编排功能,以满足常见的分布式应用程序管理需求,包括基于区域的部署以及区域容量平衡。PinApp支持Kubernetes原生原语(如Deployments和ReplicaSets)提供的功能,也支持对部署语义的扩展,以满足业务需求并增强可管理性。
PinScaler是一个抽象,支持应用的自动缩放。它集成了Pinterest的本地指标仪表板Statsboard,允许用户配置应用级指标与所需阈值,以触发缩放以及缩放保护措施,如冷却窗口和复本的最小/最大限制。PinScaler支持简单的CPU和内存指标缩放、计划缩放和自定义指标缩放,以支持各种生产场景。
回到更大的图景,PinCompute利用下一代原语(PinPod, PinApp, PinScaler)、本地Kubernetes和开源社区的构建块,以及与联邦架构的深度集成,提供以下用例:
(1)通用计算和服务部署: 由PinCompute的新原语类型处理。PinApp和PinScaler帮助长时间运行的无状态服务快速部署和扩展。PinPod作为通用计算单元,目前为Pinterest开发人员提供Jupyter Notebook。
(2)运行到完成作业: PinterestJobSet利用作业为用户提供一种执行运行到完成、无框架并行处理的机制; PinterestTrainingJob基于Kubeflow社区的TFJob和PyTorchJob[2]进行分布式训练; PinterestCronJob基于CronJob来执行基于cron表达式的调度任务。
(3)基础设施服务: 基于DaemonSet的PinterestDaemon,以及专有的PinterestSideCar支持不同的基础设施服务部署模式。能够被多个租户共享的组件(例如,日志代理、指标代理、配置部署代理)被部署为PinterestDaemons,确保每个节点有一个副本,被该节点上所有pod所共享。不能共享的服务将基于PinterestSideCar部署在用户pod中的sidecar容器中。
PinCompute原语使Pinterest开发人员能够托管基础设施管理以及相关的故障排除和运维问题,从而能够专注于不断发展的业务逻辑,以更好地为用户服务。
访问PinCompute
用户通过PinCompute的平台接口访问PinCompute原语,该平台接口由API层、API客户端层和支持这些API的底层服务/存储组成。
PinCompute API
PinCompute API是用户访问平台的网关,提供了三组API: 工作负载API、运维API和洞察API。工作负载API包含在计算工作负载上执行CRUD操作的方法,调试API(提供流日志或打开容器shell等机制),以对实时工作负载进行故障排除,洞察API为用户提供运行时信息,如应用程序状态变更和系统内部事件,以帮助用户了解现在和过去工作负载的状态。
为什么使用PinCompute API
在原始Kubernetes API之上引入PinCompute API有很多好处。首先,当PinCompute联合许多Kubernetes集群时,PinCompute API将用户请求与联合集成,并聚合跨集群信息,形成计算平台的整体用户端视图。其次,PinCompute API高效访问Kubernetes API,其包含缓存层可以有效提供读API,从而从Kubernetes API服务器中卸载昂贵的列表和查询API调用。最后,作为网关服务,在访问不同的PinCompute后端服务(如Kubernetes、节点服务、洞察服务、项目治理服务等)时,PinCompute API确保了统一的用户体验。
整合Pinterest基础设施
PinCompute API层结合了Pinterest的基础设施功能,如速率限制和安全实践,以简化Kubernetes API的使用,并为API消费者和开发人员提供稳定的接口。PinCompute API利用流量团队的速率限制边车和可重用的Pinterest组件,实现了速率限制机制,以确保公平使用资源。PinCompute API还与Pinterest的专有安全原语完全集成,以确保身份验证、授权和审计机制遵循现有路径。集成这些能力使我们能够为Pinterest开发人员提供API调用和API资源级别粒度的统一访问控制体验,对于确保PinCompute API的可靠性、安全性和兼容性至关重要。
API语义增强
PinCompute API在Kubernetes API之上提供了增强的API语义,以改善用户体验。其中一个重要改进是以简化方式呈现了原始Kubernetes数据模型,在其中只包含与Pinterest构建软件相关的信息,这不仅减少了专注于构建高级应用程序逻辑的开发人员的基础设施学习曲线,还提高了API服务的数据效率。例如,删除托管字段将PinCompute API调用减少了多达50%的数据。我们还以一种更具描述性的方式设计了API,用于暂停、停止、重新启动容器等用例,这些用例在许多场景中都是直观且易于使用的。PinCompute提供OpenAPI文档和自动生成的客户端、文档和SDK,帮助用户在PinCompute上自助构建应用程序。
PinCompute SDK
为客户构建SDK,从而标准化对PinCompute的访问,是我们的战略投资方向。通过SDK,我们能够将最佳实践(如错误处理、带回退的重试、日志记录和度量)封装为可重用构建块,确保这些最佳实践始终应用于客户端。我们还发布和管理版本化SDK,并提供如何基于SDK开发的明确指导。我们与用户密切合作,确保采用最新版本的SDK,以优化与PinCompute的交互。
在PinCompute中管理资源
资源模型
PinCompute支持三种资源层: 预留(Reserved)、按需(OnDemand)和可抢占(Preemptible),用户为每一层定义其项目的资源配额。预留级配额由固定大小的资源池和专用工作负载调度队列支持,保证调度吞吐量和容量可用性。按需级配额利用全局共享和动态大小的资源池,以先到先服务的方式为工作负载提供服务。抢占层是为了有机会利用未被使用的预留层和按需层的资源,这些资源将在相应的层需要时被回收。PinCompute集群还配备了一个由活跃但未被使用的资源组成的缓冲空间,以适应突发的工作负载。下图说明了PinCompute的资源模型。
调度架构
PinCompute由两层调度机制组成,以确保有效调度工作负载。集群级调度在PinCompute的区域联邦控制平面上执行,集群级调度接受工作负载,选择一个或多个成员集群执行。在集群级调度期间,工作负载首先通过一组过滤器,过滤掉不适合的集群,然后通过一组计分机制对候选集群进行排序。集群级调度在保证高层次布局策略和资源需求得到满足的同时,还考虑了负载分布、集群健康等因素进行区域优化。节点级调度发生在成员集群内部,其中工作负载由相应的operator转换为pod。创建Pod之后,使用Pod调度器将Pod放置到节点上执行。PinCompute的Pod调度器利用Kubernetes的调度器框架,结合上游和专有插件,以确保调度器支持开源Kubernetes中可用的所有功能,但同时针对PinCompute的特定需求进行了优化。
PinCompute成本效率
成本效率对PinCompute来说至关重要。我们制定了各种方法,在不影响用户体验的情况下成功降低了PinCompute基础设施成本。
我们通过消除不必要的资源预留以及将用户工作负载迁移到跨联邦环境共享的按需资源池来促进多租户共享。我们与主要平台用户合作,以平滑工作负载提交模式,避免请求过多资源。我们还启动了一个平台级计划,将使用的GPU从P4系列实例切换到更具成本效益的替代品(即G5系列)。下图展示了PinCompute GPU成本与容量趋势,我们在支持业务增长的同时成功降低了成本。
展望未来,PinCompute有几个正在进行的项目,以进一步提高成本效率。1)引入可抢占工作负载,鼓励更灵活的共享资源。2)加强平台资源分级和工作负载排队机制,在用户工作负载调度时兼顾公平性和效率,做出更明智的决策。
PinCompute节点运行时
节点架构是一个关键领域,我们在其中投入了大量资金,以确保应用能够在容器化的多租户环境中安全、可靠和高效的运行。
PinCompute的Pod
Pod被设计用于隔离节点上的租户。当Pod启动时,被自动授予独立的网络标识、安全主体和资源隔离边界,这些在Pod的生命周期中是不可变的。
在Pod中定义容器时,用户可以指定两个生命周期选项: 主容器和边车容器。主容器遵循Pod级重启策略,而边车容器则确保在主容器需要运行时可用。此外,用户还可以定义边车和主容器之间的启动、终止顺序。PinCompute中的Pod还支持容器级更新,这样就可以在Pod中应用新定义并重新启动容器,而不需要终止并再次启动Pod。边车容器生命周期和每个容器更新是批处理作业执行可靠性和服务部署效率的关键特性。
PinCompute有专有网络插件来支持各种容器网络需求。主机网络仅用于系统应用。"桥接端口(Bridge Port)"为不需要服务流量的Pod分配节点本地的、不可路由的IP。对于需要服务流量的Pod,提供从共享网络接口分配的可路由IP,或者Pod可以请求"专用ENI"进行完整的网络分段。通过云资源控制平面对ENI、IP分配等网络资源进行整体、高效管理。
PinCompute支持各种卷,包括EmptyDir、EBS和EFS。具体来说,我们有专有日志记录插件,集成了内部日志记录流水线,以确保高效可靠的日志收集。
整合Pinterest基础设施
PinCompute节点包含用户容器和Pinterest的基础设施生态系统之间的关键集成点,即安全性、流量、配置、日志记录和可观察性。这些功能具有与PinCompute正交的独立控制平面,因此不局限于任何"Kubernetes集群"边界。
基础设施功能以三种方式部署: 主机级守护进程、边车容器或双模式。守护进程由节点上运行的所有Pod共享,日志记录、指标和配置分发被部署为守护进程,因为它们不需要依赖Pod的租户,也不需要驻留在Pod中运行的应用程序的关键数据路径中。边车容器在Pod租户中运行,被依赖于Pod租户或需要流量和安全性等性能保证的功能所使用。
用户容器通过文件系统共享与基础设施功能(如日志记录、配置、服务发现)交互,或者通过网络(本地主机或unix域套接字)与流量和度量等功能进行交互。Pod(以及相关的租户定义)确保以安全有效的方式集成各种基础设施功能。
增强可运维性
PinCompute节点拥有专有的节点管理系统,增强了节点的可见性和可运维性,包含节点级探测机制,为节点运行状况提供补充信号,涵盖容器运行时、DNS、设备、各种守护进程等领域。这些信号用作节点就绪判定,以确保新节点只有在所有功能就绪后才可调度,并且在应用运行时期间用于协助自动化和调试。作为节点服务质量(QoS)的一部分,当一个节点被标记为预留工作负载时,可以提供增强的QoS管理,例如配置预下载或容器镜像缓存刷新。节点还公开了诸如容器shell和实时日志流之类的运行时API,以帮助用户分析工作负载故障。
管理PinCompute基础设施
优先级自动化
当涉及到减少人为错误和提高生产力时,自动化具有巨大的投资回报。PinCompute集成了一系列旨在简化日常操作的专有服务。
自动修复
运维人员经常被琐碎的节点运行状况问题所困扰。PinCompute配备了自动修复服务来自我修复这些问题。运行在节点管理器上的运行状况探测器检测节点问题,通过特定的信号注释对其进行标记,这些信号被监控并被解析为动作。然后,补救服务执行锁定或终止等操作。用于检测、监视和修复服务的组件符合解耦和可扩展性原则。此外,还可以执行限速或断路机制,从而为节点健康管理提供系统的方法。
应用程序感知集群轮换
PinCompute升级服务的主要功能是以一种安全、全自动的方式支持Kubernetes集群轮转,同时遵守PinCompute平台SLO、轮转协议和优雅终止的用户协议。在处理集群轮转时,关注的范围包括轮转不同类型节点的顺序、节点的同时轮转、并行或单独轮转的节点以及节点轮转的特定时间。这种担忧源于在PinCompute平台上运行的用户工作负载的多样性。通过PinCompute升级服务,平台运维可以明确指定希望如何进行集群轮转,通过配置定义精心管理的自动进程。
发布PinCompute
验证平台
PinCompute发布流水线由四个阶段组成,每个阶段都是一个单独的联合环境。变更通过阶段性部署,并在发布之前进行验证。端到端测试框架在PinCompute上持续运行,以验证平台的准确性。框架模拟了真正的用户,并作为恒定的金丝雀来监督平台的正确性。
机器镜像(AMI)管理
考虑到用户对硬件系列、可管理性和成本效益的需求,PinCompute选择性的提供有限的节点类型集。负责引导这些节点的AMI分为三类: 通用AMI、专注于机器学习的AMI和可定制AMI。从父AMI和配置继承的概念大大简化了对其的管理。每个AMI都根据类型和版本进行标记,利用升级服务启动自动部署。
面向运维和用户的工具
在PinCompute中,我们为平台用户和运维人员提供了一组工具,以方便的操作平台及其上运行的工作负载。我们构建了一个实时调试系统,为最终用户提供基于UI的容器shell,以便在Pod中进行调试,同时提供流控制台日志和基于文件的日志,以了解应用运行情况。该工具利用专有的节点级API将用户调试与关键控制路径(如Kubernetes API和Kubelet)解耦,并确保故障隔离和可伸缩性。自助服务项目管理和教程还减少了用户加载新项目或调整现有项目属性(如资源配额)的开销。PinCompute的集群管理系统为编辑集群属性提供了交互机制,使得迭代新硬件或调整容量设置变得非常方便。易于使用的工具链提供了高效和可扩展的运维,并随着时间推移大大改善了平台的用户体验。
可伸缩性和SLO
PinCompute旨在支持Pinterest规模的计算需求。可伸缩性是一个复杂的目标,对我们来说,每个PinCompute的Kubernetes集群都被优化到最佳点,即每分钟3000个节点、120K个Pod和1000个Pod变更操作,工作负载端到端启动延迟的P99为25秒。这些可伸缩性目标是由Pinterest上大多数应用程序的需求定义的,并且是平衡多个因素的结果,包括集群大小、工作负载敏捷性、可运维性、爆炸半径和效率。这个目标使每个Kubernetes集群成为整体计算的坚实构建块,并且PinCompute架构可以通过添加更多成员集群来水平扩展,以确保足够的可伸缩性以满足PinCompute的持续增长。
PinCompute以两种形式定义SLO: API可用性和平台响应性。PinCompute确保其关键工作负载编排相关API的可用性达到99.9%。PinCompute提供控制平面调用延迟SLO,专注于系统采取行动的延迟。根据工作负载复杂性和相应的业务需求,这种延迟从几秒到几十秒不等。对于预留层的服务质量,PinCompute提供了工作负载端到端启动速度的SLO,不仅关注平台采取的行动,还包括这些行动能够多快生效。这些SLO是平台级性能和可用性的重要信号,也为平台开发人员以高质量迭代平台功能设定了高标准。
学习和未来的工作
在过去几年里,我们已经在架构和Pinterest所需的一系列功能方面使这个平台成熟起来。引入计算作为平台即服务(PaaS)被视为Pinterest开发者的最大胜利。一项内部研究表明,超过90%的用例以及60%的基础设施占用可以从利用PaaS来迭代的软件中受益。作为平台用户,PaaS抽象了拥有和管理基础设施和Kubernetes的繁重工作,使用户能够专注于应用程序的独特方面。作为平台运营商,PaaS通过标准化实现了整体基础设施管理,为提高效率和降低维护基础设施更新的成本提供了机会。PinCompute支持"API优先",定义了清晰的支持合约,使平台支持可编程性和可伸缩性。此外,平台中"租户"的可靠定义在用例之间以及与基础设施功能的交互之间建立了清晰的边界,这对于多租户平台的成功至关重要。最后,通过加强自动化,改善了支持响应时间,减少了团队KTLO和随叫随到的开销。
随着PinCompute在Pinterest的使用不断扩大,有很多令人兴奋的机会。资源管理和效率是我们正在努力的一个大领域;多租户成本归属、高效装箱、自动扩缩容和容量预测等项目对于支持Pinterest高效和负责任的基础设施至关重要。编排有状态应用程序在技术上具有挑战性,对Pinterest的业务来说也很重要,虽然PinPod和PinApp为编排应用程序提供了坚实的基础,但我们正在积极与有状态系统的利益相关者合作,开发可复用的解决方案,以提高运营效率并降低维护成本。我们也认识到能够访问Kubernetes API的用例的重要性。随着Kubernetes及其社区的积极发展,遵循行业趋势并采用行业标准实践是一个很大的好处,因此我们正在积极与合作伙伴团队和供应商合作,使更多的Pinterest开发人员能够这样做。与此同时,我们正在努力回馈社区,因为我们相信广泛信任的社区是建立共享理解,贡献功能和改进,分享和吸收生产中的经验和学习的最佳平台,可以造福所有人。最后,我们正在评估利用托管服务进一步将基础设施管理工作移交给云供应商的机会。
你好,我是俞凡,在Motorola做过研发,现在在Mavenir做技术工作,对通信、网络、后端架构、云原生、DevOps、CICD、区块链、AI等技术始终保持着浓厚的兴趣,平时喜欢阅读、思考,相信持续学习、终身成长,欢迎一起交流学习。为了方便大家以后能第一时间看到文章,请朋友们关注公众号"DeepNoMind",并设个星标吧,如果能一键三连(转发、点赞、在看),则能给我带来更多的支持和动力,激励我持续写下去,和大家共同成长进步!
Building a Kubernetes Platform at Pinterest: https://medium.com/pinterest-engineering/building-a-kubernetes-platform-at-pinterest-fb3d9571c948
[2]Training Operators | Kubeflow: https://www.kubeflow.org/docs/components/training
本文由 mdnice 多平台发布