鏡像下載、域名解析、時間同步請點擊 阿里巴巴開源鏡像站
一、概述
本文主要講 Apache Spark on Kubernetes 的 PodTemplate 配置問題,其中涉及Spark Operator 裡關於 PodTemplate 的問題,以及 Apache Spark 2.2 on Kubernetes 的 Fork 版本問題。
通常 Apache Spark on Kubernetes 在配置 Pod 的時候會有一些限制,比如針對 Pod 的調度,想加個 NodeSelector 或者 Tolerations。這在集群公用,或者有各種類型任務的集群裡,是經常會遇到的情況,而在 Spark 2.x 裡卻是很難實現的。目前最新 Release 的版本 2.4.5 還沒有支持通過 PodTemplate 來自定義 Pod 的配置,而社區的計劃是在 Spark 3.0 的時候實現該功能,他支持的方式其實也比較簡單,就是通過 PodTemplate 的一個文件,去描述 Driver/Executor 的 metadata/spec 字段,這樣就可以在模板文件里加入調度所需要的一些字段。接下來,我們會為大家介紹配置 PodTemplate 的具體方法。
二、配置 PodTemplate
實際上,在 Spark Operator 裡,本身就支持 Pod Template 的配置 SparkPodSpec,也就是說,像 NodeSelector, Tolerations 之類的,可以在創建 CRD 對象的時候在 YAML 上添加上,非常方便,比如下面的例子。
apiVersion: sparkoperator.k8s.io/v1beta2
kind: SparkApplication
metadata:
name: spark-pi
namespace: default
spec:
type: Scala
mode: cluster
image: gcr.io/spark/spark:v2.4.5
mainClass: org.apache.spark.examples.SparkPi
mainApplicationFile: local:///opt/spark/examples/jars/spark-examples_2.11-2.4.5.jar
nodeSelector:
key: value
可以看出,通過 Spark Operator 來配置,是比較靈活的。而在 Apache Spark 3.0 中,PodTemplate 是需要在 spark-submit
階段將模板文件加到 spark.kubernetes.driver.podTemplateFile
或者 spark.kubernetes.executor.podTemplateFile
裡的。
眾所周知, Spark 在下載依賴文件的時候,可以通過 HTTP/HDFS/S3 等協議來下載需要的文件。但是讀取 PodTemplate 的 API,目前只支持本地文件系統(當然要改成支持 http 也不是很複雜),SparkConf 的配置如下:
# template 在本地
spark.kubernetes.driver.podTemplateFile=/opt/spark/template.yaml
spark.kubernetes.executor.podTemplateFile=/opt/spark/template.yaml
關於 Apache Spark 3.0 是如何加載這些 PodTemplate 的文件,我們可以通過源碼看出。在將 PodTemplate 文件加載到系統裡的關鍵方法是是 KubernetesUtils.loadPodFromTemplate()
。
def loadPodFromTemplate(
kubernetesClient: KubernetesClient,
templateFile: File,
containerName: Option[String]): SparkPod = {
try {
// 主要的還是利用 K8S 的客戶端去 load 模板文件
// load 模板文件目前只能支持本地文件系統,因為底層調用的是 File 接口
val pod = kubernetesClient.pods().load(templateFile).get()
// 這裡需要注意會從模板裡把指定 Container 撈出來
// 目的主要是撈出來 Driver 和 Executor 容器
// 否則就是以第一個容器作為 Driver/Executor 的容器
selectSparkContainer(pod, containerName)
} catch {
case e: Exception =>
logError(
s"Encountered exception while attempting to load initial pod spec from file", e)
throw new SparkException("Could not load pod from template file.", e)
}
}
通過上述方法就可以利用 PodTemplate 來做一些 Pod 的定義了,避免了大量極其繁瑣的 SparkConf 的配置。
由於通過 PodTemplate 來引導定義的操作相對來說是比較前置的,所以有些屬性,可能會被後面針對 Pod 的其他配置給 overwrite,在 Spark 的最新文檔的 running-on-kubernetes,可以找到那些屬性可能會被後置配置覆蓋掉。
關於 Spark Driver Pod 配置的具體步驟如下圖所示:
val features = Seq(
new BasicDriverFeatureStep(conf),
new DriverKubernetesCredentialsFeatureStep(conf),
new DriverServiceFeatureStep(conf),
new MountSecretsFeatureStep(conf),
new EnvSecretsFeatureStep(conf),
new MountVolumesFeatureStep(conf),
new DriverCommandFeatureStep(conf),
new HadoopConfDriverFeatureStep(conf),
new KerberosConfDriverFeatureStep(conf),
new PodTemplateConfigMapStep(conf),
new LocalDirsFeatureStep(conf))
val spec = KubernetesDriverSpec(
initialPod,
driverKubernetesResources = Seq.empty,
conf.sparkConf.getAll.toMap)
features.foldLeft(spec) { case (spec, feature) =>
val configuredPod = feature.configurePod(spec.pod)
val addedSystemProperties = feature.getAdditionalPodSystemProperties()
val addedResources = feature.getAdditionalKubernetesResources()
KubernetesDriverSpec(
configuredPod,
spec.driverKubernetesResources ++ addedResources,
spec.systemProperties ++ addedSystemProperties)
}
看完整個過程,可以發現,裝配 Driver Pod 的步驟非常複雜。主要是延續了 Spark 2.2 on K8S 那個 Fork 的思路。具體如下:
- 通過自定義鏡像,將 PodTemplate 文件置入鏡像的某個目錄中,如
/opt/spark/template.yaml
- 在 SparkConf 填入參數
spark.kubernetes.driver.podTemplateFile=/opt/spark/template/driver.yaml
- 如果 Pod 裡準備用其他容器,則需要在 SparkConf 指定 Driver Container 的名字,例如:
spark.kubernetes.driver.podTemplateContainerName=driver-container
三、運行
下面給出一個例子,來給 Spark 的 Drvier/Executor 都加一個 initContainer,將 PodTemplate 文件 template-init.yaml
放在 /opt/spark
目錄下,下面是 PodTemplate 的具體內容,即添加一個會 sleep 1s
的 initContainer。
apiversion: v1
kind: Pod
spec:
initContainers:
- name: init-s3
image: hub.oa.com/runzhliu/busybox:latest
command: ['sh', '-c', 'sleep 1']
SparkConf 裡添加以下內容:
spark.kubernetes.driver.podTemplateFile=/opt/spark/template-init.yaml
spark.kubernetes.executor.podTemplateFile=/opt/spark/template-init.yaml
配置完成後,運行一個 SparkPi 的例子,spark-submit
命令如下:
/opt/spark/bin/spark-submit
--deploy-mode=cluster
--class org.apache.spark.examples.SparkPi
--master=k8s://https://172.17.0.1:443
--conf spark.kubernetes.namespace=demo
--conf spark.kubernetes.driver.container.image=hub.oa.com/public/spark:v3.0.0-template
--conf spark.kubernetes.executor.container.image=hub.oa.com/public/spark:v3.0.0-template
--conf=spark.driver.cores=1
--conf=spark.driver.memory=4096M
--conf=spark.executor.cores=1
--conf=spark.executor.memory=4096M
--conf=spark.executor.instances=2
--conf spark.kubernetes.driver.podTemplateFile=/opt/spark/template-init.yaml
--conf spark.kubernetes.executor.podTemplateFile=/opt/spark/template-init.yaml
--conf=spark.kubernetes.executor.deleteOnTermination=false
local:///opt/spark/examples/jars/spark-examples_2.12-3.0.0-SNAPSHOT.jar
100
運行結束,查看一下 Driver Pod 的 YAML 文件,發現 initContainer 已經加上,並且運行正常。
...
initContainers:
- command:
- sh
- -c
- sleep 1
image: hub.oa.com/runzhliu/busybox:latest
imagePullPolicy: Always
name: init
resources: {}
...
initContainerStatuses:
- containerID: docker://526049a9a78c4b29d4e4f7b5fcc89935d44c0605bcbf427456c7d7bdf39a6172
image: hub.oa.com/runzhliu/busybox:latest
lastState: {}
name: init
ready: true
restartCount: 0
state:
terminated:
containerID: docker://526049a9a78c4b29d4e4f7b5fcc89935d44c0605bcbf427456c7d7bdf39a6172
exitCode: 0
finishedAt: "2020-04-02T00:03:35Z"
reason: Completed
startedAt: "2020-04-02T00:03:34Z"
注意:PodTemplate 文件裡大小寫要符合 Kubernetes 的規範,比如 Pod 不能寫成 pod,initContainer 不能寫成 initcontainer,否則是不生效的。
四、總結
Apache Spark 3.0 支持 PodTemplate,所以用戶在配置 Driver/Executor 的 Pod 的時候,會更加靈活,但是 Spark 本身是不會校驗 PodTemplate 的正確性的,所以這也給調試帶來了很多麻煩。關於 NodeSelector, Taints, Tolerations 等,這些字段在 Spark Operator 中設置,倒是比較方便的。
“ 阿里巴巴開源鏡像站 提供全面,高效和穩定的系統鏡像、應用軟件下載、域名解析和時間同步服務。”