请注意本文部分内容经过AI辅助生成,虽然经过笔者检查但是并不保证内容的正确性,请自行判断准确性,本文对相关后果不承担责任
参考资料
- https://aws.github.io/aws-emr-best-practices/docs/bestpractices/Features/Managed Scaling/best_practices/
- https://docs.aws.amazon.com/emr/latest/ManagementGuide/emr-managed-scaling.html
- https://www.infoq.cn/article/Sta3PqwDz7pKRiGRrMNt
本文主要内容为梳理 EMR 集群中的核心组件Instance Controller(简称 IC)。IC 是整个 EMR 集群的"大脑",负责集群编排、应用管理、Step 执行、健康监控、RPC 通信、状态管理等几乎所有核心功能。
本文的测试环境是emr7.12版本,在master节点上检查IC 的版本号为 instance-controller-1.85.0-1.noarch,主类是 aws157.instancecontroller.Main。它运行在 EMR 集群的每个节点上,但在 Master 和 Worker 上的行为差异巨大。

IC架构和节点环境
instance controller的核心职责如下:
- 集群编排:协调 Master/Core/Task 节点的生命周期
- 应用管理:安装、配置、启动和监控 Hadoop 生态组件(HDFS、YARN、HBase 等)
- Step 执行:接收并执行 EMR Step(作业步骤)
- 健康监控:监控节点和应用健康状态,上报给 EMR 控制面
- RPC 通信:Master 与 Worker 节点之间的双向通信
- 状态管理:维护集群状态(job-flow-state)并持久化到本地数据库
- Bootstrap Actions:执行集群启动时的自定义脚本
- 指标收集:收集 CPU、内存、磁盘等系统指标
关键特性
| 特性 | 说明 |
|---|---|
| Master/Slave 架构 | 同一个 JAR,根据 isMaster 标志走不同代码路径 |
| RPC 双向通信 | 外部接口(8443)接收 EMR 控制面指令,内部接口(8444)管理 Worker |
| HSQLDB 持久化 | 使用嵌入式数据库存储集群状态、Step、S3 策略等 |
| HA 支持 | 通过 ZooKeeper 实现多 Master 选举 |
| 定时轮询 | Poller 线程每 30 秒检查集群状态 |
| SSL/TLS 加密 | 所有 RPC 通信使用 SSL 加密 |
进程信息
bash
# 服务名称
instance-controller.service
# 启动脚本
/usr/bin/instance-controller
# PID 文件
/emr/instance-controller/run/instance-controller.pid
# Java 进程
PID: 4314
用户: hadoop
内存限制: -Xmx1024m
JVM: JRE 17
主类: aws157.instancecontroller.Main
Master 和 Worker比较
虽然Master 和 Worker 运行的是同一个 JAR、同一个主类,但实际上会根据 instance.json 中的 isMaster 标志在启动时走不同的代码路径。Master IC 是集群的控制中心,运行 Jetty RPC Server、Poller、Step 管理、实例清理等所有集群级功能;Worker IC 是被管理的代理,主要通过 RemoteInstanceStateUpdater 每 60 秒向 Master 上报状态。
资源对比
| 指标 | Master | Worker (Core) |
|---|---|---|
| 线程数 | ~340 | ~53 |
| 内存 | ~774 MB | ~610 MB |
| CPU 时间 (4.8h) | 43 min | 2 min 45s |
| 日志量/小时 | ~2.8 MB | ~560 KB |
线程对比
| 线程/模块 | Master | Worker | 说明 |
|---|---|---|---|
| Poller | ✅ | ❌ | 集群状态轮询,仅 Master |
| AppPoller-Bg-Thread-{1-5} | ✅ | ❌ | HDFS/YARN 健康监控 |
| YarnApplicationStepUpdater | ✅ | ❌ | Step 状态跟踪 |
| StepExecutionManager | ✅ | ❌ | Step 执行管理 |
| cleanup-thread | ✅ | ❌ | 终止实例清理 |
| delete-krb-principals | ✅ | ❌ | Kerberos 清理 |
| ex-rpc- | ✅ (大量) | ❌ | 接收 EMR 控制面请求 |
| in-rpc- | ✅ (大量) | ❌ | 接收 Worker 请求 |
| RemoteInstanceStateUpdater | ❌ | ✅ | 向 Master 上报状态 |
| RemoteInstanceConfigurator | ❌ | ✅ | 从 Master 获取配置 |
| MetricsCollectorCoreTaskManager | ❌ | ✅ | Worker 级指标管理 |
| healthSignalMonitor | ✅ | ✅ | 健康信号监控 |
| instance-metrics-collector-{1-5} | ✅ | ✅ | 系统指标采集 |
| arpc- | ✅ | ✅ | LogPusher 本地 RPC |
| JobFlowStateUpdater | ✅ | ✅ | 状态文件更新 |
网络端口对比
| 端口 | Master | Worker | 用途 |
|---|---|---|---|
| 8443 | ✅ | ❌ | External RPC(EMR 控制面) |
| 8444 | ✅ | ❌ | Internal RPC(Worker 上报) |
| 8321 | ✅ | ✅ | Local REST(LogPusher) |
数据库对比
| 内容 | Master | Worker |
|---|---|---|
| 数据库表 | 13 张表全部创建并使用 | 仅创建 DDL,无业务数据 |
| LocalInstanceState | ✅ 有数据 | ❌ |
| RemoteInstanceState | ✅ 存储所有 Worker 状态 | ❌ |
| SlaveRecordDbRow | ✅ 存储所有 Worker 记录 | ❌ |
| StepEntity | ✅ 存储 Step | ❌ |
| S3Policy | ✅ 存储并分发给 Worker | ✅ 从 Master 获取后本地存储 |
RPC 三层接口
IC 使用三层 RPC 接口进行通信,所有外部通信均使用 SSL/TLS 加密。
External Interface(端口 8443)
Servlet: ExternalInterfaceServlet
最大线程: 10
Accept 队列: 20
SSL KeyStore: /emr/instance-controller/lib/sslKeys/ic_keystore
SSL TrustStore: /emr/instance-controller/lib/sslKeys/ic_external_trust_store
支持的操作:
| 操作 | Activity 类 | 说明 |
|---|---|---|
| ConfigureMaster | ConfigureMasterActivity | 集群初始配置,包含 ComponentConfigurations |
| AddSteps | AddStepsActivity | 添加 EMR Step |
| CancelSteps | CancelStepsActivity | 取消 EMR Step |
| GetStepsStatus | GetStepsStatusActivity | 查询 Step 状态 |
| SetSignedPolicy | SetSignedPolicyActivity | 更新 S3 上传策略 |
| SetManagedResizeConfiguration | - | 设置 Managed Scaling 配置 |
| ModifyConfiguration | ModifyConfigurationActivity | 修改集群配置 |
| SetAdditionalConfig | SetAdditionalConfigActivity | 设置额外配置 |
| SetReverseCommConfig | SetReverseCommConfigActivity | 设置反向通信配置 |
Internal Interface(端口 8444)
Servlet: InternalInterfaceServlet
最大线程: 100
Accept 队列: 200
SSL KeyStore: /emr/instance-controller/lib/sslKeys/ic_keystore
SSL TrustStore: /emr/instance-controller/lib/sslKeys/ic_internal_trust_store
支持的操作:
| 操作 | RPC Method | 说明 |
|---|---|---|
| GetStatus | GetStatusMethod | Worker 状态上报,返回 decommission 状态 |
| GetManagedResizeConfigForWorker | - | 获取 Managed Scaling 配置 |
| NodeProvisionCheckin | NodeProvisionCheckinMethod | 节点 Provision 签到 |
| Configuration | ConfigurationMethod | 配置下发 |
| GetDebuggingPolicy | GetDebuggingPolicyMethod | 获取调试策略 |
| TransferMetrics | TransferMetricsMethod | 指标传输 |
| GetInstanceMetric | GetInstanceMetricMethod | 获取实例指标 |
| GetMonitorData | GetMonitorDataMethod | 获取监控数据 |
| Shutdown | ShutdownMethod | 关机指令 |
Local REST Interface(端口 8321)
绑定地址: 127.0.0.1(仅本地访问)
最大线程: 20
主要用途是 LogPusher 每 ~5 秒查询 IC 的 decommission 状态。为什么查这么频繁?因为 Spot 实例回收只给 2 分钟警告,5 秒间隔确保 LogPusher 能在几秒内感知下线信号,留出尽可能多的时间上传日志。
完整信号链路如下
EMR 控制面决定缩容/终止节点
↓ (ex-rpc 8443)
Master IC Poller 设置 doLogPusherDecommissioning = true
↓ (写入本地数据库 + 通过 in-rpc 8444 发送给 Worker IC)
Worker IC 数据库中标记 decommissioning = true
↓ (LogPusher 每 ~5 秒通过 arpc- 8321 调用 /getStatus)
LogPusher 发现 decommissioning: false → true
↓
切换到 SHUTDOWN_MODE:上传线程 10 → 50,全力上传
SSL/TLS 配置
bash
/emr/instance-controller/lib/sslKeys/
├── ic_keystore # IC 密钥库 (1438 字节)
├── ic_internal_trust_store # 内部信任库 (580 字节)
└── ic_external_trust_store # 外部信任库 (1177 字节)
# 证书 Subject: emr instance controller, emr-primary.internal
Poller 轮询详解
Poller 是 IC 的核心调度线程(主循环),每 30 秒执行一次,按顺序跑以下子任务:
| 子任务 | 说明 |
|---|---|
| pollEmrStepsDbState | 查数据库有没有待执行/待取消的 EMR Step |
| pollMasterInstanceReactions | 检查 EMR 控制面是否有待处理指令 |
| pollEmrInstanceState | 检查本节点状态(是否收到关机指令、Provision 是否成功) |
| pollAppStatus | 读取 AppPoller 后台线程的最新监控结果,汇总 HDFS/YARN 健康状态 |
| pollInstanceGroups | 检查每个实例组的实际节点数是否匹配期望 |
| doRollingReconfiguration | 检查是否有实例组需要滚动重配置 |
| renewKerberosTicketIfExpired | Kerberos 票据续期 |
| doGracefulShrink | 检查是否有节点需要优雅缩容 |
| updateJointStates | 更新每个 Worker 的 LogPusher decommission 标志 |
| healthMonitor | 汇总所有健康信号 |
| cleanupTerminatedInstances | 清理已终止实例 |
| pollSnapshotUpdates | 快照更新 |
Poller 日志示例(一个完整周期)
# 1. Step 和指令检查
INFO Poller: long poll - pollEmrStepsDbState took 0ms
INFO Poller: long poll - pollMasterInstanceReactions took 0ms
# 2. 本节点状态
INFO Poller: LIS 0 doShutdown:false lss:COMPLETED duration:16919442 lba:0 provision: SUCCESSFUL
这行日志的含义:
-
LIS 0:LocalInstanceState ID 为 0 -
doShutdown:false:没有收到关机指令 -
lss:COMPLETED:本地启动状态已完成 -
duration:16919442:运行时长(毫秒) -
lba:0:最后 Bootstrap Action 编号 -
provision: SUCCESSFUL:Provision 成功3. 应用健康汇总
INFO Poller: IJSM reported 3 nodes, 3 SR in database
INFO Poller: Active hadoop slave number: 3
INFO Poller: InstanceRoleStatusMap (Core:2 Task:1)
INFO Poller: HdfsStatusMap (R:2 null:1 ; T-> )
INFO Poller: YarnStatusMap (R:3 ; T-> )
INFO Poller: InstanceJointStatusMap contains 3 entries (R:3)4. 实例组匹配检查
INFO Poller: ig-33ZFTULCGKIVG Core active instance count 2 matches configuration
INFO Poller: ig-2XG3P7I0YNLTF Task - 1 active instance count 1 matches configuration5. 缩容检查
INFO Poller: instancesToShrink: , instancesToUnShrink:
6. LogPusher decommission 标志更新
INFO Poller: Poller updated LogPusher decommissioning mode flag ... false for i-071748862bcf56f53
7. 周期完成
INFO Poller: Poller cycle 565 completed in 2 ms
INFO Poller: Poller sleeping for 30 seconds
状态缩写速查表
| 缩写 | 全称 | 含义 |
|---|---|---|
| LIS | LocalInstanceState | 本节点状态记录 |
| IJSM | InstanceJointStatusMap | 综合所有维度的节点状态汇总表 |
| SR | SlaveRecord (SlaveRecordDbRow) | 数据库中的 Worker 节点记录 |
| lss | LocalStartupState | 本地启动状态(STARTING → COMPLETED) |
| lba | LastBootstrapAction | 最后执行的 Bootstrap Action 编号 |
| R | Running | 节点/服务运行中 |
| S | Starting | 节点启动中 |
| T | Terminated / Terminating | 节点已终止或终止中 |
| null | 无状态 | 该节点不运行此服务(如 Task 不跑 DataNode) |
| T-> | Terminating 列表 | 箭头后为正在终止的节点列表 |
| IG | InstanceGroup | 实例组 |
| NP:P / NP:S | NodeProvisioner | Pending / Successful |
| LP:R / LP:L | LogPusher 状态 | Running / Loading |
| LSS:C | LocalStartupState | Completed |
HSQLDB 数据库
IC 使用 HSQLDB 嵌入式数据库存储集群状态,通过 Hibernate ORM 访问。
连接信息
bash
# 数据库文件
/emr/instance-controller/db/data.data # 数据文件
/emr/instance-controller/db/data.log # 事务日志
/emr/instance-controller/db/data.script # DDL 脚本
/emr/instance-controller/db/data.properties # 数据库属性
/emr/instance-controller/db/data.lck # 锁文件
# JDBC 连接
URL: jdbc:hsqldb:file:/emr/instance-controller/db/data;shutdown=true
数据库配置
sql
SET DATABASE DEFAULT TABLE TYPE CACHED
SET DATABASE TRANSACTION CONTROL LOCKS
SET FILES CACHE SIZE 10000
SET FILES CACHE ROWS 50000
SET FILES LOG SIZE 50
SET FILES WRITE DELAY 500 MILLIS
完整表结构
LocalInstanceState 表(本节点状态):
sql
CREATE TABLE LocalInstanceState (
id INTEGER NOT NULL PRIMARY KEY,
appsReadyToShutDown BOOLEAN NOT NULL,
doLogPusherDecommissioning BOOLEAN DEFAULT FALSE NOT NULL,
doShutDown BOOLEAN NOT NULL,
encodedCir VARBINARY(4096) NOT NULL,
instanceState INTEGER,
localStartupStateUpdated TIMESTAMP(9) NOT NULL
);
RemoteInstanceState 表(Worker 节点状态):
sql
CREATE TABLE RemoteInstanceState (
instanceId VARCHAR(255) NOT NULL PRIMARY KEY,
doLogPusherDecommissioning BOOLEAN DEFAULT FALSE NOT NULL,
encodedCir VARBINARY(1024) NOT NULL,
initialInstanceControllerCheckInInstant TIMESTAMP(9) NOT NULL,
lastInstanceControllerCheckInInstant TIMESTAMP(9) NOT NULL,
privateDnsName VARCHAR(255) NOT NULL,
privateIp VARCHAR(255) NOT NULL
);
StepEntity 表(EMR Step):
sql
CREATE TABLE StepEntity (
stepId BIGINT NOT NULL PRIMARY KEY,
cancellationRequested BOOLEAN,
encodedDataTransferStatus VARBINARY(255),
encodedStep VARBINARY(16384) NOT NULL,
encodedStepId VARCHAR(255) NOT NULL UNIQUE,
encodedStepRecord VARBINARY(16384) NOT NULL,
encodedStepRecordState INTEGER NOT NULL,
executorId INTEGER,
heartbeatInstant BIGINT,
isSystemStep BOOLEAN,
useYarnStepManager BOOLEAN,
yarnWrapperApplicationId VARCHAR(255)
);
CREATE INDEX CancellationRequestedIndex ON StepEntity (cancellationRequested);
CREATE INDEX StepUidIndex ON StepEntity (encodedStepId);
CREATE INDEX StepStateIndex ON StepEntity (encodedStepRecordState);
CREATE INDEX ExecutorIdIndex ON StepEntity (executorId);
SlaveRecordDbRow 表(Worker 记录):
sql
CREATE TABLE SlaveRecordDbRow (
instanceId VARCHAR(255) NOT NULL PRIMARY KEY,
encodedRecord VARBINARY(1024) NOT NULL,
lastStateChangeTime TIMESTAMP(9) NOT NULL,
privateDnsName VARCHAR(255) NOT NULL,
privateIp VARCHAR(255) NOT NULL
);
InstanceToCleanup 表(待清理实例):
sql
CREATE TABLE InstanceToCleanup (
instanceId VARCHAR(255) NOT NULL PRIMARY KEY,
finalAttempt BOOLEAN NOT NULL,
privateDnsName VARCHAR(255) NOT NULL,
privateIp VARCHAR(255) NOT NULL,
removedFromDatabase BOOLEAN NOT NULL,
removedFromHdfs BOOLEAN NOT NULL,
removedFromYarn BOOLEAN NOT NULL,
removedKerberosServicePrincipals BOOLEAN NOT NULL,
terminationTime BIGINT NOT NULL
);
其他表:MasterInstanceState、S3Policy、S3DebuggingPolicy、InstanceGroupConfigurationEntity、MasterInstanceConfigs、RefreshNodesTimestamps、ResourceManagerHostnames、LocalKeyValueStoreEntity。Master 上共 13 张表。
状态文件列表
job-flow-state.txt
由 JobFlowStateUpdater 线程定期更新,包含完整的集群状态信息(Protocol Buffers 文本格式)。路径:/emr/instance-controller/lib/info/job-flow-state.txt。
job-flow.json
集群基本信息的 JSON 格式:
json
{
"jobFlowId": "j-1234567X2F51T",
"jobFlowCreationInstant": 1774176697527,
"instanceCount": 4,
"masterInstanceId": "i-0dfe72c99b62a3a2f",
"masterPrivateDnsName": "localhost",
"masterInstanceType": "m5.xlarge",
"slaveInstanceType": "m5.xlarge",
"hadoopVersion": "3.4.1",
"instanceGroups": [
{
"instanceGroupId": "ig-1II4XRQC9ERZE",
"instanceGroupName": "Primary",
"instanceRole": "Master",
"marketType": "OnDemand",
"instanceType": "m5.xlarge",
"requestedInstanceCount": 1
},
{
"instanceGroupId": "ig-33ZFTULCGKIVG",
"instanceGroupName": "Core",
"instanceRole": "Core",
"marketType": "OnDemand",
"instanceType": "m5.xlarge",
"requestedInstanceCount": 2
},
{
"instanceGroupId": "ig-2XG3P7I0YNLTF",
"instanceGroupName": "Task - 1",
"instanceRole": "Task",
"marketType": "OnDemand",
"instanceType": "c5.xlarge",
"requestedInstanceCount": 1
}
]
}
instance.json
当前节点的角色信息:
json
{
"instanceGroupId": "ig-1II4XRQC9ERZE",
"isMaster": true
}
extraInstanceData.json
集群的详细元数据,关键字段:
| 字段 | 说明 | 示例 |
|---|---|---|
| jobFlowId | 集群 ID | j-1234567X2F51T |
| instanceRole | 节点角色 | master / core / task |
| releaseLabel | EMR 版本 | emr-7.12.0 |
| hadoopVersion | Hadoop 版本 | Hadoop_3_4_1 |
| region | AWS 区域 | cn-north-1 |
| accountId | AWS 账户 ID | 1234567X2F51T |
| runNameNode | 是否运行 NameNode | true (Master) |
| runResourceManager | 是否运行 RM | true (Master) |
| runDataNode | 是否运行 DataNode | false (Master) |
| stepConcurrencyLevel | Step 并发级别 | 1 |
健康监控体系
IC 有三个健康监控子系统:
healthSignalMonitor(每 30 秒)
监控项和阈值:
| 监控项 | 阈值 | 严重级别 |
|---|---|---|
| CPU 使用率 | 80% | 超阈值告警 |
| 内存使用率 | 80% | 超阈值告警 |
| EMR 磁盘使用率 | 75% / 85% / 95% | Normal/Warning/Critical |
| LogPusher 周期时间 | 900s (15 分钟) | 周期过期告警 |
| LogPusher 周期耗时 | 900s (15 分钟) | 超阈值告警 |
| MetricsCollector | 运行状态 | 守护进程检查 |
磁盘使用率严重级别:
| 使用率 | 严重级别 |
|---|---|
| < 75% | Normal |
| 75% - 85% | Warning |
| 85% - 95% | Critical |
| > 95% | Emergency |
日志示例:
INFO healthSignalMonitor: found CPU metric: 0.76%, threshold: 80.0%, above threshold: false
INFO healthSignalMonitor: Found memory utilization metric: 50.58%, threshold: 80.0%, above threshold: false
INFO healthSignalMonitor: Found EMR disk usage metric: 3.00%, thresholds: [95.0, 85.0, 75.0]%, severity: Normal
INFO healthSignalMonitor: LogPusher last cycle started at: 2026-03-22 15:29:49, time elapsed: 236s, threshold: 900s, cycle stale: false
INFO healthSignalMonitor: MetricsCollector daemon running status: true
AppPoller(5 个后台线程)
HdfsMonitor:执行 hdfs dfsadmin -report,超时阈值 30 秒。
INFO AppPoller-Bg-Thread-2: Completed Command: [bash -l -c hdfs dfsadmin -report] in 4002ms
INFO AppPoller-Bg-Thread-2: Configured Capacity: 124283478016 (115.75 GB)
INFO AppPoller-Bg-Thread-2: DFS Used%: 0.02%
YarnMonitor:通过 YARN REST API 监控。
INFO AppPoller-Bg-Thread-4: GET request for http://...:8088/ws/v1/cluster/nodes
instance-metrics-collector(5 个线程)
通过 shell 脚本采集系统指标:
| 脚本 | 采集内容 |
|---|---|
/usr/bin/cpumet.sh |
CPU iowait%, 总使用率% |
/usr/bin/memmet.sh |
空闲内存 MB, 使用率% |
/usr/bin/diskmet.sh |
各分区使用率, Top 10 目录 |
/usr/bin/proc_cpu_mem.sh |
进程 CPU/内存消耗 |
cleanup-thread
清理已终止的实例,从数据库、HDFS 和 YARN 中移除:
INFO cleanup-thread: Instance Counts IJSM=3, DB=[3/3], HDFS=2, YARN=3
实例计数含义
- IJSM=InstanceJointStatusMap 节点数
- DB=[RemoteInstanceState/SlaveRecord] 数量
- HDFS=DataNode 数量
- YARN=NodeManager 数量
YarnApplicationStepUpdater
每 5 秒查询 YARN 应用状态,更新 EMR Step 的执行状态:
GET http://<rm>:8088/ws/v1/cluster/apps?states=RUNNING
GET http://<rm>:8088/ws/v1/cluster/apps?states=FINISHED,FAILED,KILLED&finishedTimeBegin=<now-1h>
启动流程(5 个阶段)
前置服务 (libinstance-controller-java.service)
- 从 IMDS 获取 SSH 密钥
- 配置 DNS
- 创建目录结构
/emr/instance-controller/ - 创建 HDFS/YARN exclude 文件
- 设置权限 (hadoop:hadoop)
初始化 (instance-controller.service)
- 加载 log4j2 配置
- 初始化 IC 版本: 1.85.0
- 加载 ExtraInstanceData(集群元数据)
- 初始化 Hibernate + HSQLDB 数据库
- 设置 SSL KeyStores
- 初始化 S3 Policies
- 检测磁盘挂载
RPC Server 启动
- 启动 External ProtoBuf Server(端口 8443, SSL)
- 启动 Internal ProtoBuf Server(端口 8444, SSL)
- 启动 Local REST Server(端口 8321, localhost)
后台线程启动
- Poller(集群状态轮询)
- AppPoller 线程池(5 线程, HDFS/YARN 监控)
- cleanup-thread(终止实例清理)
- YarnApplicationStepUpdater(Step 状态更新)
- healthSignalMonitor(健康信号监控)
- instance-metrics-collector(系统指标收集)
- JobFlowStateUpdater(集群状态文件更新)
- steps-cancelling-service(Step 取消服务)
- LogPusherManager(每 5 分钟检查 LogPusher)
等待 ConfigureMaster
- 接收集群配置(ComponentConfigurations)
- 初始化 Hadoop 配置
- 安装应用 [HDFS, HBASE, YARN, ...]
- 执行 Bootstrap Actions
- 标记 Master 就绪
Managed Scaling
EMR Managed Scaling 根据集群工作负载自动增减节点数量。IC 在其中扮演指标采集和配置中转的角色。
托管扩缩容的数据流如下
EMR 控制面 (每 ~30 秒)
↓ ex-rpc (8443) SetManagedResizeConfiguration
Master IC
↓ 转发给 MetricsCollector 守护进程
MetricsCollector(独立 Java 进程, JDK 8)
↓ 采集 YARN/HDFS/Spark 指标 (每 5 秒)
↓ 通过 WebSocket (wss://) 上报 (每 10 秒)
API Gateway → EMR Scaling 服务
↓ 扩缩容决策 (每 5-10 秒评估)
↓ ex-rpc → Master IC → 执行扩缩容
配置参数
| 参数 | 值 | 含义 |
|---|---|---|
| retentionPeriod | 600 | 指标保留时间(秒) |
| collectorFrequency | 5 | 指标采集频率(秒) |
| publisherFrequency | 10 | 指标发布频率(秒) |
| collectorFrequencyForSparkShuffle | 30 | Spark Shuffle 指标采集频率 |
| apiGatewayPingPongSchedulePeriodInSeconds | 30 | WebSocket 心跳间隔 |
| targetURL | wss://snbxldp692.execute-api.cn-north-1.amazonaws.com.cn/prod | WebSocket 端点 |
扩缩容过程
实际的结果来自实际测试集群的观察结果,展示了 Managed Scaling 的完整生命周期。
原始集群状态:
Master: i-0dfe72c99b62a3a2f 192.168.25.65 m5.xlarge OnDemand
Core: i-071748862bcf56f53 192.168.17.183 m5.xlarge OnDemand
Core: i-0f1501a00c0e28803 192.168.23.18 m5.xlarge OnDemand
Task: i-0dbd1db31701c86db 192.168.22.99 c5.xlarge OnDemand
第一阶段:扩容(15:58 - 16:02)
15:58:51 --- EMR 控制面决定扩容,3 个新实例启动。Managed Scaling 自动创建了新实例组 ig-39SHXO1CFWH3Q(Task_c5.xlarge_SPOT_By_Managed_Scaling):
15:58:51 INFO in-rpc: i-0d07774894e5675ed: new instance started
15:58:52 INFO in-rpc: i-04a84669331c32607: new instance started
15:58:53 INFO in-rpc: i-0d07774894e5675ed: all bootstrap actions complete ← 2 秒完成
15:59:20 INFO in-rpc: i-04cf0307763b428c8: new instance started
新增节点:
| 实例 | IP | 角色 | 购买方式 |
|---|---|---|---|
| i-04a84669331c32607 | 192.168.25.67 | Task | Spot |
| i-0d07774894e5675ed | 192.168.26.85 | Task | Spot |
| i-04cf0307763b428c8 | 192.168.18.96 | Core | OnDemand |
第二阶段:旧节点替换(16:01:23)
Managed Scaling 用 Spot Task 节点替换 OnDemand Task 节点(成本优化):
INFO Poller: Identified 1 instances in Task - 1 to shrink: i-0dbd1db31701c86db
INFO Poller: Update i-0dbd1db31701c86db InstanceState RUNNING => DECOMMISSIONING
第三阶段:新节点就绪(16:02 - 16:03)
从 IJSM 详细状态可以跟踪新节点的启动过程:
# 16:01:53 --- 新节点还在 Starting
i-04a84669331c32607 181s S NP:P Y:null
# 16:02:23 --- 新节点变为 Running
i-04a84669331c32607 211s R NP:S Y:R c:0
# 16:03:25 --- 新节点已开始接收容器
i-0d07774894e5675ed Y:R c:1 am:2176
IJSM 状态字段说明:
| 字段 | 含义 | 示例 |
|---|---|---|
| 181s | 实例存活时间 | 181 秒 |
| S / R | 实例状态 | Starting / Running |
| NP:P / NP:S | NodeProvisioner | Pending / Successful |
| Y:null / Y:R | YARN NodeManager | 未就绪 / Running |
| H:null / H:R | HDFS DataNode | 不运行(Task) / Running(Core) |
| c:0 | 当前运行的容器数 | 0 个 |
| am:3072 | YARN 可用内存 (MB) | 3072 MB |
| I:58s | 上次 IC check-in 时间 | 58 秒前 |
第四阶段:稳定运行(16:03 - 16:08)
INFO Poller: IJSM reported 6 nodes, 6 SR in database
INFO Poller: InstanceRoleStatusMap (Core:3 Task:3)
INFO Poller: HdfsStatusMap (R:3 null:3) ← 3 Core 跑 DataNode,3 Task 不跑
INFO Poller: YarnStatusMap (R:6) ← 6 个 NodeManager 全部 Running
第五阶段:缩容回原始规模(~16:15 - 16:17)
负载不足,Managed Scaling 决定缩容回 3 个 Worker。
Managed Scaling 做了一次试探性扩容 + 成本优化替换(用 Spot 替换 OnDemand),在负载不足时自动缩回。
IC核心类结构
aws157/instancecontroller/
├── Main.class # 入口类
├── InstanceController.class # 核心控制器
├── app/ # 应用生命周期管理
│ ├── ApplicationsManager.class
│ ├── HdfsStateChecker.class
│ └── YarnStateChecker.class
├── apphealth/monitor/ # 应用健康监控
│ ├── HdfsMonitor.class
│ ├── YarnMonitor.class
│ └── PrestoMonitor.class
├── common/ # 公共组件
│ ├── Poller.class # 核心轮询器
│ ├── JobFlowStateUpdater.class
│ ├── DiskSpaceManager.class
│ └── SpotTerminationNoticePoller.class
├── coordinator/ # HA 协调
│ ├── LocalCoordinator.class # 单 Master
│ └── ZkCoordinator.class # ZooKeeper HA
├── master/ # Master 专用
│ ├── EmbeddedJettyServer.class
│ ├── server/
│ │ ├── ExternalInterfaceServlet.class
│ │ ├── InternalInterfaceServlet.class
│ │ └── ConfigureMasterActivity.class
│ └── steprunner/StepExecutionManager.class
├── slave/ # Worker 专用
│ ├── RemoteInstanceConfigurator.class
│ └── RemoteInstanceStateUpdater.class
├── stepsv2/ # Step 执行引擎
│ ├── StepsExecutionEngine.class
│ ├── YarnApplicationStepUpdater.class
│ └── HadoopJarStepRunner.class
└── rpc/methods/ # RPC 方法
├── GetStatusMethod.class
├── ConfigurationMethod.class
└── TransferMetricsMethod.class
运维指南
常用命令
bash
# 服务管理
sudo systemctl status instance-controller.service
sudo systemctl restart instance-controller.service
# 日志查看
tail -f /emr/instance-controller/log/instance-controller.log
grep "Poller cycle" /emr/instance-controller/log/instance-controller.log | tail -5
grep "ERROR" /emr/instance-controller/log/instance-controller.log
# 数据库查看
cat /emr/instance-controller/db/data.script
# 状态文件
cat /emr/instance-controller/lib/info/job-flow.json
cat /emr/instance-controller/lib/info/instance.json
# 健康检查
grep "healthSignalMonitor" /emr/instance-controller/log/monitor.log | tail -10
grep "Instance Counts" /emr/instance-controller/log/instance-controller.log | tail -3
# Managed Scaling 状态
grep "Managed Resize flag" /emr/instance-controller/log/instance-controller.log | tail -5
sudo systemctl status metricscollector
日常巡检清单
bash
# 1. IC 服务状态
sudo systemctl status instance-controller.service
# 2. CPU/内存/磁盘
grep "healthSignalMonitor: found CPU\|Found memory\|Found EMR disk" /emr/instance-controller/log/monitor.log | tail -6
# 3. 节点数量一致性
grep "Instance Counts" /emr/instance-controller/log/instance-controller.log | tail -3
# 4. 错误检查
grep -c "ERROR" /emr/instance-controller/log/instance-controller.log
告警条件
| 条件 | 严重级别 | 建议动作 |
|---|---|---|
| IC 服务停止 | Critical | 检查日志,重启服务 |
| CPU > 80% | Warning | 检查进程,考虑扩容 |
| 内存 > 80% | Warning | 检查内存泄漏 |
| 磁盘 > 85% | Critical | 清理日志,扩容磁盘 |
| LogPusher 周期 > 900s | Warning | 检查 LogPusher 日志 |
| IJSM 节点数不匹配 | Warning | 检查 Worker 连接 |