使用SparkLauncher发布k8s作业

一.背景

在大数据处理场景规模化、复杂化的当下,企业对 Spark 作业的 "标准化发布、弹性调度、资源管控、全生命周期管理" 提出了更高要求。Spark 作为大数据批处理、流处理的核心引擎,广泛应用于数据仓库构建、离线分析、实时计算等场景,但传统的 Spark 作业部署方式(如 YARN 集群模式、本地提交)已难以适配云原生时代的运维诉求,痛点日益突出:

传统部署模式的核心问题集中在三方面:一是作业发布碎片化,多依赖手工脚本、运维平台定制化工具,不同环境(测试 / 生产)的配置不一致,易引发部署故障,且缺乏统一的发布入口和标准化流程;二是资源调度效率低,YARN 等传统调度系统与云原生基础设施(K8s)割裂,无法充分利用 K8s 的容器化弹性扩缩容、资源隔离、多租户管理能力,硬件资源利用率低,且难以应对突发的计算资源需求;三是作业运维成本高,作业提交后缺乏统一的状态监控、日志归集、故障自愈能力,跨环境部署需重复适配底层资源,运维复杂度随集群规模增长呈指数级上升。

与此同时,K8s 已成为云原生时代的核心容器编排平台,凭借容器化部署、弹性调度、资源隔离、高可用等特性,成为企业统一的资源管理底座。将 Spark 作业迁移至 K8s 集群运行,能充分发挥云原生架构的优势,但原生的spark-submit命令行提交方式存在配置繁琐、集成性差、难以嵌入自动化流程(如 CI/CD)的问题,无法满足企业级标准化、自动化发布的需求。

SparkLauncher 作为 Spark 官方提供的 Java/Scala 编程式作业提交工具,恰好弥补了这一短板:它通过 API 化的方式封装 Spark 作业提交逻辑,支持灵活配置 K8s 集群的运行参数(如容器镜像、资源配额、命名空间、PVC 挂载等),可无缝集成至企业内部的运维平台、CI/CD 流水线,实现 Spark 作业的标准化、自动化发布。在此背景下,"SparkLauncher + K8s" 的组合成为解决传统 Spark 作业部署痛点的最优解,各环节形成互补:

  1. SparkLauncher 作为标准化发布入口 :通过编程接口统一封装作业提交参数(如主类、jar 包路径、资源配置、K8s 专属配置),替代手工spark-submit命令,避免配置遗漏或格式错误,同时支持与企业现有运维系统(如监控平台、权限系统)集成,实现发布流程的可管控、可审计;
  2. K8s 作为资源调度底座:为 Spark 作业提供容器化运行环境,支持按作业需求弹性分配 CPU / 内存资源,通过 Pod 副本机制保障作业高可用,利用 K8s 的命名空间实现多租户隔离,解决传统集群资源争抢、利用率低的问题;
  3. 编程式发布适配自动化场景:SparkLauncher 的 API 特性使其可嵌入 Java/Scala 编写的运维平台、自动化脚本,轻松对接 CI/CD 流水线(如 Jenkins、GitLab CI),实现 "代码提交 - 编译打包 - 作业发布 - 状态校验" 的全流程自动化,大幅降低人工干预成本。

采用 SparkLauncher 发布 K8s 作业,本质是构建 "云原生 + 标准化" 的 Spark 作业发布体系:既充分利用 K8s 的弹性调度、资源隔离能力,提升大数据处理的资源利用率和运维灵活性,又通过 SparkLauncher 的编程式接口实现作业发布的标准化、自动化,解决传统部署模式的碎片化、低效率问题。该方案可广泛应用于企业级大数据平台的作业发布、跨环境 Spark 作业迁移、云原生大数据集群建设等场景,帮助企业打通 "Spark 计算引擎" 与 "K8s 资源底座" 的衔接壁垒,实现大数据作业的云原生转型,降低运维成本的同时提升资源调度效率,为大规模、高并发的大数据处理场景提供稳定、可扩展的发布支撑。

二.具体实现

1.创建一个java工程,引入依赖

复制代码
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-core_2.12</artifactId>
            <version>3.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.spark</groupId>
            <artifactId>spark-yarn_2.12</artifactId>
            <version>3.2.2</version>
        </dependency>

2.定义SparkLauncher实例

复制代码
SparkLauncher sparkLauncher = new SparkLauncher()
                .setAppName("xxx")
                .setMaster("k8s://https://apiproxy.xxx.com:8001/proxy/k8s/xxx")
                .setDeployMode("cluster")
                .setConf("spark.user", "hadoop");

3.定义参数,并设置

复制代码
Map<String, Object> baseConfigMap = new HashMap<>();
        baseConfigMap.put("spark.kubernetes.namespace", k8s命名空间);
        baseConfigMap.put("spark.kubernetes.file.upload.path", "hdfs://xxx/temp");
        baseConfigMap.put("spark.kubernetes.container.image.pullSecrets", "default-secret");
        baseConfigMap.put("spark.kubernetes.authenticate.driver.serviceAccountName", "xxx");
        baseConfigMap.put("spark.kubernetes.container.image", "spark作业镜像");
        baseConfigMap.put("spark.kubernetes.driver.podTemplateFile","hdfs://xxx/driver.yml");
        baseConfigMap.put("spark.kubernetes.executor.podTemplateFile","hdfs://xxx/executor.yml");
        baseConfigMap.put("spark.kubernetes.authenticate.caCertFile","/var/run/secrets/kubernetes.io/serviceaccount/ca.crt");
          baseConfigMap.put("spark.kubernetes.authenticate.oauthTokenFile","/var/run/secrets/kubernetes.io/serviceaccount/token");
        baseConfigMap.put("spark.kubernetes.driver.volumes.persistentVolumeClaim.spark-local-dir-1.options.storageClass", "csi-disk-ssd-topology");
        baseConfigMap.put("spark.kubernetes.driver.volumes.persistentVolumeClaim.spark-local-dir-1.mount.path", "/var/data");
        baseConfigMap.put("spark.kubernetes.driver.volumes.persistentVolumeClaim.spark-local-dir-1.options.sizeLimit","30Gi");
        baseConfigMap.put("spark.kubernetes.driver.volumes.persistentVolumeClaim.spark-local-dir-1.options.claimName","OnDemand");
        baseConfigMap.put("spark.kubernetes.executor.volumes.persistentVolumeClaim.spark-local-dir-1.options.storageClass", "csi-disk-ssd-topology");
        baseConfigMap.put("spark.kubernetes.executor.volumes.persistentVolumeClaim.spark-local-dir-1.mount.path", "/var/data");
        baseConfigMap.put("spark.kubernetes.executor.volumes.persistentVolumeClaim.spark-local-dir-1.options.sizeLimit","30Gi");
        baseConfigMap.put("spark.kubernetes.executor.volumes.persistentVolumeClaim.spark-local-dir-1.options.claimName","OnDemand");
        baseConfigMap.put("spark.kubernetes.driver.pod.name", "pod名称");
        baseConfigMap.put("spark.kubernetes.driver.limit.cores", 2);
        baseConfigMap.put("spark.kubernetes.driver.request.cores", 2);
        baseConfigMap.put("spark.driver.memory", "4g");
        baseConfigMap.put("spark.executor.memory", "4g");
        baseConfigMap.put("spark.kubernetes.executor.limit.cores", 2);
        baseConfigMap.put("spark.kubernetes.executor.request.cores", 2);
        baseConfigMap.put("spark.executor.instances", 5);


        for (Map.Entry<String, Object> baseConfig : baseConfigMap.entrySet()) {
            sparkLauncher.setConf(baseConfig.getKey(), String.valueOf(baseConfig.getValue()));
        }

4.设置作业的信息

复制代码
sparkLauncher.setMainClass("镜像里面jar的主类")
                .setAppResource("镜像里面jar的地址")
                .addAppArgs("镜像里面jar的主类的参数");

5.发布作业

复制代码
sparkLauncher.redirectError(new File("/xxx/sparkLauncher.log"))
                    // 这里将标准错误输出重定向到标准输出
                    .startApplication(new SparkAppHandle.Listener() {
                        // 状态监听没什么用,主要使用SparkAppHandle 的 stop/kill/getState 等操作
                        @Override
                        public void stateChanged(SparkAppHandle sparkAppHandle) {
                            logger.info("info ----- "+ JSON.toJSONString(sparkAppHandle));
                        }
                        @Override
                        public void infoChanged(SparkAppHandle sparkAppHandle) {
                            logger.info("info ----- "+JSON.toJSONString(sparkAppHandle));
                        }

});
相关推荐
哦你看看1 小时前
K8S-Service资源对象
云原生·容器·kubernetes
pp-周子晗(努力赶上课程进度版)1 小时前
Docker入门学习笔记
spring cloud·docker·容器
士心凡1 小时前
数据仓库教程
大数据·数据仓库·spark
会编程的李较瘦1 小时前
Spark SQL 窗口函数全面解析:概念、语法与实战案例
大数据·spark
听风吟丶1 小时前
云原生 APM 实战:Prometheus Operator+K8s 构建容器化微服务监控体系
云原生·kubernetes·prometheus
壹米饭2 小时前
Kubernetes 节点 DNS 解析异常问题排查与解决方案
后端·kubernetes
victory04312 小时前
K8S etcd 数据存储路径迁移
容器·kubernetes·etcd
士心凡2 小时前
Spark
大数据·ajax·spark
ylmzfun2 小时前
从Borg到Kubernetes:云原生时代的容器编排利器
云原生·容器·kubernetes