亚马逊AWS官方博客
基于开源工具构建 EMR 数据分析平台(五)EMR 最佳实践
![]() |
Amazon EMR 是一个简化大数据框架(例如 Apache Hadoop 和 Apache Spark)的运行的托管集群平台, 它使数据工程师和分析师能够轻松且成本高效地运行使用开源大数据框架(例如 Apache Spark、Apache Hive 或 Presto)构建的应用程序,而无需调整、运营、优化、保护或管理集群。
![]() |
使用 EMR 部署和运行我们的数据分析任务具有多方面优势。首先,它能够节省成本,提供灵活多样的定价选项,包括按需实例、预留实例和 Spot 实例。此外,EMR 支持灵活的集群规模调整,通过 EMRFS 实现与 S3 的无缝存储集成,同时保证高可靠性。在安全方面,EMR 充分利用 IAM、安全组、加密和 VPC 等 AWS 服务,有效保护集群和数据安全。最后,EMR 提供了丰富的管理界面选择,包括控制台、命令行界面(CLI)、软件开发工具包(SDK)和应用程序接口(API),使用户能够便捷地创建和管理集群。
版本选择
Amazon EMR 发行版是一组来自大数据生态系统的开源应用程序,您在创建集群时选择让 Amazon EMR 安装和配置这些应用程序、组件和功能。请根据您的任务版本,选择对应的 EMR 版本安装,如果条件允许,请尽量使用 EMR 最新的版本来部署和运行您的任务。如果一定要选择低版本,请查询版本发布说明,看是否有一些已知的问题,是否影响您的应用。EMR 和开源版本对应关系,版本发布说明请参考【Amazon EMR 版本指南】。
节点和节点组选择
节点
集群是 Amazon EMR 的核心组件,是Amazon EC2实例的集合。集群中的每个实例称作节点。集群中的每个节点都有一个角色,称作节点类型。Amazon EMR 还在每个节点类型上安装不同的软件组件,在分布式应用程序(如 Apache Hadoop)中为每个节点赋予一个角色。
Amazon EMR 中的节点类型有:
- 主节点:该节点管理集群,它通过运行软件组件来协调在其他节点之间分配数据和任务的过程以便进行处理。主节点跟踪任务的状态并监控集群的运行状况。每个集群有一个主节点,并且可以创建仅包含主节点的单节点集群。
- 核心节点:该节点具有运行任务并在集群上的 Hadoop Distributed File System(HDFS)中存储数据的软件组件DataNode。多节点集群至少具有一个核心节点。
- 任务节点:该节点具有仅运行任务但不在 HDFS 中存储数据的软件组件(NodeManager)。任务节点是可选的。
大部分 EMR 集群可以运行在 M 系这类常规负载类型的实例,例如 m5、m6、m7 系列,如果集群的负载大部分为计算密集型任务,则可以考虑计算优化型的实例类型,例如 c5、c6、c7 系列。如果有较多的内存缓存需求,例如 Spark 常规任务,则可以考虑使用内存优化型的内存实例,例如 r5、r6、r7 系列。
对于 Master 节点来说,一般没有大量计算需求。对于大部分小于 50 个节点的集群,可以考虑使用常规类型,例如 m6 系列。不过,由于 Master 节点会运行关键服务(例如 Resource Manager、Namenode、Hiveserver2 等),所以一般建议使用较大的按需实例类型(例如 8xlarge 以上)。除此之外,单 Master 集群会有单点故障问题。对关键业务,建议使用 Multi-Master 配置。
在 EMR 6.1.0 之后的版本,支持了 EC2 Graviton 实例类型。这种实例类型相较 x86 架构实例(例如 m5 系),可以提供更高的性价比,也就是更高的性能的同时,成本更低。基于新的 Graviton2 实例,相较于上一代实例类型,EMR runtime for Spark 可以提供额外最高 30% 成本节省,并提升 15% 的性能。
对于任务节点来说,采用 Spot 实例可以提高高达 90% 的折扣(对比 On-Demand 价格),所以尽可能的可以使用 spot 实例降低成本。由于 EMR 上运行的应用如 Spark、MR、Hive 等原生支持了 task 重试的功能,所以在 task 由于 Spot 节点回收而导致失败的情况下,会自动在其他节点重试 task。对整个 job 的正常运行基本不会有太大影响。
对于核心节点,由于节点运行了 HDFS,Spot 实例强制回收的机制会导致节点上的数据丢失,所以核心节点建议配置 On-Demand 实例。
节点组
当您创建集群并指定主节点、核心节点和任务节点的配置时,有两个配置选项。您可以使用实例队列 或统一实例组。您选择的配置选项适用于所有节点,将在集群的生命周期内应用,并且实例队列和实例组无法在集群中共存。
统一实例组
统一实例组可提供比实例集简单的设置。每个 Amazon EMR 集群最多可以包含 50 个实例组:一个包含一个 Amazon EC2 实例的主实例组、一个包含一个或多个EC2实例的核心实例组以及最多 48 个可选任务实例组。每个核心实例组和任务实例组可以包含任意数量的 Amazon EC2 实例。您可以通过手动添加和删除 Amazon EC2 实例来扩展每个实例组,也可以设置自动扩展。
对于实例组配置,每个节点类型(主节点、核心节点或任务节点)均包含相同的实例类型和相同的实例购买选项:按需或 Spot。在创建实例组时,您可以指定这些设置。它们在以后均不可更改。但您可以将类型和购买选项相同的实例添加到核心和任务实例组。您也可以删除实例。要在创建集群后添加不同的实例类型,您可以添加其它任务实例组。您可以为每个实例组选择不同的实例类型和购买选项。
![]() |
统一实例组的好处是配置比较灵活,在集群启动后,可以增加实例组,在实例组中增加、删除实例,在使用自定义集群扩缩策略中,可以针对每个实例组单独设置扩缩策略。
但统一实例组有以下缺点:
- 实例组只能增加,不能删除(客户不能自己删除,需要提工单后台处理),需要特殊的处理去避免任务调度到这些废弃的实例组。
- 使用托管的自动扩缩策略时,是对整体所有实例组控制,客户无法干涉扩缩机制,无法控制扩缩哪个实例组;
- 需要使用自定义自动扩缩策略+标签匹配的方式,将任务调度到指定实例组,扩缩指定实例组,整体扩缩效率比较低;
实例队列
实例队列配置为 Amazon EC2 实例提供了最广泛的配置选项,Amazon EMR 集群的实例队列配置允许您为 Amazon EC2 实例选择各种配置选项,并帮助您为集群中的每种节点类型制定灵活而弹性的资源配置策略。每个节点类型均有一个实例集。使用 Console 创建集群时,您最多可以为每个队列指定 5 种实例类型,使用 AWS CLI 可以为每个队列指定 30 种实例类型。
对于主节点类型,Amazon EMR 会从您的实例列表中选择一种实例类型,您可以指定将其预置为按需实例还是竞价实例。实例队列还提供了竞价实例和按需购买的额外选项。竞价实例选项包括一个超时设置,用于指定在无法预置竞价容量时应采取的操作,以及用于启动竞价实例队列的首选分配策略(容量优化)。您也可以使用分配策略(最低价格)选项启动按需实例队列。
您也可以为不同的可用区选择多个子网。当 Amazon EMR 启动集群时,它会查看这些子网以找到您指定的实例和购买选项。如果 Amazon 在一个或多个可用区中EMR检测到 AWS 大规模事件,Amazon EMR 会自动尝试将流量从受影响的可用区域引出,并尝试根据您的选择启动您在备用可用区创建的新集群。请注意,只有在创建集群时才能选择集群可用区。如果发生可用区中断,现有的集群节点不会在新的可用区中自动重新启动。
![]() |
在选择实例队列创建集群时,集群的容量将以 unit 为单位定义,其中 unit 是指所选实例类型的 vCore 数量,比如选择了 m5.xlarge 实例,那一个节点代表 4 个 units。
通过 CLI 创建集群,指定实例队列的命令行如下:
使用实例队列的优势:
- 灵活性 – 可为核心节点、任务节点和主节点分别配置不同的实例类型和购买选项(按需/竞价),满足不同的工作负载需求。
- 成本优化 – 通过使用竞价实例可降低计算成本,并可设置目标容量平衡成本和性能。
- 可为每个队列指定多达 30 种不同的实例类型组合,以及各种竞价实例参数,提供更多定制选项。
- 配合托管的自动扩缩策略,可以快速(1 分钟粒度)扩缩集群。
需要注意:
- 实例队列在集群启动时定义好后,在集群运行期间无法修改,这一特性产品团队已经在做改进。
自动扩缩机制
当我们在 EMR 集群运行大量任务时,由于任务执行的周期、时长不同,通常对资源的需求是有波动的,这样要求 EMR 能够根据资源需求自动扩缩。 Amazon EMR 集群支持自动或手动调整 Amazon EC2 实例数量,以应对不同需求的工作负载。要使用自动扩展,您有两个选项。您可以启用 Amazon EMR 托管扩展或创建自定义自动扩展策略。下表介绍了两个选项之间的区别。
A | Amazon EMR 托管扩展 | 自定义自动扩展 | |
1 | 扩展策略和规则 | 无需策略。Amazon 通过持续评估集群指标和做出优化的扩展决策来 EMR 管理自动扩展活动。 | 您需要定义和管理自动伸缩策略和规则,如触发扩缩活动的特定条件、评估期、冷却时间等。 |
2 | 支持的 Amazon EMR 版本 | Amazon EMR 版本 5.30.0 及更高版本(Amazon EMR 6.0.0 版本除外) | Amazon EMR 4.0.0 及更高版本 |
3 | 支持的集群构成 | 实例组或实例队列 | 仅实例组 |
4 | 扩展限制配置 | 为整个集群配置扩展限制。 | 只能为每个实例组配置扩展限制。 |
5 | 指标评估频率 | 每 5 到 10 秒一次 更频繁地评估指标可以让 Amazon EMR 做出更精确的扩展决策。 |
您只能以五分钟的增量定义评估期。 |
6 | 受支持的应用程序 | 仅支持 YARN 应用程序,例如 Spark、Hadoop、Hive、Flink。Amazon EMR 托管扩展不支持不基于 YARN 的应用程序,例如 Presto 或 HBase。 | 在定义自动扩展规则时,您可以选择支持哪些应用程序。 |
自定义扩展策略
缺点:
- 扩缩效率比较低,指标评估时间较长,采用 5 分钟平均指标,每 5 分钟判断一次指标是否符合扩缩条件,使得整个扩缩周期最快要 10 分钟,而且容易有错误的感知。详见下面的扩缩机制分析。
- 每次扩缩的实例数量固定,在自定义扩展策略时,需要手动设置每次扩缩条件满足时增加或减少的实例数量,这样,集群的扩缩效率会变得太激进或太慢,因为每 5 分钟,集群才会做一次扩缩判断。
- 自定义扩展只支持实例组集群,需要针对每个实例组配置策略,无法对集群整体做扩缩配置。
- 任务需要根据规则(比如 label)来确定调度到哪个实例组,无法在一个整体的资源池中进行调度。
优点:
- 实例组在集群启动后,可以继续调整实例类型。
扩展机制分析:
首先,自定义扩展策略每 5 分钟评估一次 CloudWatch Metrics 的 5 分钟平局值,决定是否扩缩。
然后,我们根据 CloudWatch Metrics 中对于 “时间段”的定义,当对某个时段的统计数据进行聚合时,将为聚合的数据标记上对应的时段起始时间。例如,从晚上 7:00 到晚上 8:00 聚合的数据将会加上晚上 7:00 的时间戳。此外,在晚上 7:00 和晚上 8:00 之间聚合的数据将在晚上 7:00 开始可见,然后该聚合数据的值可能会随着 CloudWatch 收集该时间段内的更多样本而发生更改。
所以,9:20 的数据点是统计的 09:20-09:25 的数据平均值,而在 9:20 auto scaling 检查指标时,9:20 的数据点其实还未生成,因此 auto scaling 此时的判断依据是 9:15 的数据点。
假设 8:24 auto scaling 检查指标时,8:20 数据点的指标是 08:20-08:25 的平均值还未生成,因此其当下查看的指标其实是 8:15 数据点,就会发生 8:15 到 8:20 的指标是需要缩容,但 8:20 到 8:24 的集群实际情况有任务提交需要扩容,导致扩缩容无法按预期进行。
![]() |
托管扩展
托管扩展可让您根据工作负载自动增加或减少集群中实例或单元的数量。 Amazon EMR 不断评估集群指标,以制定扩展决策,从而优化集群的成本和速度。托管扩展适用于由实例组或实例队列组成的集群。
您必须为托管扩展配置以下参数。该限制仅适用于核心节点和任务节点。初始配置后,无法扩展主节点。
- 最小值(MinimumCapacityUnits)-集群中允许 EC2 容量的下限。它是通过虚拟中央处理单元(vCPU)内核或实例组来测量的。其衡量方式为通过实例集单位进行衡量。
- 最大值(MaximumCapacityUnits)-集群中允许 EC2 容量的上限。它是通过虚拟中央处理单元(vCPU)内核或实例组来测量的。其衡量方式为通过实例集单位进行衡量。
- 按需限制(MaximumOnDemandCapacityUnits)(可选)— 集群中按需 EC2 容量上限。如果未指定此参数,则默认为 MaximumCapacityUnits 的值。
- 此参数用于在按需实例和竞价型实例之间拆分容量分配。例如,如果您将最小参数设置为 2 个实例,将最大参数设置为 100 个实例,将按需限制设置为 10 个实例,则 Amazon EMR 托管扩展最多可扩展到 10 个按需实例,并将剩余容量分配给竞价型实例。
- 最大核心节点数(MaximumCoreCapacityUnits)(可选)-群集中核心节点类型允许 EC2 容量的上限。如果未指定此参数,则默认为 MaximumCapacityUnits 的值。
- 此参数用于在核心节点和任务节点之间分配容量。例如,如果您将最小参数设置为 2 个实例,将最大值设置为 100 个实例,将最大核心节点设置为 17 个实例,则 Amazon EMR 托管扩展最多可扩展到 17 个核心节点,并将剩余 83 个实例分配给任务节点。
对于托管扩展,如果实例组选择统一实例组,以上 CapacityUnits 需要使用实例数量来计算,如果实例组选择实例队列,以上 CapacityUnits 需要使用 vCore 数量来计算。
![]() |
托管扩展的优势:
- 扩缩效率高,每 5-10 秒进行一次评估,评估 1 分钟之内的指标情况,能够更加快速、精准的掌握集群的实时压力,及时做出扩缩调整。
- 配合实例队列,能够在更大、更丰富的实例队列中进行任务分配和容量调整,可以更加充分地利用集群资源。
- 配置简单,无需客户自己配置指标,EMR 控制面根据内部指标和算法自动评估,更加科学。
托管扩展的缺点:
- 当前的托管扩展机制存在一定的局限性:当任务请求资源时,系统会尽可能地扩展更多资源。然而,如果集群负载呈现出频繁的高资源需求,随后迅速转为低资源需求的模式,可能会出现资源扩展完成后再进行缩容的情况,从而导致一定的资源浪费。
成本优化
集群选择
EMR 的部署可以采用常驻集群、瞬态集群和 Serverless 三种模式,根据任务的大小、运行时间、调度周期和成本混合部署可以提升资源利用率,节约成本。
通常,客户的数据分析任务有天级、小时级、半天级、3 小时级、2 小时级,并且任务有的很小,几分钟即可运行完毕,有的任务需要 1 个小时甚至数个小时才能运行完毕,所有任务调度起来后,EMR 集群的资源利用率会有高峰、有低谷,如下图:
![]() |
理论上讲,采用集群自动扩缩后,会自动根据资源利用率进行资源的调节,理想的自动扩缩效果是这样的:
![]() |
但实际情况,EMR 的自动扩缩无法做到精确的按需扩缩,特别是当大量的采用 Spot 实例后,为了减少 Spot 回收带来的任务重算,EMR 会将任务的 executor 尽量分散在较多的节点上,这样会导致 auto scaling 检查到集群需要缩容时,由于节点上还有未释放的 executor,无法缩容,导致很多空闲的节点无法释放,造成集群资源利用率偏低,成本增加。
为了避免这种情况,我们需要根据任务资源使用的规律,削峰填谷,利用混合集群的模式,尽量使集群的资源使用保持平稳。
- 对于运行频率比较高,比如 1 小时、2 小时级的任务,我们保留在常驻集群,这些长期、持续运行的任务保持比较平稳的资源需求;
- 对于运行频率较低的任务,特别是每次运行时间较长的任务,采用瞬态集群或者是 EMR Serverless。
- 对于瞬态集群和 Serverless 的选择,需要根据实际情况测试来看。通常,就单任务使用的资源而言,EMR Serverless 的成本会高于集群,但集群会有 Master 节点、核心节点的成本,所以一般如果单任务运行时间长、规模大时(比如超过 1 个小时),使用按需实例的瞬态集群合适,因为长时间运行的任务遇到 Spot 回收的概率更大,更容易发生任务失败和重试。规模稍小的话 Serverless 比较合适,特别是作为平滑集群资源波动而言,Serverless 比较好,另外通过实际项目对比,EMR Serverles 的实际成本低于原生的 Spark on EKS/Yarn。
另外,在集群构建时,我们最好将所有集群视为瞬态集群,避免在集群的 Master 节点安装额外的服务和软件、保存数据、个性化定制,所有需要安装或配置的内容,放到集群的启动脚本中,保持集群无状态,这样可以随时切换集群、更新配置、快速从故障中恢复,配合我们的数据平台方案,在集群更换后,任务调度、管理可以快速的切换到新集群。
扩缩容配置
EMR 集群提供了灵活的实例组形式和自定义、托管扩缩策略,希望 EMR 的负载能像上图一样,资源随着需求自动伸缩,没有浪费,但实际上目前 EMR 的扩缩有一定的局限性。
自定义扩缩,指标收集周期长,评估周期长,扩缩响应跟不上实际资源的需求变化,而且每次固定的扩缩步长,也限制了扩缩的速度。
托管扩缩策略虽然在理论上具有较高的扩缩效率,但在实际运行中可能出现过于激进的扩展行为:当任务申请资源时,系统可能直接按照集群配置的最大容量启动节点。另一方面,如果系统分配的节点数量超过任务实际所需,Executor 会尽可能分布在更多节点上,导致单个节点利用率偏低,进而降低了整个集群的资源利用率。
![]() |
从上面的 YARN Memory 监控可以看到,大部分时间,集群的资源利用率是很低的,但在局部会非常高,甚至出现任务排队,这是因为当 auto scaling 需要扩展时,超扩了资源,但又无法快速缩回来,导致空闲率快速升高并持续空闲。
因为造成这个问题的原因是 auto scaling 会按集群资源上限扩展,所以需要降低集群的上限配置,但是因为有尖峰的存在,如果降低上限又会导致局部任务拥堵、排队、失败。所以,我们需要分析任务调度周期和对应的资源需求峰值,将波峰时刻的大任务单独拿出来,通过 Serverless 运行。
这样,通过常驻集群+Serverless 的形式,削峰填谷,使集群整体资源的利用率保持在较高的水平,降低总体成本。同时,如果任务调度周期是规律的,可预知的,我们也可以通过自动或手动调整集群上限的方式来协助集群调整资源数量,保持在合理的利用率水平。参考【集群容量自动调整方案】。
通过以上方式,在实际项目中,我们有 1200 多个任务,5 个 EC2 常驻集群,7 个 EMR Serverless Application 运行 15 个大任务,经过 Serverless 削峰填谷和降低集群上限配置,整体成本优化 30% 以上,从下图可以看出,集群的空闲率明显下降。比如 22:00-2:00 时间段,之前的空闲率大部分在 50% 以上,但调整后的空闲率大部分都在 50% 以下(蓝色折线图)。
![]() |
实例的选择
- 对于任务负载比较高的 Spark 常驻集群,建议 Master 节点和 Core 节点,采用 On-Demand 实例,任务节点采用 Spot 实例,因为 Spark 任务有自动重算功能,即使遇到 Spot 实例回收,任务也能正常完成,只是可能会延长任务运行时间,但由于 Spot 实例提供了高达 90% 的折扣,能降低整体成本。
- 对于 Flink、Presto/Trino 集群,建议采用 On-Demand 节点,禁用集群自动扩缩。
- 如果在规划集群时 Spot 占比非常高,可以将 Spark Driver 节点运行在 OD 节点,参考【EMR Best Practices】。
- 对于任务节点使用 Spot 实例的情况,运行时间较长的任务,更容易受到 Spot 实例回收的影响,所以结合上面章节的集群选择和扩缩容优化,这样的任务更适合放到 Serverless 运行。
- 另外在选择实例类型时,我们建议尽量的采用实例队列的形式,添加更多类型的实例,保障 Spot 有足够的容量,也避免使用太大的实例,比如 4x、8x 这样的实例,一般 Spot 回收的概率比较高,而且一旦回收对任务的影响比较大。
- 我们建议在集群中适当增加 Graviton 实例。当前,Graviton 实例的可用容量持续增加,Spot 价格的折扣也基本与 x86 实例持平。对于 EMR 集群,任务能够在 ARM 与 x86 架构之间实现平滑调度。但需要注意,用户应避免在任务中显式依赖特定平台的包或库,以确保跨架构兼容性。
总结
本文系统的从版本选择、节点选择、自动扩缩机制、集群配置等各个角度介绍和总结了常用的 EMR 最佳实践,从性能、可靠性、成本多方面阐述了 EMR 的优化方案和原理,最后通过项目实践展示了应用最佳实践后的效果,进一步验证了基于 EMR 和开源组件构建的数据分析平台在性能、成本和可靠性上的优势。
参考资料
https://aws.github.io/aws-emr-best-practices/docs/bestpractices/Features/Spot%20Usage/best_practices