Data Analyzer Design Document
1. Background & Motivation
Falcon 项目正在进行模块化重构,各模块独立提供服务,HTTP/GRPC 服务统一由 Falcon API Server 托管。Exp 是各种场景的抽象,底层由 job 产生各种任务制品。Data Analyzer 是其中一个模块,负责对 job 产生的制品进行数据分析。
Exp:一个抽象 class,持久化在 MySQL 中。每个 Exp 条目对应一个实际的 job(工作负载),上层业务包括 benchmark、profiling 等。
制品(Artifacts):每个 job 产出的数据文件,存储在 GCS 特定目录下。制品类型由业务决定,例如 profiling 业务的制品包括 JAX/Pallas 的 HLO 文件和 profiling 文件。
Data Analyzer: 数据分析组件,对各种类型的制品进行数据分析。分析任务分为两类,两者本质上是同一种实体,区别仅在触发方式:
- 离线分析:由 analyzer-controller 轮询 Exp 数据库,自动发现已完成的 job 并创建分析任务。
- 在线分析:由用户通过 CLI 发起请求,经 Falcon API Server 通过 GRPC 通知 analyzer-controller 创建分析任务。
2. Design Goals
2.1 Goals
- 统一的分析任务模型——离线分析和在线分析使用相同的实体和执行方式,均以独立 K8s Pod 运行
- analyzer-controller 作为核心组件,负责所有分析任务的生命周期管理、状态管理、DB 同步。通过轮询 Exp 数据库,自动发现已完成 job 并触发离线分析任务
- 在线分析支持用户提交自定义脚本或使用镜像内置工具
- 同一制品支持多次分析,每次分析独立记录
- 分析插件机制,支持不同制品类型的分析扩展,针对不同的业务可以灵活的组织插件
2.2 Non-Goals
- 分析脚本的版本管理或审核
- 用户自定义分析的资源限额与计费
- 分析结果的前端可视化
2.3 Success Criteria
系统以鲁棒性作为核心衡量标准:
- 分析 Pod 被外部删除(如 kubectl delete)时,controller 能感知并标记为失败
- controller 发生 panic 时,能在重启后自动恢复中断的分析任务
- controller 支持多副本部署,主副本故障时备用副本能接管并恢复未完成的任务
3. The Design
3.1 Architecture
- CLI 通过 HTTP 请求 Falcon API Server
- Falcon API Server 负责所有 DB 读写操作,与 Analyzer Controller 通过 GRPC 双向通信
- Analyzer Controller 统一管理所有分析任务(离线 + 在线),创建 Pod 并 watch 其状态,通过 GRPC 请求 API Server 完成 DB 读写
- 所有分析任务均以独立 K8s Pod 执行,离线和在线使用相同的镜像和执行方式,区别仅在触发方式和启动参数
3.2 Analyzer Controller
职责:
- 分析任务生命周期管理 — 创建分析 Pod、watch Pod 状态、处理取消/清理
- 状态管理 — 维护所有分析任务的状态(pending / running / succeeded / failed / cancelled)
- GRPC 双向通信 — 接受 API Server 的在线分析请求;通过 GRPC 请求 API Server 读写 DB(轮询 Exp、同步分析状态等)
离线分析触发:
- 通过 GRPC 请求 API Server 查询 Exp 数据库,查找状态为"已完成"且未进行数据分析的 Exp
- 根据 Exp 关联的制品类型,确定对应的插件和命令
- 创建分析 Pod 执行分析
在线分析触发:
- 用户通过 CLI 发起 HTTP 请求到 Falcon API Server,参数包含 exp-id、本地脚本(可选)、运行命令
- API Server 通过 GRPC 调用 controller
- Controller 创建 Pod 执行分析
分析 Pod:
- 所有分析任务(离线/在线)均以独立 K8s Pod 执行
- Pod 镜像一致,内置多种插件工具,通过 PATH 设置可直接使用
- Pod 应当在受限的安全上下文中运行(如非 root 用户、禁用特权模式)
- 离线分析:controller 自动确定插件和命令作为 Pod 启动参数
- 在线分析:用户指定命令(和可选脚本)作为 Pod 启动参数
并发控制: controller 内部维护离线分析队列和在线分析队列,分别控制各自的并发任务数,防止大量分析任务同时运行。队列长度(即并发上限)通过 controller 部署参数配置。
高可用: 多副本部署,通过 client-go Lease 做 leader election,仅一个副本活跃。非 leader 副本 standby 待命。
任务恢复: 进程启动时扫描分析记录表,找到状态为"running"的任务,检查 K8s 中是否存在对应 Pod,决定是继续 watch 还是重新创建。采用带上限的指数退避重试策略。
取消分析任务: 标记分析记录为"已取消",controller 终止 Pod。保留 GCS 路径下的中间结果及日志以供调试,除非用户显式调用 DELETE 接口清理。
3.3 Plugin System
- 插件为独立的可执行文件或脚本,语言不限,打包在镜像特定文件夹中
- 一个插件输出一个 JSONL 文件,文件名与插件名对应
- 离线分析时,controller 根据制品类型确定使用哪个插件
- 在线分析时,用户可直接使用镜像内置的插件工具(通过 PATH),也可提交自定义脚本
3.4 日志持久化
离线分析和在线分析的 Pod 输出(stdout/stderr)均持久化到 GCS 对应的分析结果目录下,文件为 gs://{bucket}/analysis/{exp-id}/{analysis-id}/output.log。当任务失败或 Pod 被删除后,用户仍可通过接口查询日志,定位脚本错误或插件异常。
3.5 Interfaces & Data Flow
数据表设计:
分析记录表(analysis_records):
| 字段 | 类型 | 说明 |
|---|---|---|
| analysis_id | string | 主键 |
| name | string | 分析任务的名字 |
| exp_id | string | 关联 Exp |
| analysis_type | enum | offline / online |
| plugin_name | string | 使用的插件名称 |
| status | enum | pending / running / succeeded / failed / cancelled |
| result_gcs_path | string | 结果 GCS 路径 |
| error_message | string | 错误信息 |
| command | string | 运行命令 |
| script_content | mediumtext | 用户自定义脚本内容(可选,限制大小 1MB) |
| created_at | timestamp | 创建时间 |
| updated_at | timestamp | 更新时间 |
GRPC 接口(Controller 提供):
Controller 仅暴露需要 Controller 端副作用(Pod 创建/删除、队列准入、in-memory override)或 Controller 独占数据(插件 catalog)的接口。纯记录读路径(GetAnalysis / ListAnalysis / GetAnalysisStatus)不在此服务中——前端/CLI 直接通过 API Server 的 FalconAPIService 读 DB,避免 "前端 → API Server → Controller → API Server → DB" 的无意义中转。
| 方法 | 请求参数 | 返回 | 说明 |
|---|---|---|---|
| CreateAnalysis | name, exp_id, analysis_type (offline/online), plugin_name, command, script_content(可选,≤1MB), resource_override(可选:cpu_request/cpu_limit/memory_request/memory_limit) | analysis_id | 校验请求 → 查 Exp → 持久化 PENDING 记录 → 异步入对应队列。plugin_name 与 command 至少有一个非空;plugin_name 必须在 catalog 中存在;resource_override 走 side-channel,不持久化到 DB |
| CancelAnalysis | analysis_id | — | 幂等地将非终态记录置为 CANCELLED 并尽力删除 Pod;已是终态/已取消时返回成功 |
| DeleteAnalysis | analysis_id, purge_gcs (bool) | — | 调用 API Server 删除分析记录(purge_gcs 字段在协议中保留,GCS 清理在 Phase 6 接入) |
| GetAnalysisLogs | analysis_id | content (bytes) | 返回 Pod 的 stdout/stderr 日志(Phase 6 接入 GCS,当前返回 Unimplemented) |
| ListPlugins | — | repeated Plugin | 返回 Controller 启动时从 ConfigMap 加载的插件 catalog(运行时只读,更新需重启 Controller) |
| GetPlugin | name | Plugin | 按名称返回单个插件条目,未知名称返回 NotFound |
GRPC 接口(API Server 提供):
API Server 是 Exp/Artifact 数据与 DB 的唯一权威,Controller 是下游消费者。Controller 通过
ListExpsAwaitingAnalysis一次性拿齐所需字段,请求处理路径上不再回查 API Server。单条 Exp/Artifact 读接口故意不提供——前端/API Server 内部真有需要时再加。
| 方法 | 请求参数 | 返回 | 说明 |
|---|---|---|---|
| ListExpsAwaitingAnalysis | limit | repeated ExpAwaiting | 返回已 flatten 的 (Exp, Artifact) 对,Controller offline poller 直接拿来组装 CreateAnalysisRequest,无需二次查询 |
| CreateAnalysisRecord | AnalysisRecord | analysis_id | 创建 analysis 记录 |
| GetAnalysisRecord | analysis_id | AnalysisRecord | 查询单条记录 |
| ListAnalysisRecords | exp_id(可选过滤) | repeated AnalysisRecord | 列出记录,可按 exp 过滤 |
| UpdateAnalysisRecord | analysis_id, record, update_mask | — | 部分更新,仅更新 update_mask 中列出的字段 |
| DeleteAnalysisRecord | analysis_id | — | 删除记录 |
| ListRunningAnalyses | — | repeated AnalysisRecord | 返回所有 RUNNING 状态记录,Phase 7 HA 启动恢复预留,当前无生产调用方 |
GCS 路径规范:
- 分析结果:
gs://{bucket}/analysis/{exp-id}/{analysis-id}/{plugin_name}.jsonl - 执行日志:
gs://{bucket}/analysis/{exp-id}/{analysis-id}/output.log - bucket 通过 Deployment 配置获取
3.6 Trade-offs
| 决策 | 选择 | 理由 |
|---|---|---|
| 统一任务模型 | 离线/在线使用相同 Pod 执行 | 减少维护成本,代码路径一致,避免子进程与 Pod 两套机制的复杂性 |
| Controller 通信 | GRPC | 与 API Server 高效通信,支持强类型接口定义 |
| 控制面语言 | Go | 与 K8s 生态原生集成(client-go),适合常驻 worker 和控制器模式 |
| 插件执行方式 | Pod 内执行 | 所有分析任务统一以 Pod 运行,插件语言不限,环境隔离 |
| 任务发现机制 | 轮询 Exp 数据库 | 场景足够,避免引入消息队列增加系统复杂度 |
| 高可用方式 | Leader election(K8s Lease) | 利用 K8s 原生能力,无需额外依赖 |
| 插件分发 | 打包进镜像 | 简单可靠,Dockerfile 多阶段构建 |
3.7 Test Strategy
- Controller 主循环单元测试
- 插件调用集成测试
- Pod 状态监控测试
- GRPC 接口测试
3.8 Deployment & Dependencies
- Controller 以 K8s Deployment 部署,支持多副本(leader election)
- Dockerfile 多阶段构建:Go 编译 + 插件文件夹打包进镜像
- 外部依赖:MySQL(Exp 数据库)、GCS(制品与结果存储)、K8s API(Pod 管理、Lease)
- 配置项:GCS bucket、并发上限、轮询间隔等通过 Deployment 配置
4. Alternatives Considered
消息队列(NATS / Kafka)代替轮询 Exp 数据库
方案描述: 当 job 完成时,由 Exp 或上层业务发送消息到消息队列,controller 订阅消息并触发分析,而非轮询数据库。
优势:
- 实时性更好,job 完成后可立即触发分析
- 减少对数据库的轮询压力
否决理由:
- 当前轮询场景完全够用,实时性要求不高
- 引入消息队列会增加系统复杂度,需要额外部署和运维 NATS/Kafka 集群
- 增加了故障面——消息丢失、重复消费等问题需要额外处理
离线分析使用子进程而非独立 Pod
方案描述: 离线分析在 controller 进程内通过子进程调用插件,而非创建独立 Pod。
优势:
- 启动速度更快,无需等待 Pod 调度
- 实现更简单
否决理由:
- 与在线分析使用不同的执行模型,增加两套代码路径的维护成本
- 子进程共享 controller 资源,缺乏隔离性
- 统一使用 Pod 后,离线和在线分析的生命周期管理、日志持久化、状态同步逻辑完全一致