DevOpsDays Taiwan 2026

以 TrendAI 為例 · 如何實現低成本高效率的

Observability 2.0

Herber Wang · TrendAI · Staff Engineer
Scott Liao · AWS · Lead Solutions Architect
前情提要 · DEVOPSDAYS TAIPEI 2025

一年前,我們在這個舞台分享了一件事

那就是

Observability 2.0
with Lakehouse

2025 完整簡報 QR
掃描下載
2025 完整簡報
為什麼要 2.0

過去 Observability 1.0 所帶來的問題

  • Three pillars 各自為政 — metrics / logs / traces 分開存、分開查,跨系統排查得在三套工具間手動拼湊,花費大量時間。
  • 依賴關係爆炸 — 微服務之間的呼叫鏈越來越複雜,一個請求跨越數十個服務,傳統工具難以還原全貌。
  • 規模放大延遲要求 — 企業越大,可觀測延遲的容忍度越低;監控本身反而成為瓶頸。
  • AI 把 telemetry 推上新高度 — 資料量與維度爆發性成長,傳統 infra monitoring 已經不符合需求
過去
API First
現在
AI First

以使用者為軸心的 AI Observability

每個推論、每個 token、每條 trace 都是使用者訊號。

1.0 的取樣與分庫架構,已經跟不上 AI First 時代的觀測需求。

本場重點

用 TrendAI 的實戰告訴你

怎麼做、踩了哪些坑、省了多少錢。

Herber Wang · TrendAI · Staff Engineer
先給結論
Jaeger + ES 同規格 · 年
~$5M
我們做到
~$348K

~14× 便宜 · 同時完整保留、不採樣 · 90 天保留 · 跨 trace 分析

全球 10 region prod 實際成本:S3 $11K + MSK $9K + 運算 $9K = $29K/月

回顧 · 2025 DevOpsDays

Tracing 1.0 的三道牆

① 採樣是謊言
1% 採樣聽起來合理
但你想看的那個 bug,正好在被丟掉的 99% 裡。
② 成本失控
完整保留 = 帳單線性漲
SaaS APM 按 ingestion 計費,micro service 翻倍 → 帳單翻倍。
③ 單 trace 視角
看不到使用者
「這次 API 死了沒」看到的是技術,不是使用者。

去年講「為什麼要去 2.0」· 今年講「PB 級怎麼做」

玩真的

TrendAI 的實際規模

10
AWS Region
每區一套獨立部署
182 TB
US-East-1 累積實測
1.43 兆 spans · ~2 TB/day
850K
全球尖峰 spans/s
auto-scaling 實測扛住

raw OTLP 展開 ~37 TB/day → Parquet+ZSTD 存 ~2 TB/day · 壓縮 19× · 90 天滾動 ~0.6 PB 落地(全球實測)

承上 · 接續 Scott 的概念

去年說「即將來臨」· 今年「上線了」

2025 · 概念
即將來臨
  • 為什麼 Observability 2.0 + Lakehouse
  • event-centric · 開放格式 · 地板價儲存
  • 別人的例子:Netflix · Uber · Airbnb
2026 · 實現
TrendAI 真的跑起來了
  • PB 級 · 10 region · production
  • 我們的真實帳單 · 我們踩過的坑
  • 不是 demo,是上線中的系統

Observability 2.0 = metrics + logs + traces · metrics 有 Thanos、logs 有 Loki 已夠好 · trace 最難、資料最大 → 我們先啃。今天只講 trace。

第二幕 — 怎麼做到的

低成本 × 高效率 × 真的能上線

接下來每一頁,都在證明這三件事其中一件 —— 全用 TrendAI 的真實數字

Lakehouse

Lakehouse 解鎖了什麼

① 完整保留
不再採樣
S3 $23/TB/月 · Parquet+ZSTD ~5%(19×)
展開 2,366 → 存 127 bytes/span · vs 傳統 row/JSON
② 儲存運算分離
成本 Linear
S3 靜態儲存按量付 · Trino/Spark 用時才開
不查就不付 compute · vs SaaS 固定 ingestion
③ 開放格式
不被綁架
Iceberg + Parquet · 換引擎不搬資料
Trino / Spark / Athena / DuckDB 全能查
架構

TrendAI Big Tracing 架構

✏ WRITE PATH 寫一次 · 串流 · 不可中斷 micro service OTel SDK Istio sidecar raw ~37 TB/day OTel Collector 收集 · 批次轉發 轉成 Kafka event 統一出口 Kafka (MSK) spans topic IAM auth · 單 AZ 緩衝層 (disk) Spark Streaming EMR on EKS · 180s 批次 Dynamic Alloc 3–8 exec pure Spark scaling ⚙ 寫入時壓縮 Parquet + ZSTD Columnar · 字典編碼 壓至 ~5% (≈19×) 📦 STORAGE Apache Iceberg on S3 Glue Catalog · Parquet + ZSTD · 512MB target 4 層分區:hours · is_valid_company · bucket(device_id) · bucket(trace_id) ⭐ 壓縮後 ~2 TB/day · 累積 178 TB (US 實測) ⚙ Maintenance 獨立 Spark CronJob 不同 AZ · 分散風險 • 小檔合併 (rewrite) • Snapshot 過期 • Orphan 清理 📖 READ PATH 讀多次 · 多種消費者 · 同一份資料 Trino SQL 引擎 partition prune · 秒級 ★ 自研 · 主路徑 Tempo API Adapter row → OTLP protobuf Grafana (Tempo DS) 看單 trace 瀑布 Grafana (Trino DS) SQL · CUJ 分析 Spark Analytics 直讀 Iceberg · 不走 Trino AI Agent (未來) LLM 查 trace 直讀 Iceberg
真正的痛點

Spark Streaming 的擴展機制

# prod_values.yaml · Dynamic Allocation
minExecutors: 3   maxExecutors: 8
executor: 8 cores · 16GB

# 背壓三道閘
maxOffsetsPerTrigger:   180,000
minPartitions:          256
maxRecordsPerPartition: 2,000
processingTime:         180s
  • 3–8 executors:低水位活著、高水位追資料
  • 180 秒:延遲可接受、寫 Iceberg 小檔不爆
  • 256 partitions:強制平行、不讓 Spark 偷懶
  • 純 Spark 原生 scaling:不靠 KEDA / HPA

教訓:不追求秒級 · 換來成本可控 + Iceberg 寫入健康。

Kafka (MSK):64 partition · RF2 · 保留 ~1h · 每則 ~400 spans · offset 存 S3 checkpoint 不在 Kafka → Kafka UI 看不到 consumer lag

部署模型

單 AZ + EMR on EKS

為什麼鎖單 AZ?跨 AZ 流量 $0.01/GB。

  • Spark 拉 Kafka 壓縮後 ~2.8 TB/day
  • 鎖單 AZ 省 ~$30–40K/年(全球)
  • 壓縮前是 ~$200K → Kafka 壓縮 + 單 AZ 雙重砍流量

EMR on EKS 帶來什麼

  • 跟其他 K8s workload 共用 cluster
  • EMRFS · Glue Catalog 直接接 · IRSA
  • 不養 EMR cluster · 不開 EC2 fleet

⚠ 代價:單 AZ failure = streaming 停 · 恢復靠 checkpoint @ S3(多 AZ)

Iceberg Partition

四層分區設計

CREATE TABLE spans (...) USING iceberg
PARTITIONED BY (
  hours(start_time),
  is_valid_company,
  bucket(8, device_id),
  bucket(8, trace_id)
)
TBLPROPERTIES (
  'target-file-size-bytes'='536870912' -- 512MB
)
  • hours(start_time) — 時間範圍查詢 99% 跳過
  • is_valid_company — 過濾 invalid / null tenant
  • bucket(8, device_id)CUJ 的關鍵,同 device 落同 bucket
  • bucket(8, trace_id) — 單 trace 單點查詢直達,不掃整小時
PB 級必死的細節

Iceberg 維運:做了 → 沒爆

① Rewrite
小檔合併
180s 寫一批 × 4 partition → 一天上千小檔
每小時合併 · 結果 us1 p50 506MB、小檔僅 14.7%
② Expire
Snapshot 過期
每 2.7 分鐘 commit 一個 → metadata 爆炸
定期過期 · us1 穩定 706 個 / ~32h
③ Clean
Orphan 清理
失敗 commit 留孤兒檔 → S3 帳單偷漲
每週掃 S3 prefix · 對照 manifest 刪

做這些 → us1 沒爆 · 維運用獨立 Helm chart 部署在不同 AZ(跟 streaming 分散風險)

關鍵架構決策

為什麼選 Trino?因為我們不需要永遠選它

✅ 現狀 · Trino
自管 EKS · 平衡點。Athena SQL 完全相容、partition prune 成熟。
挑它的理由:保留兩條未來通道
🔵 路線 A → Athena
Managed · Serverless。Query 零改寫、同 Glue catalog 直接接。
代價:按 scan 計費 · 比自管貴
🚀 路線 B → StarRocks
MPP · 向量化。OLAP 快數倍、原生 Iceberg connector。
代價:SQL 方言不同 · 要重寫

資料一份不動 · 引擎隨時換 —— 這才是 Lakehouse 的真正威力

關鍵魔術

Tempo API Adapter

Grafana 已經會跟 Tempo 講話。不要重寫 Grafana —— 寫一個假 Tempo,背後接 Trino + Iceberg。

  • GET /api/traces/{traceID}
  • GET /api/search · /api/search/tags

⚠ 只 mimic API · 不 mimic 架構:Tempo Ingester 用 in-memory queue → RAM-bound;我們用 Kafka 當 buffer → disk 便宜、省一個元件。

# Trino row → OTLP protobuf
async def get_trace(trace_id):
    rows = await trino.query("""
      SELECT * FROM spans
      WHERE trace_id = ?
        AND start_time BETWEEN ? AND ?
    """, trace_id, t0, t1)
    return rows_to_otlp(rows)
# Grafana 不知道、也不在乎背後是什麼

✅ Grafana 設定零修改 · ✅ 換掉儲存層上層無感 · ✅ 不買 Tempo SaaS

查詢端的痛

不走分區就死

❌ 沒時間範圍

SELECT * FROM spans
WHERE trace_id = 'abc...';
-- us1 planner 估:
-- 掃 82.73 TB / 176 億 spans
-- → coordinator OOM

✅ 加時間範圍

SELECT * FROM spans
WHERE start_time BETWEEN ? AND ?
  AND trace_id = 'abc...';
-- partition + bucket prune
-- 實讀 100 MB · CPU 1.1s

黃金法則:外層 query 也要 start_time,否則 prune 跑不掉

深入一層 · SELECT 也是效能

Parquet:存得起 · 也查得起

Columnar:SELECT 8 欄只抓那 8 根 column chunk,其餘連下載都不下載

resource_attrs/span_attrs 是巢狀 rich payload(http 細節、自訂欄位)—— 全留的價值所在。不需要時不付錢、需要時只付那一欄。

查法PhysicalCPU
不走分區82.73 TB會死
SELECT * 寬窗935 MB52 s
只選 8 欄 窄窗100 MB1.1 s

省的大頭是反序列化、不是 I/O(不碰巢狀 attr 就全免)· Parquet 不是 index:粗篩靠 partition、省讀靠 columnar,中間沒索引(刻意的)

第三幕 — 換來什麼

看得更多 · 花得更少

跨 trace 看見 APM 看不到的 · 同時全球 $29K/月、便宜 ~14×

為什麼需要跨 trace

micro service 複雜到沒人看得到全貌

57
服務(部分)
14 產品線 · 120 條依賴 · 仍在擴充
32%
依賴是跨產品的
沒有一個團隊擁有完整鏈
31
個循環依賴
跨產品繞一圈又回到自己

一個 Auth 服務 被 23 個服務依賴、8,600 萬次呼叫 · 這圖是一句 GROUP BY SQL 跑出來的,APM 採樣 1% 根本畫不出來

TrendAI 真實依賴圖 · 已匿名(功能角色)

57 服務 · 373 條呼叫交織

dependency graph
57服務(目前收錄的一部分)
373條呼叫交織成網
31個跨產品循環依賴
23個服務依賴同一個 Auth
而且這只是一部分服務 —— 完整系統更纏,沒人腦袋裝得下這張圖
殺手級應用

從 Trace 到 Critical User Journey

Tracing 1.0 視角
「這次 API 死了沒?」
看單一 trace_id 生命週期。單次 API 成功 ≠ 使用者成功
CUJ 視角
「使用者裝好 agent、政策真的套用了嗎?」
多條 trace 用 device_id 串成 journey。看使用者真的成功了沒

一次 agent 安裝橫跨 5 條產品線 · 平均 12 條 trace · Tempo/Jaeger 做不到 · bucket(8, device_id) 把同 device 物理聚一起才划算

某 region · 6 小時 · 起點「身分註冊」→ 終點「套用設定」

真實 drop-off:鑽進去看每一個

2,088
開始安裝的 device
99.6%
走到底 · 套用政策
2,080 / 2,088 完成
8
沒走到底 (0.4%)
案例 A · 被刪
+8s 後 +60s 被停用並刪除 → 永遠不回查設定。
案例 B · VDI
走預設設定路徑,之後只剩 telemetry,本來就不主動查。
案例 C · 早夭
enroll 後 +0.2s 就回報錯誤 → 安裝即崩。

root cause 不是系統卡關,是 agent 生命週期 · APM 只看到「99.6%」一個數字 · 我們看到每一個沒走完的 agent 發生什麼事

實作

Two-Pass Aggregation

Phase 1 · 每條 trace_id 一行

WITH session_matrix AS (
  SELECT trace_id, ANY_VALUE(device_id) device_id,
    BOOL_OR(span_name LIKE '%onboarding%') step1,
    BOOL_OR(span_name = 'Completed') step6, ...
  FROM spans
  WHERE start_time BETWEEN ? AND ?
  GROUP BY trace_id )

Phase 2 · 每個 device_id 一行

SELECT device_id,
  array_join(array_agg(status
    ORDER BY time), ' → ') AS journey
FROM session_matrix
GROUP BY device_id
-- dev-002 | A(OK)  ← 漏斗破洞

bucket(8, device_id) 把同 device 物理聚一起 · za 實測:一條 journey 跨 12 條 trace · 延遲 p50 48s · p95 86s · p99 114s

實際成果

三方對比

對手:同規格 3 TB/day 估算(≈ 單一大區)· 我們:全球 10 region prod 實際帳單

指標Jaeger + ESTempoBig Tracing on Lakehouse
月成本~$300–500K~$60–100K~$29K
年成本~$5M~$700K–1M~$348K
採樣率實務 1–10%100%100%
保留期7–14 天90 天90 天
跨 trace (CUJ)❌ TraceQL 不行✅ SQL 直接
引擎可替換綁 ES綁 TempoTrino → Athena/StarRocks

便宜 ~14× —— 而且這是我們全球 vs 對手單區,正規化後差距更大 · vs Tempo 便宜 2–3× 還多 5 倍能力

這些比成功故事還重要

三個最痛的坑

Spark Streaming 不會自己長大
maxExecutors=8 是 hard cap。重啟追資料 lag 噴 → 背壓三道閘要全設。
Trino 不走 partition = 死
一個沒加 start_time 的 SQL = coordinator 卡死、其他查詢被拖。
單 AZ 是權衡不是失誤
跨 AZ 流量費。壓縮 + 鎖單 AZ 雙重砍,代價是 AZ 掛掉 streaming 停。

PB 級沒有捷徑 —— 這三個坑,你遲早會踩。

回到開場

去年說它即將來臨,
今年它在 production 跑著。

Q & A · Thank you

TrendAI 徵才 QR
🚀 我們正在徵才 · Trend Micro
職缺什麼都有 · 掃 QRCode 看所有開放職位 · 104.com.tw/company/apio4y0
AWS Solutions Architect 徵才 QR
🚀 我們正在徵才 · AWS Solutions Architect
本場完整簡報 QR
掃描下載
本場完整簡報