这次改的是用jupyterlab 跑 spark on k8s

jupyterlab本身是跑在k8s里面的,然后甲方因为比较不懂计算机这东西, 导致无论开多大内存,无论用vaex还是pandas都会内存溢出,所以打算用spark on k8s方式跑,认为这样就不会溢出了,当然,实际上还是会溢出的。

如何搭建spark on k8s就不说了,官网就有教程。只说一下改造思路。

这里有两个难点,一个是spark kernel的创建,如果采用提前创建spark集群的方式, 那么jupyterlab跑在pod里面,不能连接到已经创建好的集群。如果是cluster方式启动driver, jupyter又不能用交互的方式编写pyspark。所以我们采用的方式是按需在lab里面创建spark kernel,然后再用这个创建的spark kernel去启动spark driver,用这个driver去拉起一个spark on k8s集群。第二个web ui这块是因为jupyterlab on k8s是多人使用的,所以官网和其他资料上说的将spark driver的4040端口通过kubectl port-forward映射出来,但是因为多人使用, 每个人的driver都是在pod里面的localhost:4040,我们不知道同时有多少人使用spark on k8s,如果采用端口映射并且采用顺位绑定宿主机端口(就是这个不行换下一个),jupyter里面无法获知pod外面映射的端口是什么。所以我撸了两段代码, 一段是在lab里面创建 spark kernel,一段是把spark driver的web ui 通过代理方式映射到 jupyterlab的url里面。


大概说一下改造的思路

一、创建spark kernel

  1. 在lab页面里增加菜单项, 用以添加spark kernel 

    image.png

  2. 前端弹窗让用户填写spark相关参数

    image.png

  3. 然后写入jupyter的 kernel.json

    image.png

  4. 然后用户使用该kernel创建 spark on k8s-ed notebook, 编写spark作业.


二、代理spark webUI页面到jupyterlab的url里面

  image.png

重点在代理映射的url,  spark ui这部分实际是整个开发里难度最高的, 为此我专门在jupyterlab的 pod 里面撸了两个代理服务器, 一个http 7层代理, 一个socks5四层代理一起开发测试.


然后贴一下生成的 kernel.json供开发人员参考

{
    "display_name":"PySparK8S Cluster",
    "language":"python",
    "argv":[
        "/opt/miniconda3/bin/python",
        "-m",
        "ipykernel",
        "-f",
        "{connection_file}"
    ],
    "env":{
        "PYSPARK_PYTHON":"/opt/miniconda3/bin/python",
        "SPARK_HOME":"/opt/spark-3.0.0-bin-hadoop2.7",
        "PYSPARK_SUBMIT_ARGS":"  --master k8s://https://172.16.191.209:6443 --deploy-mode client --name xxx-sms-1111-0.xx-sms-1111-pyspark.ns.svc.cluster.local --conf spark.executor.instances=40 --conf spark.kubernetes.container.image=172.16.191.206:8043/matrix/spark-executor:v3.0.7 --conf spark.kubernetes.container.image.pullPolicy=IfNotPresent --conf spark.kubernetes.authenticate.driver.serviceAccountName=spark --conf spark.kubernetes.namespace=ns --conf spark.ui.port=4041 --conf spark.driver.host=xxx-sms-1111-0.xxx-sms-1111-pyspark.ns.svc.cluster.local --conf spark.kubernetes.driver.pod.name=xxx-sms-1111-0 --conf spark.kubernetes.authenticate.subdmission.caCertFile=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt --conf spark.kubernetes.authenticate.submission.oauthTokenFile=/var/run/secrets/kubernetes.io/serviceaccount/token --conf spark.kubernetes.executor.limit.cores=1 --conf spark.kubernetes.executor.request.cores=1 --conf spark.executor.memory=10g --conf spark.driver.memory=15g --conf spark.driver.cores=3 --conf spark.pyspark.driver.python=/opt/miniconda3/bin/python --conf spark.pyspark.python=/opt/miniconda3/bin/python --conf spark.kubernetes.executor.volumes.persistentVolumeClaim.jingyanshan-1t.options.claimName=jingyanshan-1t --conf spark.kubernetes.executor.volumes.persistentVolumeClaim.crm.mount.readOnly=false --conf spark.kubernetes.executor.volumes.persistentVolumeClaim.crm.options.claimName=crm --conf spark.kubernetes.executor.volumes.persistentVolumeClaim.crm.mount.readOnly=false --conf spark.kubernetes.executor.volumes.persistentVolumeClaim.crm-v2.options.claimName=crm-v2 --conf spark.kubernetes.executor.volumes.persistentVolumeClaim.crm.mount.readOnly=false --conf spark.kubernetes.executor.volumes.persistentVolumeClaim.jingyanshan-1t.mount.path=/home/jovyan --conf spark.kubernetes.executor.volumes.persistentVolumeClaim.crm.mount.path=/home/jovyan/crm --conf spark.kubernetes.executor.volumes.persistentVolumeClaim.crm-v2.mount.path=/home/jovyan/crm-v2  --conf spark.sql.shuffle.partitions=1000  pyspark-shell",
        "JAVA_HOME":"/opt/jdk1.8.0_141",
        "PYTHONPATH":"/opt/miniconda3/lib/python3.7/site-packages:/opt/spark-3.0.0-bin-hadoop2.7/python:/opt/spark-3.0.0-bin-hadoop2.7/python/lib/py4j-0.10.9-src.zip",
        "PYTHONSTARTUP":"/opt/spark-3.0.0-bin-hadoop2.7/python/pyspark/shell.py"
    }
}


这里只讲改造思路, 具体代码我就先不贴了, 以后机会合适, 再公开出来.


关键信息打码或用xxx代替, 但是长期关注我的, 应该会知道我主要服务的是哪家公司.