引言
在Spark 2.3中,Kubernetes成为Spark的官方调度器后端,区别于独立的调度器Mesos和Yarn。相较于在Kubernetes之上部署独立的Spark集群并提交应用程序在独立集群上运行的替代方法,将Kubernete作为本机调度器后端的方案提供了一些重要的好处——如Spark-18278中所述,这是一个巨大的飞跃。然而,Spark应用程序的生命周期管理方式(例如,如何提交应用程序以在Kubernetes上运行,以及如何跟踪应用程序状态),与Kubernetes上的其他类型的工作负载(例如Deployments、DaemonSets和StatefulSets)有很大不同。Apache Spark的Kubernetes Operator减少了差距,并允许在Kubernete上以惯用方式指定、运行和监控Spark应用程序。
具体来说,Apache Spark的Kubernetes Operator遵循了最近的趋势,即利用Operator模式来管理Kubernetes集群上Spark应用程序的生命周期。Operator允许以声明的方式(例如,在YAML文件中)指定Spark应用程序,并在不需要处理Spark提交过程的情况下运行。它还使Spark应用程序的状态能够像Kubernetes上的其他类型的工作负载一样被跟踪和惯用地呈现。本文档讨论了Operator的设计和架构。有关Spark应用程序规范的CustomResourceDefinition文档,请参阅API定义。
架构
Spark的Operator包含以下功能/组件:
- SparkApplication控制器:用于监听SparkApplication对象的创建、更新和删除事件并根据监听事件进行操作
- 提交运行程序:对从控制器接收的提交运行spark-submit
- Spark pod监控:用于监听Spark pod并向控制器发送pod状态更新
- 可变准入webhook(Mutating Admission Webhook):根据控制器添加的pod上的注释来处理与Spark driver和executor pod相关的自定义设置
- sparkctl命令行工具:用于与Operator交互
下图显示了不同组件如何相互作用和协同工作的。
具体来说,用户使用sparkctl(或kubectl)来创建SparkApplication对象。SparkApplication控制器通过观察程序从API服务器接收对象,创建带有spark-submit参数的提交,并将提交发送给提交运行程序。提交运行程序提交要运行的应用程序,并创建应用程序的driver pod。启动后,driver pod将创建executor pod。当应用程序运行时,Spark pod监控监听应用程序的pod,并将pod的状态更新发送回控制器,然后控制器相应地更新应用程序的状态。
CRD控制器
SparkApplication控制器(或简称CRD控制器),监视Kubernetes集群中任何命名空间中SparkApplication对象的创建、更新和删除事件,并根据监听事件采取操作。当添加一个新的SparkApplication对象时(即,当调用ResourceEventHandlerFuncs的AddFunc回调函数时),它会将该对象放入一个内部工作队列,Operator从中提取该对象,准备提交并将提交发送给提交运行器,提交运行器实际上会提交应用程序以在Kubernetes集群中运行。提交内容包括spark-submit命令的参数列表。提交运行程序具有可配置数量的工作程序,用于提交要在集群中运行的应用程序。当SparkApplication对象被删除时,该对象将从内部工作队列中移除,并且与该应用程序关联的所有Kubernetes资源都将被删除或垃圾回收。
当SparkApplication对象被更新时(即,当ResourceEventHandlerFuncs的UpdateFunc回调函数被调用时),例如,来自使用kubectl apply应用更新的用户。控制器检查SparkApplicationSpec中的应用程序规格是否已更改。如果应用程序规格保持不变,则控制器简单地忽略更新。这确保了在没有应用程序规格更改的情况下进行更新,例如由缓存重新同步触发的更新,不会导致应用程序重新提交。如果对应用程序规范进行了更新,则控制器通过删除当前运行的driver pod来取消应用程序的当前运行,并提交具有更新规格的应用程序的新运行。请注意,删除应用程序旧运行的driver pod会有效地终止运行,并导致executor pod也被删除,因为driver是executor pods的所有者。
控制器还负责在Spark pod监控的帮助下更新SparkApplication对象的状态,该监控监听Spark pod并根据pod的状态更新相应SparkApplication的SparkApplicationStatus字段。Spark pod监控监听Spark pod的创建、更新和删除事件,根据pod的状态创建状态更新消息,并将消息发送给控制器进行处理。当控制器收到状态更新消息时,它会从缓存中获取相应的SparkApplication对象,并相应地更新状态。
如API定义中所述,状态字段(类型为SparkApplicationStatus)记录应用程序的总体状态以及每个executor pod的状态。请注意,应用程序的总体状态由driver pod状态决定,除非提交失败,在这种情况下,不会启动driver pod。特别地,最终应用状态在适用时会被设置为driver pod的终止状态,即,如果driver pod完成,则为COMPLETED,或者如果driver pod失败,则为FAILED。如果driver pod在运行时被删除,则最终应用程序状态设置为FAILED。如果提交失败,则应用程序状态设置为FAILED_SUBMISSION。有两种终止状态:COMPLETED和FAILED,这意味着Operator永远不会重试处于这些状态的任何应用程序。所有其他状态都是非终止状态,可以根据状态以及RestartPolicy重试。
作为为新创建的SparkApplication对象准备提交的一部分,控制器解析该对象,并添加配置选项,用于向应用程序的driver和executor pod添加某些注解。这些注解稍后会被MutatingAdmissionWebhook用来在pod开始运行之前对其进行配置。例如,如果Spark应用程序需要将某个Kubernetes ConfigMap安装到driver和executor pod中,则控制器会添加一个注解,指定要安装的ConfigMap的名称。稍后,MutatingAdmissionWebhook会看到pod上的注释,并将ConfigMap装载到pod。
应用重启和故障处理
Operator通过SparkApplicationSpec的RestartPolicy字段提供了一个可配置的选项(有关更多详细信息,请参阅配置自动化应用重启和故障处理),用于指定应用程序重启策略。操作员根据应用程序的终止状态和重新启动策略来确定是否应重新启动应用程序。如上所述,应用程序的终止状态基于driver pod的终止状态。因此,有效的决策是基于driver pod的终止状态和重启策略。具体而言,以下条件之一适用:
- 如果重新启动策略类型为“Never”,则终止时不会重新启动应用程序。
- 如果重新启动策略类型为“Always”,则无论应用程序的终止状态如何,都会重新启动应用程序。请注意,这样的应用程序永远不会以“COMPLETED”或“FAILED”的终端状态结束。
- 如果重新启动策略类型为“OnFailure”,则仅当应用程序失败且未达到重试限制时,应用程序才会重新启动。请注意,如果driver pod在运行时被删除,则应用程序将被视为失败,如上所述。在这种情况下,如果重新启动策略为OnFailure,则应用程序将重新启动。
当Operator决定重新启动应用程序时,它会清理与上次终止的应用程序运行相关的Kubernetes资源,并将应用程序的SparkApplication对象排入内部工作队列,由处理提交的worker从中提取。请注意,Operator只需重新提交应用程序,并让提交客户端创建一个新的driver pod,而不是重新启动driver pod。
可变准入webhook(Mutating Admission Webhook)
该Operator提供了一个可选的可变准入webhook,用于根据CRD控制器添加的pod上的某些注解自定义Spark driver和executor pod。注解由Operator根据应用程序规格进行设置。除了Spark在Kubernetes上原生支持的那些需求之外,所有Spark pod定制需求都由可变准入webhook处理。
命令行工具:Sparkctl
sparkctl是一个用于操作Operator的命令行工具。它支持从YAML文件创建SparkApplication对象,列出现有的SparkApplication物体,检查SparkApplication的状态,从本地端口转发到运行Spark driver的远程端口,以及删除SparkApplication对象。有关sparkctl的更多详细信息,请参阅README文件。