Spark in action on Kubernetes - 儲存篇(一)

阿里云云棲社群發表於2019-04-19

前言

在上篇文章中,我們分析了Spark Operator內部的機制,今天我們會討論一個在大資料領域中最重要的話題 - 儲存。大資料已經無聲無息的融入了每個人的生活中。大到旅遊買房,小到外賣叫車,都可以看到通過大資料提供資料分析、資料推薦、資料決策的使用場景。大資料要想能夠更準確地協助決策,需要在資料多維度、資料完備性等方面有較高要求。可預知的在未來,資料的量級會越來越大,特別是隨著5G時代的到來,資料的吞吐量級成指數的增長,資料的維度與來源會越來越多,資料的種類也會變得越來越異質化,對大資料平臺也帶來新的挑戰。成本低、存得多、讀寫快成為大資料儲存的三大問題,而今天我們就會針對這三大問題進行探討。

容器化大資料的計算與儲存

計算和儲存分離是大資料領域被大家討論過很多次的問題了,通常我們會通過如下幾個角度來看這個問題:

  • 硬體限制:機器的頻寬成倍數的增長,但是磁碟的速度基本不變,從而造成資料本地讀寫優勢減弱。
  • 計算成本:計算和儲存的量級不匹配,可能造成算力的大量浪費,獨立計算資源可以節約成本。
  • 儲存成本:集中式儲存可以在保證更高SLA的前提下降低儲存成本,使得自建資料倉儲的優勢減少。

這三大問題,隨著容器時代的到來也變得愈發的凸顯。我們知道在kubernetes中,Pod是執行在底層的資源池上,而Pod所需要的儲存是通過PV或者PVC的方式動態分配與掛載的,從某種意義來講,容器本身的架構就是計算與儲存分離的。那麼使用了儲存與計算分離方式的大資料容器叢集會帶來哪些變化與優勢呢?

  • 成本更低

通常在阿里雲上建立一個Spark大資料平臺的時候,首先會選擇D系列的機器,在上面搭建HDFS、Hadoop等一系列的基礎元件,然後再將Spark等作業任務通過Yarn進行排程,跑在這個叢集之上。D系列的內網頻寬範圍是3Gbps-20Gbps,預設可以繫結(4-28塊) 5.5T的本地盤。因為在雲上,雲盤的IO和網路的IO是共享的,而本地盤的IO是獨立的,因此D系列+本地盤的IO效能會比同規格傳統機型+雲盤的效能更好。

但是在實際生產中,我們會發現儲存的資料對著時間變得越來越多,而由於資料具有一定的時效性,造成單位時間的算力與儲存的增長是不相匹配的,這個時候就會帶來了成本的浪費。那麼如果我們使用計算和儲存分離的思想,使用外部儲存,例如OSS、Nas或者DFS(阿里雲HDFS產品),會有哪些變化呢?

首先,我們遮蔽由於儲存的IO差異造成的影響,先都使用遠端的DFS作為檔案儲存。然後我們選擇了ecs.ebmhfg5.2xlarge(8C32G6Gbps)和ecs.d1ne.2xlarge (8C32G6Gbps) 兩款分別面向計算和大資料場景規格配置相同的熱門機型,進行了比對。

ecs.ebmhfg5.2xlarge(8C32G)的測試結果

Spark in action on Kubernetes - 儲存篇(一)

ecs.d1ne.2xlarge (8C32G)的測試結果

Spark in action on Kubernetes - 儲存篇(一)

通過Hibench我們可粗略的估算,在假定IO效能基本一致的場景下,ecs.ebmhfg5.2xlarge會比ecs.d1ne.2xlarge計算效能高30%左右,而成本上ecs.ebmhfg5.2xlarge會比ecs.d1ne.2xlarge低25%左右。

也就是說如果單單隻看計算的能力,是可以有更高效、更節省的機型選擇的。當儲存和計算分離後,我們可以從儲存和計算兩個維度分開去估算所需的用量,在機型上可以更多的考慮高主頻計算能力較強ECS,而儲存上可以使用OSS或者DFS,儲存成本也相較本地儲存更為低廉。此外通常D系列的機型都是1:4的CPU記憶體比,隨著大資料作業的場景越來越豐富,1:4的CPU記憶體比也不完全是最佳的配比,當儲存與計算分離後,我們可以根據業務的型別選擇合適的計算資源,甚至可以在一個計算資源池中維護多種計算資源,從而提高資源使用率。

資料儲存的SLA和計算任務的SLA也是完全不同的,儲存上是無法忍受當機或者中斷的,但是對於計算任務而言,本身已經被切割為子任務了,單個子任務的異常只需重試即可,那麼進一步就可以使用類似競價例項這種成本更低的資源來作為計算任務執行時環境,實現成本的進一步優化。

此外容器最大的特點就是彈性,通過彈性的能力,容器可以在短時間內獲得超遠原本自身幾十甚至上百倍的計算資源,而計算任務完成後又自動釋放掉。目前阿里雲容器服務提供autoscaler進行節點級別的彈性伸縮,可以做到在1分半內伸縮500臺節點。傳統的計算與儲存耦合的場景下,儲存是阻礙彈性的一大障礙,而儲存和計算分離後,就可以針對近乎無狀態的計算來實現彈性的能力,實現真正的按需使用、按量消費。

  • 存的更多

使用外部儲存後,我們不止儲存量級上可以做到近乎無限,而且可以有更多的選擇。在本文開頭位置,我們已經提到了大資料時代的到來,將引入更多維度、更異質化的資料。而這也對資料儲存的方式與型別也帶來了更多的挑戰。單純的HDFS、Hbase、Kafka等資料儲存與鏈路將無法滿足我們的需求。例如從IoT裝置採集的資料更傾向於使用時序儲存進行離線、從應用上下游的產生的資料更傾向於存放在結構化資料庫中,資料的來源與鏈路會越來越多,大資料平臺的底層基礎設施與依賴就會越變越多。在雲上,阿里雲提供了多種型別的儲存服務,可以滿足各種大資料處理的場景。除了傳統的HDFS、Hbase、kafka、OSS、Nas、CPFS等儲存,還包包含MNS、TSDB、OAS(冷資料歸檔)等等。使用儲存服務可以讓大資料平臺更關注在業務的開發,而不是底層基礎架構的運維。不但能夠存的更多,還可以存的更好、存的更省。

  • 讀寫更快

從某種角度來講,讀寫更快是不可能的,因為獨立本地盤可以通過掛足夠盤並行的方式進行提升的,但是要注意的問題在於,當我們通過MR進行任務切割後,每個子任務的瓶頸是否還是在磁碟IO上,大部分情況下答案是否定。上面我們測試的ECS規格內網的頻寬已經可以到達6Gbps,如果全部網路頻寬都換算成磁碟的IO的話,這個量級的資料吞吐IO相比8C32G的算力而言是冗餘的,所以此處我們提到的讀寫更快是指在IO冗餘的前提下提升讀寫速度的方式。OSS是阿里雲上提供的物件儲存,讀取不同單個檔案的IO是並行的,也就是說如果你的業務場景是大量中小型檔案的並行讀取,例如在Spark中讀寫目錄的方式,那麼此時IO的讀寫速度近似是線性增強的。如果依然希望使用HDFS的開發者,阿里雲也提HDFS儲存服務,提供了大量儲存與查詢的優化,和傳統的自建的HDFS相比有50%左右的提升。

Spark in action on Kubernetes - 儲存篇(一)

阿里雲容器服務的儲存方案

阿里雲容器服務在多個維度多個層次滿足大資料處理中的需求。開發者可以根據不同的業務場景和IO的新更能指標要求,選擇合適自己的儲存方式。

大量小檔案儲存

OSS是面向這個場景的最佳使用方式,在容器中可以使用兩種方式操作OSS,一種是將OSS掛載為一個檔案系統,一種是直接在Spark中使用SDK來操作。第一種方案在大資料的場景下是非常不適用的,特別是檔案比較多的場景,如果沒有類似SmartFS的優化手段,會帶來很大的時延與不一致性。而使用SDK的方式則非常直接簡單,只需將相應的Jar放在CLASSPATH下即可,可以參考如下程式碼,直接處理OSS中的檔案內容。

package com.aliyun.emr.example

object OSSSample extends RunLocally {
  def main(args: Array[String]): Unit = {
    if (args.length < 2) {
      System.err.println(
        """Usage: bin/spark-submit --class OSSSample examples-1.0-SNAPSHOT-shaded.jar <inputPath> <numPartition>
          |
          |Arguments:
          |
          |    inputPath        Input OSS object path, like oss://accessKeyId:accessKeySecret@bucket.endpoint/a/b.txt
          |    numPartitions    the number of RDD partitions.
          |
        """.stripMargin)
      System.exit(1)
    }

    val inputPath = args(0)
    val numPartitions = args(1).toInt
    val ossData = sc.textFile(inputPath, numPartitions)
    println("The top 10 lines are:")
    ossData.top(10).foreach(println)
  }

  override def getAppName: String = "OSS Sample"
}

複製程式碼

另外針對Spark SQL的場景,阿里雲也提供了yq.aliyun.com/articles/59…的方式進行支援,可以通過SparkSQL的方式對單檔案檢索和查詢。程式碼倉庫地址。特別注意:當使用Spark Operator的方式進行任務執行是,需要在Driver Pod與Exector Pod的CLASSPATH下預置好相應的Jar包。

OSS的方式主要面向單個檔案在百兆之下,檔案數目比較多的場景優化較好,資料儲存是幾種常見儲存中最便宜的,支援冷熱資料的分離,主要面向讀多寫少或者不寫的場景。

HDFS檔案儲存

阿里雲上新推出了DFS服務,可以像在Hadoop分散式檔案系統 (Hadoop Distributed File System) 中管理和訪問資料。無需對現有大資料分析應用做任何修改,即可使用具備無限容量及效能擴充套件、單一名稱空間、多共享、高可靠和高可用等特性的分散式檔案系統。

DFS服務相容HDFS協議,開發者只需將相應的呼叫Jar包放置在Driver Pod與Exector Pod的CLASSPATH中即可,呼叫時可以如下的方式進行呼叫。

/* SimpleApp.scala */
import org.apache.spark.sql.SparkSession

object SimpleApp {
  def main(args: Array[String]) {
    val logFile = "dfs://f-5d68cc61ya36.cn-beijing.dfs.aliyuncs.com:10290/logdata/ab.log"
    val spark = SparkSession.builder.appName("Simple Application").getOrCreate()
    val logData = spark.read.textFile(logFile).cache()
    val numAs = logData.filter(line => line.contains("a")).count()
    val numBs = logData.filter(line => line.contains("b")).count()
    println(s"Lines with a: $numAs, Lines with b: $numBs")
    spark.stop()
  }
}
複製程式碼

DFS服務的方式主要是面向高IO讀寫的熱資料場景,價格會高於OSS儲存,但低於Nas以及其他結構化儲存。對於已經習慣了HDFS的開發者而言,是最佳的的方案。在所有的儲存方案中,目前IO效能最佳,同容量場景,IO優於本地盤。

常規檔案儲存

OSS的方式對於某些場景而言,資料的上傳與傳輸依賴SDK,操作會略顯不便。那麼Nas也是一種備選的方案,Nas的本身的協議是強一致性的,開發者可以像操作本地檔案的方式,讀寫資料。使用方式如下:

  1. 首先在容器服務控制檯建立Nas相關的儲存PV與PVC。
Spark in action on Kubernetes - 儲存篇(一)
  1. 然後在Spark Operator的定義中宣告所使用的PodVolumeClain。
apiVersion: "sparkoperator.k8s.io/v1alpha1"
kind: SparkApplication
metadata:
  name: spark-pi
  namespace: default
spec:
  type: Scala
  mode: cluster
  image: "gcr.io/spark-operator/spark:v2.4.0"
  imagePullPolicy: Always
  mainClass: org.apache.spark.examples.SparkPi
  mainApplicationFile: "local:///opt/spark/examples/jars/spark-examples_2.11-2.4.0.jar"
  restartPolicy:
    type: Never
  volumes:
  - name: pvc-nas
    persistentVolumeClaim:
        claimName: pvc-nas
  driver:
    cores: 0.1
    coreLimit: "200m"
    memory: "512m"
    labels:
      version: 2.4.0
    serviceAccount: spark
    volumeMounts:
      - name: "pvc-nas"
        mountPath: "/tmp"
  executor:
    cores: 1
    instances: 1
    memory: "512m"
    labels:
      version: 2.4.0
    volumeMounts:
      - name: "pvc-nas"
        mountPath: "/tmp"    
複製程式碼

當然對於Kubernetes比較熟悉的開發者,同樣可以使用動態儲存的方式直接掛載。具體文件地址如下。

Nas儲存的方式在Spark的場景下用途比較少,主要是因為在IO方面與HDFS有一定差距,在儲存價格方面比OSS也貴了不少。不過對於需要複用一些data workflow的產生結果,且IO要求要求不是特別高的場景,Nas的使用還是非常簡單的。

其他儲存結構

在Spark Streaming的場景中,我們還經常使用例如mns或者kafka,有的時候也會使用Elasticsearch與Hbase等等。這些在阿里雲上面也都有對應的服務支援,開發者可以通過這些雲服務的整合與使用,將精力更多的放在資料開發上。

最後

本文主要和大家探討了當容器遇到大資料後,改如何通過儲存與計算分離的方式,降低資源的使用成本,通過不同的場景,選擇合適的儲存方式,實現雲原生的大資料容器化計算。在下一篇文章中,我們會為大家介紹如何通過彈性的方式,在儲存和計算分離的場景下,實現計算資源池的成本節約。


本文作者:莫源

原文連結

本文為雲棲社群原創內容,未經允許不得轉載。


相關文章