Skip to content

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: 数据分析组件,对各种类型的制品进行数据分析。分析任务分为两类,两者本质上是同一种实体,区别仅在触发方式:

  1. 离线分析:由 analyzer-controller 轮询 Exp 数据库,自动发现已完成的 job 并创建分析任务。
  2. 在线分析:由用户通过 CLI 发起请求,经 Falcon API Server 通过 GRPC 通知 analyzer-controller 创建分析任务。

2. Design Goals

2.1 Goals

  1. 统一的分析任务模型——离线分析和在线分析使用相同的实体和执行方式,均以独立 K8s Pod 运行
  2. analyzer-controller 作为核心组件,负责所有分析任务的生命周期管理、状态管理、DB 同步。通过轮询 Exp 数据库,自动发现已完成 job 并触发离线分析任务
  3. 在线分析支持用户提交自定义脚本或使用镜像内置工具
  4. 同一制品支持多次分析,每次分析独立记录
  5. 分析插件机制,支持不同制品类型的分析扩展,针对不同的业务可以灵活的组织插件

2.2 Non-Goals

  1. 分析脚本的版本管理或审核
  2. 用户自定义分析的资源限额与计费
  3. 分析结果的前端可视化

2.3 Success Criteria

系统以鲁棒性作为核心衡量标准:

  1. 分析 Pod 被外部删除(如 kubectl delete)时,controller 能感知并标记为失败
  2. controller 发生 panic 时,能在重启后自动恢复中断的分析任务
  3. controller 支持多副本部署,主副本故障时备用副本能接管并恢复未完成的任务

3. The Design

3.1 Architecture

Data Analyzer 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

职责:

  1. 分析任务生命周期管理 — 创建分析 Pod、watch Pod 状态、处理取消/清理
  2. 状态管理 — 维护所有分析任务的状态(pending / running / succeeded / failed / cancelled)
  3. GRPC 双向通信 — 接受 API Server 的在线分析请求;通过 GRPC 请求 API Server 读写 DB(轮询 Exp、同步分析状态等)

离线分析触发:

  1. 通过 GRPC 请求 API Server 查询 Exp 数据库,查找状态为"已完成"且未进行数据分析的 Exp
  2. 根据 Exp 关联的制品类型,确定对应的插件和命令
  3. 创建分析 Pod 执行分析

在线分析触发:

  1. 用户通过 CLI 发起 HTTP 请求到 Falcon API Server,参数包含 exp-id、本地脚本(可选)、运行命令
  2. API Server 通过 GRPC 调用 controller
  3. 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_idstring主键
namestring分析任务的名字
exp_idstring关联 Exp
analysis_typeenumoffline / online
plugin_namestring使用的插件名称
statusenumpending / running / succeeded / failed / cancelled
result_gcs_pathstring结果 GCS 路径
error_messagestring错误信息
commandstring运行命令
script_contentmediumtext用户自定义脚本内容(可选,限制大小 1MB)
created_attimestamp创建时间
updated_attimestamp更新时间

GRPC 接口(Controller 提供):

Controller 仅暴露需要 Controller 端副作用(Pod 创建/删除、队列准入、in-memory override)或 Controller 独占数据(插件 catalog)的接口。纯记录读路径(GetAnalysis / ListAnalysis / GetAnalysisStatus)不在此服务中——前端/CLI 直接通过 API Server 的 FalconAPIService 读 DB,避免 "前端 → API Server → Controller → API Server → DB" 的无意义中转。

方法请求参数返回说明
CreateAnalysisname, 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_namecommand 至少有一个非空;plugin_name 必须在 catalog 中存在;resource_override 走 side-channel,不持久化到 DB
CancelAnalysisanalysis_id幂等地将非终态记录置为 CANCELLED 并尽力删除 Pod;已是终态/已取消时返回成功
DeleteAnalysisanalysis_id, purge_gcs (bool)调用 API Server 删除分析记录(purge_gcs 字段在协议中保留,GCS 清理在 Phase 6 接入)
GetAnalysisLogsanalysis_idcontent (bytes)返回 Pod 的 stdout/stderr 日志(Phase 6 接入 GCS,当前返回 Unimplemented
ListPluginsrepeated Plugin返回 Controller 启动时从 ConfigMap 加载的插件 catalog(运行时只读,更新需重启 Controller)
GetPluginnamePlugin按名称返回单个插件条目,未知名称返回 NotFound

GRPC 接口(API Server 提供):

API Server 是 Exp/Artifact 数据与 DB 的唯一权威,Controller 是下游消费者。Controller 通过 ListExpsAwaitingAnalysis 一次性拿齐所需字段,请求处理路径上不再回查 API Server。单条 Exp/Artifact 读接口故意不提供——前端/API Server 内部真有需要时再加。

方法请求参数返回说明
ListExpsAwaitingAnalysislimitrepeated ExpAwaiting返回已 flatten 的 (Exp, Artifact) 对,Controller offline poller 直接拿来组装 CreateAnalysisRequest,无需二次查询
CreateAnalysisRecordAnalysisRecordanalysis_id创建 analysis 记录
GetAnalysisRecordanalysis_idAnalysisRecord查询单条记录
ListAnalysisRecordsexp_id(可选过滤)repeated AnalysisRecord列出记录,可按 exp 过滤
UpdateAnalysisRecordanalysis_id, record, update_mask部分更新,仅更新 update_mask 中列出的字段
DeleteAnalysisRecordanalysis_id删除记录
ListRunningAnalysesrepeated 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 后,离线和在线分析的生命周期管理、日志持久化、状态同步逻辑完全一致