<?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>Health on Tarragon</title><link>https://tarrragon.github.io/blog/tags/health/</link><description>Recent content in Health on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Sat, 20 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/health/index.xml" rel="self" type="application/rss+xml"/><item><title>DevOps Dashboard 設計</title><link>https://tarrragon.github.io/blog/monitoring/04-collector/dashboard-devops/</link><pubDate>Sat, 20 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/monitoring/04-collector/dashboard-devops/</guid><description>&lt;p>DevOps dashboard 的消費者是維護 collector 的人 — 可能是開發者自己、可能是開源使用者的運維人員。這個 dashboard 不看被監控 app 的業務邏輯，只看 collector 這個基礎設施本身是否健康、各 SDK 實例是否正常回報。&lt;/p>
&lt;p>使用模式是混合型：平時靠告警被動通知，收到通知後到 dashboard 查看細節。日常監控視圖提供「一眼確認系統正常」的能力，告警觸發視圖提供「出事了去哪裡查」的排障路徑。&lt;/p>
&lt;h2 id="日常監控視圖">日常監控視圖&lt;/h2>
&lt;h3 id="服務狀態卡">服務狀態卡&lt;/h3>
&lt;p>一個狀態卡顯示 collector 的存活狀態和各 SDK 實例的最後心跳時間。狀態卡的設計是「綠色代表正常、紅色代表異常」的二元判斷 — 不需要使用者解讀數字。&lt;/p>
&lt;p>Collector 存活的判斷依據是 health endpoint 回應。各 SDK 實例的狀態依據是最後一次 &lt;code>sdk.heartbeat&lt;/code> 事件的時間 — 超過設定的逾時閾值（預設 10 分鐘）標為離線。&lt;/p>
&lt;p>需要的事件：&lt;code>collector.health.check&lt;/code>（collector 自身定期產生）、&lt;code>sdk.heartbeat&lt;/code>（各 SDK 定期送出）、&lt;code>sdk.init&lt;/code>（SDK 啟動時送出、標記上線）。&lt;/p>
&lt;h3 id="吞吐量曲線">吞吐量曲線&lt;/h3>
&lt;p>折線圖顯示過去 24 小時每分鐘收到的事件數量。多個 SDK 實例用不同顏色區分。吞吐量的正常範圍由歷史資料建立基線 — 突然下降代表某個 SDK 停止送資料，突然上升代表 error storm 或重複送出。&lt;/p>
&lt;p>需要的事件：&lt;code>collector.ingestion.count&lt;/code>（collector 每分鐘記錄收到的事件數，按 source.app 分群）。&lt;/p>
&lt;h3 id="儲存用量">儲存用量&lt;/h3>
&lt;p>磁碟使用率的趨勢圖 + 保留策略的執行狀態。開發者需要知道「磁碟什麼時候會滿」和「purge 有沒有正常跑」。&lt;/p>
&lt;p>需要的事件：&lt;code>collector.storage.disk_usage&lt;/code>（定期取樣、metric 類型）、&lt;code>collector.storage.purge.completed&lt;/code>（每次 purge 完成時記錄清了多少空間）。&lt;/p>
&lt;h3 id="sdk-連線列表">SDK 連線列表&lt;/h3>
&lt;p>表格列出所有已知的 SDK 實例，每行顯示：app 名稱、版本、平台、最後回報時間、最後一次 init 時間。表格按「最後回報時間」排序 — 最久沒回報的在最上面，方便發現異常。&lt;/p>
&lt;p>需要的事件：&lt;code>sdk.init&lt;/code>（帶 source 完整資訊）、&lt;code>sdk.heartbeat&lt;/code>（定期更新最後回報時間）。&lt;/p>
&lt;p>Heartbeat 的觸發機制是 flush timer 的副作用 — SDK 的 flush timer 觸發時，如果 buffer 為空且距上次 heartbeat 超過設定間隔（預設 5 分鐘），自動注入一筆 &lt;code>sdk.heartbeat&lt;/code> 事件後送出。不需要獨立的 heartbeat timer。App idle 時 heartbeat 仍會送出，dashboard 的 SDK 連線列表因此能偵測 SDK 是否仍存活。&lt;/p>
&lt;h2 id="告警觸發視圖">告警觸發視圖&lt;/h2>
&lt;p>告警由 rule engine 觸發，觸發後開發者進入 dashboard 查看細節。每種告警條件對應一個排障路徑。&lt;/p>
&lt;h3 id="health-check-失敗">Health check 失敗&lt;/h3>
&lt;p>Collector 的 health endpoint 連續 N 次回應失敗（由外部 uptime check 偵測、如 cron + curl）。&lt;/p>
&lt;p>進入 dashboard 後看：最後一次 &lt;code>collector.health.check&lt;/code> 的時間和結果、collector 的 stderr log（systemd journal）、process 是否存活。如果 collector 已經掛了，dashboard 本身也不可達 — 這時的排障路徑是 SSH 到主機查 systemd 狀態。&lt;/p>
&lt;h3 id="sdk-停止回報">SDK 停止回報&lt;/h3>
&lt;p>某個 SDK 實例超過逾時閾值沒有送 &lt;code>sdk.heartbeat&lt;/code>。可能原因：被監控 app 當掉、網路斷開、SDK 初始化失敗。&lt;/p>
&lt;p>進入 dashboard 後看：該 SDK 的最後事件（什麼類型、什麼時間）、最後 &lt;code>sdk.init&lt;/code> 的 source 資訊（版本、平台）、同時段其他 SDK 是否正常（區分「單一 SDK 問題」和「collector 端問題」）。&lt;/p>
&lt;h3 id="磁碟用量超過閾值">磁碟用量超過閾值&lt;/h3>
&lt;p>&lt;code>collector.storage.disk_usage&lt;/code> 超過 80%。&lt;/p>
&lt;p>進入 dashboard 後看：各 backend 的空間佔比（SQLite DB 大小 + 匯出檔大小）、最近一次 purge 的執行時間和清理量、保留策略的設定值。如果 purge 正常執行但空間仍不足，代表事件產生速度超過清理速度 — 需要調整保留策略或擴容磁碟。&lt;/p>
&lt;h3 id="事件吞吐量異常下降">事件吞吐量異常下降&lt;/h3>
&lt;p>每分鐘事件數從正常基線突然下降超過 50%。&lt;/p></description><content:encoded><![CDATA[<p>DevOps dashboard 的消費者是維護 collector 的人 — 可能是開發者自己、可能是開源使用者的運維人員。這個 dashboard 不看被監控 app 的業務邏輯，只看 collector 這個基礎設施本身是否健康、各 SDK 實例是否正常回報。</p>
<p>使用模式是混合型：平時靠告警被動通知，收到通知後到 dashboard 查看細節。日常監控視圖提供「一眼確認系統正常」的能力，告警觸發視圖提供「出事了去哪裡查」的排障路徑。</p>
<h2 id="日常監控視圖">日常監控視圖</h2>
<h3 id="服務狀態卡">服務狀態卡</h3>
<p>一個狀態卡顯示 collector 的存活狀態和各 SDK 實例的最後心跳時間。狀態卡的設計是「綠色代表正常、紅色代表異常」的二元判斷 — 不需要使用者解讀數字。</p>
<p>Collector 存活的判斷依據是 health endpoint 回應。各 SDK 實例的狀態依據是最後一次 <code>sdk.heartbeat</code> 事件的時間 — 超過設定的逾時閾值（預設 10 分鐘）標為離線。</p>
<p>需要的事件：<code>collector.health.check</code>（collector 自身定期產生）、<code>sdk.heartbeat</code>（各 SDK 定期送出）、<code>sdk.init</code>（SDK 啟動時送出、標記上線）。</p>
<h3 id="吞吐量曲線">吞吐量曲線</h3>
<p>折線圖顯示過去 24 小時每分鐘收到的事件數量。多個 SDK 實例用不同顏色區分。吞吐量的正常範圍由歷史資料建立基線 — 突然下降代表某個 SDK 停止送資料，突然上升代表 error storm 或重複送出。</p>
<p>需要的事件：<code>collector.ingestion.count</code>（collector 每分鐘記錄收到的事件數，按 source.app 分群）。</p>
<h3 id="儲存用量">儲存用量</h3>
<p>磁碟使用率的趨勢圖 + 保留策略的執行狀態。開發者需要知道「磁碟什麼時候會滿」和「purge 有沒有正常跑」。</p>
<p>需要的事件：<code>collector.storage.disk_usage</code>（定期取樣、metric 類型）、<code>collector.storage.purge.completed</code>（每次 purge 完成時記錄清了多少空間）。</p>
<h3 id="sdk-連線列表">SDK 連線列表</h3>
<p>表格列出所有已知的 SDK 實例，每行顯示：app 名稱、版本、平台、最後回報時間、最後一次 init 時間。表格按「最後回報時間」排序 — 最久沒回報的在最上面，方便發現異常。</p>
<p>需要的事件：<code>sdk.init</code>（帶 source 完整資訊）、<code>sdk.heartbeat</code>（定期更新最後回報時間）。</p>
<p>Heartbeat 的觸發機制是 flush timer 的副作用 — SDK 的 flush timer 觸發時，如果 buffer 為空且距上次 heartbeat 超過設定間隔（預設 5 分鐘），自動注入一筆 <code>sdk.heartbeat</code> 事件後送出。不需要獨立的 heartbeat timer。App idle 時 heartbeat 仍會送出，dashboard 的 SDK 連線列表因此能偵測 SDK 是否仍存活。</p>
<h2 id="告警觸發視圖">告警觸發視圖</h2>
<p>告警由 rule engine 觸發，觸發後開發者進入 dashboard 查看細節。每種告警條件對應一個排障路徑。</p>
<h3 id="health-check-失敗">Health check 失敗</h3>
<p>Collector 的 health endpoint 連續 N 次回應失敗（由外部 uptime check 偵測、如 cron + curl）。</p>
<p>進入 dashboard 後看：最後一次 <code>collector.health.check</code> 的時間和結果、collector 的 stderr log（systemd journal）、process 是否存活。如果 collector 已經掛了，dashboard 本身也不可達 — 這時的排障路徑是 SSH 到主機查 systemd 狀態。</p>
<h3 id="sdk-停止回報">SDK 停止回報</h3>
<p>某個 SDK 實例超過逾時閾值沒有送 <code>sdk.heartbeat</code>。可能原因：被監控 app 當掉、網路斷開、SDK 初始化失敗。</p>
<p>進入 dashboard 後看：該 SDK 的最後事件（什麼類型、什麼時間）、最後 <code>sdk.init</code> 的 source 資訊（版本、平台）、同時段其他 SDK 是否正常（區分「單一 SDK 問題」和「collector 端問題」）。</p>
<h3 id="磁碟用量超過閾值">磁碟用量超過閾值</h3>
<p><code>collector.storage.disk_usage</code> 超過 80%。</p>
<p>進入 dashboard 後看：各 backend 的空間佔比（SQLite DB 大小 + 匯出檔大小）、最近一次 purge 的執行時間和清理量、保留策略的設定值。如果 purge 正常執行但空間仍不足，代表事件產生速度超過清理速度 — 需要調整保留策略或擴容磁碟。</p>
<h3 id="事件吞吐量異常下降">事件吞吐量異常下降</h3>
<p>每分鐘事件數從正常基線突然下降超過 50%。</p>
<p>進入 dashboard 後看：吞吐量曲線標注「下降起始時間」、SDK 連線列表確認哪些 SDK 在該時間點後停止回報、collector 的 ingestion error log。</p>
<h2 id="需要的事件總表">需要的事件總表</h2>
<table>
  <thead>
      <tr>
          <th>事件名稱</th>
          <th>類型</th>
          <th>產生者</th>
          <th>用途</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>collector.health.check</td>
          <td>lifecycle</td>
          <td>Collector</td>
          <td>服務狀態卡</td>
      </tr>
      <tr>
          <td>collector.started</td>
          <td>lifecycle</td>
          <td>Collector</td>
          <td>部署追蹤</td>
      </tr>
      <tr>
          <td>collector.shutdown</td>
          <td>lifecycle</td>
          <td>Collector</td>
          <td>異常關閉偵測</td>
      </tr>
      <tr>
          <td>collector.ingestion.count</td>
          <td>metric</td>
          <td>Collector</td>
          <td>吞吐量曲線</td>
      </tr>
      <tr>
          <td>collector.storage.disk_usage</td>
          <td>metric</td>
          <td>Collector</td>
          <td>儲存用量圖</td>
      </tr>
      <tr>
          <td>collector.storage.purge.completed</td>
          <td>lifecycle</td>
          <td>Collector</td>
          <td>purge 執行記錄</td>
      </tr>
      <tr>
          <td>sdk.heartbeat</td>
          <td>lifecycle</td>
          <td>SDK</td>
          <td>連線列表、存活判斷</td>
      </tr>
      <tr>
          <td>sdk.init</td>
          <td>lifecycle</td>
          <td>SDK</td>
          <td>版本/平台資訊、上線記錄</td>
      </tr>
      <tr>
          <td>deployment.started</td>
          <td>lifecycle</td>
          <td>CI/CD hook</td>
          <td>部署追蹤</td>
      </tr>
      <tr>
          <td>deployment.completed</td>
          <td>lifecycle</td>
          <td>CI/CD hook</td>
          <td>部署追蹤</td>
      </tr>
      <tr>
          <td>rule.matched</td>
          <td>event</td>
          <td>Collector</td>
          <td>alert 歷史</td>
      </tr>
  </tbody>
</table>
<p>這些事件是 collector 自身的營運事件，和被監控 app 的事件走同一個 Storage interface 儲存。Collector 同時是事件的生產者和消費者 — <code>collector.ingestion.count</code> 由 collector 自己產生、自己儲存、自己在 dashboard 顯示。</p>
<p><code>deployment.started</code> / <code>deployment.completed</code> 這兩個 lifecycle event 在 server-side 部署流程中對應 <a href="/blog/backend/05-deployment-platform/deployment-rollout-drain-rollback/" data-link-title="5.8 Deployment Rollout with Drain and Rollback（實作示範）" data-link-desc="以 checkout service 示範部署切換如何交付 canary evidence、drain signal、release gate 與 incident decision log。">Backend 5.8 Deployment Rollout</a> 的 evidence package——rollout 的每一批切換需要可判讀的部署事件作為證據。自架 collector 場景的部署追蹤規模遠小於 production server-side rollout，但 event schema 設計（timestamp / version / environment / result）可以跟 server-side 的 evidence 欄位對齊，讓未來規模成長時 event 格式不用重新設計。</p>
<h2 id="自動恢復設計">自動恢復設計</h2>
<p>自用工具場景下「凌晨三點 collector 掛了」的處理策略是自動恢復，不需要人介入。</p>
<table>
  <thead>
      <tr>
          <th>機制</th>
          <th>做法</th>
          <th>恢復時間</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>systemd watchdog</td>
          <td><code>WatchdogSec=30s</code>，collector 定期寫 watchdog notify</td>
          <td>30 秒內重啟</td>
      </tr>
      <tr>
          <td>Restart policy</td>
          <td><code>Restart=on-failure</code>、<code>RestartSec=5s</code></td>
          <td>5 秒後自動重啟</td>
      </tr>
      <tr>
          <td>Health endpoint</td>
          <td><code>/health</code> 回應 200 + 最後寫入時間</td>
          <td>外部 check 偵測</td>
      </tr>
      <tr>
          <td>啟動自檢</td>
          <td>collector 啟動時檢查 storage 完整性、重建索引</td>
          <td>啟動時自動修復</td>
      </tr>
  </tbody>
</table>
<p>自動恢復後 collector 送出 <code>collector.started</code> 事件，dashboard 的服務狀態卡從紅轉綠。如果連續重啟（10 分鐘內重啟 3 次以上），systemd 的 <code>StartLimitBurst</code> 阻止無限重啟、改為發送告警通知人工介入。</p>
<h2 id="存取控制">存取控制</h2>
<p>Day-one 的 dashboard 預設無認證 — 同區網內的任何裝置都能打開 dashboard URL。這是同區網信任模型的設計選擇，和 collector 的 HTTP endpoint 無認證一致。</p>
<h3 id="風險告知">風險告知</h3>
<p>無認證的 dashboard 暴露以下資訊給同區網的所有裝置：</p>
<ul>
<li><strong>DevOps dashboard</strong>：SDK 版本、平台、IP、collector 的磁碟用量</li>
<li><strong>Developer dashboard</strong>：error stack trace（可能包含檔案路徑和程式碼片段）、session 回放（使用者操作序列）</li>
<li><strong>中台 dashboard</strong>：行為事件明細、funnel 轉換率</li>
</ul>
<p>家用 LAN 的場景下，家裡的其他裝置（IoT、家人的電腦）也能存取這些資訊。</p>
<h3 id="最小防護">最小防護</h3>
<p>Go 的 <code>net/http</code> middleware 可以用幾行程式碼加 basic auth：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kd">func</span> <span class="nf">basicAuth</span><span class="p">(</span><span class="nx">next</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span><span class="p">,</span> <span class="nx">user</span><span class="p">,</span> <span class="nx">pass</span> <span class="kt">string</span><span class="p">)</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Handler</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">    <span class="k">return</span> <span class="nx">http</span><span class="p">.</span><span class="nf">HandlerFunc</span><span class="p">(</span><span class="kd">func</span><span class="p">(</span><span class="nx">w</span> <span class="nx">http</span><span class="p">.</span><span class="nx">ResponseWriter</span><span class="p">,</span> <span class="nx">r</span> <span class="o">*</span><span class="nx">http</span><span class="p">.</span><span class="nx">Request</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">        <span class="nx">u</span><span class="p">,</span> <span class="nx">p</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">r</span><span class="p">.</span><span class="nf">BasicAuth</span><span class="p">()</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">        <span class="k">if</span> <span class="p">!</span><span class="nx">ok</span> <span class="o">||</span> <span class="nx">u</span> <span class="o">!=</span> <span class="nx">user</span> <span class="o">||</span> <span class="nx">p</span> <span class="o">!=</span> <span class="nx">pass</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">            <span class="nx">w</span><span class="p">.</span><span class="nf">Header</span><span class="p">().</span><span class="nf">Set</span><span class="p">(</span><span class="s">&#34;WWW-Authenticate&#34;</span><span class="p">,</span> <span class="s">`Basic realm=&#34;monitor&#34;`</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">            <span class="nx">http</span><span class="p">.</span><span class="nf">Error</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="s">&#34;Unauthorized&#34;</span><span class="p">,</span> <span class="mi">401</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">            <span class="k">return</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">        <span class="nx">next</span><span class="p">.</span><span class="nf">ServeHTTP</span><span class="p">(</span><span class="nx">w</span><span class="p">,</span> <span class="nx">r</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="p">})</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>帳密在 collector 的配置檔設定。Day-one 可選（不設就不啟用），但配置檔中應有 commented-out 的範例讓使用者知道這個選項存在。</p>
<h3 id="tripwire">Tripwire</h3>
<p>Collector 暴露到公網或跨網路存取時，dashboard 的認證從可選變成必要。公網上的無認證 dashboard 等於公開了 error stack trace 和行為資料。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>Developer dashboard 設計 → <a href="/blog/monitoring/04-collector/dashboard-developer/" data-link-title="Developer Dashboard 設計" data-link-desc="Bug 在哪、多嚴重、怎麼重現 — Error 列表和趨勢的日常監控、Session 回放和 Stack trace 的深入 debug">Developer Dashboard 設計</a></li>
<li>中台 dashboard 設計 → <a href="/blog/monitoring/04-collector/dashboard-business/" data-link-title="中台 Dashboard 設計" data-link-desc="使用者怎麼用、在哪流失、怎麼讓他們回來 — 營運和行銷的日常指標監控與深入分析視圖，全部需要 PostgreSQL 層">中台 Dashboard 設計</a></li>
<li>Rule engine 的告警設計 → <a href="/blog/monitoring/04-collector/rule-engine/" data-link-title="Rule engine 設計" data-link-desc="條件 → 動作 → 模板的三段式規則結構 — 讓 collector 從被動儲存變成主動回應">Rule engine 設計</a></li>
<li>Collector 自我監控的 bootstrapping 問題 → <a href="/blog/monitoring/04-collector/scaling-evolution/" data-link-title="規模演進" data-link-desc="可插拔 Storage Backend 架構 — SQLite 預設、PostgreSQL 觸發切換、時間序列 DB 長期演進">規模演進</a></li>
<li>服務探活與自動恢復 → <a href="/blog/devops/04-service-health/" data-link-title="模組四：服務探活與自動恢復" data-link-desc="服務掛了怎麼自動發現和恢復 — health check 設計、liveness vs readiness、systemd watchdog、process supervisor">DevOps 服務探活</a></li>
</ul>
]]></content:encoded></item></channel></rss>