请注意本文部分内容经过AI辅助生成,虽然经过笔者检查但是并不保证内容的正确性,请自行判断准确性,本文对相关后果不承担责任
HDFS 和 YARN 节点下线机制
EMR 节点下线由三层协同完成:
EMR 控制面 → Instance Controller (IC) → HDFS/YARN
- EMR 控制面:通过 ex-rpc (8443) 下发缩容指令给 Master IC
- Instance Controller:Poller 线程(每 30 秒)检测到需要缩容的实例,写入 exclude 文件并刷新 HDFS/YARN
- HDFS NameNode / YARN ResourceManager:读取 exclude 文件,执行 graceful decommission
关键配置文件如下
| 文件/参数 | 用途 |
|---|---|
/emr/instance-controller/lib/yarn.nodes.exclude.xml |
YARN 节点排除列表 |
/emr/instance-controller/lib/dfs.hosts.exclude |
HDFS DataNode 排除列表 |
yarn.resourcemanager.nodemanager-graceful-decommission-timeout-secs |
YARN 下线超时(默认 3600s) |
下线 Task 节点时 yarn.nodes.exclude.xml 内容:
xml
<!-- Master /emr/instance-controller/lib/yarn.nodes.exclude.xml -->
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<hosts>
<!-- Generated by EMR instance-controller on 2026-03-25 05:21:30 -->
<host>
<name>ip-192-168-22-156.cn-north-1.compute.internal</name>
</host>
</hosts>
下线 Core 节点时 dfs.hosts.exclude 内容:
shell
# Master /emr/instance-controller/lib/dfs.hosts.exclude
ip-192-168-26-228.cn-north-1.compute.internal
下线完成或恢复后,IC Poller 会自动清空这些文件。
Task 节点下线流程
Task 节点只运行 YARN NodeManager,不运行 HDFS DataNode,因此下线只涉及 YARN。
# Master /emr/instance-controller/log/instance-controller.log, /var/log/hadoop-yarn/hadoop-yarn-resourcemanager-*.log
05:20:57 EMR API: modify-instance-groups InstanceCount=0
05:21:30 IC Poller: Identified 1 instances in Task - 1 to shrink: i-05b5abb5832904d04:2147483647
05:21:30 IC Poller: 写入 yarn.nodes.exclude.xml(添加 ip-192-168-22-156)
05:21:33 IC YARNRefresh: 读取 yarn-site.xml 获取 graceful-decommission-timeout=3600s
05:21:36 YARN RM: "Adding node ip-192-168-22-156 to excluded hosts"
05:21:36 YARN RM: "Gracefully decommission node ip-192-168-22-156 with state RUNNING"
05:21:36 YARN RM: "Node Transitioned from RUNNING to DECOMMISSIONING"
05:21:36 YARN RM: 更新节点资源为当前容器占用量(不再分配新容器)
05:22:00 IC Poller: Update i-05b5abb5832904d04 InstanceState RUNNING => DECOMMISSIONING
日志来源:
| 时间线条目 | 来源文件 |
|---|---|
IC Poller / IC YARNRefresh |
Master: /emr/instance-controller/log/instance-controller.log |
YARN RM |
Master: /var/log/hadoop-yarn/hadoop-yarn-resourcemanager-*.log |
关键行为:
- YARN 进入 DECOMMISSIONING 后,不再向该节点分配新容器,已有容器继续运行
- 但不是必然等待超时才处理 ,YARN 会在以下条件提前完成 decommission:
- 节点上没有运行中的容器 → 立即 DECOMMISSIONED
- 所有容器正常完成 → 立即 DECOMMISSIONED
- 超时到达(默认 3600s)→ 强制 DECOMMISSIONED,杀死剩余容器
- Task 节点不涉及 HDFS 数据迁移,IC 日志显示
returning shrinkHdfs since nothing to do
Core 节点下线流程
Core 节点同时运行 HDFS DataNode 和 YARN NodeManager,下线需要双重 decommission。
# Master /var/log/hadoop-hdfs/hadoop-hdfs-namenode-*.log, /var/log/hadoop-yarn/hadoop-yarn-resourcemanager-*.log
05:31:51 HDFS NN: "Adding node ip-192-168-26-228 to excluded hosts"
05:31:51 HDFS NN: "Starting decommission of 192.168.26.228:9866 with 5 blocks"
05:31:54 YARN RM: "Gracefully decommission node ip-192-168-26-228 with state RUNNING"
05:31:54 YARN RM: "Node Transitioned from RUNNING to DECOMMISSIONING"
05:32:06 HDFS NN: "Node 192.168.26.228 still has 10 blocks to replicate"
05:32:36 HDFS NN: 数据块复制完成(dfs.replication=1,数据量小)
日志来源:
| 时间线条目 | 来源文件 |
|---|---|
HDFS NN |
Master: /var/log/hadoop-hdfs/hadoop-hdfs-namenode-*.log |
YARN RM |
Master: /var/log/hadoop-yarn/hadoop-yarn-resourcemanager-*.log |
| DataNode 侧复制日志 | Core Worker: /var/log/hadoop-hdfs/hadoop-hdfs-datanode-*.log |
Core vs Task 下线的关键区别:
| 维度 | Task 节点 | Core 节点 |
|---|---|---|
| HDFS DataNode | 不运行 | 需要 decommission |
| YARN NodeManager | graceful decommission | graceful decommission |
| 数据迁移 | 无 | 需要复制 HDFS 数据块 |
| 下线速度 | 快(等容器完成即可) | 慢(需等 HDFS 数据复制 + 容器完成) |
| 风险 | 低 | 高(可能丢失 HDFS 数据) |
| EMR 保护 | 无特殊保护 | 不允许缩减到低于 dfs.replication |
手动下线 Core 节点步骤
仅写入 exclude 文件是不够的,必须执行 refreshNodes 通知 NameNode/ResourceManager 重新读取文件。同时需要注意 IC Poller 每 30 秒会覆盖 exclude 文件(如果 IC 认为不需要缩容会清空它)。
bash
# 在 Master 节点执行,/emr/instance-controller/lib/dfs.hosts.exclude, yarn.nodes.exclude.xml
# 1. 写入 HDFS exclude 文件
echo "ip-192-168-26-228.cn-north-1.compute.internal" | sudo tee /emr/instance-controller/lib/dfs.hosts.exclude
# 2. 通知 NameNode 刷新(必须,否则 NN 不感知文件变化)
hdfs dfsadmin -refreshNodes
# 3. 写入 YARN exclude 文件
sudo bash -c 'cat > /emr/instance-controller/lib/yarn.nodes.exclude.xml << EOF
<?xml version="1.0" encoding="UTF-8"?>
<hosts>
<host><name>ip-192-168-26-228.cn-north-1.compute.internal</name><timeout>3600</timeout></host>
</hosts>
EOF'
# 4. 通知 ResourceManager 刷新(必须)
yarn rmadmin -refreshNodes -g -server
# 5. 验证状态
hdfs dfsadmin -report | grep -E "Name:|Decommission"
yarn node -list -all
注意事项:
- IC Poller 每 30 秒检查一次,如果 EMR 控制面没有下发缩容指令,IC 会在下一个周期清空 exclude 文件并 recommission 节点(实验中已验证)
- 推荐通过 EMR API
modify-instance-groups缩减 Core 实例组,让 IC 自动完成 exclude + refresh 流程 - 如果必须手动操作,可临时停止 IC(
sudo systemctl stop instance-controller),但会影响集群管理功能,不推荐用于生产环境
Instance Controller 在下线中的角色
IC 的 Poller 线程是下线的核心协调者:
# Master /emr/instance-controller/log/instance-controller.log 中 Poller 线程日志
Poller 每 30 秒执行:
1. pollInstanceGroups() → 检测实例组配置变化
2. doGracefulShrink() → 识别需要缩容的实例
├── 写入 yarn.nodes.exclude.xml
├── 执行 yarn rmadmin -refreshNodes
├── 写入 dfs.hosts.exclude(仅 Core)
└── 执行 hdfs dfsadmin -refreshNodes(仅 Core)
3. updateJointStates() → 更新实例状态 RUNNING → DECOMMISSIONING
4. cleanupTerminatedInstances() → 清理已终止实例
Spark 任务在节点下线过程中的表现
测试任务代码如下,设计思路:
- 200 次循环 + sleep 3s:保证任务长时间运行,留出时间做下线操作
parallelize(5000000, 100)+reduceByKey:产生 shuffle 操作,模拟真实分布式计算dynamicAllocation.enabled=false:固定 4 个 executor,方便观察下线前后变化task.maxFailures=8:提高容错,避免节点下线导致任务直接失败
python
# Master /tmp/long_spark.py
from pyspark.sql import SparkSession
import time
spark = SparkSession.builder.appName("DecommissionTest").getOrCreate()
sc = spark.sparkContext
print("=== App ID: {} ===".format(sc.applicationId))
for i in range(200):
print("\n=== Iteration {} at {} ===".format(i, time.strftime('%H:%M:%S')))
try:
rdd = sc.parallelize(range(5000000), 100)
result = rdd.map(lambda x: (x % 500, x)).reduceByKey(lambda a, b: a + b).count()
print("Result: {} keys".format(result))
except Exception as e:
print("ERROR: {}".format(e))
time.sleep(3)
spark.stop()
提交命令:
bash
# 在 Master 节点执行
spark-submit \
--master yarn \
--deploy-mode cluster \
--name DecommissionTest \
--num-executors 4 \
--executor-cores 1 \
--executor-memory 1g \
--conf spark.dynamicAllocation.enabled=false \
--conf spark.task.maxFailures=8 \
/tmp/long_spark.py
Spark executor 变化 (Spark REST API + hdfs:///var/log/spark/apps/ event log):
# Spark REST API (http://<AM-host>:<port>/api/v1/applications/<app-id>/executors)
下线前:
Executor 1 (Core-192.168.26.228) tasks=2008
Executor 2 (Core-192.168.28.197) tasks=2035
Executor 3 (Core-192.168.21.229) tasks=2029
Executor 4 (Task-192.168.22.156) tasks=379 ← Task 节点
下线后:
Executor 1 (Core-192.168.26.228) tasks=4658
Executor 2 (Core-192.168.28.197) tasks=5014
Executor 3 (Core-192.168.21.229) tasks=4983
Executor 4 (Task-192.168.22.156) tasks=379 ← 不再增长(被黑名单)
Executor 5 (Core-192.168.28.197) tasks=4552 ← 新启动
Executor 6 (Core-192.168.28.197) tasks=614 ← 新启动
关键发现:
- Executor 4 的 tasks 停止增长(379 不变),
spark.blacklist.decommissioning.enabled=true生效 - Spark 在其他节点上启动了新 executor (5, 6) 来补偿
- Spark 任务没有中断,继续正常运行
Executor 4 并没有被停止(仍然 active=True),只是被加入黑名单不再接收新 task。新增 2 个 executor 的原因:
- Spark 在
spark.blacklist.decommissioning.enabled=true下,将 DECOMMISSIONING 节点上的 executor 视为"不可用" - 实际可用 executor 少于请求的 4 个,Spark 向 YARN 申请新 executor 补偿
- 同时 Managed Scaling 检测到缩容,自动创建了新 Task 实例组(
ig-16YZOPOKMVAQL ON_DEMAND)替换被缩减的 Spot 实例组 - 最终出现 6 个 executor(4 原始 + 2 补偿),其中 Executor 4 逐渐空闲,等待 YARN decommission 超时后退出
Spark 相关配置
properties
# Master /etc/spark/conf/spark-defaults.conf
spark.shuffle.service.enabled true
spark.dynamicAllocation.enabled true
spark.blacklist.decommissioning.enabled true
spark.blacklist.decommissioning.timeout 1h
spark.stage.attempt.ignoreOnDecommissionFetchFailure true
spark.decommissioning.timeout.threshold 20
spark.resourceManager.cleanupExpiredHost true
spark.files.fetchFailure.unRegisterOutputOnHost true
各配置的作用
| 配置 | 作用 |
|---|---|
spark.blacklist.decommissioning.enabled=true |
将 DECOMMISSIONING 节点加入黑名单,不再分配新 task |
spark.blacklist.decommissioning.timeout=1h |
黑名单超时时间,与 YARN decommission timeout 对齐 |
spark.stage.attempt.ignoreOnDecommissionFetchFailure=true |
从 decommissioning 节点 fetch shuffle 数据失败时,不标记 stage 失败 |
spark.decommissioning.timeout.threshold=20 |
节点下线前 20 秒开始准备 |
spark.resourceManager.cleanupExpiredHost=true |
清理已过期主机的 shuffle 数据引用 |
spark.files.fetchFailure.unRegisterOutputOnHost=true |
fetch 失败时注销该主机上的所有 shuffle output |
优化节点下线对 Spark 任务的影响
启用 Spark Node Decommission
properties
# 创建集群时通过 EMR Configuration 设置
spark.decommission.enabled=true
spark.storage.decommission.enabled=true
spark.storage.decommission.rddBlocks.enabled=true
spark.storage.decommission.shuffleBlocks.enabled=true
当 YARN 将节点标记为 DECOMMISSIONING 时,会向 executor 进程发送 SIGPWR 信号,触发以下流程:
YARN 发送 SIGPWR 信号给 Executor
↓
Executor 收到信号,通知 Driver: "我要下线了"
↓
Driver 将该 Executor 从调度列表中移除(不再分配新 task)
Driver 通知 BlockManager Master 标记该 Executor 的 BlockManager 为 decommissioned
↓
Executor 启动 BlockManagerDecommissioner,开始数据迁移:
├── Shuffle 数据迁移(spark.storage.decommission.shuffleBlocks.enabled)
│ → 将本地 shuffle 文件发送到其他存活的 executor
│ → Driver 更新 shuffle 元数据映射(MapOutputTracker)
│ → 下游 task 从新位置 fetch shuffle 数据
└── RDD 缓存迁移(spark.storage.decommission.rddBlocks.enabled)
→ 将缓存的 RDD block 复制到 peer executor 的 BlockManager
→ 如果没有可用 peer,可写入 fallback storage(如 HDFS/S3)
↓
迁移完成 + 无运行中 task → Executor 优雅退出,通知 Driver: "Finished decommissioning"
关键细节:
- 迁移目标选择:Executor 从 Driver 的 BlockManager Master 获取存活的 peer executor 列表
- 如果没有可用 peer,可配置
spark.storage.decommission.fallbackStorage.path(如 HDFS/S3 路径)作为备用存储 - 迁移不保证 100% 完成 --- 如果节点在迁移完成前被强制终止(如 Spot 回收),仍会丢失未迁移的数据
- 迁移完成后 Driver 日志会出现
Updating map output for <shuffle_id> to BlockManagerId(<executor_id>, ...)表示 shuffle 元数据已更新
Spark decommission 与 YARN decommission 的关系:
spark.decommission.enabled 依赖 YARN decommission 作为触发信号,两者是不同层面的机制:
YARN decommission(基础设施层)→ 发送 SIGPWR 信号 → Spark decommission(应用层)
| 维度 | YARN decommission | Spark decommission |
|---|---|---|
| 层面 | 基础设施层(节点/容器) | 应用层(executor/数据) |
| 做什么 | 不再分配新容器,等待已有容器完成或超时后杀死 | 迁移 shuffle 数据和 RDD 缓存到其他 executor |
| 触发方式 | exclude 文件 + refreshNodes | 收到 YARN 发来的 SIGPWR 信号 |
| 不启用的后果 | 节点直接被移除,容器被杀 | 节点正常 decommission,但 shuffle 数据丢失需要重算 |
spark.decommission.enabled 必须配合 YARN decommission 使用,它不是独立机制,而是在 YARN decommission 流程中插入了"数据迁移"步骤。没有 YARN 的 SIGPWR 信号,Spark 的 decommission 逻辑不会被触发。
确认 External Shuffle Service 已启用
当前集群 ESS 已正确配置(Core/Task 节点的 yarn-site.xml 中注册了 spark_shuffle,端口 7337 在监听)。
注意:检查 ESS 配置时应查看 Core/Task 节点 的 yarn-site.xml,而非 Master 节点(Master 不运行 NodeManager,其配置中不包含 spark_shuffle 是正常的)。
bash
# 在 Core/Task 节点上验证
grep spark_shuffle /etc/hadoop/conf/yarn-site.xml
ss -tlnp | grep 7337
调整 YARN Decommission 超时
xml
<!-- Master /etc/hadoop/conf/yarn-site.xml, 创建集群时通过 EMR Configuration 设置 -->
<property>
<name>yarn.resourcemanager.nodemanager-graceful-decommission-timeout-secs</name>
<value>7200</value> <!-- 设为最长任务运行时间 -->
</property>
使用 Node Labels 限制 AM 调度
EMR 支持两种 Node Label 维度来限制 AM(Application Master)的调度位置:
按节点类型(CORE/TASK)
json
// 创建集群时通过 EMR Configuration API 设置
[{
"Classification": "yarn-site",
"Properties": {
"yarn.node-labels.enabled": "true",
"yarn.node-labels.am.default-node-label-expression": "CORE"
}
}]
效果:AM 只调度到 Core 节点,Task 节点只运行 executor。Managed Scaling 会根据 AM 需求扩 Core,根据 executor 需求扩 Task。
按购买类型(ON_DEMAND/SPOT)--- EMR 7.2+ 推荐
json
// 创建集群时通过 EMR Configuration API 设置
[{
"Classification": "yarn-site",
"Properties": {
"yarn.node-labels.enabled": "true",
"yarn.node-labels.am.default-node-label-expression": "ON_DEMAND"
}
}]
效果:AM 只调度到 ON_DEMAND 节点,SPOT 节点只运行 executor。这样 Spot 中断不会杀死 AM。
当前集群状态 (Master /etc/hadoop/conf/yarn-site.xml):
# Master /etc/hadoop/conf/yarn-site.xml
yarn.node-labels.enabled = false ← 未启用
yarn.node-labels.am.default-node-label-expression = (空) ← 未设置
yarn.nodemanager.node-labels.provider.configured-node-partition = ON_DEMAND ← 各节点已配置 partition
虽然各节点已配置了 configured-node-partition=ON_DEMAND,但因为 yarn.node-labels.enabled=false,Node Labels 功能实际未生效,AM 可以调度到任意节点(包括 Task/Spot 节点)。
启用方法:创建集群时通过 EMR Configuration 设置,不建议在运行中的集群修改(需要重启 ResourceManager)。
Hive 任务和节点下线
hive任务会受影响,影响程度取决于执行引擎:
Hive on Tez(EMR 默认)
- Tez AM 运行在某个节点上,如果该节点被 decommission,AM 会被杀死
- Tez 有 DAG 级别的容错,AM 重启后可以从 checkpoint 恢复
- Tez 使用
tez_shuffleaux service(当前集群已配置),shuffle 数据存储在 NodeManager 进程中 - 节点下线后,该节点上的 shuffle 数据丢失,需要重新计算上游 task
- Tez shuffle 日志可在 Worker 节点
/var/log/hadoop-yarn/hadoop-yarn-nodemanager-*.log中查看
Hive on Spark
- 与 Spark 任务相同的影响
- 受益于
spark.blacklist.decommissioning.enabled等配置
缓解措施
- 将 Hive AM 调度到稳定的 Core/ON_DEMAND 节点
- 增大
yarn.resourcemanager.nodemanager-graceful-decommission-timeout-secs - 对于关键 Hive 任务,使用
hive.exec.parallel=true提高并行度,减少单节点依赖
HBase RegionServer 下线
测试集群安装了 HBase 2.6.2,HBase Master 在 Master 节点,3 个 RegionServer 分布在 Core 节点上。
HBase 的下线是独立于 HDFS/YARN decommission 的第三层:
EMR 控制面 → IC Poller → HDFS decommission (数据块复制)
→ YARN decommission (容器排空)
→ HBase decommission (Region 迁移) ← 需要额外处理
EMR 的 IC 在缩容 Core 节点时会处理 HDFS 和 YARN 的 decommission,但不会自动迁移 HBase Region。如果直接终止 Core 节点,该节点上的 RegionServer 会被杀死,其 Region 会经历 WAL 恢复过程(server recovery),影响集群可用性。
HBase graceful decommission 流程
使用 graceful_stop.sh 对 Core 节点 192.168.26.228 执行 graceful decommission:
bash
# Master 执行
sudo -u hbase /usr/lib/hbase/bin/graceful_stop.sh --restart ip-192-168-26-228.cn-north-1.compute.internal
# graceful_stop.sh 输出 + Master /var/log/hbase/hbase-hbase-master-*.log
08:33:23 graceful_stop.sh: Disabling load balancer
08:33:31 graceful_stop.sh: Previous balancer state was true
08:33:31 graceful_stop.sh: Unloading ip-192-168-26-228 region(s)
08:33:36 HBase Master: Client=hbase move hri=1588230740, source=ip-192-168-26-228, destination=ip-192-168-21-229
08:33:36 HBase Master: TransitRegionStateProcedure table=hbase:meta, region=1588230740, REOPEN/MOVE
08:33:36 HBase Master: regionState=CLOSING, regionLocation=ip-192-168-26-228
08:33:37 HBase Master: regionState=CLOSED
08:33:37 HBase Master: regionState=OPENING, regionLocation=ip-192-168-21-229
08:33:37 HBase Master: regionState=OPEN, regionLocation=ip-192-168-21-229
08:33:38 graceful_stop.sh: Unloaded ip-192-168-26-228 region(s)
08:33:38 graceful_stop.sh: Restoring balancer state to true
RegionServer 侧日志
# Core(192.168.26.228) /var/log/hbase/hbase-hbase-regionserver-ip-192-168-26-228.cn-north-1.compute.internal.log
08:33:36 INFO UnassignRegionHandler: Close 1588230740
08:33:36 INFO HRegion: Closing region hbase:meta,,1.1588230740
08:33:36 INFO HRegion: Flushing 1588230740 3/3 column families, dataSize=2.79 KB
08:33:37 INFO HRegion: Closed hbase:meta,,1.1588230740
08:33:37 INFO HRegionServer: Adding 1588230740 move to ip-192-168-21-229 record at close sequenceid=21
Region 迁移结果:
# hbase shell 'status "detailed"'
迁移前: ip-192-168-26-228 numberOfOnlineRegions=1 (hbase:meta)
迁移后: ip-192-168-26-228 numberOfOnlineRegions=0
ip-192-168-21-229 numberOfOnlineRegions=2 (接收了迁移的 region)
HBase graceful decommission 的完整流程
graceful_stop.sh 执行:
1. 禁用 HBase Balancer(防止迁移过程中 Balancer 干扰)
2. 调用 region_mover 将目标 RegionServer 上的所有 Region 逐个迁移到其他 RS
├── 对每个 Region: Master 发起 CLOSE → RS flush memstore → RS 关闭 Region
├── Master 选择目标 RS → 发起 OPEN → 目标 RS 打开 Region
└── 更新 hbase:meta 表中的 Region 位置信息
3. 停止目标 RegionServer 进程(可选 --restart 参数会重启)
4. 恢复 HBase Balancer
手动下线 Core 节点 HBase
在下线 Core 节点前,必须先迁移 HBase Region,否则会触发 WAL recovery:
bash
# 在 Master 节点执行
# 1. 先迁移 HBase Region(graceful_stop.sh 会自动处理)
sudo -u hbase /usr/lib/hbase/bin/graceful_stop.sh ip-192-168-26-228.cn-north-1.compute.internal
# 2. 确认 Region 已全部迁移
echo 'status "simple"' | hbase shell -n
# 3. 然后再执行 HDFS/YARN decommission
echo "ip-192-168-26-228.cn-north-1.compute.internal" | sudo tee /emr/instance-controller/lib/dfs.hosts.exclude
hdfs dfsadmin -refreshNodes
注意事项:
- EMR 通过
modify-instance-groups缩减 Core 节点时,IC 会处理 HDFS/YARN decommission,但不会自动执行 HBase Region 迁移 - 如果不先迁移 Region 就终止节点,HBase Master 会检测到 RegionServer 死亡,触发 WAL split + server recovery,期间相关 Region 不可用
graceful_stop.sh在 EMR 上的--restart和 stop 功能可能不完整(缺少hbase-daemons.sh),但 Region 迁移(unload)功能正常- 对于 Spot 节点上的 RegionServer,建议配置 HBase WAL 到 S3(EMR WAL 功能)以减少 recovery 时间
External Shuffle Service (ESS)
测试集群的ESS 已正确启用并运行。
注意:Master 和 Worker 节点的 yarn-site.xml 配置不同(这是正常的):
| 节点 | yarn.nodemanager.aux-services |
原因 |
|---|---|---|
| Master | mapreduce_shuffle,tez_shuffle, |
Master 不运行 NodeManager,无需 spark_shuffle |
| Core/Task | mapreduce_shuffle,spark_shuffle,tez_shuffle, |
运行 NM,正确注册了 spark_shuffle |
Core 节点 /etc/hadoop/conf/yarn-site.xml
xml
<!-- Core 节点 /etc/hadoop/conf/yarn-site.xml -->
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle,spark_shuffle,tez_shuffle,</value>
</property>
<property>
<name>yarn.nodemanager.aux-services.spark_shuffle.class</name>
<value>org.apache.spark.network.yarn.YarnShuffleService</value>
</property>
验证 ESS 运行状态:Core 节点端口 7337 正在监听。
# Core 节点执行 ss -tlnp | grep 7337
$ ss -tlnp | grep 7337
LISTEN 0 4096 0.0.0.0:7337 0.0.0.0:*
ESS 是运行在 YARN NodeManager 中的 aux service,独立于 Spark executor 进程:
- 解耦 shuffle 数据与 executor 生命周期:executor 退出后 shuffle 数据仍可通过 ESS 提供
- 支持 Dynamic Resource Allocation (DRA):executor 可以动态释放,shuffle 数据不会丢失
- 减少 executor 内存压力:shuffle 数据的服务由 ESS 承担
有/无 ESS 在节点下线中的对比:
没有 ESS:
Executor 退出 → shuffle 数据丢失 → 下游 task fetch 失败 → 重新计算上游 task
有 ESS:
Executor 退出 → shuffle 数据仍在 ESS 中 → 下游 task 正常 fetch → 无需重算
但: 节点被终止 → ESS 也消失 → shuffle 数据仍然丢失
关键点 :ESS 只能保护 executor 退出(如 DRA 回收)的场景,不能保护节点被终止的场景。节点下线后 ESS 也会消失。
ESS 在节点下线中的影响
| 场景 | ESS 是否受影响 | 说明 |
|---|---|---|
| Executor 被 DRA 回收 | 不受影响 | ESS 独立于 executor,shuffle 数据保留 |
| 节点 DECOMMISSIONING | 部分影响 | ESS 仍在运行,但不接受新 task |
| 节点被终止 | 完全丢失 | ESS 随 NodeManager 一起消失 |
| Spot 中断 | 完全丢失 | 节点立即终止 |
ESS 只能保护 executor 退出场景(如 DRA 回收),不能保护节点被终止的场景。
原始日志摘录
Task 节点下线 --- IC 日志
Master /emr/instance-controller/log/instance-controller.log
# Master /emr/instance-controller/log/instance-controller.log
2026-03-25 05:21:30,805 INFO Poller: Identified 1 instances in Task - 1 to shrink: i-05b5abb5832904d04:2147483647
2026-03-25 05:21:30,805 INFO Poller: instancesToShrink: i-05b5abb5832904d04 , instancesToUnShrink:
2026-03-25 05:21:30,805 INFO Poller: Calling updateJointStates before executing the shrink plan
2026-03-25 05:21:30,806 INFO Poller: Beginning execution of the shrink plan
2026-03-25 05:21:30,806 INFO Poller: shrinkYarnNodes for 1 nodes to shrink and 0 nodes to unshrink
2026-03-25 05:21:30,808 INFO Poller: Reading exclude file /emr/instance-controller/lib/yarn.nodes.exclude.xml
2026-03-25 05:21:30,810 INFO Poller: MergeAndWrite: Excludes File: /emr/instance-controller/lib/yarn.nodes.exclude.xml, Hosts in ExcludeFile BeforeMerge: 0, nodesToShrink: 1, nodesToUnShrink: 0
2026-03-25 05:21:30,857 INFO Poller: Wrote /emr/instance-controller/lib/yarn.nodes.exclude.xml:
<name>ip-192-168-22-156.cn-north-1.compute.internal</name>
2026-03-25 05:21:30,859 INFO Poller: returning shrinkHdfs since nothing to do
2026-03-25 05:21:33,427 INFO YARNRefresh: retrieveConfigEntry: filename=yarn-site.xml, key=yarn.resourcemanager.nodemanager-graceful-decommission-timeout-secs, result=3600
2026-03-25 05:22:00,864 INFO Poller: Update i-05b5abb5832904d04 InstanceState RUNNING => DECOMMISSIONING
2026-03-25 05:22:00,864 INFO Poller: Instance i-05b5abb5832904d04 started DECOMMISSIONING at 2026-03-25T05:22:00.864Z.
Task 节点下线 --- YARN RM 日志
Master /var/log/hadoop-yarn/hadoop-yarn-resourcemanager-ip-192-168-30-19.cn-north-1.compute.internal.log
# Master /var/log/hadoop-yarn/hadoop-yarn-resourcemanager-ip-192-168-30-19.cn-north-1.compute.internal.log
2026-03-25 05:21:36,064 INFO HostsFileReader: Adding a node "ip-192-168-22-156.cn-north-1.compute.internal" to the list of excluded hosts from /emr/instance-controller/lib/yarn.nodes.exclude.xml
2026-03-25 05:21:36,064 INFO NodesListManager: Gracefully decommission node ip-192-168-22-156.cn-north-1.compute.internal:8041 with state RUNNING
2026-03-25 05:21:36,064 INFO RMNodeImpl: Put Node ip-192-168-22-156.cn-north-1.compute.internal:8041 in DECOMMISSIONING.
2026-03-25 05:21:36,064 INFO RMNodeImpl: ip-192-168-22-156.cn-north-1.compute.internal:8041 Node Transitioned from RUNNING to DECOMMISSIONING
2026-03-25 05:21:36,121 INFO AbstractYarnScheduler: Update resource on node: ip-192-168-22-156.cn-north-1.compute.internal from: <memory:3072, vCores:4>, to: <memory:1408, vCores:1> in 0 ms
Core 节点下线 --- HDFS NN 日志
Master /var/log/hadoop-hdfs/hadoop-hdfs-namenode-ip-192-168-30-19.cn-north-1.compute.internal.log
# Master /var/log/hadoop-hdfs/hadoop-hdfs-namenode-ip-192-168-30-19.cn-north-1.compute.internal.log
2026-03-25 05:31:51,540 INFO HostsFileReader: Adding a node "ip-192-168-26-228.cn-north-1.compute.internal" to the list of excluded hosts from /emr/instance-controller/lib/dfs.hosts.exclude
2026-03-25 05:31:51,540 INFO DatanodeAdminManager: Starting decommission of 192.168.26.228:9866 with 5 blocks
2026-03-25 05:32:06,518 INFO DatanodeAdminDefaultMonitor: Node 192.168.26.228:9866 still has 10 blocks to replicate before it is a candidate to finish Decommission In Progress.
2026-03-25 05:32:36,019 INFO HeartbeatManager: Stopping decommissioning of live node 192.168.26.228:9866
2026-03-25 05:32:36,022 INFO BlockManager: Invalidated 10 extra redundancy blocks on 192.168.26.228:9866 after it is in service
Core 节点下线 --- YARN RM 日志
Master /var/log/hadoop-yarn/hadoop-yarn-resourcemanager-ip-192-168-30-19.cn-north-1.compute.internal.log
# Master /var/log/hadoop-yarn/hadoop-yarn-resourcemanager-ip-192-168-30-19.cn-north-1.compute.internal.log
2026-03-25 05:31:54,614 INFO NodesListManager: Gracefully decommission node ip-192-168-26-228.cn-north-1.compute.internal:8041 with state RUNNING
2026-03-25 05:31:54,614 INFO RMNodeImpl: Put Node ip-192-168-26-228.cn-north-1.compute.internal:8041 in DECOMMISSIONING.
2026-03-25 05:31:54,614 INFO RMNodeImpl: ip-192-168-26-228.cn-north-1.compute.internal:8041 Node Transitioned from RUNNING to DECOMMISSIONING
2026-03-25 05:32:35,621 INFO NodesListManager: Recommission node ip-192-168-26-228.cn-north-1.compute.internal:8041 with state DECOMMISSIONING
2026-03-25 05:32:35,621 INFO RMNodeImpl: Node ip-192-168-26-228.cn-north-1.compute.internal:8041 in DECOMMISSIONING is recommissioned back to RUNNING.
2026-03-25 05:32:35,621 INFO RMNodeImpl: ip-192-168-26-228.cn-north-1.compute.internal:8041 Node Transitioned from DECOMMISSIONING to RUNNING
注: Core 节点被 recommission 是因为 IC Poller 在下一个周期清空了 exclude 文件(EMR 控制面未真正下发 Core 缩容指令)。