Spark in action on Kubernetes - 儲存篇

阿里系統軟體技術發表於2019-04-09

Spark in action on Kubernetes - 儲存篇

作者 | 阿里雲智慧事業群技術專家 莫源

前言

在上篇文章中,Spark in action on Kubernetes - Spark Operator 的原理解析我們分析了 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 的場景,阿里雲也提供了  的方式進行支援,可以透過 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 - 儲存篇

2. 然後在 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 等等。這些在阿里雲上面也都有對應的服務支援,開發者可以透過這些雲服務的整合與使用,將精力更多的放在資料開發上。

最後

本文主要和大家探討了當容器遇到大資料後,改如何透過儲存與計算分離的方式,降低資源的使用成本,透過不同的場景,選擇合適的儲存方式,實現雲原生的大資料容器化計算。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31555606/viewspace-2640808/,如需轉載,請註明出處,否則將追究法律責任。

相關文章