电子说
可能国内读者不太熟悉 Pinterest,但它在美国,可是坐拥 3 亿用户的美版“小红书”,媲美社媒巨头!如果你熟悉“小红书”,那么你就很容易理解 Pinterest 的定位了。在如此庞大体量的 Pinterest,是如何建立 Kubernetes 平台的? 1 为什么选择 Kubernetes?
在过去的几年里,有 3 亿多 Pinner 在 Pinterest 上保存了超过 2000 亿次 Pin,覆盖了超过 40 亿个 Board。为了服务于这个庞大的用户群和内容池,我们开发了数以千计的服务,从少数几个 CPU 的微服务到占用整个虚拟机群的巨大整体服务。还有来自各种不同框架的批处理作业,它们可以是 CPU、内存或 I/O 密集型。
译注:要理解这段话,译者有必要在这里简单介绍一下 Pinterest 及其基本元素。Pinterest 是一个图片社交平台,堪称图片版的 Twitter,用户可以发表自己的 Pin,也可以转发自己喜欢的图片。Pinterest 采用的是瀑布流的形式展现图片内容,无需用户翻页,新的图片不断自动加载在页面底端,让用户不断的发现新的图片。Pinterest 里面有一些基本元素。如 Pin。Pin 即你在 Pinterest 上发表的帖子,发布 Pin 的人统称被称为 Pinner。一个 Pin 通常是由一张图片,一个到外部网页的链接和几句简短的描述组成,并且发布的时候需要将其归类到它所属的 Board 里。
为了支持这些不同的工作负载,Pinterest 的基础架构团队面临着多种挑战:
工程师在启动工作负载时并没有统一的体验。无状态服务、有状态服务和批处理作业由完全不同的技术栈部署和管理。如此一来,给工程师们带来了陡峭的学习曲线,同时也给基础架构团队带来巨大的维护和客户支持负担。
管理自己的虚拟机群的工程师给 infra 团队带来了巨大的维护负担。操作系统或 AMI 升级等简单的操作可能需要耗时数周到数月的时间才能完成。生产工作负载在这些过程中也会遇到干扰,而这些过程本应对生产工作负载是透明的。
很难在独立的管理系统之上构建基础架构治理工具。对我们来说,更难确定哪些机器的归属,以及它们是否可以安全回收。
容器编排系统提供了统一工作负载管理的方法。它们还为更快的开发速度和更容易的基础架构治理铺平了道路,因为所有运行的资源都由集中式系统管理。
图 1:基础架构优先级(服务可靠性、开发人员生产力和基础架构效率)
Pinterest 的云管理平台团队早在 2017 年就开始了在 Kubernetes 的旅程。到 2017 年上半年,我们将大部分生产工作负载进行了容器化(包括核心 API 和 Web 服务器集群)。然后,通过构建产品集群并在其上运行实际工作负载,来对不同的容器编排系统进行广泛的评估。到 2017 年底,我们决定沿着 Kubernetes 的道路走下去,因为它具有灵活性,而且还有广泛的社区支持。
到目前为止,我们已经基于 Kops 构建了自己的集群引导工具,并将现有的基础架构组件集成到 Kubernetes 集群中,如网络、安全性、指标、日志记录、身份管理和流量等。我们还引入了 Pinterest 特定的自定义资源来模拟我们的独特工作负载,同时对开发人员隐藏运行时的复杂性。我们现在工作的重点是集群稳定性、可扩展性和客户支持。
2 Kubernetes:Pinterest 选择的路
运行 Kubernetes 来支持 Pinterest 这样规模的工作负载,同时又要让它成为我们工程师喜爱的平台,真的是一个很大的挑战。
作为一个大型组织,我们在基础架构工具上投入了大量资金,例如处理证书和密钥分发的安全工具、支持服务注册和发现的流量组件,以及提供日志和指标的可见性组件。这些组件都是基于艰难的经验教训上构建的,因此我们希望将它们整合到 Kubernetes 内,而不是“重新发明轮子”。这一做法也使得迁移变得更加容易,因为我们的内部应用已经获得了所需的支持。
另一方面,Kubernetes 本机工作负载模型(如部署、作业和守护进程集)并不足以为我们自己的工作负载进行建模。可用性问题是采用 Kubernetes 的巨大障碍。例如,我们曾经听到服务开发人员抱怨丢失或错误配置的原因“扰乱”了他们的终端。我们还看到批处理作业用户使用模板工具生成数百个同一作业规范的副本,最终导致了调试噩梦。
对工作负载的运行时支持也在不断发展,因此在同一个 Kubernetes 集群上支持不同版本将会变得异常困难。想象一下,如果我们需要面对许多版本的运行时,客户支持的复杂性,以及为它们进行升级或修补 bug 该有多大的困难。
3 Pinterest 自定义资源和控制器
为了让我们的工程师更容易地采用 Kubernetes,并使基础架构开发更快速、更顺畅,我们设计了自己的自定义资源(Custom Resource Definitions,CRD)
CRD 提供了以下功能:
将各种本机 Kubernetes 资源捆绑在一起,使它们作为单一工作负载进行工作。例如,PinterestService 资源将部署、服务、入口和应用配置管理组合在一起,因此服务开发人员无需担心为他们的服务设置 DNS。
为应用程序注入必要的运行时支持。用户只需关注自己业务逻辑的容器规范即可,而 CRD 控制器将必要的边车容器(sidecar)、初始容器、环境变量和卷(volume)注入到它们的容器节点(pod)规格中。这为应用工程师带来了开箱即用的体验。
CRD 控制器还可以对本机资源进行生命周期管理,并处理可见性和可调试性。这包括但不限于协调所需的规格和实际规格、CRD 状态更新和事件记录。如果没有 CRD 的话,应用工程师必须管理更多的资源,而且这个过程已经被证明很容易出错。下面是 PinterestService 和由控制器转换的本机资源的示例:
图 2:CRD 到本机资源。左边是用户编写的 Pinterest CR,右边是控制器生成的本机资源定义。
如图所示,为了支持用户的容器,我们需要插入一个初始容器和几个边车容器,以保证安全性、可见性和网络流量。此外,我们在批处理作业中引入了应用配置管理模板和 PVC 模板支持,以及许多环境变量来跟踪身份、资源利用率和垃圾收集。
难以想象工程师会愿意在没有 CRD 支持的情况下手工编写这些配置文件,更不用说维护和调试配置了。
4 应用程序部署工作流
图 3:Pinterest CRD 概述
图 3 展示了如何将 Pinterest 自定义资源部署到 Kubernetes 集群:
开发人员通过命令行界面(CLI)和用户界面(UI)与我们的 Kubernetes 集群进行交互。
CLI/UI 工具从 Artifactory 检索工作流配置 YAML 文件和其他构件属性(如版本 ID),并将它们发送到作业提交服务。这样可以确保只向 Kubernetes 集群提交已审查和已登录的工作负载。
作业提交服务是各种计算平台(包括 Kubernetes)的“网关”。用户身份验证、配额强制和部分 Pinterest CRD 配置验证都在这里进行。
一旦 CRD 通过了作业提交服务验证,它就会被发送到 Kubernetes API。
我们的 CRD 控制器监视所有自定义资源上的事件。它将 CR 转换为 Kubernetes 本机资源,将必要的边车容器添加到用户定义的容器节点中,设置适当的环境变量,并执行其他必要的内务处理工作,以确保用户的应用程序容器具有足够的基础架构支持。
然后,CRD 控制器将生成的本机资源写回 Kubernetes API 中,以便调度器(scheduler)可以提取这些资源并开始运行。
注意:这是新的基于 Kubernetes 的计算平台的早期采用者使用的预发布部署工作流。我们正对这一体验进行改进,使其与我们新的 CI/CD 平台完全继承,以避免暴露过多 Kubernetes 具体的细节。我们期待在即将发布的博文《为 Pinterest 构建 CI/CD 平台》中分享我们的动机、进展和后续影响。
5 自定义资源类型
基于 Pinterest 的具体需求,我们设计了以下适合不同工作流的 CRD:
PinterestService是长期运行的无状态服务。许多核心系统都基于一组此类服务。
PinterestJobSet为运行到完成的批处理作业建模。Pinterest 中一个非常常见的模式是,多个作业并行运行相同的容器,每个作业都只占用工作负载的一小部分,而不依赖于彼此。
PinterestCronJob被据用轻量级周期性工作负载的团队广泛采用。PinterestCronJob 是围绕本机 cron 作业的包装器,支持 Pinterest 特有的安全性、流量、日志和指标等。
PinterestDaemon仅限于与基础架构相关的守护进程。随着我们在集群上添加更多的支持,PinterestDaemon 的家族仍在增长。
PinterestTrainingJob封装了 TensorFlow 和 PyTorch 作业,提供了与所有其他 CRD 相同级别的运行时支持。由于 Pinterest 大量使用 TensorFlow 和其他机器学习框架,因此围绕他们构建专门的 CRD 是有意义的。
我们还有正在构建的PinterestStatefulSet,将很快被用于存储和其他有状态系统。
6 运行时支持
当应用程序容器节点在 Kubernetes 上启动时,它会自动获得一个证书标识自己。此证书用于通过 mTLS 访问机密存储或与其他服务进行通信。同时,配置管理初始容器和守护进程将确保在应用程序容器启动之前就下载好所有必需的依赖项。当应用程序容器准备就绪时,流量边车容器和守护进程将会向容器节点注册到 Zookeeper,以便让客户端可以发现它。甚至在容器节点启动之前,网络守护进程就已经为容器节点设置好了网络。
以上就是服务工作负载的典型运行时支持的示例。其他工作负载类型可能需要稍微不同的支持,但他们都是以容器节点级边车容器、节点级守护进程集或虚拟机级守护进程的形式出现的。我们确保所有这些应用程序都是由基础架构团队部署,以便它们在所有应用程序之间保持一致,从而极大减少我们的维护和客户支持的负担。
7 测试与质量保证
我们在本机 Kubernetes 测试基础上构建了一个端到端的测试管道。这些测试部署到所有的集群。这个管道在到达生产集群之前就已经经历了多次回归。
除了测试基础架构之外,还有监视和报警系统,这些系统持续监控系统组件的健康状态、资源利用率和其他关键指标,在需要人工干预时通知我们。
8 备选方案
我们考虑了一些自定义资源的备选方案,比如变异许可控制器和模板系统。但是,所有的备选方案都存在重大问题,因此我们选择了 CRD 的路径。
变异许可控制器以用于注入边车容器、环境变量和其他运行时支持。然而,它很难讲资源捆绑在一起以及管理它们的生命周期,而 CRD 则需要协调、状态更新和生命周期管理功能。
模板系统(如 Helm charts)也被广泛用于启动具有类似配置的应用程序。但是,我们的工作负载过于多样化,无法通过模板进行管理。我们还需要支持持续部署,这在使用模板时很容易出错。
9 未来的工作
目前,我们在所有的 Kubernetes 集群上运行混合工作负载。为了支持不同大小和类型的工作负载,我们正在开展一下方面的工作:
集群联邦(Cluster Federation)将大型应用程序分布在不同的集群上,以实现可扩展性和稳定性。
集群稳定性、可扩展性和可见性,确保应用程序到达其服务级别协议。
资源和配额管理,以确保应用程序不会相互干扰,集群规模得到控制。
新的 CI/CD 平台,支持 Kubernetes 上的应用程序部署。
全部0条评论
快来发表一下你的评论吧 !