基于实际剩余资源的多集群Spark作业调度与分发

更新时间:2025-03-13 05:23:19

如果您已经拥有多个正在运行在线服务的ACK集群,并希望在不影响这些服务的情况下,充分利用集群的空闲资源来运行Spark作业,您可以利用ACK One舰队的多集群Spark作业调度和分发能力来提高资源利用率。本文将介绍如何通过ACK One舰队和ACK Koordinator组件,根据各集群实际剩余资源(而非请求资源)来调度和分发多集群Spark作业,帮助您最大化多集群中闲置资源的利用效率,并通过优先级控制和离线混合部署能力,确保在线服务的正常运行。

背景信息

基于集群实际剩余资源的多集群Spark作业的调度和分发方案,主要依赖以下三部分能力:

  • ACK One舰队的多集群Spark作业的调度和分发,包括对实际剩余资源的感知调度。

  • ACK Spark Operator支持Koordinator在离线混部特性。

  • ACK Koordinator的单集群在离线混部能力。

image

整体流程如下:

  1. 将多个ACK集群关联到舰队,并在各关联集群中安装ACK Koordinator组件和ACK Spark Operator组件。

  2. 为舰队创建SparkApplicationPropagationPolicy

  3. 舰队的多集群调度组件(Global Scheduler)会根据每个关联子集群的剩余资源,来匹配Spark作业资源请求完成调度。

    对集群版本为1.28及以上的子集群,舰队支持通过资源预占来提高Spark作业调度的成功率。

  4. 舰队调度完作业后,通过多集群应用分发SparkApplication至调度选中的关联集群中。

  5. 在关联集群中,ACK Spark Operator负责运行Spark作业的driverexecutor。同时舰队会监视子集群中Spark作业运行状态,若driver由于资源不足而无法运行,舰队会在一定时间后回收该SparkApplication,并重调度到其他资源充足的关联集群中。

前提条件

步骤一:在关联集群中配置ack-koordinator

  1. 登录容器服务管理控制台,在左侧导航栏选择集群列表

  2. 集群列表页面,单击目标集群名称,然后在左侧导航栏,选择配置管理 > 配置项

  3. 配置项页面,单击使用YAML创建资源,使用以下示例内容添加配置。详情请参见在离线混部快速入门

    #ConfigMap ack-slo-config样例。
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: ack-slo-config
      namespace: kube-system
    data:
      colocation-config: |-
        {
          "enable": true
        }
      resource-qos-config: |-
        {
          "clusterStrategy": {
            "lsClass": {
              "cpuQOS": {
                "enable": true
              },
              "memoryQOS": {
                "enable": true
              },
              "resctrlQOS": {
                "enable": true
              }
            },
            "beClass": {
              "cpuQOS": {
                "enable": true
              },
              "memoryQOS": {
                "enable": true
              },
              "resctrlQOS": {
                "enable": true
              }
            }
          }
        }
      resource-threshold-config: |-
        {
          "clusterStrategy": {
            "enable": true
          }
        }

步骤二:(可选)在舰队中创建命名空间,并下发到子集群

在各子集群安装ack-spark-operator之前,需要确保各子集群已有待分发的spark应用的命名空间,否则组件安装会异常。可以通过在舰队中创建命名空间,使用ClusterPropagationPolicy将其分发至各集群。本文示例命名空间为spark

  1. 使用舰队的KubeConfig连接舰队实例,执行以下命令,创建相应命名空间。

    kubectl create ns spark
  2. 使用ClusterPropagationPolicy将舰队的命名空间下发至指定子集群,如想下发到所有舰队的关联集群,可以省略clusterAffinity字段整个内容。

    apiVersion: policy.one.alibabacloud.com/v1alpha1
    kind: ClusterPropagationPolicy
    metadata:
      name: ns-policy
    spec:
      resourceSelectors:
      - apiVersion: v1
        kind: Namespace
        name: spark
      placement:
        clusterAffinity:
          clusterNames:
          - ${cluster1-id} # 您的集群ID。
          - ${cluster2-id} # 您的集群ID。
        replicaScheduling:
          replicaSchedulingType: Duplicated

步骤三:在关联集群中安装ack-spark-operator

请在期望运行Spark作业的子集群上安装ack-spark-operator组件,且版本为2.1.2及以上。

  1. 登录容器服务管理控制台,在左侧导航栏选择市场 > 应用市场

  2. 应用市场页面单击应用目录页签,然后搜索并选中ack-spark-operator

  3. ack-spark-operator页面,单击一键部署

  4. 创建面板中,选择集群和命名空间,然后单击下一步

  5. 参数配置页面,设置相应参数,选择Chart 版本2.1.2或更高版本,jobNamespaces中添加spark,然后单击确定

    重要

    必须将SparkApplication所在的namespace添加到spark.jobNamespaces

    下表列出了部分配置参数的说明。完整的参数配置详情,您可以在ack-spark-operator页面中的配置项查看。

    参数

    描述

    示例值

    参数

    描述

    示例值

    controller.replicas

    控制器副本数量。

    1(默认值)

    webhook.replicas

    Webhook副本数量。

    1(默认值)

    spark.jobNamespaces

    可运行Spark任务的命名空间列表。包含空字符串表示允许所有命名空间。多个命名空间使用英文半角逗号 , 隔开。

    • ["default"](默认值)

    • [""](所有命名空间)

    • ["ns1","ns2","ns3"](多个命名空间)

    spark.serviceAccount.name

    Spark作业会在spark.jobNamespaces指定的每个命名空间中自动创建名为spark-operator-sparkServiceAccountRBAC资源并进行相关授权。您可以自定义ServiceAccount名称,后续提交Spark作业时请指定自定义创建的ServiceAccount名称。

    spark-operator-spark(默认值)

步骤四:在舰队上创建PriorityClass,并分发到子集群

为了保证提交的Spark作业不会抢占在线服务资源、影响在线服务产品的正常运行,建议对要提交的Spark作业设置低优先级。

    1. 使用舰队的KubeConfig,创建一个低优先级的PriorityClass,其value设置为负数。

      apiVersion: scheduling.k8s.io/v1
      kind: PriorityClass
      metadata:
        name: low-priority
      value: -1000
      globalDefault: false
      description: "Low priority for Spark applications"
    2. 在舰队中创建ClusterPropagationPolicy,将PriorityClass分发到指定集群中。若您希望将PriorityClass分发到所有关联集群,可以将clusterAffinity部分删除。

      apiVersion: policy.one.alibabacloud.com/v1alpha1
      kind: ClusterPropagationPolicy
      metadata:
        name: priority-policy
      spec:
        preserveResourcesOnDeletion: false
        resourceSelectors:
        - apiVersion: scheduling.k8s.io/v1
          kind: PriorityClass
        placement:
          clusterAffinity:
            clusterNames:
            - ${cluster1-id} # 您的集群ID。
            - ${cluster2-id} # 您的集群ID。
      #      labelSelector:
      #        matchLabels:
      #          key: value
          replicaScheduling:
            replicaSchedulingType: Duplicated

步骤五:在舰队中提交在离线混部策略的SparkApplicaiton

  1. 在舰队中创建如下PropagationPolicy,可以将所有sparkoperator.k8s.io/v1beta2SparkApplication资源都分发到相应的集群中,若要分发到所有关联集群,可以省略clusterAffinity整个字段内容。

    apiVersion: policy.one.alibabacloud.com/v1alpha1
    kind: PropagationPolicy
    metadata:
      name: sparkapp-policy 
      namespace: spark
    spec:
      preserveResourcesOnDeletion: false
      propagateDeps: true
      placement:
        clusterAffinity:
          clusterNames:
          - ${cluster1-id} # 您的集群ID。
          - ${cluster2-id} # 您的集群ID。
    #      labelSelector:
    #        matchLabels:
    #          key: value
        replicaScheduling:
          replicaSchedulingType: Divided
          customSchedulingType: Gang
      resourceSelectors:
        - apiVersion: sparkoperator.k8s.io/v1beta2
          kind: SparkApplication
  2. 在舰队中创建Spark作业。可以通过在SparkApplication中添加注解 sparkoperator.k8s.io/koordinator-colocation: "true" 来标识driverexecutor是否需要根据实际剩余资源进行调度。本例配置如下所示,表示driverexecutor都需要根据实际剩余资源进行调度。

    apiVersion: sparkoperator.k8s.io/v1beta2
    kind: SparkApplication
    metadata:
      name: spark-pi
      namespace: spark
    spec:
      arguments:
      - "50000"
      driver:
        coreLimit: 1000m
        cores: 1
        memory: 512m
        priorityClassName: low-priority
        template:
          metadata:
            annotations:
              sparkoperator.k8s.io/koordinator-colocation: "true"
          spec:
            containers:
            - name: spark-kubernetes-driver
            serviceAccount: spark-operator-spark
      executor:
        coreLimit: 1000m
        cores: 1
        instances: 1
        memory: 1g
        priorityClassName: low-priority
        template:
          metadata:
            annotations:
              sparkoperator.k8s.io/koordinator-colocation: "true"
          spec:
            containers:
            - name: spark-kubernetes-executor
      image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/spark:3.5.4
      mainApplicationFile: local:///opt/spark/examples/jars/spark-examples_2.12-3.5.4.jar
      mainClass: org.apache.spark.examples.SparkPi
      mode: cluster
      restartPolicy:
        type: Never
      sparkVersion: 3.5.4
      type: Scala

步骤六:查看Spark作业状态

  1. 在舰队中执行以下命令,查看舰队中Spark作业的状态。

    kubectl get sparkapp -nspark

    预期输出:

    NAME       STATUS    ATTEMPTS   START                  FINISH       AGE
    spark-pi   RUNNING   1          2025-03-05T11:19:43Z   <no value>   48s
  2. 在舰队中执行以下命令,在事件中查看Spark作业调度至哪个关联集群。

    kubectl describe sparkapp spark-pi  -nspark

    预期输出:

    Normal   ScheduleBindingSucceed  2m29s                  default-scheduler                   Binding has been scheduled successfully. Result: {c6xxxxx:0,[{driver 1} {executor 1}]}
  3. 在舰队中执行以下命令,查看资源调度分发状态。

    kubectl get rb  spark-pi-sparkapplication -nspark 

    预期输出:

    NAME                        SCHEDULED   FULLYAPPLIED   OVERRIDDEN   ALLAVAILABLE   AGE
    spark-pi-sparkapplication   True        True           True         True     
  4. 在舰队中执行以下命令,查看关联集群中Spark作业的状态。

    kubectl amc get sparkapp -M -nspark

    预期输出:

    NAME       CLUSTER     STATUS      ATTEMPTS   START                  FINISH                 AGE   ADOPTION
    spark-pi   c6xxxxxxx   COMPLETED   1          2025-02-24T12:10:34Z   2025-02-24T12:11:20Z   61s   Y
  5. 在舰队中执行以下命令,查看Pod状态。

    kubectl amc get pod -M -nspark    

    预期输出:

    NAME                               CLUSTER     READY   STATUS      RESTARTS   AGE
    spark-pi-3c0565956608ad6d-exec-1   c6xxxxxxx   1/1     Running            0          2m35s
    spark-pi-driver                    c6xxxxxxx   1/1     Running            0          2m50s
  6. 在舰队中执行以下命令,查看关联集群中Spark作业的详情。

    kubectl amc get sparkapp spark-pi -m ${member clusterid} -oyaml -nspark   
  • 本页导读 (1)
  • 背景信息
  • 前提条件
  • 步骤一:在关联集群中配置ack-koordinator
  • 步骤二:(可选)在舰队中创建命名空间,并下发到子集群
  • 步骤三:在关联集群中安装ack-spark-operator
  • 步骤四:在舰队上创建PriorityClass,并分发到子集群
  • 步骤五:在舰队中提交在离线混部策略的SparkApplicaiton
  • 步骤六:查看Spark作业状态