如何在同一AWS EMR集群中并发运行Spark作业?可行性及实现细节
当然没问题!AWS EMR完全支持在同一个集群上并发运行多个Spark作业——不管是批量处理任务还是交互式分析,只要做好资源调度配置和作业参数优化,就能让多个作业高效共享集群资源,互不干扰。下面我就详细拆解可行性、实现方法和具体操作步骤:
一、核心原理:YARN调度器的关键作用
EMR默认采用YARN作为资源管理器,所有Spark作业都运行在YARN框架之上。YARN的调度器(主要是Capacity Scheduler和Fair Scheduler)就像集群的「资源调度管家」,负责把CPU、内存等资源合理分配给多个并发作业,避免单个作业占用全部资源导致其他任务饥饿。
- Capacity Scheduler是EMR的默认选项,适合多租户场景,你可以把集群资源划分为多个队列,每个队列分配固定的资源容量,作业提交到指定队列后,队列内还能配置并发作业数。
- Fair Scheduler则更灵活,不需要预先划分固定资源容量,作业会动态公平地共享集群资源,适合资源需求波动较大的场景。
二、具体实现方法
1. 选择并配置合适的YARN调度器
(1)Capacity Scheduler(默认推荐)
如果你是多团队共享集群,或者作业资源需求相对固定,用这个调度器最省心:
- 可以在EMR集群创建时,通过「配置分类」里的
yarn-site来定义队列,比如新增一个专门的Spark作业队列:<property> <name>yarn.scheduler.capacity.root.queues</name> <value>default,spark_prod</value> <!-- 新增spark_prod队列 --> </property> <property> <name>yarn.scheduler.capacity.root.spark_prod.capacity</name> <value>60</value> <!-- 该队列占集群总资源的60% --> </property> <property> <name>yarn.scheduler.capacity.root.spark_prod.maximum-capacity</name> <value>80</value> <!-- 峰值时最多占用80%资源 --> </property> - 也可以在集群运行时修改配置,然后重启YARN资源管理器生效:
sudo stop-yarn-resourcemanager && sudo start-yarn-resourcemanager
(2)Fair Scheduler(灵活场景)
如果你的作业资源需求波动大,或者希望作业能动态共享资源,可以切换到Fair Scheduler:
- 在EMR集群创建时,修改
yarn-site配置:<property> <name>yarn.resourcemanager.scheduler.class</name> <value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler</value> </property> <property> <name>yarn.scheduler.fair.allocation.file</name> <value>/etc/hadoop/conf/fair-scheduler.xml</value> <!-- 指定调度器配置文件路径 --> </property> - 然后编写
fair-scheduler.xml来定义队列、资源权重等规则,比如:<allocations> <queue name="spark"> <weight>2</weight> <!-- 资源权重是default队列的2倍 --> <maxRunningApps>10</maxRunningApps> <!-- 最多同时运行10个作业 --> </queue> </allocations>
2. 为Spark作业配置合理的资源参数
每个Spark作业提交时,一定要明确设置资源参数,避免单个作业把集群资源吃光:
- 驱动程序资源:
--driver-memory(驱动内存)、--driver-cores(驱动CPU核数) - 执行器资源:
--executor-memory(每个执行器内存)、--executor-cores(每个执行器CPU核数)、--num-executors(执行器数量) - 还可以用
spark.yarn.queue指定作业提交的目标队列(对应YARN的队列配置)
举个提交作业的命令示例:
spark-submit \ --class com.example.MyBatchJob \ --master yarn \ --deploy-mode cluster \ --queue spark_prod \ --driver-memory 2G \ --executor-memory 4G \ --executor-cores 2 \ --num-executors 5 \ s3://my-bucket/spark-jobs/my-job.jar
3. 启用Spark动态资源分配(推荐)
动态资源分配允许Spark作业根据负载自动增减执行器数量,这样多个作业能更高效地共享资源,避免闲置浪费:
- 你可以在EMR集群创建时,通过
spark-defaults配置分类全局开启:spark.dynamicAllocation.enabled=true spark.shuffle.service.enabled=true - 也可以在单个作业提交时临时指定:
注:EMR默认已经配置了YARN的shuffle服务,所以开启这个功能不需要额外部署。spark-submit \ --conf spark.dynamicAllocation.enabled=true \ --conf spark.shuffle.service.enabled=true \ # 其他作业参数...
4. 交互式作业的并发处理
如果是用EMR Notebook、Spark Shell或者Zeppelin运行交互式作业,同样可以并发执行:
- 比如打开多个EMR Notebook实例,每个实例对应一个独立的Spark应用,YARN会自动为它们分配资源(只要集群有剩余资源)
- 注意给每个交互式作业设置合理的资源上限,比如在Notebook的Spark配置里限制
executor-memory和num-executors,避免影响批量作业。
三、并发运行的实操步骤
假设你已经有一个运行中的EMR集群,按以下步骤提交并发作业:
(可选)配置YARN队列
按照上面Capacity Scheduler的配置方法,新增一个spark_prod队列并分配资源,重启YARN生效。提交第一个Spark作业
用spark-submit提交第一个批量作业,指定队列和资源参数:spark-submit \ --class com.job.UserBehaviorAnalysis \ --master yarn \ --deploy-mode cluster \ --queue spark_prod \ --executor-memory 3G \ --executor-cores 2 \ --num-executors 4 \ s3://my-job-bucket/jobs/user-behavior.jar提交第二个Spark作业
不需要等待第一个作业完成,直接提交第二个作业,同样指定队列和合理的资源:spark-submit \ --class com.job.SalesDataAggregation \ --master yarn \ --deploy-mode cluster \ --queue spark_prod \ --executor-memory 3G \ --executor-cores 2 \ --num-executors 4 \ s3://my-job-bucket/jobs/sales-agg.jar监控并发作业状态
- 登录EMR控制台,进入集群详情页的「Applications」标签,就能看到所有运行中的Spark作业
- 也可以直接访问YARN ResourceManager的UI(EMR主节点的8088端口),查看每个作业的资源分配、运行进度和日志。
四、关键注意事项
- 资源隔离:确保所有并发作业的资源请求总和不超过集群总资源,比如集群总内存是64G,就不要让两个作业都请求40G内存
- 队列优先级:可以给重要队列设置更高优先级,比如在Capacity Scheduler里配置
yarn.scheduler.capacity.root.spark_prod.priority,让核心作业优先获取资源 - 数据冲突:如果多个作业访问同一外部数据源(比如S3上的同一份文件),要注意处理并发读写,避免数据不一致
- EMR版本适配:不同EMR版本的默认配置有差异,比如EMR 6.x+对Spark 3.x的支持更完善,动态资源分配的默认设置更合理,建议使用较新的稳定版本。
内容的提问来源于stack exchange,提问作者Kunal




