<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Metrics on Tarragon</title><link>https://tarrragon.github.io/blog/tags/metrics/</link><description>Recent content in Metrics on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Mon, 22 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/metrics/index.xml" rel="self" type="application/rss+xml"/><item><title>Cloud Monitoring Metrics Model 與 MQL</title><link>https://tarrragon.github.io/blog/backend/04-observability/vendors/gcp-cloud-operations/cloud-monitoring-mql/</link><pubDate>Mon, 22 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/04-observability/vendors/gcp-cloud-operations/cloud-monitoring-mql/</guid><description>&lt;blockquote>
&lt;p>本文是 &lt;a href="https://tarrragon.github.io/blog/backend/04-observability/vendors/gcp-cloud-operations/" data-link-title="GCP Cloud Operations" data-link-desc="GCP 原生觀測性套件（前 Stackdriver）：Logging / Monitoring / Trace / Profiler">GCP Cloud Operations&lt;/a> 的 vendor deep article，深化 overview「Cloud Monitoring uptime checks / SLO」跟「OTLP integration」段。初次接觸 GCP 觀測的讀者建議先讀 &lt;a href="https://tarrragon.github.io/blog/backend/04-observability/vendors/gcp-cloud-operations/" data-link-title="GCP Cloud Operations" data-link-desc="GCP 原生觀測性套件（前 Stackdriver）：Logging / Monitoring / Trace / Profiler">GCP Cloud Operations 服務頁&lt;/a>。&lt;/p>&lt;/blockquote>
&lt;h2 id="問題情境">問題情境&lt;/h2>
&lt;p>GCP 服務預設把 metrics 寫到 Cloud Monitoring，工程師打開 Metrics Explorer 就能看到 CPU、記憶體、request count。問題通常出在三個地方：GCP 內建 metrics 的 resource model 跟應用層的 business metrics 用不同語言描述同一件事，PromQL 使用者要重新學 MQL 語法，alerting policy 的 condition type 跟 notification channel 配置比預期複雜。理解 Cloud Monitoring 的 metrics model 才能避免 custom metrics 爆量、alert noise、跟 Prometheus 生態的銜接摩擦。&lt;/p>
&lt;h2 id="核心概念">核心概念&lt;/h2>
&lt;h3 id="monitored-resource-與-metric-descriptor">Monitored resource 與 metric descriptor&lt;/h3>
&lt;p>Cloud Monitoring 的資料模型有兩個軸：&lt;strong>monitored resource&lt;/strong> 描述「誰產生了這個 metric」，&lt;strong>metric descriptor&lt;/strong> 描述「這個 metric 量什麼」。&lt;/p>
&lt;p>Monitored resource 是 GCP 自動帶入的標籤集合。GKE pod 的 monitored resource type 是 &lt;code>k8s_pod&lt;/code>，帶 &lt;code>project_id&lt;/code>、&lt;code>location&lt;/code>、&lt;code>cluster_name&lt;/code>、&lt;code>namespace_name&lt;/code>、&lt;code>pod_name&lt;/code>。Cloud Run revision 是 &lt;code>cloud_run_revision&lt;/code>，帶 &lt;code>service_name&lt;/code>、&lt;code>revision_name&lt;/code>、&lt;code>location&lt;/code>。這層標籤不需要工程師手動設定，GCP agent 或 SDK 自動填入。&lt;/p>
&lt;p>Metric descriptor 定義 metric 的名稱、型別（GAUGE / DELTA / CUMULATIVE）、value type（INT64 / DOUBLE / DISTRIBUTION）與自訂 label。GCP 內建 metrics 用 &lt;code>compute.googleapis.com/instance/cpu/utilization&lt;/code> 這樣的命名空間格式；custom metrics 用 &lt;code>custom.googleapis.com/&amp;lt;your-name&amp;gt;&lt;/code> 或 &lt;code>workload.googleapis.com/&amp;lt;your-name&amp;gt;&lt;/code>（後者透過 OTel Collector 或 Managed Prometheus 寫入時使用）。&lt;/p>
&lt;p>兩個軸相乘就是 time series 的數量。Cardinality 管理在 GCP 上等同於控制 monitored resource × metric label 的組合數。GCP 對 custom metrics 有每個 project 的 time series 配額（預設 500 per metric descriptor、可申請提高），超過時寫入會被拒。&lt;/p>
&lt;h3 id="mql-vs-promql">MQL vs PromQL&lt;/h3>
&lt;p>Cloud Monitoring 有兩種查詢語言。MQL（Monitoring Query Language）是 GCP 自家設計的 pipeline 語法：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">fetch k8s_container
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">| metric &amp;#39;kubernetes.io/container/cpu/core_usage_time&amp;#39;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">| align rate(1m)
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">| every 1m
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">| group_by [resource.cluster_name, resource.namespace_name],
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl"> [value_cpu_usage: aggregate(value.core_usage_time)]&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>PromQL 在 Cloud Monitoring 上也可用（透過 Managed Service for Prometheus）。兩者的核心差異：&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p>本文是 <a href="/blog/backend/04-observability/vendors/gcp-cloud-operations/" data-link-title="GCP Cloud Operations" data-link-desc="GCP 原生觀測性套件（前 Stackdriver）：Logging / Monitoring / Trace / Profiler">GCP Cloud Operations</a> 的 vendor deep article，深化 overview「Cloud Monitoring uptime checks / SLO」跟「OTLP integration」段。初次接觸 GCP 觀測的讀者建議先讀 <a href="/blog/backend/04-observability/vendors/gcp-cloud-operations/" data-link-title="GCP Cloud Operations" data-link-desc="GCP 原生觀測性套件（前 Stackdriver）：Logging / Monitoring / Trace / Profiler">GCP Cloud Operations 服務頁</a>。</p></blockquote>
<h2 id="問題情境">問題情境</h2>
<p>GCP 服務預設把 metrics 寫到 Cloud Monitoring，工程師打開 Metrics Explorer 就能看到 CPU、記憶體、request count。問題通常出在三個地方：GCP 內建 metrics 的 resource model 跟應用層的 business metrics 用不同語言描述同一件事，PromQL 使用者要重新學 MQL 語法，alerting policy 的 condition type 跟 notification channel 配置比預期複雜。理解 Cloud Monitoring 的 metrics model 才能避免 custom metrics 爆量、alert noise、跟 Prometheus 生態的銜接摩擦。</p>
<h2 id="核心概念">核心概念</h2>
<h3 id="monitored-resource-與-metric-descriptor">Monitored resource 與 metric descriptor</h3>
<p>Cloud Monitoring 的資料模型有兩個軸：<strong>monitored resource</strong> 描述「誰產生了這個 metric」，<strong>metric descriptor</strong> 描述「這個 metric 量什麼」。</p>
<p>Monitored resource 是 GCP 自動帶入的標籤集合。GKE pod 的 monitored resource type 是 <code>k8s_pod</code>，帶 <code>project_id</code>、<code>location</code>、<code>cluster_name</code>、<code>namespace_name</code>、<code>pod_name</code>。Cloud Run revision 是 <code>cloud_run_revision</code>，帶 <code>service_name</code>、<code>revision_name</code>、<code>location</code>。這層標籤不需要工程師手動設定，GCP agent 或 SDK 自動填入。</p>
<p>Metric descriptor 定義 metric 的名稱、型別（GAUGE / DELTA / CUMULATIVE）、value type（INT64 / DOUBLE / DISTRIBUTION）與自訂 label。GCP 內建 metrics 用 <code>compute.googleapis.com/instance/cpu/utilization</code> 這樣的命名空間格式；custom metrics 用 <code>custom.googleapis.com/&lt;your-name&gt;</code> 或 <code>workload.googleapis.com/&lt;your-name&gt;</code>（後者透過 OTel Collector 或 Managed Prometheus 寫入時使用）。</p>
<p>兩個軸相乘就是 time series 的數量。Cardinality 管理在 GCP 上等同於控制 monitored resource × metric label 的組合數。GCP 對 custom metrics 有每個 project 的 time series 配額（預設 500 per metric descriptor、可申請提高），超過時寫入會被拒。</p>
<h3 id="mql-vs-promql">MQL vs PromQL</h3>
<p>Cloud Monitoring 有兩種查詢語言。MQL（Monitoring Query Language）是 GCP 自家設計的 pipeline 語法：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">fetch k8s_container
</span></span><span class="line"><span class="ln">2</span><span class="cl">| metric &#39;kubernetes.io/container/cpu/core_usage_time&#39;
</span></span><span class="line"><span class="ln">3</span><span class="cl">| align rate(1m)
</span></span><span class="line"><span class="ln">4</span><span class="cl">| every 1m
</span></span><span class="line"><span class="ln">5</span><span class="cl">| group_by [resource.cluster_name, resource.namespace_name],
</span></span><span class="line"><span class="ln">6</span><span class="cl">    [value_cpu_usage: aggregate(value.core_usage_time)]</span></span></code></pre></div><p>PromQL 在 Cloud Monitoring 上也可用（透過 Managed Service for Prometheus）。兩者的核心差異：</p>
<table>
  <thead>
      <tr>
          <th>面向</th>
          <th>MQL</th>
          <th>PromQL（via Managed Prometheus）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>資料來源</td>
          <td>所有 Cloud Monitoring metrics</td>
          <td>透過 Managed Prometheus 寫入的 metrics</td>
      </tr>
      <tr>
          <td>查詢介面</td>
          <td>Metrics Explorer / alerting condition</td>
          <td>Grafana / Prometheus UI / API</td>
      </tr>
      <tr>
          <td>Aggregation 語法</td>
          <td>pipe-style <code>group_by</code></td>
          <td>函式風格 <code>sum by (label)</code></td>
      </tr>
      <tr>
          <td>跨 GCP 與 custom</td>
          <td>原生支援 GCP 內建 metrics</td>
          <td>需要轉成 Prometheus 格式</td>
      </tr>
      <tr>
          <td>學習曲線</td>
          <td>GCP-specific、不可搬到其他平台</td>
          <td>跨平台標準、可搬到 Mimir / Thanos</td>
      </tr>
  </tbody>
</table>
<p>選擇判讀：純 GCP 環境且團隊沒有 Prometheus 經驗 → MQL 起步快。已有 Prometheus / Grafana 生態 → 用 Managed Prometheus + PromQL、把 GCP 內建 metrics 透過 Prometheus-compatible exporter 導入。混合環境 → 兩者並存、GCP 原生 metrics 用 MQL 做 alerting、application metrics 用 PromQL 查詢。</p>
<h2 id="配置-step-by-step">配置 step-by-step</h2>
<h3 id="custom-metrics-設計與寫入">Custom metrics 設計與寫入</h3>
<p>Custom metrics 的常見路徑有三條：</p>
<p><strong>路徑一：Cloud Monitoring API 直接寫入</strong>。應用程式用 Cloud Monitoring client library 建立 metric descriptor 並寫入 time series。適合 GCP-native 應用，不需要額外 agent。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">metric type: custom.googleapis.com/checkout/latency_ms
</span></span><span class="line"><span class="ln">2</span><span class="cl">kind: GAUGE
</span></span><span class="line"><span class="ln">3</span><span class="cl">value type: DISTRIBUTION
</span></span><span class="line"><span class="ln">4</span><span class="cl">labels: [service, region, status_code]</span></span></code></pre></div><p><strong>路徑二：OTel Collector + GCP exporter</strong>。應用程式用 OTel SDK 產生 metrics，OTel Collector 透過 <code>googlecloud</code> exporter 寫到 Cloud Monitoring。Metrics 命名空間是 <code>workload.googleapis.com/</code>。適合已有 OTel instrumentation 的服務。</p>
<p><strong>路徑三：Managed Service for Prometheus</strong>。部署 GCP 的 Managed Prometheus collector（或自管 Prometheus + remote write），metrics 存在 GCP 託管的 Monarch backend。查詢用 PromQL。適合 Kubernetes 環境且團隊熟悉 Prometheus 生態。</p>
<p>三條路徑可以共存。選擇判讀：先看團隊的 metrics 生態是 GCP-native 還是 Prometheus-native，再看 multi-cloud 需求。Managed Prometheus 的優勢是 PromQL 可搬、劣勢是 GCP 內建 metrics 需要額外整合。</p>
<h3 id="alerting-policy-配置">Alerting policy 配置</h3>
<p>Cloud Monitoring alerting policy 由三部分組成：condition、notification channel、documentation。</p>
<p>Condition types：</p>
<ul>
<li><strong>Metric threshold</strong>：metric 超過閾值 N 分鐘。適合「error rate &gt; 1% 持續 5 分鐘」。</li>
<li><strong>Metric absence</strong>：metric 消失。適合偵測 scrape 斷裂或服務停擺。</li>
<li><strong>Forecasting</strong>：預測 metric 在 N 小時後超過閾值。適合 disk 滿、quota 耗盡。</li>
<li><strong>Process health</strong>：GCE instance 的 process 是否存活。</li>
<li><strong>Log-based</strong>：Cloud Logging 出現特定 pattern 時觸發。適合把 error log 轉成 alert。</li>
<li><strong>SLO burn rate</strong>：SLO 設定後、burn rate 超過閾值。對應 <a href="/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">burn-rate</a> 概念。</li>
</ul>
<p>Notification channels：Email / PagerDuty / Slack / Pub/Sub / Webhook / SMS。Pub/Sub channel 適合接自定義 automation（收到 alert → trigger Cloud Function）。</p>
<p>Snooze 與 maintenance window：暫時抑制特定 alerting policy。部署期間或已知維護時使用。</p>
<h3 id="managed-prometheus-整合">Managed Prometheus 整合</h3>
<p>GCP Managed Service for Prometheus 的部署模式：</p>
<ul>
<li><strong>GKE 模式</strong>：啟用 GKE monitoring、Managed Prometheus collector 自動部署。不需要自管 Prometheus server。</li>
<li><strong>Remote write 模式</strong>：自管 Prometheus server + <code>remote_write</code> 到 GCP Monarch endpoint。保留本地查詢能力，同時長期儲存在 GCP。</li>
<li><strong>OTel Collector 模式</strong>：OTel Collector 用 <code>googlemanagedprometheus</code> exporter 寫到 Monarch。</li>
</ul>
<p>查詢端：用 GCP Console 的 PromQL UI、或部署 Grafana + GMP datasource。PromQL 功能子集支援良好（rate / histogram_quantile / aggregation），少數進階功能（subquery）有限制。</p>
<h2 id="故障演練與邊界">故障演練與邊界</h2>
<h3 id="custom-metric-配額用盡">Custom metric 配額用盡</h3>
<p><strong>觸發條件</strong>：custom metric descriptor 數量超過 project 配額（預設 500），或單一 metric descriptor 的 time series 數量超過配額。</p>
<p><strong>表現</strong>：API 回傳 429 或 quota exceeded error。新 time series 寫不進去，既有的不受影響。</p>
<p><strong>修復</strong>：清理不再使用的 metric descriptor（describe → delete）、合併語意重疊的 metrics、減少 label cardinality。GCP Console → IAM → Quotas 可以申請提高配額，但先確認是設計問題而非真的需要那麼多 series。</p>
<h3 id="alerting-policy-觸發延遲">Alerting policy 觸發延遲</h3>
<p><strong>觸發條件</strong>：alerting policy 使用的 metrics 的 alignment period 或 duration 設定過長。</p>
<p><strong>表現</strong>：異常已經發生 10 分鐘，alert 才觸發。原因是 Cloud Monitoring 的 evaluation cycle 跟 metrics ingestion delay 相加。GCP 內建 metrics 的 ingestion delay 約 1-3 分鐘；custom metrics 透過 API 寫入的 delay 約 10-30 秒。</p>
<p><strong>修復</strong>：把 condition 的 alignment period 設短（1 分鐘）、duration 設短（但太短會造成 flapping）。Log-based alerting condition 的 delay 通常比 metric-based 短（秒級 vs 分鐘級），緊急異常考慮用 log-based condition。</p>
<h3 id="managed-prometheus-查詢與自管-prometheus-結果不一致">Managed Prometheus 查詢與自管 Prometheus 結果不一致</h3>
<p><strong>觸發條件</strong>：同一個 PromQL query 在本地 Prometheus 跟 GMP 的結果不同。</p>
<p><strong>表現</strong>：dashboard 數字對不上、alert 觸發行為不一致。</p>
<p><strong>修復</strong>：先確認 remote write 是否有 sample drop（看 <code>prometheus_remote_storage_samples_failed_total</code>）。再確認 GMP 的 PromQL 子集限制（部分 subquery 語法不支援）。最後確認 metric naming：local Prometheus 的 metric name 跟 GMP 儲存後的 naming convention 可能有差異（加了 <code>__name__</code> prefix 或 resource label）。</p>
<h2 id="容量與成本">容量與成本</h2>
<p>Cloud Monitoring 的計費模型基於 <strong>ingested metrics volume</strong>（per million data points）。GCP 內建 metrics（agent metrics 除外）免費。Custom metrics 的前 150 MB per billing account 免費，超過後按 volume 計費。</p>
<p>成本治理的判讀：</p>
<ul>
<li>最大成本來源通常是高頻率的 custom metrics 或高 cardinality label</li>
<li>用 <code>monitoring.googleapis.com/billing/bytes_ingested</code> metric 追蹤 ingestion 量</li>
<li>減少 scrape interval（15s → 30s 或 60s）可以直接降低 ingestion 量</li>
<li>Managed Prometheus 的計費跟 custom metrics 分開計算（per samples ingested）</li>
</ul>
<h2 id="整合與下一步">整合與下一步</h2>
<ul>
<li><a href="/blog/backend/04-observability/vendors/gcp-cloud-operations/" data-link-title="GCP Cloud Operations" data-link-desc="GCP 原生觀測性套件（前 Stackdriver）：Logging / Monitoring / Trace / Profiler">GCP Cloud Operations 服務頁</a>：overview 與日常操作</li>
<li><a href="/blog/backend/04-observability/cardinality-cost-governance/" data-link-title="4.7 Cardinality 治理與成本邊界" data-link-desc="把 metric / log / trace 的 cardinality 與成本作為平台一級治理議題">4.7 cardinality 治理</a>：cardinality 治理的完整策略</li>
<li><a href="/blog/backend/04-observability/sli-slo-signal/" data-link-title="4.6 SLI 量測與 SLO 訊號設計" data-link-desc="把可靠性目標的訊號從 metric 端設計好、餵給 6.6 SLO 政策">4.6 SLI/SLO signal</a>：SLO burn rate alert 的訊號設計</li>
<li><a href="/blog/backend/04-observability/vendors/prometheus/" data-link-title="Prometheus" data-link-desc="Pull-based metrics 主流 OSS、PromQL 與 alerting">Prometheus</a>：Managed Prometheus 的上游概念</li>
<li><a href="/blog/backend/04-observability/vendors/opentelemetry/" data-link-title="OpenTelemetry" data-link-desc="可觀測性開放標準、SDK 與 Collector">OpenTelemetry</a>：OTel Collector + GCP exporter 整合</li>
<li><a href="../cloud-logging-export-compliance/">Cloud Logging 查詢、匯出與合規</a>：同 vendor 的 logs 面</li>
</ul>
]]></content:encoded></item><item><title>4.C11 Uber：M3 大規模 Metrics 平台</title><link>https://tarrragon.github.io/blog/backend/04-observability/cases/uber-m3-metrics-platform-scale/</link><pubDate>Mon, 22 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/04-observability/cases/uber-m3-metrics-platform-scale/</guid><description>&lt;p>Uber 的 M3 案例揭露了 metrics 系統從「每個團隊各跑一套 Prometheus」到「全公司共用的 metrics 平台」的轉折點。轉折的核心判斷是：當 active series 總量超過單機 Prometheus 的記憶體上限、且多個團隊需要跨叢集查詢時，自建平台層的成本低於持續橫向複製 Prometheus 實例的成本。&lt;/p>
&lt;h2 id="業務背景">業務背景&lt;/h2>
&lt;p>Uber 的服務觀測涵蓋行程追蹤、即時定價、ETA 計算、司機定位、支付結算與推播通知。每個微服務都暴露 Prometheus-compatible metrics，隨著服務數量成長到數千個，寫入速率達到每秒數十億 data points。&lt;/p>
&lt;p>早期每個團隊各自部署 Prometheus，各管自己的 retention、scrape config 與 alerting rules。規模小時這個模式運作良好 — 每個 Prometheus 實例只需要處理自己團隊的幾萬到幾十萬 series。但當組織成長到數百個團隊、數千個服務時，散落的 Prometheus 實例帶來三個問題。&lt;/p>
&lt;h2 id="技術挑戰">技術挑戰&lt;/h2>
&lt;h3 id="單機記憶體天花板">單機記憶體天花板&lt;/h3>
&lt;p>Prometheus 的 TSDB 把 active series 放在記憶體的 head block，每個 series 消耗約 3-4 KB（詳見 &lt;a href="https://tarrragon.github.io/blog/backend/04-observability/vendors/prometheus/capacity-failure-modes/" data-link-title="Prometheus 容量規劃與故障模式" data-link-desc="說明 Prometheus 單機容量邊界、cardinality 與 retention 的資源模型、常見故障模式與判讀方式">Prometheus 容量規劃&lt;/a>）。當單一 Prometheus 實例需要 scrape 的 series 超過 1000 萬時，head block 就需要 40+ GB 記憶體。加上 query execution 跟 WAL replay 的暫時開銷，單機很容易 OOM。&lt;/p>
&lt;p>團隊的第一反應是按服務拆分多個 Prometheus 實例，但這讓跨服務查詢變得困難 — 要看一條 request 從 gateway 到 payment 的 latency 分布，需要分別查三個 Prometheus 再手動關聯。&lt;/p>
&lt;h3 id="retention-與長期趨勢">Retention 與長期趨勢&lt;/h3>
&lt;p>Prometheus 預設 retention 15 天。容量規劃與季度趨勢分析需要 90 天甚至 1 年的歷史資料。把 Prometheus retention 拉長到 90 天，disk 跟 memory 需求同步上升，而且 compaction 效率在資料量大時會下降。&lt;/p>
&lt;p>團隊需要的是分層 retention — 近期資料保留全精度、歷史資料做 downsampling 後保留更久。Prometheus 原生不支援 downsampling。&lt;/p>
&lt;h3 id="高可用與跨叢集查詢">高可用與跨叢集查詢&lt;/h3>
&lt;p>Prometheus 沒有原生 HA — 標準做法是跑兩個 instance scrape 同一批 target，靠下游去重。但兩個 instance 各自獨立儲存，查詢只打一個；instance 故障切換時會有短暫資料缺口。&lt;/p>
&lt;p>跨叢集查詢更困難。Prometheus federation 可以做簡單的 metric 聚合，但 federation 本身是 pull-based scrape — federation target 太多或 series 太大時，federation Prometheus 自己也會 OOM。&lt;/p>
&lt;h2 id="解法m3-平台">解法：M3 平台&lt;/h2>
&lt;p>Uber 開發了 M3 — 一個 Prometheus-compatible 的分散式 metrics 平台，由三個核心元件組成。&lt;/p>
&lt;h3 id="m3db分散式-time-series-storage">M3DB：分散式 time series storage&lt;/h3>
&lt;p>M3DB 是分散式 TSDB，資料按 namespace 和 shard 分布在多個節點。每個 namespace 可以有不同的 retention 和 resolution — 例如 &lt;code>realtime&lt;/code> namespace 保留 2 天全精度，&lt;code>aggregated_1m&lt;/code> namespace 保留 90 天 1 分鐘精度。這解決了 retention tiering 的問題。&lt;/p></description><content:encoded><![CDATA[<p>Uber 的 M3 案例揭露了 metrics 系統從「每個團隊各跑一套 Prometheus」到「全公司共用的 metrics 平台」的轉折點。轉折的核心判斷是：當 active series 總量超過單機 Prometheus 的記憶體上限、且多個團隊需要跨叢集查詢時，自建平台層的成本低於持續橫向複製 Prometheus 實例的成本。</p>
<h2 id="業務背景">業務背景</h2>
<p>Uber 的服務觀測涵蓋行程追蹤、即時定價、ETA 計算、司機定位、支付結算與推播通知。每個微服務都暴露 Prometheus-compatible metrics，隨著服務數量成長到數千個，寫入速率達到每秒數十億 data points。</p>
<p>早期每個團隊各自部署 Prometheus，各管自己的 retention、scrape config 與 alerting rules。規模小時這個模式運作良好 — 每個 Prometheus 實例只需要處理自己團隊的幾萬到幾十萬 series。但當組織成長到數百個團隊、數千個服務時，散落的 Prometheus 實例帶來三個問題。</p>
<h2 id="技術挑戰">技術挑戰</h2>
<h3 id="單機記憶體天花板">單機記憶體天花板</h3>
<p>Prometheus 的 TSDB 把 active series 放在記憶體的 head block，每個 series 消耗約 3-4 KB（詳見 <a href="/blog/backend/04-observability/vendors/prometheus/capacity-failure-modes/" data-link-title="Prometheus 容量規劃與故障模式" data-link-desc="說明 Prometheus 單機容量邊界、cardinality 與 retention 的資源模型、常見故障模式與判讀方式">Prometheus 容量規劃</a>）。當單一 Prometheus 實例需要 scrape 的 series 超過 1000 萬時，head block 就需要 40+ GB 記憶體。加上 query execution 跟 WAL replay 的暫時開銷，單機很容易 OOM。</p>
<p>團隊的第一反應是按服務拆分多個 Prometheus 實例，但這讓跨服務查詢變得困難 — 要看一條 request 從 gateway 到 payment 的 latency 分布，需要分別查三個 Prometheus 再手動關聯。</p>
<h3 id="retention-與長期趨勢">Retention 與長期趨勢</h3>
<p>Prometheus 預設 retention 15 天。容量規劃與季度趨勢分析需要 90 天甚至 1 年的歷史資料。把 Prometheus retention 拉長到 90 天，disk 跟 memory 需求同步上升，而且 compaction 效率在資料量大時會下降。</p>
<p>團隊需要的是分層 retention — 近期資料保留全精度、歷史資料做 downsampling 後保留更久。Prometheus 原生不支援 downsampling。</p>
<h3 id="高可用與跨叢集查詢">高可用與跨叢集查詢</h3>
<p>Prometheus 沒有原生 HA — 標準做法是跑兩個 instance scrape 同一批 target，靠下游去重。但兩個 instance 各自獨立儲存，查詢只打一個；instance 故障切換時會有短暫資料缺口。</p>
<p>跨叢集查詢更困難。Prometheus federation 可以做簡單的 metric 聚合，但 federation 本身是 pull-based scrape — federation target 太多或 series 太大時，federation Prometheus 自己也會 OOM。</p>
<h2 id="解法m3-平台">解法：M3 平台</h2>
<p>Uber 開發了 M3 — 一個 Prometheus-compatible 的分散式 metrics 平台，由三個核心元件組成。</p>
<h3 id="m3db分散式-time-series-storage">M3DB：分散式 time series storage</h3>
<p>M3DB 是分散式 TSDB，資料按 namespace 和 shard 分布在多個節點。每個 namespace 可以有不同的 retention 和 resolution — 例如 <code>realtime</code> namespace 保留 2 天全精度，<code>aggregated_1m</code> namespace 保留 90 天 1 分鐘精度。這解決了 retention tiering 的問題。</p>
<p>M3DB 的記憶體模型跟 Prometheus 不同 — 近期資料在記憶體，冷資料在 disk，不像 Prometheus 把所有 active series 都放 head block。這讓它能處理遠超單機 Prometheus 的 series 數量。</p>
<h3 id="m3-coordinator統一查詢入口">M3 Coordinator：統一查詢入口</h3>
<p>M3 Coordinator 接收 PromQL 查詢，轉譯後分發到 M3DB 節點，聚合結果後返回。對 Grafana 和 alerting rules 來說，M3 Coordinator 的 API 跟 Prometheus 完全相容 — 不需要改 dashboard 或 alert config。</p>
<h3 id="m3-aggregator寫入路徑聚合">M3 Aggregator：寫入路徑聚合</h3>
<p>高 cardinality 的原始 series 在寫入 M3DB 前先經過 M3 Aggregator 做 pre-aggregation — 例如把每秒的 request count 聚合成每分鐘，再寫入長期 namespace。這控制了長期儲存的資料量跟成本。</p>
<h2 id="取捨">取捨</h2>
<table>
  <thead>
      <tr>
          <th>面向</th>
          <th>Prometheus standalone</th>
          <th>M3 平台</th>
          <th>Mimir / Thanos（替代）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>部署複雜度</td>
          <td>低（單一 binary）</td>
          <td>高（M3DB + Coordinator + Aggregator）</td>
          <td>中到高</td>
      </tr>
      <tr>
          <td>單機 series 上限</td>
          <td>~500 萬-1000 萬</td>
          <td>不適用（分散式）</td>
          <td>不適用</td>
      </tr>
      <tr>
          <td>Retention tiering</td>
          <td>無</td>
          <td>原生支援</td>
          <td>Thanos compactor / Mimir 支援</td>
      </tr>
      <tr>
          <td>PromQL 相容</td>
          <td>原生</td>
          <td>相容</td>
          <td>相容</td>
      </tr>
      <tr>
          <td>社群活躍度</td>
          <td>高（CNCF）</td>
          <td>低（Uber 主導、2023 後維護縮減）</td>
          <td>高（Grafana Labs / 社群）</td>
      </tr>
      <tr>
          <td>適用規模</td>
          <td>單團隊到中型組織</td>
          <td>大型組織（數十億 series）</td>
          <td>中型到大型</td>
      </tr>
  </tbody>
</table>
<p>M3 的最大風險是社群活躍度 — Uber 自 2023 年後縮減了 M3 的開發投入，Grafana Mimir 成為更活躍的替代。新專案選型時，Mimir 跟 Thanos 的社群支援度跟 Grafana 生態整合度都優於 M3。M3 的價值在於它驗證了「分散式 TSDB + 寫入路徑聚合 + retention tiering」這組設計模式，這組模式在 Mimir 跟 Thanos 裡以不同形式被採用。</p>
<h2 id="回寫教材的連結">回寫教材的連結</h2>
<ul>
<li><a href="/blog/backend/04-observability/metrics-basics/" data-link-title="4.2 metrics 與 SLI/SLO" data-link-desc="整理 counter、gauge、histogram 與服務健康指標">4.2 Metrics Basics</a>：active series、cardinality 與 recording rules 的基礎模型，M3 的 pre-aggregation 對應 recording rules 的平台化版本。</li>
<li><a href="/blog/backend/04-observability/telemetry-pipeline/" data-link-title="4.11 Telemetry Pipeline 架構" data-link-desc="把 log / metric / trace 的 agent → collector → ingest → storage → query 分層治理">4.11 Telemetry Pipeline</a>：M3 的 Aggregator 是 pipeline 中 processing 層的實例。</li>
<li><a href="/blog/backend/04-observability/vendors/prometheus/remote-write-long-term-storage/" data-link-title="Remote Write 與長期儲存整合" data-link-desc="說明 Prometheus remote write 的配置、三家長期儲存後端比較（Mimir / Thanos / Cortex）、故障模式與容量規劃">Prometheus Remote Write 與長期儲存</a>：M3 是 remote write 目標之一，跟 Mimir / Thanos / Cortex 的比較在該文。</li>
<li><a href="/blog/backend/04-observability/cardinality-cost-governance/" data-link-title="4.7 Cardinality 治理與成本邊界" data-link-desc="把 metric / log / trace 的 cardinality 與成本作為平台一級治理議題">4.7 Cardinality 治理</a>：M3 的 per-namespace cardinality limit 是治理機制的生產實例。</li>
</ul>
<h2 id="判讀徵兆">判讀徵兆</h2>
<p>讀者在自己的系統看到以下訊號時，應該回讀本案例：</p>
<ul>
<li>單一 Prometheus 實例 memory 接近機器上限，開始 OOM restart</li>
<li>多個 Prometheus 實例各自 scrape，跨服務查詢需要手動關聯</li>
<li>Retention 15 天不夠做季度趨勢分析，但拉長 retention 資源撐不住</li>
<li>團隊開始問「我們的 metrics 總共有多少 series、誰佔最多」但沒有統一的 cardinality 觀測</li>
<li>Grafana federation dashboard 查詢越來越慢或經常 timeout</li>
</ul>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://www.uber.com/en-GB/blog/m3/">M3: Uber&rsquo;s Open Source, Large-scale Metrics Platform for Prometheus</a></li>
</ul>
]]></content:encoded></item></channel></rss>