[INFRA] EMR集群节点下线 (Decommission) 机制和逻辑深入分析

请注意本文部分内容经过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 的原因:

  1. Spark 在 spark.blacklist.decommissioning.enabled=true 下,将 DECOMMISSIONING 节点上的 executor 视为"不可用"
  2. 实际可用 executor 少于请求的 4 个,Spark 向 YARN 申请新 executor 补偿
  3. 同时 Managed Scaling 检测到缩容,自动创建了新 Task 实例组(ig-16YZOPOKMVAQL ON_DEMAND)替换被缩减的 Spot 实例组
  4. 最终出现 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_shuffle aux 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 进程:

  1. 解耦 shuffle 数据与 executor 生命周期:executor 退出后 shuffle 数据仍可通过 ESS 提供
  2. 支持 Dynamic Resource Allocation (DRA):executor 可以动态释放,shuffle 数据不会丢失
  3. 减少 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 缩容指令)。

参考资料

相关推荐
AutoMQ15 小时前
别再每月浪费数千美元:拆解 AWS/GCP Kafka 背后的隐性账单
kafka·消息队列·aws
92year2 天前
AWS Agent Registry实操:用一个注册中心管好所有AI Agent
aws·ai agent·mcp·bedrock·agent registry
亚马逊云开发者3 天前
Claude Code Token费用暴涨3倍?SageMaker+LiteLLM动态路由方案帮你省70%
aws
亚马逊云开发者3 天前
文搜视频、图搜视频、视频搜视频——Nova Multimodal Embeddings 多模态素材搜索实战
aws
亚马逊云开发者3 天前
3小时从零到生产:用Kiro IDE写Agent + AgentCore一键部署到云端
aws
亚马逊云开发者5 天前
都2026了,你的游戏服务器还在裸奔?聊聊用Player Gateway彻底隐藏IP的DDoS防护方案
aws
翼龙云_cloud6 天前
亚马逊云代理商:三步用 CloudWatch 高效监控 AWS Lambda 日志
云计算·aws·云服务器
zhojiew6 天前
关于AWS Direct Connect with Transit Gateway和Direct Connect Gateway
云计算·gateway·aws
观测云6 天前
AWS DevOps Agent 接入观测云最佳实践
aws·devops·可观测性·观测云
亚马逊云开发者9 天前
试了 8 种方式全失败后,我用双通道架构把 Kiro CLI 变成了 REST API
aws