Restructure section 2 from component overview to tech selection table covering Scheduler, Worker, Queue, Job State, Artifact Store, Web UI. Document why Redis Stream for the queue (language-neutral, consumer groups, single Redis instance, Crash Reset alignment) and detail worker horizontal scaling via consumer groups. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
14 KiB
14 KiB
Kneron Toolchain Convert Orchestrator — 設計文件(Crash 即 Reset 版)
0. 目的與範圍
目的
將 Kneron Toolchain 的模型轉換流程(ONNX → BIE → NEF)拆分為可水平擴充的 Worker Pool, 由 Scheduler 統一派工與狀態管理,並使用 MinIO 作為跨主機的 artifact 交換區, 以解決單一 VM / 單一 Toolchain instance 在高併發下造成 CPU 爆滿與 Crash 的問題。
範圍
- Web UI:上傳 / 查詢狀態 / 下載結果
- Task Scheduler:建立任務、寫入狀態、推送 queue、接收完成事件
- Worker Pools:
- ONNX Flow Worker Pool
- BIE Flow Worker Pool
- NEF Flow Worker Pool
- Redis:暫存 job 狀態(重啟即清空)
- Queue:暫存待處理任務與完成事件(重啟即清空)
- MinIO:存放 input/output/logs(可保留或定期清理)
非目標(Non-goals)
- 不做任務持久化(durability)
- 不做 crash 後 resume
- 不做 exactly-once / at-least-once 保證
- 不做 Scheduler HA
- 不做自動 retry(失敗即請使用者重送)
1. 核心設計哲學:Crash 即 Reset
- Redis 重啟 → 所有 job 狀態消失
- Queue 重啟 → 所有排隊任務消失
- Scheduler / Worker 重啟 → 不嘗試恢復任何舊任務
- UI 查不到 job → 顯示「系統已重啟,請重新送出」
- 任務資料不具不可遺失性(loss-tolerant)
2. 技術選型
| 元件 | 技術 | 說明 |
|---|---|---|
| Task Scheduler | Node.js / Express | I/O 密集、event loop 適合調度角色 |
| Worker (ONNX/BIE/NEF) | Python / FastAPI | 需要 Kneron Toolchain Python 環境 |
| Task Queue | Redis Stream | 語言中立,Node.js 與 Python 都是 Redis client;支援 Consumer Group 做水平擴展 |
| Job State | Redis Hash | 與 Queue 共用同一個 Redis instance,重啟即清空 |
| Artifact Store | Shared Volume(單機)/ MinIO(跨機,未來) | Worker 之間的檔案交換區 |
| Web UI | Vue 3 + Vite | 前端 SPA |
2.1 為什麼 Queue 用 Redis Stream
- 語言中立:Scheduler (Node.js) 用
ioredis,Worker (Python) 用redis-py,雙方只需約定 queue name 與 message 格式 - Consumer Group:同一個 Consumer Group 內,每筆訊息只會被一個 Worker 消費,天然支援水平擴展
- 不額外引入元件:Queue 與 Job State 共用同一個 Redis,部署簡單
- 符合 Crash Reset 哲學:Redis 不開 persistence,重啟即清空
2.2 Worker 水平擴展(Consumer Group)
queue:onnx
└─ Consumer Group: "onnx-workers"
├─ onnx-worker-1 ← XREADGROUP 拿到 Job A
├─ onnx-worker-2 ← XREADGROUP 拿到 Job B
└─ onnx-worker-3 ← XREADGROUP 拿到 Job C
queue:bie
└─ Consumer Group: "bie-workers"
├─ bie-worker-1
└─ bie-worker-2
queue:nef
└─ Consumer Group: "nef-workers"
└─ nef-worker-1
- 同一個 Consumer Group 內,每筆訊息只會被一個 Worker 拿到,Redis 自動分配
- 擴展只需多啟 Worker process(例如
docker-compose up --scale bie-worker=3),不需改程式碼 - 每個 Worker 一次只處理一個 job,透過
XACK確認完成
3. 系統元件與責任
3.1 Web UI
- 提供使用者:
- 上傳 model.onnx、quant.zip
- 建立轉換任務
- 查詢任務狀態
- 下載 BIE / NEF
- Web UI 不直接讀寫 Redis 或 Queue
- Web UI 透過 Scheduler API 上傳檔案 / 下載結果
- 狀態更新:以 SSE 為主,Polling 為備援(3-5 秒)
3.2 Task Scheduler(控制面,Node.js)
- 唯一的控制面元件
- 職責:
- 提供 REST API(建立 job、查詢狀態)
- 建立 job_id(UUID v4)
- 建立 Redis job record(Hash)
- 將 job 推入對應 task queue(
XADD queue:onnx) - 監聽 worker 完成事件(
XREADGROUP queue:done) - 推進 job 狀態(ONNX → BIE → NEF → COMPLETED)
- 提供 SSE 事件串流
- 流程固定:ONNX → BIE → NEF,不支援跳過或只跑部分
- Scheduler 不等待 worker、不執行 heavy CPU 任務
3.3 Task Queue(Redis Stream)
- Queue 列表:
queue:onnx— ONNX 轉換任務queue:bie— BIE 量化任務queue:nef— NEF 編譯任務queue:done— Worker 完成回報
- 每個 queue 對應一個 Consumer Group
- Queue 僅用於「暫存」任務(重啟可清空)
3.4 Worker Pools(Python)
- ONNX / BIE / NEF Worker 各自獨立 pool
- Worker 特性:
- 無狀態
- pull-based(
XREADGROUP從 queue 取任務) - 一次只處理一個 job
- 加入 Consumer Group,自動分配任務
- Worker 失敗:
- 回報 fail 到
queue:done - Scheduler 將 job 標記為 FAILED
- 回報 fail 到
3.5 Artifact Store(Shared Volume)
- 所有 Worker 掛載同一個 Docker volume(
/data/jobs) - 每個 job 的檔案存放在
/data/jobs/{job_id}/下 - Worker 之間透過檔案系統直接讀寫,無需上傳/下載
- 單機部署:Docker named volume 或 bind mount
- 未來跨機部署:可替換為 MinIO,只需改 Worker I/O 層
4. 命名與資料結構
4.1 Job ID
- 使用 UUID v4
- 全系統唯一
4.2 Job 工作目錄結構
掛載路徑:/data/jobs
/data/jobs/{job_id}/
input/
model.onnx # 使用者上傳的模型檔
ref_images/ # 量化用參考圖片
*.jpg / *.png
out.onnx # ONNX Worker 產出
out.bie # BIE Worker 產出
out.nef # NEF Worker 產出
logs/
onnx.log
bie.log
nef.log
所有 Worker 共用同一個 volume,直接讀寫同一個 job 目錄。
4.3 Redis Job Record(Hash / JSON)
Key:job:{job_id}
job_id
created_at
status: ONNX | BIE | NEF | COMPLETED | FAILED
stage: ONNX | BIE | NEF
progress: 0-100
updated_at
output:
bie_key
nef_key
error:
step
reason
4.4 Redis Stream Queue 協定
Scheduler 與 Worker 之間透過 Redis Stream 溝通,需約定以下格式:
Task Message(Scheduler → Worker Queue)
{
"job_id": "uuid-v4",
"created_at": "ISO-8601",
"input_dir": "jobs/{job_id}/",
"parameters": {
"model_id": 1,
"version": "0001",
"platform": "720",
"enable_evaluate": false,
"enable_sim_fp": false
}
}
Done Message(Worker → queue:done)
{
"job_id": "uuid-v4",
"step": "onnx|bie|nef",
"result": "ok|fail",
"reason": "optional error message",
"output_files": ["out.onnx"],
"completed_at": "ISO-8601"
}
Consumer Group 命名規則
| Queue | Consumer Group | Consumer 命名 |
|---|---|---|
queue:onnx |
onnx-workers |
onnx-worker-{hostname} |
queue:bie |
bie-workers |
bie-worker-{hostname} |
queue:nef |
nef-workers |
nef-worker-{hostname} |
queue:done |
scheduler |
scheduler-{hostname} |
5. 成功流程(Happy Path)
- 使用者透過 Web UI 建立轉換任務
- Web UI 上傳 input 到 Scheduler,Scheduler 存入
/data/jobs/{job_id}/input/ - Scheduler 建立 job record,push 到 queue:onnx
- ONNX worker pull 任務、從 job 目錄讀取 input、執行、寫出 out.onnx、push done
- Scheduler 推進狀態,push 到 queue:bie
- BIE worker 從同一個 job 目錄讀取 out.onnx + ref_images、執行、寫出 out.bie、push done
- Scheduler 推進狀態,push 到 queue:nef
- NEF worker 從 job 目錄讀取 out.bie、執行、寫出 out.nef、push done
- Scheduler 標記 COMPLETED
- Web UI 取得下載路徑
5.1 工作目錄與 Worker I/O 規格(落地版)
以下為實作落地時的檔案布局與 worker 互動規格(與 MinIO 路徑可一對一對應):
5.1.1 工作目錄
- API 取號後建立
task_id - API 將使用者上傳檔案放入工作目錄:
{base_path}/{task_id}/- 參考圖片固定放在
ref_images/
{base_path}/{task_id}/
<single_input_file> # 唯一輸入檔(副檔名不固定)
ref_images/
<image files...>
5.1.2 ONNX Worker
- 輸入:工作目錄下的唯一檔案(不假設檔名 / 副檔名)
- 輸出:
out.onnx - 輸出位置:同一工作目錄
- 可選旗標:
enable_evaluate(default:false):是否執行 IP evaluator(原 Web GUI 流程為 OFF)enable_sim_fp(default:false):是否執行浮點 E2E 模擬(尚未接線)
5.1.3 BIE Worker
- 輸入:
out.onnx+ref_images/* - 輸出:
out.bie - 輸出位置:同一工作目錄
- 可選旗標:
enable_sim_fixed(default:false):是否執行定點 E2E 模擬(尚未接線)
5.1.4 NEF Worker
- 輸入:
out.bie - 輸出:
out.nef - 輸出位置:同一工作目錄
- 可選旗標:
enable_sim_hw(default:false):是否執行硬體 E2E 模擬(尚未接線)
5.1.5 流程預設開關對照(原 Web GUI vs 現在 Workers)
| 步驟 | 原 Web GUI 預設 | 現在 Workers 預設 | 開關 |
|---|---|---|---|
| ONNX 轉換/最佳化 | ON | ON | 無 |
| IP Evaluator | OFF | OFF | enable_evaluate |
| FP E2E 模擬 | OFF | OFF | enable_sim_fp |
| BIE 量化 | ON | ON | 無 |
| Fixed-Point E2E 模擬 | OFF | OFF | enable_sim_fixed |
| NEF Compile | ON | ON | 無 |
| HW E2E 模擬 | OFF | OFF | enable_sim_hw |
5.1.6 Core / Toolchain 路徑一致性
- Worker 需將工作目錄 path 傳給 core
- Core 需設定 toolchain 相關 path(輸出與中間檔)都指向該工作目錄
6. 失敗行為(簡化)
- Worker 例外 → 回報 fail
- Scheduler 標記 job 為 FAILED
- UI 顯示失敗並要求使用者重新送出
- Redis / Queue Crash → 所有 job 消失,視同 reset
7. API(Scheduler)
POST /jobs
建立新 job
Request:
{
"model_key": "...",
"quant_key": "..."
}
Response:
{
"job_id": "...",
"status": "ONNX"
}
GET /jobs/{job_id}
查詢狀態
Response:
{
"job_id": "...",
"status": "BIE",
"output": {
"bie_key": null,
"nef_key": null
}
}
Job 不存在:
404 JOB_NOT_FOUND
GET /jobs/{job_id}/events
SSE 事件串流(主動推送狀態更新)
建議:
- 若 SSE 斷線,改用 GET /jobs/{job_id} 每 3-5 秒輪詢
8. Worker 行為規範
- 從對應 queue pull job_id(
XREADGROUP) - 從 shared volume 讀取 input(
/data/jobs/{job_id}/) - 執行 Toolchain flow
- 將 output 寫入同一個 job 目錄
XACK確認訊息已處理- push done event 到
queue:done:
{
"job_id": "...",
"step": "onnx|bie|nef",
"result": "ok|fail",
"reason": "optional"
}
9. 併發與限流
- BIE Worker 數量 = 最大同時 BIE 任務數
- 每個 BIE Worker 一次只跑一個 job
- 不在 Scheduler 做複雜限流邏輯
10. 最小部署建議
- web-ui x1
- scheduler x1
- redis x1(不開 persistence)
- minio x1
- onnx-worker xN
- bie-worker xM
- nef-worker xK
11. 本地開發與測試
11.1 docker-compose 一鍵啟動
提供 docker-compose.yml,一個指令啟動整套系統:
docker-compose up
服務清單:
volumes:
job-data: # 所有 Worker + Scheduler 共用的 job 工作目錄
services:
redis:
image: redis:7-alpine
ports: ["6379:6379"]
# 不開 persistence,符合 Crash Reset 哲學
scheduler:
build: ./apps/task-scheduler
ports: ["4000:4000"]
depends_on: [redis]
volumes:
- job-data:/data/jobs
environment:
- REDIS_URL=redis://redis:6379
- JOB_DATA_DIR=/data/jobs
onnx-worker:
build: ./services/workers/onnx
depends_on: [redis]
volumes:
- job-data:/data/jobs
environment:
- REDIS_URL=redis://redis:6379
- JOB_DATA_DIR=/data/jobs
- WORKER_MODE=real # real | stub
bie-worker:
build: ./services/workers/bie
depends_on: [redis]
volumes:
- job-data:/data/jobs
environment:
- REDIS_URL=redis://redis:6379
- JOB_DATA_DIR=/data/jobs
- WORKER_MODE=real
nef-worker:
build: ./services/workers/nef
depends_on: [redis]
volumes:
- job-data:/data/jobs
environment:
- REDIS_URL=redis://redis:6379
- JOB_DATA_DIR=/data/jobs
- WORKER_MODE=real
web-ui:
build: ./apps/web
ports: ["3000:3000"]
depends_on: [scheduler]
水平擴展 Worker:
docker-compose up --scale bie-worker=3
11.2 Stub Worker 模式
開發 Scheduler 或 Web UI 時,不需要啟動真正的 Kneron Toolchain 環境。
透過環境變數 WORKER_MODE=stub 切換為 Stub 模式:
# 啟動整套系統,Worker 使用 stub 模式(不需要 toolchain 環境)
WORKER_MODE=stub docker-compose up
Stub Worker 行為:
- 收到任務後 sleep 數秒(模擬處理時間)
- 產生假的輸出檔案(空檔或固定內容)
- 回報成功到
queue:done - 不依賴任何 Kneron Toolchain 二進位檔
# Stub 實作範例
async def process_onnx_core_stub(input_paths, output_path, parameters):
await asyncio.sleep(3) # 模擬處理時間
stub_file = output_path / "out.onnx"
stub_file.write_bytes(b"STUB_ONNX_OUTPUT")
return {"file_path": str(stub_file), "file_size": stub_file.stat().st_size}
11.3 開發情境對照
| 開發情境 | 需要什麼 | 指令 |
|---|---|---|
| 改 Scheduler 邏輯 | Redis + stub workers | WORKER_MODE=stub docker-compose up redis scheduler onnx-worker bie-worker nef-worker |
| 改 Web UI | Redis + Scheduler + stub workers | WORKER_MODE=stub docker-compose up |
| 改 Worker 核心邏輯 | DevContainer + Redis(跑單元測試) | docker run -d redis && pytest tests/ |
| 完整 E2E 測試 | docker-compose(真實 Worker) | docker-compose up |
| 測試水平擴展 | docker-compose + scale | docker-compose up --scale bie-worker=3 |
12. MVP 驗證項目
- 單一 job 成功完成(ONNX → BIE → NEF → COMPLETED)
- 多 job 排隊,BIE 不超量
- Worker 失敗 → job FAILED
- Redis 重啟 → job 消失,UI 提示重送
- Stub Worker 模式可正常走完完整流程
- Worker 水平擴展(scale=3)可正常分配任務