Spark/k8s:如何在客户端模式下在 Kubernetes 上运行 Spark Submit

8

我正在尝试在Kubernetes pod中使用client模式的spark-submit来提交作业到EMR(由于其他基础设施问题,我们不允许cluster模式)。默认情况下,spark-submit使用pod的hostname作为spark.driver.host,而该hostname是pod的主机名,因此spark executor无法解析它。同时,spark.driver.port也只能作用于pod(容器)内。

我知道一种方法可以通过传递一些配置给spark-submit,以便spark executor可以与driver通信,这些配置如下:

--conf spark.driver.bindAddress = 0.0.0.0 --conf spark.driver.host = $ HOST_IP_OF_K8S_WORKER --conf spark.driver.port = 32000 --conf spark.driver.blockManager.port = 32001

并在Kubernetes中创建一个服务,以便spark executor可以与driver通信:

apiVersion: v1
kind: Service
metadata:
  name: spark-block-manager
  namespace: my-app
spec:
  selector:
    app: my-app
  type: NodePort
  ports:
    - name: port-0
      nodePort: 32000
      port: 32000
      protocol: TCP
      targetPort: 32000
    - name: port-1
      nodePort: 32001
      port: 32001
      protocol: TCP
      targetPort: 32001
    - name: port-2
      nodePort: 32002
      port: 32002
      protocol: TCP
      targetPort: 32002

但问题是一个k8s节点上可能会有多个pod运行,甚至在一个pod中可能会有多个spark-submit作业。因此,在启动pod之前,我们需要动态选择k8s节点上的几个可用端口,并创建一个服务来进行端口映射,然后在启动pod时将这些端口传递到pod中告诉spark-submit使用它们。我觉得这有点复杂。
使用hostNetwork: true可能可以解决这个问题,但它会在我们的基础设施中引入很多其他问题,所以这不是一个选项。
如果spark-submit能像driver.bindAddressdriver.host一样支持bindPort概念,或者支持proxy,那么解决问题就更加清晰明了。
有人有类似的情况吗?请分享一些见解。
谢谢。
附加背景: spark版本2.4

或者创建一个代理 Pod,在每个工作节点上运行,以查找可用端口并将其绑定到需要运行 spark-submit 的 Pod 上。 - Ping.Goblue
EMR不提供用于运行Spark的YARN吗? - OneCricketeer
1
你可以在EMR上使用Livy API以集群模式提交作业吗? - Aliaksandr Sasnouskikh
@cricket_007 是的,但我们希望 Spark 客户端(其中大部分是 Airflow 工作器)在 k8s 中运行。 - Ping.Goblue
由于其他基础设施问题,我们不允许集群模式。 - Ping.Goblue
你是否考虑过在EKS和EMR VPC之间进行VPC对等连接?这可能需要深入的网络配置,但是设置好后可能会给你带来最美丽的解决方案。 - Aliaksandr Sasnouskikh
1个回答

5

Spark submit可以使用额外的参数,例如--conf spark.driver.bindAddress, --conf spark.driver.host, --conf spark.driver.port, --conf spark.driver.blockManager.port, --conf spark.port.maxRetries。其中spark.driver.hostdriver.port用于告诉Spark Executor使用此主机和端口来连接回Spark submit。

我们使用hostPortcontainerPort来公开容器内的端口,注入端口范围以及hostIP作为环境变量到Pod中,以便spark-submit知道该使用什么。因此这些额外的参数是:

--conf spark.driver.bindAddress=0.0.0.0` # has to be `0.0.0.0` so that it is accessible outside pod
--conf spark.driver.host=$HOST_IP # k8s worker ip, can be easily injected to the pod
--conf spark.driver.port=$SPARK_DRIVER_PORT # defined as environment variable
--conf spark.driver.blockManager.port=$SPARK_DRIVER_PORT # defined as environment variable
--conf spark.port.maxRetries=$SPARK_PORT_MAX_RETRIES # defined as environment variable

hostPort 属于 Kubernetes worker 的本地端口,这意味着我们不必担心端口用尽。k8s 调度器可以找到一个主机来运行 Pod。

我们可以在每个主机上保留 40000 到 49000 的端口,并为每个 Pod 打开 8 个端口(因为每个 spark-submit 需要 2 个开放的端口)。端口是根据 pod_id 选择的。由于 Kubernetes 建议每个节点运行少于 100 个 Pod,因此端口冲突非常罕见。


更糟糕的是,NodePort 在集群级别上运行,您很容易用完端口。 - Ping.Goblue
1
我正在寻找完全相同的解决方案。我尝试使用NodePort服务在Spark笔记本Pod上打开两个端口,但仍然出现连接关闭错误。如果您有一些可行的解决方案,请告诉我,我卡在最后一步了。 - Mousam Singh
以上解决方案对我有效。你设置了 spark.driver.bindAddress=0.0.0.0 吗? - Ping.Goblue

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接