天天看點

spark過節監控告警系統實作

作者:浪尖 原文連結

本文轉載自公衆号:Spark學習技巧

馬上要過年了,大部分公司這個時候都不會再去謀求開新業務,而大資料工匠們,想要過好年,就要保證過年期間自己對自己的應用了如執掌。一般公司都會有輪值人員,至少要有春節應急預案,尤其是對于我們這些搞平台,或者線上應用的,應急預案更是必不可少。今天浪尖主要是分享一下關于在yarn上的spark 任務我們應該做哪些監控,如何監控。

Spark on yarn這種應用形态目前在企業中是最為常見的,對于這種spark的任務,浪尖覺得大家關心的名額大緻有:app存活,spark streaming的job堆積情況,job運作狀态及進度,stage運作進度,rdd緩存監控,記憶體監控等。

其實,春節最為重要的就是app存活了,春節期間各大應用應該都會有一部分資料增量,那麼實際上就需要我們的程式能有一定的抗流量尖峰的能力,這個也很常見,因為正常的app都會有流量尖峰和低谷,你做一個實時應用程式,必須要去應對流量尖峰,也就是說你程式的處理能力正常要大于流量尖峰的,要是你的資料流量有曆史資訊,那麼就簡單了,隻需要将spark streaming和flink的處理能力蓋過流量最高值即可。當然,會有人說spark streaming 和flink不是有背壓系統嗎,短暫的流量尖峰可以抗住的呀,當然太短暫的幾分鐘的流量尖峰,而且你的任務對實時性要求不高,那是可以,否則不行。

1. App存活監控

企業中,很多時候spark的任務都是運作與yarn上的,這個時候可以通過yarn的用戶端擷取rm上運作 任務的狀态。

Configuration conf = new YarnConfiguration();
YarnClient yarnClient = YarnClient.createYarnClient();
yarnClient.init(conf);
yarnClient.start();
try{
   List<ApplicationReport> applications = yarnClient.getApplications(EnumSet.of(YarnApplicationState.RUNNING, YarnApplicationState.FINISHED));
   System.out.println("ApplicationId ============> "+applications.get(0).getApplicationId());
   System.out.println("name ============> "+applications.get(0).getName());
   System.out.println("queue ============> "+applications.get(0).getQueue());
   System.out.println("queue ============> "+applications.get(0).getUser());
} catch(YarnException e) {
   e.printStackTrace();
} catch(IOException e) {
   e.printStackTrace();
}
       yarnClient.stop();           

這種api隻适合,spark 和 MapReduce這兩類應用,不适合flink。做過flink的應該都很容易了解吧,yarn上運作的flink任務顯示,running,但是flink app内部的job卻已經挂掉了,這種yarn的flink任務存活不适合,隻能用RestClusterClient,具體浪尖在這裡就不舉例子了,本文主要是講監控spark應用體系,後續會給出demo測試。

寫個yarn的監控

對于這個APP的監控,還有更加細節的監控,比如executor數,記憶體,CPU等。擷取名額的方法:

1.1 ApplicationInfo

通過SparkContext對象的AppStatusStore對象擷取ApplicationInfo

val statusStore = sparkContext.statusStore
statusStore.applicationinfo()           

擷取一個ApplicationInfo對象,然後主要包含以下schema

case class ApplicationInfo private[spark](
    id: String,
    name: String,
    coresGranted: Option[Int],
    maxCores: Option[Int],
    coresPerExecutor: Option[Int],
    memoryPerExecutorMB: Option[Int],
    attempts: Seq[ApplicationAttemptInfo])           

1.2 AppSummary

通過SparkContext對象的AppStatusStore對象 擷取AppSummary

val statusStore = sparkContext.statusStore
statusStore.appSummary()

statusStore.appSummary().numCompletedJobs
statusStore.appSummary().numCompletedStages           

2.Job監控

主要包括job的運作狀态資訊,spark streaming的job堆積情況。這個浪尖知識星球裡面也分享過主要是自己實作一個StreamingListener,然後通過StreamingContext的執行個體對象注冊到SparkListenerbus即可。

浪尖這裡隻會舉一個就是spark streaming 資料量過大,導緻batch不能及時處理而使得batch堆積,實際上就是active batch -1,針對這個給大家做個簡單的案例,以供大家參考。

spark過節監控告警系統實作
val waitingBatchUIData = new HashMap[Time, BatchUIData]
ssc.addStreamingListener(new StreamingListener {
  override def onStreamingStarted(streamingStarted: StreamingListenerStreamingStarted): Unit = println("started")

  override def onReceiverStarted(receiverStarted: StreamingListenerReceiverStarted): Unit = super.onReceiverStarted(receiverStarted)

  override def onReceiverError(receiverError: StreamingListenerReceiverError): Unit = super.onReceiverError(receiverError)

  override def onReceiverStopped(receiverStopped: StreamingListenerReceiverStopped): Unit = super.onReceiverStopped(receiverStopped)

  override def onBatchSubmitted(batchSubmitted: StreamingListenerBatchSubmitted): Unit = {
    synchronized {
      waitingBatchUIData(batchSubmitted.batchInfo.batchTime) =
        BatchUIData(batchSubmitted.batchInfo)
    }
  }

  override def onBatchStarted(batchStarted: StreamingListenerBatchStarted): Unit =     waitingBatchUIData.remove(batchStarted.batchInfo.batchTime)
  
  override def onBatchCompleted(batchCompleted: StreamingListenerBatchCompleted): Unit = super.onBatchCompleted(batchCompleted)

  override def onOutputOperationStarted(outputOperationStarted: StreamingListenerOutputOperationStarted): Unit = super.onOutputOperationStarted(outputOperationStarted)

  override def onOutputOperationCompleted(outputOperationCompleted: StreamingListenerOutputOperationCompleted): Unit = super.onOutputOperationCompleted(outputOperationCompleted)
})           

最終,我們使用waitingBatchUIData的大小,代表待處理的batch大小,比如待處理批次大于10,就告警,這個可以按照任務的重要程度和持續時間來設定一定的告警規則,避免誤操作。

3. Stage監控

Stage的運作時間監控,這個重要度比較低。使用的主要API是statusStore.activeStages()得到的是一個Seq[v1.StageData] ,StageData可以包含的資訊有:

class StageData private[spark](
    val status: StageStatus,
    val stageId: Int,
    val attemptId: Int,
    val numTasks: Int,
    val numActiveTasks: Int,
    val numCompleteTasks: Int,
    val numFailedTasks: Int,
    val numKilledTasks: Int,
    val numCompletedIndices: Int,

    val executorRunTime: Long,
    val executorCpuTime: Long,
    val submissionTime: Option[Date],
    val firstTaskLaunchedTime: Option[Date],
    val completionTime: Option[Date],
    val failureReason: Option[String],

    val inputBytes: Long,
    val inputRecords: Long,
    val outputBytes: Long,
    val outputRecords: Long,
    val shuffleReadBytes: Long,
    val shuffleReadRecords: Long,
    val shuffleWriteBytes: Long,
    val shuffleWriteRecords: Long,
    val memoryBytesSpilled: Long,
    val diskBytesSpilled: Long,

    val name: String,
    val description: Option[String],
    val details: String,
    val schedulingPool: String,

    val rddIds: Seq[Int],
    val accumulatorUpdates: Seq[AccumulableInfo],
    val tasks: Option[Map[Long, TaskData]],
    val executorSummary: Option[Map[String, ExecutorStageSummary]],
    val killedTasksSummary: Map[String, Int])           

具體細節大家也可以詳細測試哦。

4. RDD監控

這個其實大部分時間我們也是不關心的,主要是可以擷取rdd相關的名額資訊:

通過SparkContext對象的AppStatusStore

val statusStore = sparkContext.statusStore
statusStore.rddList()           

可以擷取一個Seq[v1.RDDStorageInfo]對象,可以擷取的名額有:

class RDDStorageInfo private[spark](
    val id: Int,
    val name: String,
    val numPartitions: Int,
    val numCachedPartitions: Int,
    val storageLevel: String,
    val memoryUsed: Long,
    val diskUsed: Long,
    val dataDistribution: Option[Seq[RDDDataDistribution]],
    val partitions: Option[Seq[RDDPartitionInfo]])

class RDDDataDistribution private[spark](
    val address: String,
    val memoryUsed: Long,
    val memoryRemaining: Long,
    val diskUsed: Long,
    @JsonDeserialize(contentAs = classOf[JLong])
    val onHeapMemoryUsed: Option[Long],
    @JsonDeserialize(contentAs = classOf[JLong])
    val offHeapMemoryUsed: Option[Long],
    @JsonDeserialize(contentAs = classOf[JLong])
    val onHeapMemoryRemaining: Option[Long],
    @JsonDeserialize(contentAs = classOf[JLong])
    val offHeapMemoryRemaining: Option[Long])

class RDDPartitionInfo private[spark](
    val blockName: String,
    val storageLevel: String,
    val memoryUsed: Long,
    val diskUsed: Long,
    val executors: Seq[String])           

其中,還有一些api大家自己也可以看看。

5. Rdd記憶體及緩存監控

主要是監控executor的記憶體使用情況,然後對一些高記憶體的任務能及時發現,然後積極排查問題。這個問題監控也比較奇葩,主要是監控RDD的記憶體和磁盤占用即可。對于緩存的rdd擷取,隻需要statusStore.rddList()擷取的時候給定boolean參數true即可。擷取之後依然是一個RDD清單,可以參考4,去進行一些計算展示。

6.Executor監控

關于記憶體的監控,除了存活監控之外,還有單個executor記憶體細節。Executor的注冊,啟動,挂掉都可以通過SparkListener來擷取到,而單個executor内部的細節擷取也還是通過SparkContext的一個内部變量,叫做SparkStatusTracker。

sc.statusTracker.getExecutorInfos           

得到的是一個Array[SparkExecutorInfo],然後通過SparkExecutorInfo就可以擷取細節資訊:

private class SparkExecutorInfoImpl(
    val host: String,
    val port: Int,
    val cacheSize: Long,
    val numRunningTasks: Int,
    val usedOnHeapStorageMemory: Long,
    val usedOffHeapStorageMemory: Long,
    val totalOnHeapStorageMemory: Long,
    val totalOffHeapStorageMemory: Long)
  extends SparkExecutorInfo           

7.總結

浪尖常用的監控就是一app存活監控,二就是定義sparklistener,實作檢測sparkstreaming 隊列積壓了。

總有粉絲問浪尖,如何發現這些細節的,當然是看源碼的api,分析源碼得到的,架構細節隻能如此獲得。

阿裡巴巴開源大資料技術團隊成立Apache Spark中國技術社群,定期推送精彩案例,技術專家直播,問答區近萬人Spark技術同學線上提問答疑,隻為營造純粹的Spark氛圍,歡迎釘釘掃碼加入!

spark過節監控告警系統實作

對開源大資料和感興趣的同學可以加小編微信(下圖二維碼,備注“進群”)進入技術交流微信群。

spark過節監控告警系統實作