[K8S] On-Demand Spark Cluster on AKS
最近在研究如何在 K8S 上面跑一個 On-Demand 的 Spark Cluster 服務,基本上有兩條路可以走,一條是利用 k8s 的 Deployment 來自建 Spark Cluster,另外一條路則是利用 Kubernetes 既有與 Spark 對接的介面 (這邊是利用 spark-submit) 來實作,概念上就是直接執行一個類似下方的指令,所以想要擁有一個 On-Demand Spark Cluster on AKS 這兩種方法個有什麼優劣?
$ ./bin/spark-submit \
--master k8s://https://<k8s-apiserver-host>:<k8s-apiserver-port> \
--deploy-mode cluster \
--name spark-pi \
--class org.apache.spark.examples.SparkPi \
--conf spark.executor.instances=5 \
--conf spark.kubernetes.container.image=<spark-image> \
local:///path/to/examples.jar
方法一:利用 K8S 中的 Deployment 與 Services 自建 Spark Cluster
自建 Spark Cluster 的方法又可以分成兩個方法,分別是 Single-Pod 與 Multiple-Pods 兩種,Single-Pods 的部分,🍋 爸嘗試去利用以下的 Yaml 設定檔去產生一個Pod 包含多個 containers,但是由於使用的是 Azure Standard_D13_v2 的 VM 所以以下 Pod 索要的資源超過 8 cpus,因此無法成功開啟。備註:Microsoft 的工程師曾經實作部署一個 Pod 跨在不同的節點上,這邊還無法釐清是否能夠將同一個 Pod 的不同 container 部署在不同的節點上?
備註:後來從 Microsoft 的 Documentation 得知同一個 Pod 雖然是可以有多個 container 但是只能夠在同一個 node 上面。
apiVersion: v1
kind: Pod
metadata:
name: spark-test
spec:
containers:
- name: master
image: docker.io/spark:3.0.1
ports:
- containerPort: 8080
- containerPort: 7077
resources:
limits:
cpu: "3"
requests:
cpu: "3"
imagePullPolicy: IfNotPresent
command: ["/bin/sh", "-ec", "while :; do echo '.'; sleep 5 ; done"]
- name: worker-1
image: docker.io/spark:3.0.1
- containerPort: 8081
resources:
limits:
cpu: "3"
requests:
cpu: "3"
imagePullPolicy: IfNotPresent
command: ["/bin/sh", "-ec", "while :; do echo '.'; sleep 5 ; done"]
- name: worker-2
image: docker.io/spark:3.0.1
ports:
- containerPort: 8081
resources:
limits:
cpu: "3"
requests:
cpu: "3"
imagePullPolicy: IfNotPresent
command: ["/bin/sh", "-ec", "while :; do echo '.'; sleep 5 ; done"]
imagePullSecrets:
- name: acr-secret
關於 Multiple-Pods 檸檬爸主要是參考連結,https://testdriven.io/blog/deploying-spark-on-kubernetes/ 實作了一個由一個 Master 與多個 Worker 所組成的 Spark Cluster,基本上是使用多個 Pods,然後再由 Ingress 將 Spark WebUI 導出。
流程簡單可以分成幾個步驟:
- kubectl create -f ./spark-master-deployment.yaml 建立 Master Node
- kubectl create -f ./spark-master-service.yaml 將 master node 的 port 建立成一個 Service
- kubectl create -f ./spark-worker-deployment.yaml 建立 worker node 並且利用 Service 去連結 Master Node
分別列出 spark-master-deployment.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
name: spark-master-1
spec:
replicas: 1
selector:
matchLabels:
component: spark-master
template:
metadata:
labels:
component: spark-master
spec:
containers:
- name: spark-master
image: docker.io/spark:3.0.1
command: ["/bin/sh", "-ec"]
args: ["/home/spark-current/sbin/start-master.sh && while :; do echo '.'; sleep 5 ; done"]
ports:
- containerPort: 7077
- containerPort: 8080
resources:
requests:
cpu: 100m
spark-master-service.yaml
kind: Service
apiVersion: v1
metadata:
name: spark-master
spec:
ports:
- name: webui
port: 8080
targetPort: 8080
- name: spark
port: 7077
targetPort: 7077
selector:
component: spark-master
spark-worker-deployment.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
name: spark-worker
spec:
replicas: 6
selector:
matchLabels:
component: spark-worker
template:
metadata:
labels:
component: spark-worker
spec:
containers:
- name: spark-worker-1
image: docker.io/spark:3.0.1
env:
- name: MASTER_SPARK_URL
value: spark-master
command: ["/bin/sh", "-c"]
args: ["/home/spark-current/sbin/start-slave.sh spark://spark-master:7077 && while true; do sleep 3000; done"]
ports:
- containerPort: 8081
resources:
requests:
cpu: 100m
imagePullSecrets:
- name: acr-secret
最後利用 kubectl port-forward spark-master-7f548444d6-vdk9s 8080:8080 可以看到 Spark Master 的 WebUI.
備註:這邊需要特別注意,在 spark cluster 建置完成之後,下達執行工作之前,需要特別標注兩個 configurations (spark.driver.bindAddress, spark.driver.host),如以下指令所示:
kubectl exec -it spark-master-xxxxxx -- spark-submit \
--conf spark.driver.bindAddress=10.244.3.16 \
--conf spark.driver.host=10.244.3.16 \
--conf spark.dynamicAllocation.enabled=false \
--class org.apache.spark.examples.SparkPi \
--master spark://spark-master:7077 \
/opt/spark/examples/jars/spark-examples_2.12-3.0.2.jar \
1000
否則會一直出現 TaskSchedulerImple 的錯誤訊息:
WARN TaskSchedulerImpl: Initial job has not accepted any resources; check your cluster UI to ensure that workers are registered and have sufficient resources
方法二:使用 spark-submit 交付工作到 Kubernetes
因應 k8s 社群裡面想要使用 Spark 技術的需求,k8s 也有支援直接從 spark-submit 將 spark job 送到 k8s 平台的選項,有興趣的讀者可以參考下面的 Youtube 影片介紹 spark + kubernetes。🍋 爸在參考官網上的資料之後(主要是AWS的服務的範例),在這邊主要記錄一些實作 spark-submit + azure kubernetes services 遇到的問題。
讀者可以直接使用成功送到 AKS 上的指令:
spark-submit --master k8s://http://127.0.0.1:8001 \
--deploy-mode cluster \
--name spark-pi \
--class org.apache.spark.examples.SparkPi \
--packages org.apache.hadoop:hadoop-azure:3.3.1,org.apache.hadoop:hadoop-common:3.3.1 \
--conf spark.kubernetes.authenticate.driver.serviceAccountName=spark \
--conf spark.hadoop.fs.azure.account.auth.type=OAuth \
--conf spark.hadoop.fs.azure.account.oauth.provider.type=org.apache.hadoop.fs.azurebfs.oauth2.ClientCredsTokenProvider \
--conf spark.hadoop.fs.azure.account.oauth2.client.id=xxxx\
--conf spark.hadoop.fs.azure.account.oauth2.client.secret=xxxx \
--conf spark.hadoop.fs.azure.account.oauth2.client.endpoint=https://login.microsoftonline.com/xxxx/oauth2/token \
--conf spark.executor.instances=3 \
--conf spark.kubernetes.file.upload.path=abfss://test@testaccount.dfs.core.windows.net/\
--conf spark.kubernetes.container.image=docker.io/xxxx/spark:3.0.3 \
--conf spark.driver.extraJavaOptions="-Divy.cache.dir=/tmp -Divy.home=/tmp" \
~/spark-3.0.3-bin-hadoop3.2/examples/jars/spark-examples_2.12-3.0.3.jar
Notes:
- 首先使用 kubectl proxy 將 localhost:8001 指向遠端的 Azure Kubernetes Services。
- 須提供 spark.kubernetes.file.upload.path 否則會報錯,由於我們是使用 AKS,所以提供以 abfss:// 開頭的路徑,要跑 Spark Job 所需要用到的 Jar 檔,最終會傳到這個 upload 的路徑。
- 加上 –package 並附上 hadoop-azure, hadoop-common 兩個函式庫。
- 配合第二點需要加上 auth.type, oauth.provider.type, cliend.id, client.secret, client.endpoint 等五個憑據權限。
- –conf 一定要有 spark.kubernetes.container.image 並且一定要是 spark package 裡面 build 出來的映像檔。
- –conf 要加 spark.driver.extraJavaOptions=”-Divy.cache.dir=/tmp -Divy.home=/tmp”
Pros and Cons of these two methods:
Method 1 | Method 2 | |||
Preparation of Docker Image | 👍🏻 | Compatible with existing docker images |
👎🏻 | Need to rebuild all docker images based on specific rules |
Control Flexibity | 👍🏻 | Concept of Worker Node is clear | 👎🏻 | Concept of Worker Node is not clear |
Complexity of Workflow Management | 👎🏻 | Creation, Deletion, Monitoring of each Cluster needs to be well managed. | 👍🏻 | Easy to manage. Just submit and wait. |
One thought on “[K8S] On-Demand Spark Cluster on AKS”