如何在Spark客户端模式下阻止Executor复制JAR包
解决Spark Kubernetes客户端模式下JAR包重复复制的问题
核心问题分析
你遇到的慢启动问题,本质是客户端模式下Spark默认会将Driver端的依赖JAR包逐个传递给Executor,哪怕Executor已经能通过镜像或共享存储访问到这些JAR。之前的配置没生效,主要是两个原因:
- 保留了
--packages参数,该参数会强制Spark Driver下载依赖并自动同步给Executor; - 未显式配置让Executor优先使用本地(镜像/NFS)的JAR,而非Driver传递的版本。
下面给出两种可行的解决方案:
方案一:基于自定义Docker镜像(推荐)
将所有依赖JAR打包到Spark镜像中,从根源上避免JAR传输:
1. 构建自定义镜像
创建Dockerfile,基于官方Spark镜像添加依赖JAR:
FROM apache/spark-py:v3.3.2 # 将预先下载好的Iceberg、AWS SDK等JAR包复制到镜像内的固定目录 COPY ./extra-jars/ /opt/spark/jars/extra/
- 提前下载所需JAR包到本地
extra-jars目录(可通过spark-shell --packages 目标包名 --exclude-packages org.slf4j:slf4j-api下载后从缓存目录提取); - 构建并推送镜像到你的Kubernetes镜像仓库:
docker build -t your-registry/spark-py-extra:v3.3.2 . && docker push your-registry/spark-py-extra:v3.3.2
2. 客户端模式配置(Jupyter Notebook示例)
在SparkSession初始化时,移除--packages参数,添加以下关键配置:
from pyspark.sql import SparkSession spark = SparkSession.builder \ .master("k8s://https://10.164.64.27:6443") \ .appName("your-app-name") \ .config("spark.kubernetes.namespace", "spark-cluster") \ .config("spark.kubernetes.authenticate.driver.serviceAccountName", "spark-sa") \ .config("spark.executor.instances", "2") \ # 使用自定义镜像 .config("spark.kubernetes.driver.container.image", "your-registry/spark-py-extra:v3.3.2") \ .config("spark.kubernetes.executor.container.image", "your-registry/spark-py-extra:v3.3.2") \ # 让Executor优先使用镜像内的JAR,忽略Driver传递的依赖 .config("spark.executor.userClassPathFirst", "true") \ # 指定Executor使用镜像内的JAR路径 .config("spark.executor.extraClassPath", "/opt/spark/jars/extra/*") \ # Driver端(Jupyter所在环境)需能访问相同依赖,可挂载NFS或本地存放JAR .config("spark.driver.extraClassPath", "/tmp/spark-data/jars/*") \ .getOrCreate()
方案二:复用NFS共享存储
如果不想构建镜像,可通过显式配置Executor挂载NFS,让Executor直接读取共享存储中的JAR:
1. 配置Executor挂载NFS
在SparkSession中添加Kubernetes Volume挂载配置,确保Executor能访问NFS:
from pyspark.sql import SparkSession spark = SparkSession.builder \ .master("k8s://https://10.164.64.27:6443") \ .appName("your-app-name") \ .config("spark.kubernetes.namespace", "spark-cluster") \ .config("spark.kubernetes.authenticate.driver.serviceAccountName", "spark-sa") \ .config("spark.executor.instances", "2") \ .config("spark.kubernetes.driver.container.image", "apache/spark-py:v3.3.2") \ .config("spark.kubernetes.executor.container.image", "apache/spark-py:v3.3.2") \ # 配置Executor挂载NFS .config("spark.kubernetes.executor.volumes.nfs.spark-data.mount.path", "/tmp/spark-data") \ .config("spark.kubernetes.executor.volumes.nfs.spark-data.mount.readOnly", "true") \ .config("spark.kubernetes.executor.volumes.nfs.spark-data.server", "your-nfs-server-ip") \ .config("spark.kubernetes.executor.volumes.nfs.spark-data.path", "/nfs/shared/path") \ # 让Executor优先使用NFS中的JAR .config("spark.executor.userClassPathFirst", "true") \ .config("spark.executor.extraClassPath", "local:///tmp/spark-data/jars/*") \ # Driver端(Jupyter所在节点)需挂载同一NFS到/tmp/spark-data .config("spark.driver.extraClassPath", "/tmp/spark-data/jars/*") \ .getOrCreate()
2. 关键注意事项
- 必须移除所有
--packages参数,否则Spark仍会强制同步Driver下载的JAR; - Jupyter所在的节点必须挂载同一个NFS到
/tmp/spark-data,确保Driver能读取依赖JAR; local://前缀用于告诉Spark使用Executor本地文件系统的JAR,而非分布式存储。
内容的提问来源于stack exchange,提问作者Shivaprasad
相关产品推荐
相关产品推荐

