一、用Prometheus监控Apache Spark
在使用Apache Spark去做ETL,去做数据分析和处理的过程中,我们肯定都会涉及到监控spark程序这么一项工作。一般来说,有三种方式去做程序的监控。第一个就是使用Web UI。第二块主要是日志。第三种是Metrics。这三个信息,最大的一个问题是说,我们一般是在ETL夯住了或者失败之后,才会去查看。如果有这么一个系统能够帮助我们,在任务性能下降或者失败之前就提醒、甚至是自愈,这样就能避免很多故障的发生。
在分布式系统中,比较大的挑战之一就是如何做到在故障发生之前就能够有些规避的措施,或者一些提醒的措施。Metrics在一个分布式系统里边是非常关键和核心的一个模块。在spark里边,同样也是有很多的metrics。使用metrics我们能够发现内存泄露,一些配置错误,在性能下降之前的一些监控,以及对于流作业的一些监控等等。
Prometheus是非常有用的一个metrics监控的系统。它是一个开源的系统,能够支持监控和报警。它主要有以下四个特点。第一个特点是,它的数据模型是一个多维度的数据模型。第二个特点是,部署和运维是非常方便的。第三个特点是,在采集数据的扩展性这块,支持得非常好。最后一个特点是,提供了一个非常强大的查询语言。
在spark 3.0以前,我们一般怎么去支持Prometheus。其中第一个方法就是基于java agent的方式。主要主要分为四个步骤,如下图所示。
第二种方式,基于GraphiteSink和GraphiteExporter的组合。
第三种方式,基于Custom sink和Pushgateway server。
我们来看一下,前面三种方法有哪些好处和坏处。首先说好处,已经有很多用户通过三种方式实现了跟Prometheus的对接,是一个比较通用的方式。但是它也有坏处,在新环境中它有一些比较繁琐的setup的工作。另外,第三方插件可能对特定版本会有一些比较强的依赖,这对我们后续的运维和升级会带来困扰。
所以在spark 3.0中,就提出了新的目标。主要有两个需要关注的设计点。一个就是只使用到新的endpoint,不去跟原先的pipeline耦合,不引入对其他一些组件的依赖。另外一块就是尽量重新使用已经存在的一些资源,包括端口的资源,等等。
二、本地化支持Prometheus (Support Prometheus monitoring natively)
接下来,我会介绍一下在spark 3.0中如何实现刚才提到的目标,更好的本地化支持Prometheus。Spark的metrics模块依赖比较重的是DropWizard这么一个组件。它在spark 3.0中也做了一次升级,带来的好处是能够支持jdk11,但是也存在小的负面点,就是它的数据格式有所变化,如下图所示。
第二个增加的组件就是ExecutorMetricsSource。主要是对executor memory相关的一些metrics做了增强。
为了更好的做一些本地化支持的工作,主要加了两个组建。一个是PrometheusServlet,它会生成Prometheus兼容的一个格式,它的配置方式跟以前的metrics system配置是一样的。同时也不会引入其他的一些库,也不会对特定端口进行依赖。第二个组件是PrometheusResource。针对executor memory metrics相关的信息,提供了这么一个独立的endpoint。
在3.0中增加了spark_info metric,它的作用是针对不同版本的监控和报警。另外,对driver service annotation做了一些增强,能够实现利用Prometheus service discovery的特性,从而更方便的进行一些metrics的采集和监控。
三、在K8s集群中监控 (Monitoring in K8s cluster)
在K8s环境下面,怎么去基于已经集成Prometheus的spark版本,对我们的作业进行监控,主要有三个场景。第一个是对一些批作业的内存的情况进行监控。第二个是对动态调度和动态资源分配的监控。第三个是对流作业的监控。
我们来看一下这三种场景。首先,对批作业内存的监控场景,主要用到的是Prometheus的service discovery这一个特性。如下图所示,有四个配置。
我们来看一个简单的例子,是基于SparkPi的。假设我们现在通过spark-submit提交一个作业到K8s。主要是把前面提到的几个配置,也就是下图这个例子中加粗的部分给配置上就可以了。
如下图所示,是一个针对内存的监控。它是executor_id为1的时候,对它的内存使用的监控。可以看到绿色的这条线,我们从driver端拿到这个状态之后,就可以提前知道已经有OOM的风险,通过Prometheus从driver端采集到监控信息,就可以及时的做一些报警和预处理。
第二个场景就是动态调度。Spark 3.0支持在K8s上动态调度。主要就是把一些配置打开,如下图中的黑体部分所示。配置打开之后,在作业提交的时候,就能够开启在K8s环境的动态调度。
然后我们看一下在Prometheus上展示的一个状态信息图,每一条线表示有一个executor。我们看到随着时间推移,线的个数在不断的变多,然后又变少。隔了一分钟之后,又进入到第二轮,也是同样的先变多,再变少。
最后一种场景,是针对流作业的监控的支持。在spark 3.0中,添加了spark.sql.streaming.metricsEnabled。开启之后,它会提供6种metrics,如下图所示。对一个流作业,如果要使用Prometheus监控,最好在一开始就对nameSpace等提前做好设置,避免后续出现数据的不一致。
前面提到的6种metrics对于流作业都是比较关键的,都应该去监控并且做一些报警处理。这里重点再提两个。一个就是前面提到的延时,如果latency > micro-batch interval,就可能存在一些性能问题,需要关注。另外一个报警就是States-rowsTotal。如果它无限的增长,可能会导致流作业发生OOM。这个也是需要做好监控和报警的。
在针对Prometheus的使用方式这块,社区在3.0建议的是基于federation的模式。如下图所示,左边是在namespace1下面对批作业的一个监控。右边是在namespace2下面对流作业的一个监控。他们都可以同时发送到一个Cluster-wise Prometheus下面,然后通过spark_info等等这些信息做一些数据的细分。
以上就是spark 3.0对于Prometheus监控的一些支持。目前来说还是处在一个实验性的阶段,所以还存在一些问题。第一个问题就是新的endpoint暴露的metrics是只包含metrics_或者spark_info开头的这么一些信息。第二个问题是,PrometheusServlet目前的命名格式没有遵循Prometheus的命名风格。第三个问题是,在流作业这块,如果没有把namespace做一些很好的配置,可能会导致metrics不断的的增加,甚至互相影响。
阿里云EMR团队在做的spark on k8s,目前已经对Prometheus监控进行了原生化的支持。目前我们实现的方式是基于javaagent,后续也会计划去引进更多的原生支持。
关键词:Spark 3.0,Prometheus,metrics监控,k8s cluster,spark on k8s
获取更多 Spark+AI SUMMIT 精彩演讲视频回放,立刻点击前往:
>>SPARK + AI SUMMIT 2020 中文精华版线上峰会 7月4日第一场<<
>>SPARK + AI SUMMIT 2020 中文精华版线上峰会 7月5日第二场<<