[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 缩容指令)。

参考资料

相关推荐
亚马逊云开发者20 小时前
Bedrock 调用次数比预期多了一倍?CloudTrail 5 分钟定位元凶
aws
亚马逊云开发者20 小时前
API Key 还明文写在配置文件里?OpenClaw SecretRef 帮你摘出来
aws
zhojiew2 天前
[INFRA] EMR集群中Hive和Spark集成Glue Data Catalog过程的深入分析
hive·hadoop·spark·aws·bigdata
亚马逊云开发者2 天前
我用 Lambda Durable Functions 把五个 Lambda 缩成了一个,代码量砍半
aws
亚马逊云开发者2 天前
异构 GPU 混合部署 Whisper,我用 HyperPod 一个集群搞定了
aws
亚马逊云开发者2 天前
模型搜完网页就"脑算"数字?用 Dynamic Filtering 让它老老实实写代码
aws
亚马逊云开发者2 天前
老板让我迁 Graviton,我用 AI 工具几分钟搞定了迁移评估
aws
亚马逊云开发者2 天前
用 Kiro CLI 做 Agent 后端,1000 行代码搞定飞书 AI 聊天机器人
aws
147API3 天前
从零开始上手 AWS:架构设计、成本优化与避坑指南
云计算·claude·aws