【spark sedona k8s】spark(sedona) on kubernetes(k8s) 搭建,使用spark-operator提交任务
引言
成功搭建
spark on yarn
后,总是觉得它与我们的服务之间不好联动。之前我们的服务都运行在docker
之中,spark
又运行在hadoop
中,我们要想通过服务控制集群执行任务,结合现在的情况,已有的解决方案十分麻烦,要么就是部署一个服务在hadoop
的master
节点上,通过系统命令来控制,要么就是安装第三方工具。总之不管哪种办法我都觉得不够好,于是便想到在k8s
上跑spark
。
一、架构介绍
相较于之前的
spark
由yarn
调度,使用k8s
调度最大的好处是可以弹性伸缩,整个k8s
集群中除了跑其他服务以外还能跑spark
任务,其他服务不忙的时候,可以给spark
的任务多一点资源。又因为k8s
是自带REST Api
的,所以我们在k8s
上跑的服务可以通过访问ApiServer
接口的方式直接向k8s
提交spark
任务。它的流程看起来像这样:
这样看起来就很优雅了,将
spark
的调度交给k8s
,k8s
能动态管理我们的任务所需资源,我们与spark
的交互也可以挪到java
服务上,k8s
的健壮性、容错性我就不多提了,集群搭建起来也挺简单的。接下来我们就开始搭建spark on k8s
并且开启spark operator
管理任务。
二、搭建
在正式开始之前,你得有一个能正常运行的
k8s
集群,接下来的操作默认你有了一个能正常运行的k8s
集群。如果你的集群还没搭建起来,参考我的 另外一篇帖子,先搭建集群。
1、安装 helm
我们可以通过很多方式将
Spark Operator
引入到集群中,可以手动下载源代码来安装,也可以使用kustomize
来安装,哪种方式都可以,Spark Operator
官方 GitHub地址
我们用
helm
,所以得先安装一个helm
,执行:
sudo apt update
sudo apt install -y curl apt-transport-https
curl https://baltocdn.com/helm/signing.asc | sudo apt-key add -
echo "deb https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt-get update
sudo apt-get install helm
2、添加 helm
仓库,安装 Spark Operator
需要将
Spark Operator
所在的仓库地址添加进去。Bitnami
提供了Spark Operator
的Helm chart
。使用以下命令添加Bitnami
仓库:
helm repo add spark-operator https://kubeflow.github.io/spark-operator
helm repo update
安装
Spark Operator
:
kubectl create namespace spark-operator # 创建一个命名空间
helm install spark-operator spark-operator/spark-operator \
--namespace spark-operator \
--set image.repository=docker.io/kubeflow/spark-operator \ # 在这里指定镜像所在的仓库
--set image.tag=v1beta2-1.4.6-3.5.0 \ # 可以在这里指定要安装的版本,但是前提是镜像仓库要存在
--set sparkJobNamespace=default
如果安装报错最后一句是这句:
ensure CRDs are installed first
执行这个安装
CRDs
:
kubectl create -f https://raw.githubusercontent.com/GoogleCloudPlatform/spark-on-k8s-operator/master/manifest/crds/sparkoperator.k8s.io_sparkapplications.yaml
kubectl create -f https://raw.githubusercontent.com/GoogleCloudPlatform/spark-on-k8s-operator/master/manifest/crds/sparkoperator.k8s.io_scheduledsparkapplications.yaml
这是一个在线地址,你也可以将源代码拉下来执行:
git clone https://github.com/GoogleCloudPlatform/spark-on-k8s-operator.git
cd spark-on-k8s-operator
kubectl apply -f manifest/crds/sparkoperator.k8s.io_sparkapplications.yaml
kubectl apply -f manifest/crds/sparkoperator.k8s.io_scheduledsparkapplications.yaml
在线地址执行和拉下来执行的效果是一样的,安装好
CRDs
后再执行上面的安装
执行完后去看看
Pod
的状态,要没有报错:
kubectl get pods -n spark-operator
3、配置账户信息
要想执行
Spark
任务,我们在提交任务的时候必需得指定一个k8s
账户,它得绑定一个有权限的角色,才能有权限向k8s
集群申请资源,创建一个账户:
vi spark-account.yaml
加入内容:
apiVersion: v1
kind: ServiceAccount
metadata:
name: spark-account
namespace: spark-operator
创建一个角色绑定,我们将刚创建的账户绑定到集群管理员角色:
vi spark-role-binding.yaml
加入内容:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: spark-cluster-role-binding
subjects:
- kind: ServiceAccount
name: spark-account
namespace: spark-operator
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
分别执行两个文件,注意先后顺序:
kubectl apply -f spark-account.yaml
kubectl apply -f spark-role-binding.yaml
如果你觉得绑定管理员角色不妥,那么可以自定义一个角色,加入内容大概像:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: my-role
rules:
- apiGroups: [""]
resources: ["pods", "pods/log", "services", "configmaps", "secrets", "persistentvolumeclaims"]
verbs: ["get", "list", "create", "delete", "watch"]
- apiGroups: ["batch"]
resources: ["jobs"]
verbs: ["get", "list", "create", "delete", "watch"]
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "create", "delete", "watch"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "create", "delete", "watch"]
4、测试
编写一个定义
Spark on K8s
任务的yaml
文件,加入内容:
apiVersion: "sparkoperator.k8s.io/v1beta2"
kind: SparkApplication
metadata:
name: spark-pi
namespace: default
spec:
type: Scala
mode: cluster
image: docker.io/spark:3.5.1 # 这里与我们安装的spark operator版本不一样,但是我使用没问题
imagePullPolicy: Always
mainClass: org.apache.spark.examples.SparkPi
mainApplicationFile: "local:///opt/spark/examples/jars/spark-examples_2.12-3.5.1.jar"
sparkVersion: "3.5.1"
restartPolicy:
type: Never
driver:
cores: 2
memory: "2G"
labels:
version: 3.5.1
serviceAccount: spark-account # 这里指定我们刚才创建的有权限的账户
env:
- name: SPARK_MODE
value: "driver"
executor:
cores: 1
instances: 4
memory: "1G"
labels:
version: 3.5.1
arguments:
- "10000"
***
特别注意的是,一定要指定有权限的账户来运行任务,不然会报各种各样的错,我就是报了很多错,干脆直接用集群管理员的角色来运行任务,就没再报错了。
查看是否开启了任务,执行:
kubectl get sparkapplications
我的已经执行完了,你的应该看到提交或者执行中
想看具体的日志,可以去看
Pod
的日志:
kubectl logs spark-pi-driver
我们在开启任务后,会发现多了一个
driver
的pod
和worker
的pod
,worker
的pod
数量取决于我们之前提交任务的配置文件中指定了几个,当然它也能动态的分配。例如我们的spark-pi.yaml
任务就指定了四个worker pod
,我们查看一下:
kubectl get pods # 不加 -n default 的话默认就是 -n default
果然就是启动了四个
pod
来运行任务,等它执行完,我们再看driver
的日志:
kubectl logs spark-pi-driver
得到了结果,搭建成功
三、关于 Sedona
搭建
Sedona
就更简单了,我们只需要将上面用的Spark
的镜像拉下来,写一个Dockerfile
,将Sedona
的插件jar
包丢到对应的文件夹中去,重新构建镜像并上传到私有仓库或者公共仓库,只要k8s
能拉取到就行,然后在任务.yaml
中改一下镜像地址就行了,这里就不演示了,因为我也没弄,这个不难。
四、关于历史服务器
如果想用历史服务器,得将日志写到一个公共的持久化存储空间中,然后在
k8s
中跑一个历史服务器去读取日志文件,暴露一个service
或ingress
就能访问。
五、测试
我弄了四台机器,一台是
12C 64G
,其他三台是12C 16G
,测试上面的spark-pi
示例,计算五十万次
,开启8个worker pod
,结果是:
耗时
78s
,将计算次数换为一百万次
,开启16个worker pod
,结果为:
耗时
142s
,我记得之前使用Spark on Yarn
的时候也是计算五十万次,结果是两分钟多一点,计算一百万次失败了,半个小时没结果。现在使用Spark on Kubernetes
,效率有所提升,不知道是我的实验变量没控制好还是怎么的,有点意外,不过除了效率之外,在k8s
上跑Spark
的优点我觉得还是灵活性强,跑任务的时候一堆worker pod
就创建出来跑任务了,任务结束后worker pod
就都没了,不会占用集群中其他服务的资源,这点挺好的。此外由于k8s
是自带REST Api
的,所以和我们自己写的服务交互起来就比较方便,我们可以通过REST Api
提交任务,查看任务等等
写在最后
当然,我上面的这些办法可能不是最好的,我也在探索最好的实践,写到此处只是自己查找时方便,如果能顺道帮助你解决一些问题那再好不过。
学无止尽,瑞斯拜!