在Kubernetes中運行Spark集羣

在Kubernetes中運行Spark集羣

概述

Spark是新一代分佈式內存計算框架,Apache開源的頂級項目。相比於Hadoop Map-Reduce計算框架,Spark將中間計算結果保留在內存中,速度提高10~100倍;同時它還提供更豐富的算子,採用彈性分佈式數據集(RDD)實現迭代計算,更好地適用於數據挖掘、機器學習算法,極大提高開發效率。node

Docker是輕量級虛擬化容器技術,具備輕便性、隔離性、一致性等特色,能夠極大簡化開發者的部署運維流程,下降服務器成本。python

Kubernetes是Google開源的容器集羣管理系統,提供應用部署、維護、 擴展等功能,可以方便地管理大規模跨主機的容器應用。nginx

    相比於在物理機上部署,在Kubernetes集羣上部署Spark集羣,具備如下優點:git

  • 快速部署:安裝1000臺級別的Spark集羣,在Kubernetes集羣上只需設定worker副本數目replicas=1000,便可一鍵部署。
  • 快速升級:升級Spark版本,只需替換Spark鏡像,一鍵升級。
  • 彈性伸縮:須要擴容、縮容時,自動修改worker副本數目replicas便可。
  • 高一致性:各個Kubernetes節點上運行的Spark環境一致、版本一致
  • 高可用性:若是Spark所在的某些node或pod死掉,Kubernetes會自動將計算任務,轉移到其餘node或建立新pod。
  • 強隔離性:經過設定資源配額等方式,可與WebService應用部署在同一集羣,提高機器資源使用效率,從而下降服務器成本。

參考文檔:https://github.com/kubernetes/kubernetes/tree/master/examples/sparkgithub

接下來咱們將介紹,如何使用 Docker和Kubernetes建立Spark集羣(其中包含1個Spark-master和N個Spark-worker)。web

首先須要準備如下工具:算法

  • 安裝並運行 kubernetes 集羣
  • 安裝 kubectl 命令行工具 

1、構建Docker鏡像

1. 從源碼build:

下載:https://github.com/kubernetes/application-images/blob/master/sparkdocker

docker build -t index.caicloud.io/spark:1.5.2 .
docker build -t index.caicloud.io/zeppelin:0.5.6 zeppelin/

鏈接國外網絡較慢,構建鏡像的時間有些長,請耐心等待……後端

2. 從鏡像倉庫pull:

a)  才雲科技的鏡像倉庫(index.caicloud.io)api

docker login index.caicloud.io 
docker pull index.caicloud.io/spark:1.5.2 
docker pull index.caicloud.io/zeppelin:0.5.6

b) DockerHub的鏡像倉庫(index.docker.io)

docker login index.docker.io
docker pull index.docker.io/caicloud/spark:1.5.2
docker pull index.docker.io/caicloud/zeppelin:0.5.6

 

2、在Kubernetes上建立Spark集羣

首先下載建立Kubernetes應用所需的Yaml文件: https://github.com/caicloud/public/tree/master/spark

第一步:建立命名空間namespace

    Kubernetes經過命名空間,將底層的物理資源劃分紅若干個邏輯的「分區」,然後續全部的應用、容器都是被部署在一個具體的命名空間裏。每一個命名空間能夠設置獨立的資源配額,保證不一樣命名空間中的應用不會相互搶佔資源。此外,命名空間對命名域實現了隔離,所以兩個不一樣命名空間裏的應用能夠起一樣的名字。建立命名空間須要編寫一個yaml文件:

# namespace/namespace-spark-cluster.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: "spark-cluster"
  labels:
    name: "spark-cluster"

 

建立Namespace:
$ kubectl create -f namespace/namespace-spark-cluster.yaml

查看Namespace:
$ kubectl get ns
NAME          LABELS             STATUS
default       <none>             Active
spark-cluster name=spark-cluster Active

使用Namespace: (${CLUSTER_NAME}和${USER_NAME}可在kubeconfig文件中查看)
$ kubectl config set-context spark --namespace=spark-cluster --cluster=${CLUSTER_NAME} --user=${USER_NAME}
$ kubectl config use-context spark

 

第二步:啓動master服務

    先建立Spark-master的ReplicationController,而後建立spark所提供的兩個Service(spark-master-service,spark-webui)。讓Spark-workers使用spark-master-service來鏈接Spark-master,而且經過spark-webui來查看集羣和任務運行狀態。

1) 建立ReplicationController:

    Kubernetes追求高可用設計,經過Replication Controller來保證每一個應用時時刻刻會有指定數量的副本在運行。例如咱們經過編寫一個Replication Controller來運行一個nginx應用,就能夠在yaml中指定5個默認副本。Kubernetes會自動運行5個nginx副本,並在後期時時對每個副本進行健康檢查(能夠支持自定義的檢查策略)。當發現有副本不健康時,Kubernetes會經過自動重啓、遷移等方法,保證nginx會時刻有5個健康的副本在運行。對於spark-master,目前咱們指定其副本數爲1 (replicas: 1);對於spark-worker,咱們指定其副本數爲N (replicas: N,N >= 1)。

    spark-master-controller.yaml可參考以下:

# replication-controller/spark-master-controller.yaml
kind: ReplicationController
apiVersion: v1
metadata:
  name: spark-master-controller
spec:
  replicas: 1
  selector:
    component: spark-master
  template:
    metadata:
      labels:
        component: spark-master
    spec:
      containers:
        - name: spark-master
          image: index.caicloud.io/spark:1.5.2
          command: ["/start-master"]
          ports:
            - containerPort: 7077
            - containerPort: 8080
          resources:
            requests:
              cpu: 100m

 

建立Master-ReplicationController:
$ kubectl create -f replication-controller/spark-master-controller.yaml
replicationcontroller "spark-master-controller" created

 

2) 建立Master-Service:

    Kubernetes追求以服務爲中心,並推薦爲系統中的應用建立對應的Service。以nginx應用爲例,當經過Replication Controller建立了多個nginx的實例(容器)後,這些不一樣的實例可能運行在不一樣的節點上,而且隨着故障和自動修復,其IP可能會動態變化。爲了保證其餘應用能夠穩定地訪問到nginx服務,咱們能夠經過編寫yaml文件爲nginx建立一個Service,並指定該Service的名稱(如nginx-service);此時,Kubernetes會自動在其內部一個DNS系統中(基於SkyDNS 和etcd實現)爲其添加一個A Record, 名字就是 「nginx-service」。隨後,其餘的應用能夠經過 nginx-service來自動尋址到nginx的一個實例(用戶能夠配置負載均衡策略)。
    spark-master-service.yaml可參考以下:

# service/spark-master-service.yaml
kind: Service
apiVersion: v1
metadata:
  name: spark-master
spec:
  ports:
    - port: 7077
      targetPort: 7077
  selector:
    component: spark-master

 

建立Master-Service:
$ kubectl create -f service/spark-master-service.yaml
service "spark-master"created

 

3) 建立WebUI-Service:

    如上所述,Service會被映射到後端的實際容器應用上,而這個映射是經過Kubernetes的標籤以及Service的標籤選擇器實現的。例如咱們能夠經過以下的spark-web-ui.yaml來建立一個WebUI的Service, 而這個Service會經過 「selector: component: spark-master」來把WebUI的實際業務映射到master節點上:

# service/spark-webui.yaml
kind: Service
apiVersion: v1
metadata:
  name: spark-webui
  namespace: spark-cluster
spec:
  ports:
    - port: 8080
      targetPort: 8080
  selector:
    component: spark-master

 

建立spark-webui-service:
$ kubectl create -f service/spark-webui.yaml
service "spark-webui" created

完成建立ReplicationController(rc)、Service(svc)後,檢查 Master 是否能運行和訪問:

$ kubectl get rc
NAME                      DESIRED   CURRENT   AGE
spark-master-controller   1         1         23h

$ kubectl get svc
NAME           CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
spark-master   10.254.106.29   <none>        7077/TCP   1d
spark-webui    10.254.66.138   <none>        8080/TCP   18h

$ kubectl get pods
NAME                            READY     STATUS    RESTARTS   AGE
spark-master-controller-b3gbf   1/1       Running   0          23h

確認master正常運行後,再使用Kubernetes proxy鏈接Spark WebUI:

kubectl proxy --port=8001

而後經過http://localhost:8001/api/v1/proxy/namespaces/spark-cluster/services/spark-webui/查看spark的任務運行狀態。

第三步:啓動 Spark workers

    Spark workers 啓動時須要 Master service處於運行狀態,咱們能夠經過修改replicas來設定worker數目(好比設定 replicas: 4,便可創建4個Spark Worker)。注意咱們能夠爲每個worker節點設置了CPU和內存的配額,保證Spark的worker應用不會過分搶佔集羣中其餘應用的資源。

    spark-worker-controller.yaml可參考以下:

# replication-controller/spark-worker-controller.yaml
kind: ReplicationController
apiVersion: v1
metadata:
  name: spark-worker-controller
spec:
  replicas: 4
  selector:
    component: spark-worker
  template:
    metadata:
      labels:
        component: spark-worker
    spec:
      containers:
        - name: spark-worker
          image: index.caicloud.io/spark:1.5.2
          command: ["/start-worker"]
          ports:
            - containerPort: 8081
          resources:
            requests:
              cpu: 100m

建立spark-worker的ReplicationController:

$ kubectl create -f replication-controller/spark-worker-controller.yaml
replicationcontroller"spark-worker-controller" created

 

查看 workers是否正常運行

1.     經過WebUI查看: worker就緒後應該出如今UI中。(這可能須要一些時間來拉取鏡像並啓動pods)

2.   經過kubectl查詢狀態(可看到spark-worker都已經正常運行):

</pre><p><pre name="code" class="plain">$ kubectl get pods
NAME                            READY     STATUS              RESTARTS   AGE
spark-master-controller-b3gbf   1/1       Running             0          1d
spark-worker-controller-ill4z   1/1       Running             1          2h
spark-worker-controller-j29sc   1/1       Running             0          2h
spark-worker-controller-siue2   1/1       Running             0          2h
spark-worker-controller-zd5kb   1/1       Running             0          2h

 

第四步:提交Spark任務

兩種方式:Spark-client或Zeppelin。

經過Spark-client,能夠利用spark-submit來提交複雜的Python腳本、Java/Scala的jar包代碼;

經過Zeppelin,能夠直接在命令行或UI編寫簡單的spark代碼。

1.     經過Spark-client提交任務

$ kubectl get pods | grep worker
NAME                            READY     STATUS    RESTARTS   AGE
spark-worker-controller-1h0l7   1/1       Running   0          4h
spark-worker-controller-d43wa   1/1       Running   0          4h
spark-worker-controller-ka78h   1/1       Running   0          4h
spark-worker-controller-sucl7   1/1       Running   0          4h
$ kubectl exec spark-worker-controller-1h0l7 -it bash
$ cd /opt/spark
# 提交python spark任務
./bin/spark-submit \
    --executor-memory 4G \
    --master spark://spark-master:7077 \
    examples/src/main/python/wordcount.py \
    "hdfs://hadoop-namenode:9000/caicloud/spark/data"
# 提交scala spark任務
./bin/spark-submit 
    --executor-memory 4G
    --master spark://spark-master:7077
    --class io.caicloud.LinearRegression
    /nfs/caicloud/spark-mllib-1.0-SNAPSHOT.jar
    "hdfs://hadoop-namenode:9000/caicloud/spark/data"

 

2.   經過Zeppelin提交任務

    使用Zeppelin提交時有兩種方式:pod exec 和 zeppelin-UI。咱們先建立zeppelin的ReplicationController:

$ kubectl create -f replication-controller/zeppelin-controller.yaml
replicationcontroller "zeppelin-controller"created
查看zeppelin:
$ kubectl get pods -l component=zeppelin
NAME                        READY     STATUS    RESTARTS   AGE
zeppelin-controller-5g25x   1/1       Running   0          5h

 

a) 經過zeppelin exec pods方式提交

$ kubectl exec zeppelin-controller-5g25x -it pyspark


b) 經過zeppelin UI方式提交

使用已建立的Zeppelin pod,設置WebUI的映射端口:

$ kubectl port-forward zeppelin-controller-5g25x 8080:8080

訪問 http://localhost:8080/,並提交測試代碼:

 

使用Spark的問題

●  Spark的master與worker沒法通訊

解決辦法:查看Spark Master Service是否正常

●  Spark資源與調度問題

a) worker數量不要超過kubernetes集羣的節點數量(若是超過,spark性能會降低)

b) executor-memory不要超過機單臺器最大內存,executor-cores不要超過單臺機器的CPU核數

c) 若是遇到insufficient resources問題,檢查是否有其餘任務在運行並kill

d) 同一個kubernetes節點上,可能會分配多個worker,致使性能降低(這時最好從新建立worker)

e) 內存、磁盤IO、網絡IO、CPU均可能成爲Spark的性能瓶頸

●  任務運行時的問題

SocketTimeoutException: Accept timeout —— spark版本爲1.5.2時,試試將jdk版本由1.8下降至1.7

使用Zeppelin的問題

●  Zeppelin pod很大,拉取鏡像可能會消耗一段時間,取決於你的網絡條件

● 第一次運行Zeppelin時, pipeline可能會花費不少時間(約一分鐘),須要比較多的時間來初始化。

● kubectl port-forward可能不會長時間保持穩定狀態。若是發現Zeppelin變成斷開(disconnected),port-forward極可能出現故障,這時須要重啓

● 從ZeppelinUI提交任務,運行時間不穩定(波動較大)

 

爲了測試Spark性能,咱們須要使用大量數據,但kubernetes節點所在的機器磁盤空間有限(20G)。經過爲每臺機器掛載NFS/GlusterFS大網盤,解決大數據存儲的問題。

接下來咱們將介紹,如何使用Spark從NFS中讀取數據,以及其中遇到的問題。

1. 首先爲每一個kubernetes節點安裝nfs-client,掛載nfs網盤(假設NFS-server爲10.57.*.33,數據存於/data1T5目錄下)。

$ sudo apt-get install nfs-common
$ sudo mkdir -p /data1T5
$ sudo mount -t nfs 10.57.*.33:/data1T5 /data1T5

2. 聲明PersistentVolumes(pv)和PersistentVolumeClaims(pvc),使得Kubernetes能鏈接NFS數據網盤:

$ kubectl create -f nfs/nfs_pv.yaml
$ kubectl create -f nfs/nfs_pvc.yaml
# nfs/nfs_pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
    name: spark-cluster
spec:
    capacity:
        storage: 1535Gi
    accessModes:
        - ReadWriteMany
    nfs:
        server: 10.57.*.33
        path: "/data1T5"
# nfs/nfs_pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: spark-cluster
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1535Gi

3. 爲Spark的ReplicationController增長volumeMounts和volumes兩項,並從新create,使得Spark容器能訪問NFS數據。

修改spark-master-controller.yaml和spark-worker-controller.yaml,存爲nfs/spark-master-controller.nfs.yaml 和 nfs/spark-worker-controller.nfs.yaml

$ kubectl delete rc spark-master-controller
$ kubectl create -f nfs/spark-master-controller.nfs.yaml
$ kubectl delete rc spark-worker-controller
$ kubectl create -f nfs/spark-worker-controller.nfs.yaml

4. 使用exec方式進入master/worker所在的pod,能夠看到nfs掛載的/data1T5目錄:

$ kubectl exec spark-worker-controller-1h0l7 -it bash
root@spark-worker-controller-1h0l7:/# ls -al /data1T5/

成功掛載後,便可在Spark程序中,使用sc.textFile()讀取NFS數據。

注意事項:

a) 若是但願使用spark從nfs中讀取數據,必須事先在kubernetes集羣的全部節點上安裝nfs-client

b) 使用spark從多臺nfs-client讀取一臺nfs-server數據時,遇到IO瓶頸,讀取速率只有30MB~40MB/s

相關文章
相關標籤/搜索