<?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>Reliability on Tarragon</title><link>https://tarrragon.github.io/blog/tags/reliability/</link><description>Recent content in Reliability on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Wed, 24 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/reliability/index.xml" rel="self" type="application/rss+xml"/><item><title>Chaos Mesh：Workflow、Scope Control 與 Steady State Probe</title><link>https://tarrragon.github.io/blog/backend/06-reliability/vendors/chaos-mesh/workflow-experiment-scope-and-steady-state-probe/</link><pubDate>Tue, 23 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/vendors/chaos-mesh/workflow-experiment-scope-and-steady-state-probe/</guid><description>&lt;h2 id="問題情境">問題情境&lt;/h2>
&lt;p>單一 ChaosExperiment（PodChaos pod-kill、NetworkChaos delay）只能驗證一個故障面向。真實的可靠性驗證需要多步驟編排：先注入依賴延遲，觀察 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/steady-state/" data-link-title="Steady State" data-link-desc="說明可靠性實驗與事故恢復如何定義系統應維持的可接受狀態">steady state&lt;/a> 是否維持，再注入節點失效，最後驗證恢復路徑。Chaos Workflow 提供這個編排能力，把多個 fault injection 與 health check 組成可重播的驗證流程。&lt;/p>
&lt;p>experiment scope 的精準控制同樣關鍵。selector 選到 production 全部 pod 的 chaos experiment 會變成真實事故。scope control 的責任是讓 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/blast-radius/" data-link-title="Blast Radius" data-link-desc="說明事故影響面如何估算與隔離">blast radius&lt;/a> 從最小範圍開始，逐步放大，每一步都有停止條件。&lt;/p>
&lt;h2 id="chaos-workflow-設計">Chaos Workflow 設計&lt;/h2>
&lt;p>Chaos Workflow 是多個 ChaosExperiment 與 StatusCheck 組成的 DAG（有向無環圖），用 YAML 定義步驟順序與分支條件。&lt;/p>
&lt;h3 id="步驟類型">步驟類型&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>類型&lt;/th>
 &lt;th>責任&lt;/th>
 &lt;th>適用場景&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Serial&lt;/td>
 &lt;td>順序執行，前一步完成才進下一步&lt;/td>
 &lt;td>依賴故障 → 觀察 → 節點故障&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Parallel&lt;/td>
 &lt;td>平行執行多個注入&lt;/td>
 &lt;td>同時打多個依賴驗證交叉影響&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Suspend&lt;/td>
 &lt;td>暫停等待人工確認後再繼續&lt;/td>
 &lt;td>高風險步驟前的 approval gate&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>StatusCheck&lt;/td>
 &lt;td>對 HTTP / gRPC / custom script 做 probe&lt;/td>
 &lt;td>注入前後的 steady state 驗證&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>StatusCheck 是 workflow 的核心控制面。它在故障注入前後對目標 endpoint 做 health check，pass/fail 決定 workflow 是否繼續。StatusCheck 的 success condition 對應 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22 steady state definition&lt;/a> 的穩態門檻：success rate、latency、queue lag 都能作為 probe 判準。&lt;/p>
&lt;p>典型 workflow 編排：NetworkChaos(delay 200ms) → StatusCheck(api-latency-ok) → PodChaos(pod-kill) → StatusCheck(recovery-within-30s)。第一個 StatusCheck 驗證延遲注入後服務仍可用；第二個 StatusCheck 驗證節點失效後恢復時間可接受。&lt;/p>
&lt;h3 id="suspend-的使用時機">Suspend 的使用時機&lt;/h3>
&lt;p>Suspend 步驟適合放在 blast radius 擴大之前。例如先在 canary namespace 跑完 chaos + StatusCheck，通過後 Suspend 等待值班工程師確認，再擴大到 production namespace。Suspend 讓自動化 workflow 在關鍵決策點保留人工判斷。&lt;/p>
&lt;h2 id="experiment-scope-control">Experiment Scope Control&lt;/h2>
&lt;p>Scope control 的責任是讓每個 ChaosExperiment 的影響面可預測、可限制。Chaos Mesh 用 selector + mode 兩層控制。&lt;/p>
&lt;h3 id="selector">Selector&lt;/h3>
&lt;p>Selector 決定哪些 pod 是實驗目標。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Selector 類型&lt;/th>
 &lt;th>作用&lt;/th>
 &lt;th>範例&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>namespace&lt;/td>
 &lt;td>限制在特定 namespace&lt;/td>
 &lt;td>&lt;code>namespaces: [canary]&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>labelSelector&lt;/td>
 &lt;td>按 label 篩選&lt;/td>
 &lt;td>&lt;code>app: checkout, tier: backend&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>annotationSelector&lt;/td>
 &lt;td>按 annotation 篩選&lt;/td>
 &lt;td>&lt;code>chaos-eligible: &amp;quot;true&amp;quot;&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>fieldSelector&lt;/td>
 &lt;td>按 field 篩選（如 node name）&lt;/td>
 &lt;td>&lt;code>spec.nodeName: node-3&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>podPhase&lt;/td>
 &lt;td>只選特定狀態的 pod&lt;/td>
 &lt;td>&lt;code>Running&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>最安全的起點是 namespace + labelSelector + annotation 三層組合：只在 canary namespace、只選帶 &lt;code>chaos-eligible&lt;/code> annotation 的特定服務 pod。annotation-based opt-in 讓團隊明確標記哪些 pod 可以被 chaos 觸及。&lt;/p></description><content:encoded><![CDATA[<h2 id="問題情境">問題情境</h2>
<p>單一 ChaosExperiment（PodChaos pod-kill、NetworkChaos delay）只能驗證一個故障面向。真實的可靠性驗證需要多步驟編排：先注入依賴延遲，觀察 <a href="/blog/backend/knowledge-cards/steady-state/" data-link-title="Steady State" data-link-desc="說明可靠性實驗與事故恢復如何定義系統應維持的可接受狀態">steady state</a> 是否維持，再注入節點失效，最後驗證恢復路徑。Chaos Workflow 提供這個編排能力，把多個 fault injection 與 health check 組成可重播的驗證流程。</p>
<p>experiment scope 的精準控制同樣關鍵。selector 選到 production 全部 pod 的 chaos experiment 會變成真實事故。scope control 的責任是讓 <a href="/blog/backend/knowledge-cards/blast-radius/" data-link-title="Blast Radius" data-link-desc="說明事故影響面如何估算與隔離">blast radius</a> 從最小範圍開始，逐步放大，每一步都有停止條件。</p>
<h2 id="chaos-workflow-設計">Chaos Workflow 設計</h2>
<p>Chaos Workflow 是多個 ChaosExperiment 與 StatusCheck 組成的 DAG（有向無環圖），用 YAML 定義步驟順序與分支條件。</p>
<h3 id="步驟類型">步驟類型</h3>
<table>
  <thead>
      <tr>
          <th>類型</th>
          <th>責任</th>
          <th>適用場景</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Serial</td>
          <td>順序執行，前一步完成才進下一步</td>
          <td>依賴故障 → 觀察 → 節點故障</td>
      </tr>
      <tr>
          <td>Parallel</td>
          <td>平行執行多個注入</td>
          <td>同時打多個依賴驗證交叉影響</td>
      </tr>
      <tr>
          <td>Suspend</td>
          <td>暫停等待人工確認後再繼續</td>
          <td>高風險步驟前的 approval gate</td>
      </tr>
      <tr>
          <td>StatusCheck</td>
          <td>對 HTTP / gRPC / custom script 做 probe</td>
          <td>注入前後的 steady state 驗證</td>
      </tr>
  </tbody>
</table>
<p>StatusCheck 是 workflow 的核心控制面。它在故障注入前後對目標 endpoint 做 health check，pass/fail 決定 workflow 是否繼續。StatusCheck 的 success condition 對應 <a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22 steady state definition</a> 的穩態門檻：success rate、latency、queue lag 都能作為 probe 判準。</p>
<p>典型 workflow 編排：NetworkChaos(delay 200ms) → StatusCheck(api-latency-ok) → PodChaos(pod-kill) → StatusCheck(recovery-within-30s)。第一個 StatusCheck 驗證延遲注入後服務仍可用；第二個 StatusCheck 驗證節點失效後恢復時間可接受。</p>
<h3 id="suspend-的使用時機">Suspend 的使用時機</h3>
<p>Suspend 步驟適合放在 blast radius 擴大之前。例如先在 canary namespace 跑完 chaos + StatusCheck，通過後 Suspend 等待值班工程師確認，再擴大到 production namespace。Suspend 讓自動化 workflow 在關鍵決策點保留人工判斷。</p>
<h2 id="experiment-scope-control">Experiment Scope Control</h2>
<p>Scope control 的責任是讓每個 ChaosExperiment 的影響面可預測、可限制。Chaos Mesh 用 selector + mode 兩層控制。</p>
<h3 id="selector">Selector</h3>
<p>Selector 決定哪些 pod 是實驗目標。</p>
<table>
  <thead>
      <tr>
          <th>Selector 類型</th>
          <th>作用</th>
          <th>範例</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>namespace</td>
          <td>限制在特定 namespace</td>
          <td><code>namespaces: [canary]</code></td>
      </tr>
      <tr>
          <td>labelSelector</td>
          <td>按 label 篩選</td>
          <td><code>app: checkout, tier: backend</code></td>
      </tr>
      <tr>
          <td>annotationSelector</td>
          <td>按 annotation 篩選</td>
          <td><code>chaos-eligible: &quot;true&quot;</code></td>
      </tr>
      <tr>
          <td>fieldSelector</td>
          <td>按 field 篩選（如 node name）</td>
          <td><code>spec.nodeName: node-3</code></td>
      </tr>
      <tr>
          <td>podPhase</td>
          <td>只選特定狀態的 pod</td>
          <td><code>Running</code></td>
      </tr>
  </tbody>
</table>
<p>最安全的起點是 namespace + labelSelector + annotation 三層組合：只在 canary namespace、只選帶 <code>chaos-eligible</code> annotation 的特定服務 pod。annotation-based opt-in 讓團隊明確標記哪些 pod 可以被 chaos 觸及。</p>
<h3 id="mode">Mode</h3>
<p>Mode 決定在 selector 命中的 pod 中選多少個。</p>
<table>
  <thead>
      <tr>
          <th>Mode</th>
          <th>行為</th>
          <th>Blast radius</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>one</td>
          <td>隨機選 1 個</td>
          <td>最小</td>
      </tr>
      <tr>
          <td>fixed</td>
          <td>固定選 N 個</td>
          <td>可控</td>
      </tr>
      <tr>
          <td>fixed-percent</td>
          <td>選命中 pod 的 N%</td>
          <td>比例控制</td>
      </tr>
      <tr>
          <td>random-max-percent</td>
          <td>隨機選最多 N%</td>
          <td>有隨機性</td>
      </tr>
      <tr>
          <td>all</td>
          <td>選全部命中的 pod</td>
          <td>最大</td>
      </tr>
  </tbody>
</table>
<p>從 <code>mode: one</code> 開始驗證基礎假設，確認 StatusCheck 通過後，逐步升級到 <code>fixed-percent: 25</code> → <code>fixed-percent: 50</code>。每一步放大前檢查 steady state 是否仍維持，這個節奏對應 <a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 experiment safety boundary</a> 的漸進放大原則。</p>
<h3 id="duration-與-schedule">Duration 與 Schedule</h3>
<p>duration 控制單次故障注入持續多久，schedule 控制實驗重複頻率。duration 太短可能看不到系統完整的退化與恢復循環；太長則增加實際風險。初始建議：duration 設為 recovery SLA 的 2-3 倍（例如 RTO 30s 則 duration 設 60-90s），讓觀測窗涵蓋完整恢復。</p>
<h2 id="實作範例">實作範例</h2>
<p>一個完整的 Chaos Workflow：先對 checkout 服務注入網路延遲，驗證 API 仍可用，再 kill pod 驗證恢復。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nt">apiVersion</span><span class="p">:</span><span class="w"> </span><span class="l">chaos-mesh.org/v1alpha1</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">Workflow</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"></span><span class="nt">metadata</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">  </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">checkout-resilience-验证</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">  </span><span class="nt">namespace</span><span class="p">:</span><span class="w"> </span><span class="l">chaos-testing</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w"></span><span class="nt">spec</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">  </span><span class="nt">entry</span><span class="p">:</span><span class="w"> </span><span class="l">main</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">  </span><span class="nt">templates</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">main</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">      </span><span class="nt">templateType</span><span class="p">:</span><span class="w"> </span><span class="l">Serial</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">      </span><span class="nt">children</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">        </span>- <span class="l">network-delay</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">        </span>- <span class="l">check-api-health</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">        </span>- <span class="l">pod-kill</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">        </span>- <span class="l">check-recovery</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">network-delay</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">      </span><span class="nt">templateType</span><span class="p">:</span><span class="w"> </span><span class="l">NetworkChaos</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">      </span><span class="nt">networkChaos</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">        </span><span class="nt">action</span><span class="p">:</span><span class="w"> </span><span class="l">delay</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">        </span><span class="nt">delay</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">          </span><span class="nt">latency</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;200ms&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">        </span><span class="nt">selector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">          </span><span class="nt">namespaces</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">canary]</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">          </span><span class="nt">labelSelectors</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">            </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">checkout</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">        </span><span class="nt">mode</span><span class="p">:</span><span class="w"> </span><span class="l">one</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">        </span><span class="nt">duration</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;60s&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">check-api-health</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">      </span><span class="nt">templateType</span><span class="p">:</span><span class="w"> </span><span class="l">StatusCheck</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">      </span><span class="nt">statusCheck</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">        </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">HTTP</span><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">        </span><span class="nt">http</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">          </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;http://checkout.canary/health&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="w">          </span><span class="nt">criteria</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="w">            </span><span class="nt">statusCode</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;200&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="w">        </span><span class="nt">timeoutSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">30</span><span class="w">
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="w">        </span><span class="nt">failureThreshold</span><span class="p">:</span><span class="w"> </span><span class="m">3</span><span class="w">
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">pod-kill</span><span class="w">
</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="w">      </span><span class="nt">templateType</span><span class="p">:</span><span class="w"> </span><span class="l">PodChaos</span><span class="w">
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="w">      </span><span class="nt">podChaos</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">41</span><span class="cl"><span class="w">        </span><span class="nt">action</span><span class="p">:</span><span class="w"> </span><span class="l">pod-kill</span><span class="w">
</span></span></span><span class="line"><span class="ln">42</span><span class="cl"><span class="w">        </span><span class="nt">selector</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">43</span><span class="cl"><span class="w">          </span><span class="nt">namespaces</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">canary]</span><span class="w">
</span></span></span><span class="line"><span class="ln">44</span><span class="cl"><span class="w">          </span><span class="nt">labelSelectors</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">45</span><span class="cl"><span class="w">            </span><span class="nt">app</span><span class="p">:</span><span class="w"> </span><span class="l">checkout</span><span class="w">
</span></span></span><span class="line"><span class="ln">46</span><span class="cl"><span class="w">        </span><span class="nt">mode</span><span class="p">:</span><span class="w"> </span><span class="l">one</span><span class="w">
</span></span></span><span class="line"><span class="ln">47</span><span class="cl"><span class="w">    </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">check-recovery</span><span class="w">
</span></span></span><span class="line"><span class="ln">48</span><span class="cl"><span class="w">      </span><span class="nt">templateType</span><span class="p">:</span><span class="w"> </span><span class="l">StatusCheck</span><span class="w">
</span></span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="w">      </span><span class="nt">statusCheck</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">50</span><span class="cl"><span class="w">        </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l">HTTP</span><span class="w">
</span></span></span><span class="line"><span class="ln">51</span><span class="cl"><span class="w">        </span><span class="nt">http</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">52</span><span class="cl"><span class="w">          </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;http://checkout.canary/health&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">53</span><span class="cl"><span class="w">          </span><span class="nt">criteria</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">54</span><span class="cl"><span class="w">            </span><span class="nt">statusCode</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;200&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">55</span><span class="cl"><span class="w">        </span><span class="nt">timeoutSeconds</span><span class="p">:</span><span class="w"> </span><span class="m">60</span><span class="w">
</span></span></span><span class="line"><span class="ln">56</span><span class="cl"><span class="w">        </span><span class="nt">failureThreshold</span><span class="p">:</span><span class="w"> </span><span class="m">5</span></span></span></code></pre></div><h3 id="gitops-整合">GitOps 整合</h3>
<p>Workflow 定義存在 git repo，用 ArgoCD 或 Flux sync 到 cluster。變更 chaos experiment 走 PR review，跟 code 變更同樣的 approval 流程。這讓 experiment 的修改歷史可追蹤、可審計。</p>
<h3 id="rbac-約束">RBAC 約束</h3>
<p>Chaos Mesh 的 ServiceAccount 權限需要最小化。production namespace 的 chaos experiment 應使用獨立 ServiceAccount，只授予目標 namespace 的 ChaosExperiment create/get/list 權限。避免使用 cluster-admin 角色跑 chaos — 權限過大會讓 selector 誤配時的影響面不可控。</p>
<h2 id="邊界與陷阱">邊界與陷阱</h2>
<p><strong>StatusCheck timeout 太短</strong>：服務在 pod-kill 後需要 readiness probe 通過、load balancer 更新、cache 預熱。若 StatusCheck 的 timeoutSeconds 設太短，服務還在恢復中就被判失敗，產生 false negative。初始 timeout 建議設為預期恢復時間的 2 倍。</p>
<p><strong>Selector 太寬</strong>：namespace-level selector 不加 labelSelector 會命中該 namespace 所有 pod，包含 sidecar、monitoring agent 等非目標 pod。永遠用 labelSelector 或 annotationSelector 收窄範圍。</p>
<p><strong>Privilege 需求</strong>：Chaos Mesh 的 IOChaos 和 StressChaos 需要 container 的 SYS_ADMIN / SYS_PTRACE capability。安全團隊可能限制這些 capability 的使用。若無法取得 privilege，可以先用 PodChaos + NetworkChaos（不需額外 capability）建立 chaos 習慣，再逐步推進。</p>
<p><strong>K8s-only 限制</strong>：Chaos Mesh 只能注入 Kubernetes 上的故障。非 K8s 環境的依賴（外部 SaaS、bare-metal DB、第三方 API）需要用 <a href="/blog/backend/06-reliability/vendors/toxiproxy/" data-link-title="Toxiproxy" data-link-desc="TCP-level fault injection proxy（Shopify 開源）">Toxiproxy</a>（TCP-level fault）或 <a href="/blog/backend/06-reliability/vendors/gremlin/" data-link-title="Gremlin" data-link-desc="商業 chaos engineering 平台、跨平台與 GameDay">Gremlin</a>（跨平台 SaaS）補充。</p>
<h2 id="整合路由">整合路由</h2>
<ul>
<li>上游概念：<a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 Experiment Safety Boundary</a> — selector + mode 對應 blast radius 設計</li>
<li>上游概念：<a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22 Steady State Definition</a> — StatusCheck 對應穩態門檻</li>
<li>下游交接：<a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23 Verification Evidence Handoff</a> — Workflow 結果作為 release gate 證據</li>
<li>平行 vendor：<a href="/blog/backend/06-reliability/vendors/litmuschaos/" data-link-title="LitmusChaos" data-link-desc="Kubernetes chaos engineering 平台（CNCF graduated）">LitmusChaos</a>、<a href="/blog/backend/06-reliability/vendors/gremlin/" data-link-title="Gremlin" data-link-desc="商業 chaos engineering 平台、跨平台與 GameDay">Gremlin</a>、<a href="/blog/backend/06-reliability/vendors/toxiproxy/" data-link-title="Toxiproxy" data-link-desc="TCP-level fault injection proxy（Shopify 開源）">Toxiproxy</a></li>
<li>案例回寫：<a href="/blog/backend/06-reliability/cases/netflix/steady-state-chaos-and-fit/" data-link-title="Netflix：Steady State、Chaos 與 FIT 的驗證路徑" data-link-desc="把故障注入從工具操作升級成可驗證流程：先定義穩態，再設計注入與回復條件。">Netflix N1</a>（steady state hypothesis）、<a href="/blog/backend/06-reliability/cases/netflix/chaos-monkey-business-hours-guardrails/" data-link-title="Netflix：Business-Hours Chaos 與 Guardrails" data-link-desc="Chaos Monkey 為何刻意在 business hours 執行：把即時應變能力納入驗證，並用 guardrails 限制實驗風險。">Netflix N2</a>（business-hours guardrails 對應 scope control）</li>
</ul>
]]></content:encoded></item><item><title>k6：Threshold CI Gate 與 Scenario 設計</title><link>https://tarrragon.github.io/blog/backend/06-reliability/vendors/k6/threshold-ci-gate-and-scenario-design/</link><pubDate>Tue, 23 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/vendors/k6/threshold-ci-gate-and-scenario-design/</guid><description>&lt;h2 id="問題情境">問題情境&lt;/h2>
&lt;p>Load test 跑完會產生大量指標，但 CI pipeline 需要的是 pass/fail 訊號。若沒有 threshold 把指標轉成判讀結論，效能退化只能靠人工看 dashboard 發現，等到看見時通常已經累積數個版本。&lt;/p>
&lt;p>另一面，threshold 的判讀品質取決於 workload model 的真實度。用 &lt;code>--vus 10 --duration 30s&lt;/code> 跑出來的結果跟 production 流量結構差距太大時，threshold 通過也無法證明 production 安全。&lt;/p>
&lt;p>這篇處理兩個問題：怎麼設 threshold 讓 CI gate 可靠，怎麼設 scenario 讓 workload 接近真實。&lt;/p>
&lt;h2 id="threshold-設計">Threshold 設計&lt;/h2>
&lt;p>Threshold 的責任是把 load test 指標轉成 CI 的 pass/fail 訊號。k6 在所有 threshold 都通過時回傳 exit code 0，任一 threshold 失敗就回傳非零 — CI pipeline 直接用 exit code 判斷。&lt;/p>
&lt;h3 id="多指標-threshold">多指標 threshold&lt;/h3>
&lt;p>單一指標 threshold 容易漏風險。latency 正常但 error rate 偏高代表系統在丟請求；throughput 正常但 latency 偏高代表排隊開始堆積。完整的 threshold 至少涵蓋三個面向：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">options&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl"> &lt;span class="nx">thresholds&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl"> &lt;span class="nx">http_req_duration&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;p(95)&amp;lt;500&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;p(99)&amp;lt;1000&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl"> &lt;span class="nx">http_req_failed&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;rate&amp;lt;0.01&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl"> &lt;span class="nx">http_reqs&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;rate&amp;gt;100&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="p">};&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>latency threshold 用 percentile 而不是 average — average 會被長尾稀釋，p95/p99 更接近使用者感知的最差體驗。&lt;/p>
&lt;h3 id="門檻來源">門檻來源&lt;/h3>
&lt;p>Threshold 的門檻從 production baseline 出發。先從 observability 系統（Grafana / Datadog）取最近 7-30 天的 p95/p99 latency 與 error rate，加上可接受退化幅度（通常 10-20%）作為 threshold。門檻太緊會讓 CI 環境噪音觸發 false positive；門檻太寬會讓真退化滑過去。&lt;/p>
&lt;p>校準節奏：每月或每次重大架構變更後重新對齊 production baseline，避免 threshold 跟真實系統漂移。&lt;/p>
&lt;h3 id="path-level-threshold">Path-level threshold&lt;/h3>
&lt;p>不同 API path 的效能特徵不同。checkout 路徑的 latency 容忍度可能比 listing 路徑低很多。k6 的 group + tag 機制讓 threshold 可以按 path 設定：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="kr">import&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nx">group&lt;/span> &lt;span class="p">}&lt;/span> &lt;span class="nx">from&lt;/span> &lt;span class="s1">&amp;#39;k6&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="k">default&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl"> &lt;span class="nx">group&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;checkout&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl"> &lt;span class="c1">// checkout 請求
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl"> &lt;span class="nx">group&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;listing&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl"> &lt;span class="c1">// listing 請求
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="kr">export&lt;/span> &lt;span class="kr">const&lt;/span> &lt;span class="nx">options&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl"> &lt;span class="nx">thresholds&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl"> &lt;span class="s1">&amp;#39;http_req_duration{group:::checkout}&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;p(95)&amp;lt;300&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl"> &lt;span class="s1">&amp;#39;http_req_duration{group:::listing}&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;p(95)&amp;lt;800&amp;#39;&lt;/span>&lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="p">};&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>path-level threshold 讓 gate 的判讀粒度從「整體效能」細化到「關鍵路徑效能」。&lt;/p>
&lt;h2 id="scenario-設計">Scenario 設計&lt;/h2>
&lt;p>Scenario 的責任是讓壓測的流量結構接近 production。k6 提供五種 scenario executor，選擇取決於要控制什麼變量。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Executor&lt;/th>
 &lt;th>控制變量&lt;/th>
 &lt;th>適用場景&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>constant-vus&lt;/td>
 &lt;td>並發使用者數&lt;/td>
 &lt;td>簡單 smoke test&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>ramping-vus&lt;/td>
 &lt;td>並發使用者數&lt;/td>
 &lt;td>階梯式升壓找 saturation&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>constant-arrival-rate&lt;/td>
 &lt;td>固定 RPS&lt;/td>
 &lt;td>CI regression（穩定輸入）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>ramping-arrival-rate&lt;/td>
 &lt;td>變化 RPS&lt;/td>
 &lt;td>模擬 production peak/off-peak&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>externally-controlled&lt;/td>
 &lt;td>外部 API&lt;/td>
 &lt;td>結合 production 流量 replay&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h3 id="executor-選擇判準">Executor 選擇判準&lt;/h3>
&lt;p>constant-vus 最簡單，但 throughput 會隨 response time 波動 — 伺服器變慢時 RPS 自動下降，掩蓋了真正的壓力。constant-arrival-rate 控制 RPS 穩定，能讓 threshold 的判讀基準一致，但需要設定足夠的 preAllocatedVUs 避免 k6 因為 VU 不足而主動降速。&lt;/p></description><content:encoded><![CDATA[<h2 id="問題情境">問題情境</h2>
<p>Load test 跑完會產生大量指標，但 CI pipeline 需要的是 pass/fail 訊號。若沒有 threshold 把指標轉成判讀結論，效能退化只能靠人工看 dashboard 發現，等到看見時通常已經累積數個版本。</p>
<p>另一面，threshold 的判讀品質取決於 workload model 的真實度。用 <code>--vus 10 --duration 30s</code> 跑出來的結果跟 production 流量結構差距太大時，threshold 通過也無法證明 production 安全。</p>
<p>這篇處理兩個問題：怎麼設 threshold 讓 CI gate 可靠，怎麼設 scenario 讓 workload 接近真實。</p>
<h2 id="threshold-設計">Threshold 設計</h2>
<p>Threshold 的責任是把 load test 指標轉成 CI 的 pass/fail 訊號。k6 在所有 threshold 都通過時回傳 exit code 0，任一 threshold 失敗就回傳非零 — CI pipeline 直接用 exit code 判斷。</p>
<h3 id="多指標-threshold">多指標 threshold</h3>
<p>單一指標 threshold 容易漏風險。latency 正常但 error rate 偏高代表系統在丟請求；throughput 正常但 latency 偏高代表排隊開始堆積。完整的 threshold 至少涵蓋三個面向：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln">1</span><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">  <span class="nx">thresholds</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">    <span class="nx">http_req_duration</span><span class="o">:</span> <span class="p">[</span><span class="s1">&#39;p(95)&lt;500&#39;</span><span class="p">,</span> <span class="s1">&#39;p(99)&lt;1000&#39;</span><span class="p">],</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">    <span class="nx">http_req_failed</span><span class="o">:</span>   <span class="p">[</span><span class="s1">&#39;rate&lt;0.01&#39;</span><span class="p">],</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">    <span class="nx">http_reqs</span><span class="o">:</span>         <span class="p">[</span><span class="s1">&#39;rate&gt;100&#39;</span><span class="p">],</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="p">};</span></span></span></code></pre></div><p>latency threshold 用 percentile 而不是 average — average 會被長尾稀釋，p95/p99 更接近使用者感知的最差體驗。</p>
<h3 id="門檻來源">門檻來源</h3>
<p>Threshold 的門檻從 production baseline 出發。先從 observability 系統（Grafana / Datadog）取最近 7-30 天的 p95/p99 latency 與 error rate，加上可接受退化幅度（通常 10-20%）作為 threshold。門檻太緊會讓 CI 環境噪音觸發 false positive；門檻太寬會讓真退化滑過去。</p>
<p>校準節奏：每月或每次重大架構變更後重新對齊 production baseline，避免 threshold 跟真實系統漂移。</p>
<h3 id="path-level-threshold">Path-level threshold</h3>
<p>不同 API path 的效能特徵不同。checkout 路徑的 latency 容忍度可能比 listing 路徑低很多。k6 的 group + tag 機制讓 threshold 可以按 path 設定：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">group</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;k6&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="nx">group</span><span class="p">(</span><span class="s1">&#39;checkout&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="c1">// checkout 請求
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"></span>  <span class="p">});</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">  <span class="nx">group</span><span class="p">(</span><span class="s1">&#39;listing&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="c1">// listing 請求
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></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></span><span class="line"><span class="ln">12</span><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">  <span class="nx">thresholds</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="s1">&#39;http_req_duration{group:::checkout}&#39;</span><span class="o">:</span> <span class="p">[</span><span class="s1">&#39;p(95)&lt;300&#39;</span><span class="p">],</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="s1">&#39;http_req_duration{group:::listing}&#39;</span><span class="o">:</span>  <span class="p">[</span><span class="s1">&#39;p(95)&lt;800&#39;</span><span class="p">],</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="p">};</span></span></span></code></pre></div><p>path-level threshold 讓 gate 的判讀粒度從「整體效能」細化到「關鍵路徑效能」。</p>
<h2 id="scenario-設計">Scenario 設計</h2>
<p>Scenario 的責任是讓壓測的流量結構接近 production。k6 提供五種 scenario executor，選擇取決於要控制什麼變量。</p>
<table>
  <thead>
      <tr>
          <th>Executor</th>
          <th>控制變量</th>
          <th>適用場景</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>constant-vus</td>
          <td>並發使用者數</td>
          <td>簡單 smoke test</td>
      </tr>
      <tr>
          <td>ramping-vus</td>
          <td>並發使用者數</td>
          <td>階梯式升壓找 saturation</td>
      </tr>
      <tr>
          <td>constant-arrival-rate</td>
          <td>固定 RPS</td>
          <td>CI regression（穩定輸入）</td>
      </tr>
      <tr>
          <td>ramping-arrival-rate</td>
          <td>變化 RPS</td>
          <td>模擬 production peak/off-peak</td>
      </tr>
      <tr>
          <td>externally-controlled</td>
          <td>外部 API</td>
          <td>結合 production 流量 replay</td>
      </tr>
  </tbody>
</table>
<h3 id="executor-選擇判準">Executor 選擇判準</h3>
<p>constant-vus 最簡單，但 throughput 會隨 response time 波動 — 伺服器變慢時 RPS 自動下降，掩蓋了真正的壓力。constant-arrival-rate 控制 RPS 穩定，能讓 threshold 的判讀基準一致，但需要設定足夠的 preAllocatedVUs 避免 k6 因為 VU 不足而主動降速。</p>
<p>CI regression 測試建議用 constant-arrival-rate：輸入固定、輸出可比較、版本間的差異才有意義。</p>
<h3 id="production-traffic-shape-對齊">Production traffic shape 對齊</h3>
<p>用 ramping-arrival-rate 模擬 production 的流量形狀：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  <span class="nx">scenarios</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="nx">peak_simulation</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">      <span class="nx">executor</span><span class="o">:</span> <span class="s1">&#39;ramping-arrival-rate&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">      <span class="nx">startRate</span><span class="o">:</span> <span class="mi">50</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">      <span class="nx">stages</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        <span class="p">{</span> <span class="nx">target</span><span class="o">:</span> <span class="mi">200</span><span class="p">,</span> <span class="nx">duration</span><span class="o">:</span> <span class="s1">&#39;2m&#39;</span> <span class="p">},</span>  <span class="c1">// ramp up
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1"></span>        <span class="p">{</span> <span class="nx">target</span><span class="o">:</span> <span class="mi">200</span><span class="p">,</span> <span class="nx">duration</span><span class="o">:</span> <span class="s1">&#39;5m&#39;</span> <span class="p">},</span>  <span class="c1">// sustain peak
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"></span>        <span class="p">{</span> <span class="nx">target</span><span class="o">:</span> <span class="mi">50</span><span class="p">,</span>  <span class="nx">duration</span><span class="o">:</span> <span class="s1">&#39;1m&#39;</span> <span class="p">},</span>  <span class="c1">// ramp down
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"></span>      <span class="p">],</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">      <span class="nx">preAllocatedVUs</span><span class="o">:</span> <span class="mi">300</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">};</span></span></span></code></pre></div><p>流量形狀的參數（startRate / target / duration）從 production access log 的 peak 時段推算。Shopify 的 BFCM 準備流程把 game day 的 load test scenario 跟實際峰值形狀對齊 — 短時間爆量加高寫入比例需要特別設計 scenario 來覆蓋。</p>
<h3 id="cohort-模擬">Cohort 模擬</h3>
<p>Production 流量不是單一類型。用多 scenario 並行模擬不同 cohort：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="kr">export</span> <span class="kr">const</span> <span class="nx">options</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">  <span class="nx">scenarios</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="nx">read_traffic</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">      <span class="nx">executor</span><span class="o">:</span> <span class="s1">&#39;constant-arrival-rate&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">      <span class="nx">rate</span><span class="o">:</span> <span class="mi">150</span><span class="p">,</span> <span class="nx">exec</span><span class="o">:</span> <span class="s1">&#39;readFlow&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">      <span class="nx">preAllocatedVUs</span><span class="o">:</span> <span class="mi">200</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">      <span class="nx">duration</span><span class="o">:</span> <span class="s1">&#39;5m&#39;</span><span class="p">,</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">write_traffic</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">      <span class="nx">executor</span><span class="o">:</span> <span class="s1">&#39;constant-arrival-rate&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">      <span class="nx">rate</span><span class="o">:</span> <span class="mi">30</span><span class="p">,</span> <span class="nx">exec</span><span class="o">:</span> <span class="s1">&#39;writeFlow&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">      <span class="nx">preAllocatedVUs</span><span class="o">:</span> <span class="mi">50</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">      <span class="nx">duration</span><span class="o">:</span> <span class="s1">&#39;5m&#39;</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="kr">export</span> <span class="kd">function</span> <span class="nx">readFlow</span><span class="p">()</span> <span class="p">{</span> <span class="cm">/* GET 請求 */</span> <span class="p">}</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="kr">export</span> <span class="kd">function</span> <span class="nx">writeFlow</span><span class="p">()</span> <span class="p">{</span> <span class="cm">/* POST 請求 */</span> <span class="p">}</span></span></span></code></pre></div><p>讀寫比例從 production 的 access log 或 APM 資料推算。比例偏差會讓瓶頸位置失真 — 讀為主的模型抓不到寫入引起的 lock contention。</p>
<h3 id="資料驅動">資料驅動</h3>
<p>測試資料用 SharedArray 載入，避免每個 VU 各自載入造成記憶體浪費：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-javascript" data-lang="javascript"><span class="line"><span class="ln">1</span><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">SharedArray</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">&#39;k6/data&#39;</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="kr">const</span> <span class="nx">users</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">SharedArray</span><span class="p">(</span><span class="s1">&#39;users&#39;</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">  <span class="k">return</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">open</span><span class="p">(</span><span class="s1">&#39;./users.json&#39;</span><span class="p">));</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="p">});</span></span></span></code></pre></div><p>資料來源可以是 production sample（脫敏後）或 synthetic generation。資料分佈需要接近 production — ID 範圍、key 分佈、payload 大小都會影響 query plan 與 cache 行為。</p>
<h2 id="ci-整合實務">CI 整合實務</h2>
<h3 id="fast-path每次-push">Fast path（每次 push）</h3>
<p>固定 scenario + 短 duration（30s-2min），用 constant-arrival-rate 做 regression 偵測。threshold 設在 production baseline + 10%。這一層的目的是快速攔住明顯退化，不需要模擬完整峰值。</p>
<h3 id="slow-pathmerge-gate">Slow path（merge gate）</h3>
<p>完整 scenario + 較長 duration（5-15min），包含多 cohort 與 ramping 模擬。threshold 涵蓋 path-level 指標。這一層的目的是深層驗證變更在接近真實壓力下的行為。</p>
<h3 id="結果留存">結果留存</h3>
<p>k6 結果預設輸出到 stdout。CI 整合時用 <code>--out</code> flag 把結果送到時序資料庫（InfluxDB / Prometheus Remote Write / Grafana Cloud k6），讓歷史趨勢可查詢。趨勢比較能偵測 threshold 內但持續惡化的 slow drift。</p>
<p>LinkedIn 的自動化壓測實踐把 load test 結果跟容量預測接在一起 — saturation point 隨時間的變化趨勢直接驅動擴容決策。</p>
<h2 id="邊界與陷阱">邊界與陷阱</h2>
<p><strong>Threshold variance</strong>：CI runner 的硬體差異（shared runner 的鄰居效應、network jitter、GC pause）會讓同一份 code 在不同 run 產生不同結果。控制方式：dedicated runner 消除鄰居效應、warmup iteration 丟棄前幾輪結果、多次 run 取中位數。若 variance 超過 threshold 的退化幅度，gate 判讀就不可信。</p>
<p><strong>門檻過寬或過緊</strong>：threshold 永遠通過代表 gate 形同虛設；threshold 頻繁 false positive 會讓團隊忽略 CI 結果。兩者都會讓 gate 失去判讀價值。校準的判準是：過去 30 天的 threshold 結果中，真正需要關注的退化是否都被攔住，同時 false positive 率低於 5%。</p>
<p><strong>Scenario 跟 production drift</strong>：production 的流量結構會隨產品演進改變。定期（每月或每次重大功能上線）用 access log 校準 scenario 的 RPS、cohort 比例與資料分佈，避免模型越跑越偏。</p>
<h2 id="整合路由">整合路由</h2>
<ul>
<li>上游概念：<a href="/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2 load testing</a> 的 workload model 設計</li>
<li>下游能力：<a href="/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13 performance regression gate</a> 的 baseline 管理與退化定位</li>
<li>平行 vendor：<a href="/blog/backend/06-reliability/vendors/gatling/" data-link-title="Gatling" data-link-desc="JVM-based load test、Scala / Java / Kotlin DSL、強型別 scenario、HAR-driven recording">Gatling</a>、<a href="/blog/backend/06-reliability/vendors/locust/" data-link-title="Locust" data-link-desc="Python-based load test、distributed、易擴展">Locust</a>、<a href="/blog/backend/06-reliability/vendors/jmeter/" data-link-title="Apache JMeter" data-link-desc="老牌 load test 工具、GUI &#43; plugins">JMeter</a></li>
<li>案例回寫：<a href="/blog/backend/06-reliability/cases/shopify/bfcm-capacity-and-game-day/" data-link-title="Shopify：BFCM 容量治理與 Game Day 驗證節奏" data-link-desc="把季節性流量峰值轉成年度可靠性流程，透過容量模型、演練與隔離策略提前吸收風險。">Shopify BFCM 容量治理</a>（game day load test 對齊峰值形狀）、<a href="/blog/backend/06-reliability/cases/linkedin/automated-load-testing-and-capacity-forecasting/" data-link-title="LinkedIn：Automated Load Testing 與 Capacity Forecasting" data-link-desc="持續壓測驅動容量預測：用自動化回饋取代一次性壓測的容量規劃。">LinkedIn Automated Load Testing</a>（持續壓測驅動容量預測）</li>
</ul>
]]></content:encoded></item><item><title>Sloth：SLO YAML 與 Multi-burn-rate Alert 生成</title><link>https://tarrragon.github.io/blog/backend/06-reliability/vendors/sloth/slo-yaml-and-multi-burn-rate-alert-generation/</link><pubDate>Tue, 23 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/vendors/sloth/slo-yaml-and-multi-burn-rate-alert-generation/</guid><description>&lt;h2 id="問題情境">問題情境&lt;/h2>
&lt;p>SLO 從定義到 Prometheus 落地需要多層 rule。一個 SLO 對應 4 組 time window 的 recording rule（計算各窗口的 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">burn rate&lt;/a>），再對應 fast burn 和 slow burn 兩組 alerting rule。手動維護這些 rule 容易出錯：window 參數不一致、新增 SLO 忘記補 alert、修改 SLI expression 只改了部分 rule。&lt;/p>
&lt;p>Sloth 的責任是把這個過程自動化。輸入一份 SLO YAML，產出一組完整的 Prometheus recording + alerting rules，讓 SLO 維護回到宣告式：改 YAML、重新生成、載入 Prometheus。&lt;/p>
&lt;h2 id="slo-yaml-設計">SLO YAML 設計&lt;/h2>
&lt;p>Sloth YAML 的核心結構是 &lt;code>version&lt;/code> → &lt;code>service&lt;/code> → &lt;code>slos[]&lt;/code>。每個 SLO 定義三件事：目標數字（objective）、量測方式（SLI）、告警等級（alerting）。&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="nt">version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">prometheus/v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">service&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">checkout-api&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">slos&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">availability&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">objective&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">99.9&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">description&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;checkout API 的請求成功率&amp;#34;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">sli&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">events&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">error_query&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">sum(rate(http_requests_total{service=&amp;#34;checkout&amp;#34;,code=~&amp;#34;5..&amp;#34;}[{{.window}}]))&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">total_query&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">sum(rate(http_requests_total{service=&amp;#34;checkout&amp;#34;}[{{.window}}]))&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">alerting&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">CheckoutAvailability&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">13&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">page_alert&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">14&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">labels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">15&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">severity&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">critical&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">16&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ticket_alert&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">17&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">labels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">18&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">severity&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">warning&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>SLI 有兩種類型。events-based SLI 用 error/total ratio 定義，Sloth 自動把 &lt;code>{{.window}}&lt;/code> 參數代入各 recording rule 的 range vector。raw SLI 直接寫 PromQL expression 算 error ratio，適合非 request-based 的 SLO（如 data freshness、replication lag）。&lt;/p>
&lt;p>raw SLI 範例 — data freshness：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">data-freshness&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">objective&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">99.5&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">sli&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">raw&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">error_ratio_query&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">|&lt;/span>&lt;span class="sd">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="sd"> 1 - clamp_max(
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="sd"> replication_lag_seconds{service=&amp;#34;checkout-db&amp;#34;} / 60,
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="sd"> 1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl">&lt;span class="sd"> )&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>objective 數字的來源是 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6 SLO 政策&lt;/a> — 先從使用者旅程定義服務承諾，再把承諾轉成 objective。Sloth 不負責決定 objective 該是多少，只負責把 objective 轉成可執行的 Prometheus rule。&lt;/p>
&lt;p>alerting 分 page（嚴重，觸發即時通知）和 ticket（一般，產生工單）。兩者的 burn rate 門檻不同：page 用 fast burn window，ticket 用 slow burn window。label 設計跟 Alertmanager routing 對齊 — &lt;code>severity: critical&lt;/code> 走 PagerDuty / Slack alert channel，&lt;code>severity: warning&lt;/code> 走 ticket system（Jira / Linear）。&lt;/p></description><content:encoded><![CDATA[<h2 id="問題情境">問題情境</h2>
<p>SLO 從定義到 Prometheus 落地需要多層 rule。一個 SLO 對應 4 組 time window 的 recording rule（計算各窗口的 <a href="/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">burn rate</a>），再對應 fast burn 和 slow burn 兩組 alerting rule。手動維護這些 rule 容易出錯：window 參數不一致、新增 SLO 忘記補 alert、修改 SLI expression 只改了部分 rule。</p>
<p>Sloth 的責任是把這個過程自動化。輸入一份 SLO YAML，產出一組完整的 Prometheus recording + alerting rules，讓 SLO 維護回到宣告式：改 YAML、重新生成、載入 Prometheus。</p>
<h2 id="slo-yaml-設計">SLO YAML 設計</h2>
<p>Sloth YAML 的核心結構是 <code>version</code> → <code>service</code> → <code>slos[]</code>。每個 SLO 定義三件事：目標數字（objective）、量測方式（SLI）、告警等級（alerting）。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="l">prometheus/v1</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="nt">service</span><span class="p">:</span><span class="w"> </span><span class="l">checkout-api</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"></span><span class="nt">slos</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">availability</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="nt">objective</span><span class="p">:</span><span class="w"> </span><span class="m">99.9</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="nt">description</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;checkout API 的請求成功率&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="nt">sli</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">      </span><span class="nt">events</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">        </span><span class="nt">error_query</span><span class="p">:</span><span class="w"> </span><span class="l">sum(rate(http_requests_total{service=&#34;checkout&#34;,code=~&#34;5..&#34;}[{{.window}}]))</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">        </span><span class="nt">total_query</span><span class="p">:</span><span class="w"> </span><span class="l">sum(rate(http_requests_total{service=&#34;checkout&#34;}[{{.window}}]))</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="nt">alerting</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">CheckoutAvailability</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">      </span><span class="nt">page_alert</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">        </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">          </span><span class="nt">severity</span><span class="p">:</span><span class="w"> </span><span class="l">critical</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">      </span><span class="nt">ticket_alert</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">        </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">          </span><span class="nt">severity</span><span class="p">:</span><span class="w"> </span><span class="l">warning</span></span></span></code></pre></div><p>SLI 有兩種類型。events-based SLI 用 error/total ratio 定義，Sloth 自動把 <code>{{.window}}</code> 參數代入各 recording rule 的 range vector。raw SLI 直接寫 PromQL expression 算 error ratio，適合非 request-based 的 SLO（如 data freshness、replication lag）。</p>
<p>raw SLI 範例 — data freshness：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="w">  </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">data-freshness</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">    </span><span class="nt">objective</span><span class="p">:</span><span class="w"> </span><span class="m">99.5</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="nt">sli</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">      </span><span class="nt">raw</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">        </span><span class="nt">error_ratio_query</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="sd">          1 - clamp_max(
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="sd">            replication_lag_seconds{service=&#34;checkout-db&#34;} / 60,
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="sd">            1
</span></span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="sd">          )</span></span></span></code></pre></div><p>objective 數字的來源是 <a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6 SLO 政策</a> — 先從使用者旅程定義服務承諾，再把承諾轉成 objective。Sloth 不負責決定 objective 該是多少，只負責把 objective 轉成可執行的 Prometheus rule。</p>
<p>alerting 分 page（嚴重，觸發即時通知）和 ticket（一般，產生工單）。兩者的 burn rate 門檻不同：page 用 fast burn window，ticket 用 slow burn window。label 設計跟 Alertmanager routing 對齊 — <code>severity: critical</code> 走 PagerDuty / Slack alert channel，<code>severity: warning</code> 走 ticket system（Jira / Linear）。</p>
<h2 id="multi-window-multi-burn-rate-alert">Multi-window Multi-burn-rate Alert</h2>
<p>Sloth 預設產生 Google SRE 推薦的 4-window alert 結構。每個 SLO 生成以下 recording rules 和 alerting rules。</p>
<table>
  <thead>
      <tr>
          <th>Window 組合</th>
          <th>責任</th>
          <th>觸發行動</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>5m / 1h</td>
          <td>Fast burn 偵測</td>
          <td>短時間大量消耗 → page 通知</td>
      </tr>
      <tr>
          <td>30m / 6h</td>
          <td>Moderate burn 偵測</td>
          <td>中速消耗 → page 或 ticket</td>
      </tr>
      <tr>
          <td>2h / 1d</td>
          <td>Slow burn 偵測</td>
          <td>緩慢消耗 → ticket</td>
      </tr>
      <tr>
          <td>6h / 3d</td>
          <td>Very slow 偵測</td>
          <td>長期趨勢退化 → ticket 或 review</td>
      </tr>
  </tbody>
</table>
<p>fast burn alert 回答「error budget 是否正在被快速吃掉」。當 5 分鐘窗口的 burn rate 超過 14.4 倍（代表如果持續下去，1 小時會消耗完整個月的 budget），觸發 page。這個門檻的設計邏輯是：越短的窗口允許越高的 burn rate 容忍，因為短窗口的 false positive 率較高，需要搭配較長窗口的確認。</p>
<p>slow burn alert 回答「error budget 是否在不被注意的情況下被緩慢消耗」。6 小時窗口的 burn rate 超過 1 倍（代表月底會剛好用完 budget），觸發 ticket。slow burn 常被忽略，但它是高變更頻率服務最常見的可靠性退化模式 — 每次小回歸都不夠大到觸發 fast burn，累積到月底才發現 budget 已透支。</p>
<p>burn rate alert 跟 <a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6 SLO error budget 政策</a> 直接對應：fast burn → 凍結變更；slow burn → 提高驗證門檻；budget 健康 → 正常發版。</p>
<p>Sloth 產出的 recording rule 範例（5m window）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl">- <span class="nt">record</span><span class="p">:</span><span class="w"> </span><span class="l">slo:sli_error:ratio_rate5m</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">  </span><span class="nt">expr</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="sd">    sum(rate(http_requests_total{service=&#34;checkout&#34;,code=~&#34;5..&#34;}[5m]))
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="sd">    /
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="sd">    sum(rate(http_requests_total{service=&#34;checkout&#34;}[5m]))</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="w">    </span><span class="nt">sloth_service</span><span class="p">:</span><span class="w"> </span><span class="l">checkout-api</span><span class="w">
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="w">    </span><span class="nt">sloth_slo</span><span class="p">:</span><span class="w"> </span><span class="l">availability</span></span></span></code></pre></div><p>對應的 alerting rule（fast burn）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl">- <span class="nt">alert</span><span class="p">:</span><span class="w"> </span><span class="l">CheckoutAvailabilityFastBurn</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">  </span><span class="nt">expr</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="sd">    slo:sli_error:ratio_rate5m{sloth_slo=&#34;availability&#34;} &gt; (14.4 * 0.001)
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="sd">    and
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="sd">    slo:sli_error:ratio_rate1h{sloth_slo=&#34;availability&#34;} &gt; (14.4 * 0.001)</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w">  </span><span class="nt">labels</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="w">    </span><span class="nt">severity</span><span class="p">:</span><span class="w"> </span><span class="l">critical</span></span></span></code></pre></div><p>fast burn alert 要求 5m 和 1h 兩個窗口同時超過門檻，短窗口防止 spike false positive、長窗口確認趨勢持續。</p>
<h2 id="實作流程">實作流程</h2>
<h3 id="cli-生成">CLI 生成</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">sloth generate -i slo.yaml -o rules.yaml
</span></span><span class="line"><span class="ln">2</span><span class="cl">sloth validate -i slo.yaml</span></span></code></pre></div><p><code>generate</code> 產出的 <code>rules.yaml</code> 包含所有 recording rules 和 alerting rules，直接放入 Prometheus 的 <code>rule_files</code> 載入。<code>validate</code> 在 CI 中先行檢查 YAML 格式與 SLI expression 語法。</p>
<h3 id="k8s-operator-mode">K8s Operator mode</h3>
<p>Sloth 提供 K8s Operator，用 <code>PrometheusServiceLevel</code> CRD 定義 SLO。Operator 自動 reconcile，把 CRD 轉成 Prometheus rules 並同步到 Prometheus Operator 的 <code>PrometheusRule</code> 資源。</p>
<p>Operator mode 適合 K8s-native 環境：SLO 定義跟 service deployment 放在同一個 GitOps repo，變更 SLO 跟變更服務走同一套 PR review + CI 流程。</p>
<h3 id="ci--gitops-整合">CI / GitOps 整合</h3>
<p>在 CI pipeline 中跑 <code>sloth validate</code> 驗證 YAML，再跑 <code>sloth generate</code> 產出 rules，commit 進 GitOps repo。Prometheus 透過 config reload 或 Operator reconcile 載入新 rules。這條流程讓 SLO 變更有版本歷史、有 review、有 rollback 能力。</p>
<h2 id="邊界與陷阱">邊界與陷阱</h2>
<p>Sloth 只支援 Prometheus 作為後端。若觀測平台是 Datadog、New Relic、Honeycomb 或 Grafana Cloud，需要各平台自己的 SLO 功能或 <a href="/blog/backend/06-reliability/vendors/nobl9/" data-link-title="Nobl9" data-link-desc="SLO platform、跨 data source、企業 SLO 治理">Nobl9</a> 的 multi-source 整合。</p>
<p>SLI expression 錯誤是最常見的問題。分母為零（service 沒有流量）會產生 NaN，cascading 到所有 recording rule。label 不匹配（<code>service</code> label 拼錯）會產生空 series，alert 永遠不觸發。<code>sloth validate</code> 檢查語法但不檢查 Prometheus 中是否真的有對應 series — 上線後需要用 Prometheus query 確認 recording rule 產出非空結果。</p>
<p>SLO 數量增長會累積 recording rule 成本。每個 SLO 產生約 30 條 recording rule（4 windows × 多組 aggregation）。100 個 SLO 產生 3000 條 rule，Prometheus 的 rule evaluation 會消耗明顯的 CPU 和記憶體。定期監控 <code>prometheus_rule_evaluation_duration_seconds</code> 和 <code>prometheus_rule_group_rules</code>，在 rule 數量影響 evaluation latency 前調整。</p>
<p>升級路徑：Sloth YAML 跟 OpenSLO spec 部分相容。從 Sloth 移到 Nobl9 時，SLO 定義的語意可以保留，SLI expression 需要改寫成 Nobl9 的 data source query。這條路徑適合從 Prometheus-only 環境逐步擴展到 multi-source SLO governance。</p>
<h2 id="整合路由">整合路由</h2>
<ul>
<li>上游：<a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6 SLO 與 Error Budget 政策</a> — SLO 定義與 objective 來源</li>
<li>下游：<a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate</a> — burn rate alert 觸發凍結</li>
<li>平行：<a href="/blog/backend/06-reliability/vendors/nobl9/" data-link-title="Nobl9" data-link-desc="SLO platform、跨 data source、企業 SLO 治理">Nobl9</a>（SaaS multi-source）、Pyrra（K8s-native + UI）</li>
<li>案例回寫：<a href="/blog/backend/06-reliability/cases/google/error-budget-policy-and-release-gating/" data-link-title="Google：Error Budget 政策如何決定發布節奏" data-link-desc="把 SLO 消耗量轉成 release gate，讓可靠性與交付速度共用同一套決策語言。">Google G1</a>（error budget policy 原典）、<a href="/blog/backend/06-reliability/cases/honeycomb/burn-rate-driven-reliability-operations/" data-link-title="Honeycomb：以 Burn Rate 驅動的可靠性操作" data-link-desc="把 SLO burn rate 直接連到值班決策與改善優先序，降低高噪音告警造成的判讀失真。">Honeycomb HC1</a>（burn rate 驅動可靠性操作）</li>
</ul>
]]></content:encoded></item><item><title>GitHub Actions</title><link>https://tarrragon.github.io/blog/backend/06-reliability/vendors/github-actions/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/vendors/github-actions/</guid><description>&lt;p>GitHub Actions 是 GitHub 原生的 CI/CD 工具、承擔三個責任：PR check workflow（test / lint / coverage）、release 自動化 + environment protection rules、跨 platform matrix testing。設計取捨偏向「跟 GitHub 深度整合 + marketplace action 生態 + OIDC 認證雲端 + self-hosted runner」、是 GitHub-hosted 專案的預設 CI 選擇。&lt;/p>
&lt;h2 id="本章目標">本章目標&lt;/h2>
&lt;p>讀完本章後、你應該能：&lt;/p>
&lt;ol>
&lt;li>寫 workflow（.github/workflows/*.yml）&lt;/li>
&lt;li>設計 PR check + matrix testing&lt;/li>
&lt;li>用 reusable workflows / composite actions 復用&lt;/li>
&lt;li>配置 environment protection + approval gate&lt;/li>
&lt;li>用 OIDC + cloud auth（無 long-lived secret）&lt;/li>
&lt;/ol>
&lt;h2 id="最短路徑5-分鐘把-github-actions-跑起來">最短路徑：5 分鐘把 GitHub Actions 跑起來&lt;/h2>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c"># .github/workflows/ci.yml&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">CI&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="l">pull_request]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">jobs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">test&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">runs-on&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ubuntu-latest&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">uses&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">actions/checkout@v4&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">npm test&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="日常操作與決策形狀">日常操作與決策形狀&lt;/h2>
&lt;h3 id="workflow-設計">Workflow 設計&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>on triggers（push / pull_request / schedule / workflow_dispatch / repository_dispatch）&lt;/li>
&lt;li>job / step / action&lt;/li>
&lt;li>Matrix（OS / language version / test split）&lt;/li>
&lt;li>對應指令範例：&lt;code>gh workflow run&lt;/code>、&lt;code>gh run list&lt;/code>&lt;/li>
&lt;/ul>
&lt;h3 id="cache-策略">Cache 策略&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>actions/cache（語言依賴 / build cache）&lt;/li>
&lt;li>Cache key 設計（hashFiles + version）&lt;/li>
&lt;li>Cache scope（per branch / per repo）&lt;/li>
&lt;li>對應 build speed optimization&lt;/li>
&lt;/ul>
&lt;h3 id="reusable-workflows--composite-actions">Reusable workflows / composite actions&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>Reusable workflow：跨 repo 引用整個 workflow&lt;/li>
&lt;li>Composite action：把多 step 包成 action&lt;/li>
&lt;li>對應 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/" data-link-title="Knowledge Cards" data-link-desc="用原子化卡片整理後端服務選型前需要理解的 domain knowhow">knowledge cards reusable-action&lt;/a> (對應 DRY)&lt;/li>
&lt;/ul>
&lt;h2 id="進階主題按需閱讀">進階主題（按需閱讀）&lt;/h2>
&lt;h3 id="self-hosted-runner">Self-hosted runner&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>內網資源 / 特殊硬體（GPU）/ macOS&lt;/li>
&lt;li>Runner group + scaling&lt;/li>
&lt;li>Security：ephemeral runner（每次新建）&lt;/li>
&lt;li>對應 &lt;a href="https://tarrragon.github.io/blog/backend/07-security-data-protection/" data-link-title="模組七：資安與資料保護" data-link-desc="以問題驅動方式擴充資安知識網：先定義服務環節問題，再以案例作為觸發式參考">07 security&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="oidc--cloud-auth">OIDC + cloud auth&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>GitHub OIDC provider&lt;/li>
&lt;li>AWS / GCP / Azure 信任 GitHub&lt;/li>
&lt;li>無 long-lived access key&lt;/li>
&lt;li>對應 supply chain security&lt;/li>
&lt;/ul>
&lt;h3 id="environment-protection">Environment protection&lt;/h3>
&lt;p>子議題：&lt;/p></description><content:encoded><![CDATA[<p>GitHub Actions 是 GitHub 原生的 CI/CD 工具、承擔三個責任：PR check workflow（test / lint / coverage）、release 自動化 + environment protection rules、跨 platform matrix testing。設計取捨偏向「跟 GitHub 深度整合 + marketplace action 生態 + OIDC 認證雲端 + self-hosted runner」、是 GitHub-hosted 專案的預設 CI 選擇。</p>
<h2 id="本章目標">本章目標</h2>
<p>讀完本章後、你應該能：</p>
<ol>
<li>寫 workflow（.github/workflows/*.yml）</li>
<li>設計 PR check + matrix testing</li>
<li>用 reusable workflows / composite actions 復用</li>
<li>配置 environment protection + approval gate</li>
<li>用 OIDC + cloud auth（無 long-lived secret）</li>
</ol>
<h2 id="最短路徑5-分鐘把-github-actions-跑起來">最短路徑：5 分鐘把 GitHub Actions 跑起來</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln">1</span><span class="cl"><span class="c"># .github/workflows/ci.yml</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">CI</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">pull_request]</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w"></span><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">  </span><span class="nt">test</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="w">      </span>- <span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">npm test</span></span></span></code></pre></div><h2 id="日常操作與決策形狀">日常操作與決策形狀</h2>
<h3 id="workflow-設計">Workflow 設計</h3>
<p>子議題：</p>
<ul>
<li>on triggers（push / pull_request / schedule / workflow_dispatch / repository_dispatch）</li>
<li>job / step / action</li>
<li>Matrix（OS / language version / test split）</li>
<li>對應指令範例：<code>gh workflow run</code>、<code>gh run list</code></li>
</ul>
<h3 id="cache-策略">Cache 策略</h3>
<p>子議題：</p>
<ul>
<li>actions/cache（語言依賴 / build cache）</li>
<li>Cache key 設計（hashFiles + version）</li>
<li>Cache scope（per branch / per repo）</li>
<li>對應 build speed optimization</li>
</ul>
<h3 id="reusable-workflows--composite-actions">Reusable workflows / composite actions</h3>
<p>子議題：</p>
<ul>
<li>Reusable workflow：跨 repo 引用整個 workflow</li>
<li>Composite action：把多 step 包成 action</li>
<li>對應 <a href="/blog/backend/knowledge-cards/" data-link-title="Knowledge Cards" data-link-desc="用原子化卡片整理後端服務選型前需要理解的 domain knowhow">knowledge cards reusable-action</a> (對應 DRY)</li>
</ul>
<h2 id="進階主題按需閱讀">進階主題（按需閱讀）</h2>
<h3 id="self-hosted-runner">Self-hosted runner</h3>
<p>子議題：</p>
<ul>
<li>內網資源 / 特殊硬體（GPU）/ macOS</li>
<li>Runner group + scaling</li>
<li>Security：ephemeral runner（每次新建）</li>
<li>對應 <a href="/blog/backend/07-security-data-protection/" data-link-title="模組七：資安與資料保護" data-link-desc="以問題驅動方式擴充資安知識網：先定義服務環節問題，再以案例作為觸發式參考">07 security</a></li>
</ul>
<h3 id="oidc--cloud-auth">OIDC + cloud auth</h3>
<p>子議題：</p>
<ul>
<li>GitHub OIDC provider</li>
<li>AWS / GCP / Azure 信任 GitHub</li>
<li>無 long-lived access key</li>
<li>對應 supply chain security</li>
</ul>
<h3 id="environment-protection">Environment protection</h3>
<p>子議題：</p>
<ul>
<li>environment（dev / staging / prod）</li>
<li>Required reviewers</li>
<li>Wait timer</li>
<li>Secrets per-environment</li>
<li>對應 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate</a></li>
</ul>
<h3 id="workflow-security">Workflow security</h3>
<p>子議題：</p>
<ul>
<li>pull_request vs pull_request_target（後者有 secrets / 危險）</li>
<li>third-party action pinning（commit SHA）</li>
<li>GITHUB_TOKEN permissions（最小化）</li>
</ul>
<h3 id="deploy-workflow">Deploy workflow</h3>
<p>子議題：</p>
<ul>
<li>Deploy on tag / release</li>
<li>Rolling deploy / blue-green / canary</li>
<li>Rollback action</li>
</ul>
<h2 id="排錯快速判讀">排錯快速判讀</h2>
<h3 id="workflow-沒觸發">Workflow 沒觸發</h3>
<p>操作原則：on trigger 配置 / branch filter / paths filter。判讀：Actions tab 看 trigger event。</p>
<h3 id="permission-denied">Permission denied</h3>
<p>操作原則：GITHUB_TOKEN permissions 不夠。判讀：workflow 加 permissions: 區段。</p>
<h3 id="cache-miss">Cache miss</h3>
<p>操作原則：cache key 不穩定 / hashFiles input 變化。</p>
<h3 id="secret-沒生效">Secret 沒生效</h3>
<p>操作原則：secret name / environment 不對 / pull_request from fork 不能用 secret。</p>
<h3 id="self-hosted-runner-卡住">Self-hosted runner 卡住</h3>
<p>操作原則：runner offline / job queue 滿 / runner group 配置不對。</p>
<h2 id="何時改走其他服務">何時改走其他服務</h2>
<table>
  <thead>
      <tr>
          <th>需求形狀</th>
          <th>改走</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>進階 cache / parallelism</td>
          <td><a href="/blog/backend/06-reliability/vendors/circleci/" data-link-title="CircleCI" data-link-desc="CI/CD 平台、強 cache 與 parallelism">CircleCI</a></td>
      </tr>
      <tr>
          <td>非 GitHub-hosted</td>
          <td>GitLab CI / Bitbucket Pipelines / CircleCI</td>
      </tr>
      <tr>
          <td>Self-hosted enterprise</td>
          <td>Jenkins / Buildkite / Tekton</td>
      </tr>
      <tr>
          <td>複雜 pipeline DAG</td>
          <td>Tekton / Argo Workflows</td>
      </tr>
      <tr>
          <td>Bazel-native CI</td>
          <td>BuildBuddy / EngFlow</td>
      </tr>
  </tbody>
</table>
<h2 id="不在本頁內的主題">不在本頁內的主題</h2>
<ul>
<li>各 Marketplace action 細節</li>
<li>GitHub Enterprise self-host</li>
<li>Actions pricing</li>
<li>各語言 setup-* action 細節</li>
</ul>
<h2 id="案例回寫">案例回寫</h2>
<table>
  <thead>
      <tr>
          <th>案例方向</th>
          <th>對應主題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/google/error-budget-policy-and-release-gating/" data-link-title="Google：Error Budget 政策如何決定發布節奏" data-link-desc="把 SLO 消耗量轉成 release gate，讓可靠性與交付速度共用同一套決策語言。">Google：Error Budget 與 Release Gating</a></td>
          <td>把 SLO 消耗轉成 release gate / freeze 的 workflow 入口</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/" data-link-title="Stripe：Idempotency 與零停機遷移的交易安全設計" data-link-desc="把 API 重試與資料遷移放在同一套安全模型，維持支付交易的一致結果。">Stripe：Idempotency 與零停機遷移</a></td>
          <td>canary deploy / staged rollout 的 CI 節奏</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/microsoft/change-management-and-reliability-governance/" data-link-title="Microsoft：變更治理與可靠性門檻" data-link-desc="透過分層變更管理與發布閘門，降低大型 SaaS 平台的系統性回歸風險。">Microsoft：變更治理與可靠性門檻</a></td>
          <td>environment protection + approval gate 對應變更分層</td>
      </tr>
  </tbody>
</table>
<p><strong>待補 GitHub Actions customer case</strong>：大規模 monorepo Actions 採用、OIDC migration、self-hosted runner scaling 案例。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>上游概念：<a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate</a></li>
<li>平行 vendor：<a href="/blog/backend/06-reliability/vendors/circleci/" data-link-title="CircleCI" data-link-desc="CI/CD 平台、強 cache 與 parallelism">CircleCI</a></li>
<li>下游能力：<a href="/blog/backend/07-security-data-protection/" data-link-title="模組七：資安與資料保護" data-link-desc="以問題驅動方式擴充資安知識網：先定義服務環節問題，再以案例作為觸發式參考">07 security</a>（supply chain）、<a href="/blog/backend/05-deployment-platform/" data-link-title="模組五：部署平台與網路入口" data-link-desc="整理 Kubernetes、systemd、load balancer、container 與服務生命週期合約">5 deployment</a>（deploy gate）</li>
</ul>
]]></content:encoded></item><item><title>Google</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/google/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/google/</guid><description>&lt;p>Google 是 SRE 概念的原始來源、SRE Book 與 Workbook 是領域 canonical text。教學重點在 SRE 工程文化、量化方法與組織節奏，單一事故只是入口。&lt;/p>
&lt;h2 id="規劃重點">規劃重點&lt;/h2>
&lt;ul>
&lt;li>SLI / SLO / Error Budget：可靠性目標的量化方法、為何選 SLO 而非 100%&lt;/li>
&lt;li>Postmortem 文化：blameless / action items / 行動追蹤的閉環設計&lt;/li>
&lt;li>Toil 量化：把運維工作變成可預算的工程資產&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/on-call/" data-link-title="On-Call" data-link-desc="說明值班制度如何承接告警、事故分級與升級流程">on-call&lt;/a> 與 burnout：值班輪值、shadow / primary 結構、心理安全&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/readiness/" data-link-title="Readiness" data-link-desc="說明 instance 何時可以安全接收流量，以及 readiness 如何和部署平台協作">readiness&lt;/a> review：服務上線前的 SRE 接管門檻&lt;/li>
&lt;/ul>
&lt;h2 id="預計收錄實踐">預計收錄實踐&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>議題&lt;/th>
 &lt;th>教學重點&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>SRE Book Ch.1-4&lt;/td>
 &lt;td>概念基礎、為何 SLO、為何 50/50&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Postmortem Culture&lt;/td>
 &lt;td>blameless 操作化、action items 追蹤&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Toil &amp;amp; Engineering Time&lt;/td>
 &lt;td>量化 toil、長期投資工程的政策&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Hierarchy of Reliability&lt;/td>
 &lt;td>Monitoring → IR → PIR → Testing → Capacity → Dev → Product&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Embedded SRE / Consulting&lt;/td>
 &lt;td>SRE 介入服務的多種模式&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="章節列表">章節列表&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>章節&lt;/th>
 &lt;th>主題&lt;/th>
 &lt;th>核心責任&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/google/error-budget-policy-and-release-gating/" data-link-title="Google：Error Budget 政策如何決定發布節奏" data-link-desc="把 SLO 消耗量轉成 release gate，讓可靠性與交付速度共用同一套決策語言。">G1&lt;/a>&lt;/td>
 &lt;td>Error Budget 與 Release Gating&lt;/td>
 &lt;td>把 SLO 消耗量轉成放行、限速與凍結決策&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/google/postmortem-action-item-closure-governance/" data-link-title="Google：Postmortem Action Item Closure 治理" data-link-desc="把 blameless postmortem 從會議文件變成可追蹤的可靠性治理機制：action item 分級、完成條件與回寫節奏。">G2&lt;/a>&lt;/td>
 &lt;td>Postmortem Closure 治理&lt;/td>
 &lt;td>把事故改進項變成可追蹤、可驗證的治理節奏&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/google/toil-budget-and-automation-investment-policy/" data-link-title="Google：Toil Budget 與 Automation 投資政策" data-link-desc="把 toil 從感受問題轉成預算問題：用時間配比與自動化回報機制，避免 on-call 壓力長期侵蝕可靠性工程。">G3&lt;/a>&lt;/td>
 &lt;td>Toil Budget 投資政策&lt;/td>
 &lt;td>把手動運維工作轉成可預算、可回寫的工程投資&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="案例定位">案例定位&lt;/h2>
&lt;p>Google 這個案例在講的是可靠性如何變成一套可操作的工程制度，而不是單一工具或單一事故。讀者先抓到 SLI / SLO、error budget、postmortem 與 toil 這幾個原語各自負責什麼，再把它們組成一條可執行的可靠性路徑。&lt;/p>
&lt;h2 id="判讀重點">判讀重點&lt;/h2>
&lt;p>當服務健康開始波動時，先看 SLO 是否真的被消耗，再看監控與告警是否能對應到使用者體感。當 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/on-call/" data-link-title="On-Call" data-link-desc="說明值班制度如何承接告警、事故分級與升級流程">on-call&lt;/a> 壓力升高時，重點在團隊是否把重複性工作轉成可預算的工程投資，個人技巧層面的改善幫助有限。&lt;/p>
&lt;h2 id="可操作判準">可操作判準&lt;/h2>
&lt;ul>
&lt;li>能否用一句話說明每個 SLI 對應的使用者行為&lt;/li>
&lt;li>能否從 postmortem 找到明確 owner 與完成條件&lt;/li>
&lt;li>能否把 toil 量化成可排程的工程時間&lt;/li>
&lt;li>能否把監控、測試、容量、開發與產品決策串成同一條路由&lt;/li>
&lt;/ul>
&lt;h2 id="與其他案例的關係">與其他案例的關係&lt;/h2>
&lt;p>Google 提供的是可靠性的語言層，其他案例提供的是具體場景層。當讀者先懂 SLI / SLO 與 postmortem 這組原語，再看 Honeycomb 的 burn rate、Atlassian 的復原節奏或 GitHub 的 status communication，就能把抽象制度接到實際事故上。&lt;/p>
&lt;h2 id="代表樣本">代表樣本&lt;/h2>
&lt;ul>
&lt;li>SLO 與 error budget 讓團隊把可靠性變成可量化的工程目標。&lt;/li>
&lt;li>postmortem 將事故轉成可追蹤的 action items，而不是只留下檢討文字。&lt;/li>
&lt;li>toil budget 讓重複性工作變成可預算的工程投資。&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/readiness/" data-link-title="Readiness" data-link-desc="說明 instance 何時可以安全接收流量，以及 readiness 如何和部署平台協作">readiness&lt;/a> review 讓服務在上線前先過可靠性門檻。&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/on-call/" data-link-title="On-Call" data-link-desc="說明值班制度如何承接告警、事故分級與升級流程">on-call&lt;/a> 與 burnout 讓值班成為組織設計問題，脫離個人耐力測試的框架。&lt;/li>
&lt;li>hierarchy of reliability 讓 monitoring、testing、capacity、dev、product 串成一條路由。&lt;/li>
&lt;li>blameless culture 讓檢討聚焦在系統與流程，而不是個人責任。&lt;/li>
&lt;li>embedded SRE / consulting 讓可靠性能力可以以不同介入深度落到服務團隊。&lt;/li>
&lt;/ul>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://sre.google/">sre.google&lt;/a>：Google SRE 官方資源入口，收錄 books 與主題更新。&lt;/li>
&lt;li>&lt;a href="https://cloud.google.com/blog/products/devops-sre/the-sre-book-turns-6">The SRE book turns 6!&lt;/a>：整理 SRE Book / Workbook 與延伸資源的官方入口。&lt;/li>
&lt;li>&lt;a href="https://cloud.google.com/blog/products/devops-sre/how-to-design-good-slos-according-to-google-sres">Adopting SRE: Standardizing your SLO design process&lt;/a>：補 SLO 設計方法與實務語境。&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Google 是 SRE 概念的原始來源、SRE Book 與 Workbook 是領域 canonical text。教學重點在 SRE 工程文化、量化方法與組織節奏，單一事故只是入口。</p>
<h2 id="規劃重點">規劃重點</h2>
<ul>
<li>SLI / SLO / Error Budget：可靠性目標的量化方法、為何選 SLO 而非 100%</li>
<li>Postmortem 文化：blameless / action items / 行動追蹤的閉環設計</li>
<li>Toil 量化：把運維工作變成可預算的工程資產</li>
<li><a href="/blog/backend/knowledge-cards/on-call/" data-link-title="On-Call" data-link-desc="說明值班制度如何承接告警、事故分級與升級流程">on-call</a> 與 burnout：值班輪值、shadow / primary 結構、心理安全</li>
<li><a href="/blog/backend/knowledge-cards/readiness/" data-link-title="Readiness" data-link-desc="說明 instance 何時可以安全接收流量，以及 readiness 如何和部署平台協作">readiness</a> review：服務上線前的 SRE 接管門檻</li>
</ul>
<h2 id="預計收錄實踐">預計收錄實踐</h2>
<table>
  <thead>
      <tr>
          <th>議題</th>
          <th>教學重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>SRE Book Ch.1-4</td>
          <td>概念基礎、為何 SLO、為何 50/50</td>
      </tr>
      <tr>
          <td>Postmortem Culture</td>
          <td>blameless 操作化、action items 追蹤</td>
      </tr>
      <tr>
          <td>Toil &amp; Engineering Time</td>
          <td>量化 toil、長期投資工程的政策</td>
      </tr>
      <tr>
          <td>Hierarchy of Reliability</td>
          <td>Monitoring → IR → PIR → Testing → Capacity → Dev → Product</td>
      </tr>
      <tr>
          <td>Embedded SRE / Consulting</td>
          <td>SRE 介入服務的多種模式</td>
      </tr>
  </tbody>
</table>
<h2 id="章節列表">章節列表</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/google/error-budget-policy-and-release-gating/" data-link-title="Google：Error Budget 政策如何決定發布節奏" data-link-desc="把 SLO 消耗量轉成 release gate，讓可靠性與交付速度共用同一套決策語言。">G1</a></td>
          <td>Error Budget 與 Release Gating</td>
          <td>把 SLO 消耗量轉成放行、限速與凍結決策</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/google/postmortem-action-item-closure-governance/" data-link-title="Google：Postmortem Action Item Closure 治理" data-link-desc="把 blameless postmortem 從會議文件變成可追蹤的可靠性治理機制：action item 分級、完成條件與回寫節奏。">G2</a></td>
          <td>Postmortem Closure 治理</td>
          <td>把事故改進項變成可追蹤、可驗證的治理節奏</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/google/toil-budget-and-automation-investment-policy/" data-link-title="Google：Toil Budget 與 Automation 投資政策" data-link-desc="把 toil 從感受問題轉成預算問題：用時間配比與自動化回報機制，避免 on-call 壓力長期侵蝕可靠性工程。">G3</a></td>
          <td>Toil Budget 投資政策</td>
          <td>把手動運維工作轉成可預算、可回寫的工程投資</td>
      </tr>
  </tbody>
</table>
<h2 id="案例定位">案例定位</h2>
<p>Google 這個案例在講的是可靠性如何變成一套可操作的工程制度，而不是單一工具或單一事故。讀者先抓到 SLI / SLO、error budget、postmortem 與 toil 這幾個原語各自負責什麼，再把它們組成一條可執行的可靠性路徑。</p>
<h2 id="判讀重點">判讀重點</h2>
<p>當服務健康開始波動時，先看 SLO 是否真的被消耗，再看監控與告警是否能對應到使用者體感。當 <a href="/blog/backend/knowledge-cards/on-call/" data-link-title="On-Call" data-link-desc="說明值班制度如何承接告警、事故分級與升級流程">on-call</a> 壓力升高時，重點在團隊是否把重複性工作轉成可預算的工程投資，個人技巧層面的改善幫助有限。</p>
<h2 id="可操作判準">可操作判準</h2>
<ul>
<li>能否用一句話說明每個 SLI 對應的使用者行為</li>
<li>能否從 postmortem 找到明確 owner 與完成條件</li>
<li>能否把 toil 量化成可排程的工程時間</li>
<li>能否把監控、測試、容量、開發與產品決策串成同一條路由</li>
</ul>
<h2 id="與其他案例的關係">與其他案例的關係</h2>
<p>Google 提供的是可靠性的語言層，其他案例提供的是具體場景層。當讀者先懂 SLI / SLO 與 postmortem 這組原語，再看 Honeycomb 的 burn rate、Atlassian 的復原節奏或 GitHub 的 status communication，就能把抽象制度接到實際事故上。</p>
<h2 id="代表樣本">代表樣本</h2>
<ul>
<li>SLO 與 error budget 讓團隊把可靠性變成可量化的工程目標。</li>
<li>postmortem 將事故轉成可追蹤的 action items，而不是只留下檢討文字。</li>
<li>toil budget 讓重複性工作變成可預算的工程投資。</li>
<li><a href="/blog/backend/knowledge-cards/readiness/" data-link-title="Readiness" data-link-desc="說明 instance 何時可以安全接收流量，以及 readiness 如何和部署平台協作">readiness</a> review 讓服務在上線前先過可靠性門檻。</li>
<li><a href="/blog/backend/knowledge-cards/on-call/" data-link-title="On-Call" data-link-desc="說明值班制度如何承接告警、事故分級與升級流程">on-call</a> 與 burnout 讓值班成為組織設計問題，脫離個人耐力測試的框架。</li>
<li>hierarchy of reliability 讓 monitoring、testing、capacity、dev、product 串成一條路由。</li>
<li>blameless culture 讓檢討聚焦在系統與流程，而不是個人責任。</li>
<li>embedded SRE / consulting 讓可靠性能力可以以不同介入深度落到服務團隊。</li>
</ul>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://sre.google/">sre.google</a>：Google SRE 官方資源入口，收錄 books 與主題更新。</li>
<li><a href="https://cloud.google.com/blog/products/devops-sre/the-sre-book-turns-6">The SRE book turns 6!</a>：整理 SRE Book / Workbook 與延伸資源的官方入口。</li>
<li><a href="https://cloud.google.com/blog/products/devops-sre/how-to-design-good-slos-according-to-google-sres">Adopting SRE: Standardizing your SLO design process</a>：補 SLO 設計方法與實務語境。</li>
</ul>
]]></content:encoded></item><item><title>6.1 CI pipeline</title><link>https://tarrragon.github.io/blog/backend/06-reliability/ci-pipeline/</link><pubDate>Thu, 23 Apr 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/ci-pipeline/</guid><description>&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/ci-pipeline/" data-link-title="CI Pipeline" data-link-desc="說明持續整合流程如何在合併前驗證品質與相容性">CI pipeline&lt;/a> 把快速回饋、慢速驗證與可重現產物切成不同層，讓每次變更都能在一致條件下被判讀。&lt;/p>
&lt;p>這一層關心的是「變更能不能被穩定驗證」。pipeline 的價值在於分層、隔離與可追蹤，讓 flaky 訊號不會直接污染放行判斷。&lt;/p>
&lt;h2 id="核心判讀">核心判讀&lt;/h2>
&lt;p>CI 的健康度先看回饋節奏，再看訊號品質。fast path 應該覆蓋最常見的破壞面，slow path 負責深層驗證，artifact 則要能從同一份輸入重播。&lt;/p>
&lt;p>判讀時先看四件事：&lt;/p>
&lt;ul>
&lt;li>stage 是否按成本與風險分層&lt;/li>
&lt;li>artifact 是否重用，不是每次從 source 重建&lt;/li>
&lt;li>environment variables 是否封裝，避免跨環境漂移&lt;/li>
&lt;li>flaky test 是否有治理路徑，而不是只靠 retry&lt;/li>
&lt;/ul>
&lt;h2 id="分層策略">分層策略&lt;/h2>
&lt;p>CI 分層的責任是讓不同成本的驗證跑在不同時機，讓最常見的破壞面最快被攔住，高成本驗證只在值得時跑。&lt;/p>
&lt;h3 id="fast-path">Fast path&lt;/h3>
&lt;p>fast path 在每次 push 觸發，目標是 5 分鐘內回饋。涵蓋 lint、type check、unit test 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/contract/" data-link-title="Boundary Contract" data-link-desc="說明跨邊界約定如何維持相容與可驗證">contract&lt;/a> test。這一層只驗證單一變更的語法與邏輯正確性，不碰外部依賴。&lt;/p>
&lt;p>fast path 結果可信的條件是測試不依賴外部狀態。當 unit test 需要真實 DB 或 broker，它就不再屬於 fast path — 移到 slow path，或用 contract test 替代跨服務驗證。&lt;/p>
&lt;h3 id="slow-path">Slow path&lt;/h3>
&lt;p>slow path 在 merge request 觸發，允許較長執行時間（15-45 分鐘）。涵蓋 integration test、security scan、load baseline 與跨服務 schema 相容性。這一層用真實依賴驗證變更在服務邊界上的行為。&lt;/p>
&lt;p>Microsoft 的&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/microsoft/change-management-and-reliability-governance/" data-link-title="Microsoft：變更治理與可靠性門檻" data-link-desc="透過分層變更管理與發布閘門，降低大型 SaaS 平台的系統性回歸風險。">變更治理實踐&lt;/a>把變更按風險分層，高風險變更（schema migration、payment path、config rollout）走更完整的 slow path，低風險變更只需 fast path 通過。這種分層讓 CI 資源集中在真正需要深層驗證的變更上，同時維持低風險變更的交付速度。&lt;/p>
&lt;h3 id="scheduled-path">Scheduled path&lt;/h3>
&lt;p>scheduled path 定期（每日或每週）執行，涵蓋 full regression、&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/fuzz-campaign/" data-link-title="6.3 fuzz campaign" data-link-desc="用自動化輸入探索覆蓋未知邊界：target 設計、corpus 管理、crash reproduction 與 CI 整合">fuzz campaign&lt;/a>、chaos smoke test 與長時間 soak test。這一層驗證的是累積退化，而不是單次變更的破壞。&lt;/p>
&lt;p>scheduled path 的判讀不看單次 pass/fail，而是看趨勢：coverage delta 是否持續下降、fuzz corpus 是否收斂、regression 新增 failure 是否集中在特定模組。&lt;/p>
&lt;h2 id="artifact-管理">Artifact 管理&lt;/h2>
&lt;p>Artifact 讓同一份 build output 能從 CI 一路到 staging 到 production，每一步都可重播。&lt;/p>
&lt;p>immutable artifact 的核心約束是 build 一次、部署多次。CI 產出的 container image 或 binary 帶版本標籤（commit hash + build number），後續環境不重新 build，只替換 config。這樣才能確保 staging 驗證通過的產物跟 production 部署的產物是同一份。&lt;/p>
&lt;p>cache 策略影響 CI 回饋速度與可信度的平衡。dependency cache（npm / go mod / pip）加速 build，但需要定期 invalidation 避免過期依賴殘留。build output cache 則需要嚴格的 key 設計，確保 source 變更後不會沿用舊 artifact。&lt;/p>
&lt;p>Stripe 的&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/" data-link-title="Stripe：Idempotency 與零停機遷移的交易安全設計" data-link-desc="把 API 重試與資料遷移放在同一套安全模型，維持支付交易的一致結果。">零停機遷移實踐&lt;/a>對 artifact 有額外要求：交易路徑的變更需要 artifact 能重播到相同狀態，確保 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/idempotency/" data-link-title="Idempotency" data-link-desc="說明同一操作執行多次時如何保持結果一致">idempotency&lt;/a> 驗證在 CI 與 production 看到一致的行為。&lt;/p></description><content:encoded><![CDATA[<h2 id="概念定位">概念定位</h2>
<p><a href="/blog/backend/knowledge-cards/ci-pipeline/" data-link-title="CI Pipeline" data-link-desc="說明持續整合流程如何在合併前驗證品質與相容性">CI pipeline</a> 把快速回饋、慢速驗證與可重現產物切成不同層，讓每次變更都能在一致條件下被判讀。</p>
<p>這一層關心的是「變更能不能被穩定驗證」。pipeline 的價值在於分層、隔離與可追蹤，讓 flaky 訊號不會直接污染放行判斷。</p>
<h2 id="核心判讀">核心判讀</h2>
<p>CI 的健康度先看回饋節奏，再看訊號品質。fast path 應該覆蓋最常見的破壞面，slow path 負責深層驗證，artifact 則要能從同一份輸入重播。</p>
<p>判讀時先看四件事：</p>
<ul>
<li>stage 是否按成本與風險分層</li>
<li>artifact 是否重用，不是每次從 source 重建</li>
<li>environment variables 是否封裝，避免跨環境漂移</li>
<li>flaky test 是否有治理路徑，而不是只靠 retry</li>
</ul>
<h2 id="分層策略">分層策略</h2>
<p>CI 分層的責任是讓不同成本的驗證跑在不同時機，讓最常見的破壞面最快被攔住，高成本驗證只在值得時跑。</p>
<h3 id="fast-path">Fast path</h3>
<p>fast path 在每次 push 觸發，目標是 5 分鐘內回饋。涵蓋 lint、type check、unit test 與 <a href="/blog/backend/knowledge-cards/contract/" data-link-title="Boundary Contract" data-link-desc="說明跨邊界約定如何維持相容與可驗證">contract</a> test。這一層只驗證單一變更的語法與邏輯正確性，不碰外部依賴。</p>
<p>fast path 結果可信的條件是測試不依賴外部狀態。當 unit test 需要真實 DB 或 broker，它就不再屬於 fast path — 移到 slow path，或用 contract test 替代跨服務驗證。</p>
<h3 id="slow-path">Slow path</h3>
<p>slow path 在 merge request 觸發，允許較長執行時間（15-45 分鐘）。涵蓋 integration test、security scan、load baseline 與跨服務 schema 相容性。這一層用真實依賴驗證變更在服務邊界上的行為。</p>
<p>Microsoft 的<a href="/blog/backend/06-reliability/cases/microsoft/change-management-and-reliability-governance/" data-link-title="Microsoft：變更治理與可靠性門檻" data-link-desc="透過分層變更管理與發布閘門，降低大型 SaaS 平台的系統性回歸風險。">變更治理實踐</a>把變更按風險分層，高風險變更（schema migration、payment path、config rollout）走更完整的 slow path，低風險變更只需 fast path 通過。這種分層讓 CI 資源集中在真正需要深層驗證的變更上，同時維持低風險變更的交付速度。</p>
<h3 id="scheduled-path">Scheduled path</h3>
<p>scheduled path 定期（每日或每週）執行，涵蓋 full regression、<a href="/blog/backend/06-reliability/fuzz-campaign/" data-link-title="6.3 fuzz campaign" data-link-desc="用自動化輸入探索覆蓋未知邊界：target 設計、corpus 管理、crash reproduction 與 CI 整合">fuzz campaign</a>、chaos smoke test 與長時間 soak test。這一層驗證的是累積退化，而不是單次變更的破壞。</p>
<p>scheduled path 的判讀不看單次 pass/fail，而是看趨勢：coverage delta 是否持續下降、fuzz corpus 是否收斂、regression 新增 failure 是否集中在特定模組。</p>
<h2 id="artifact-管理">Artifact 管理</h2>
<p>Artifact 讓同一份 build output 能從 CI 一路到 staging 到 production，每一步都可重播。</p>
<p>immutable artifact 的核心約束是 build 一次、部署多次。CI 產出的 container image 或 binary 帶版本標籤（commit hash + build number），後續環境不重新 build，只替換 config。這樣才能確保 staging 驗證通過的產物跟 production 部署的產物是同一份。</p>
<p>cache 策略影響 CI 回饋速度與可信度的平衡。dependency cache（npm / go mod / pip）加速 build，但需要定期 invalidation 避免過期依賴殘留。build output cache 則需要嚴格的 key 設計，確保 source 變更後不會沿用舊 artifact。</p>
<p>Stripe 的<a href="/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/" data-link-title="Stripe：Idempotency 與零停機遷移的交易安全設計" data-link-desc="把 API 重試與資料遷移放在同一套安全模型，維持支付交易的一致結果。">零停機遷移實踐</a>對 artifact 有額外要求：交易路徑的變更需要 artifact 能重播到相同狀態，確保 <a href="/blog/backend/knowledge-cards/idempotency/" data-link-title="Idempotency" data-link-desc="說明同一操作執行多次時如何保持結果一致">idempotency</a> 驗證在 CI 與 production 看到一致的行為。</p>
<h2 id="flaky-test-治理">Flaky test 治理</h2>
<p>flaky test 的責任是讓 CI 訊號維持可信度。當 flaky 率持續上升，團隊會開始忽略 CI 結果，pipeline 從可靠性 gate 退化成形式流程。</p>
<h3 id="識別">識別</h3>
<p>flaky 識別靠 retry 分析。當同一個 test case 在同一份 commit 上連續跑出不同結果，那就是 flaky 候選。按連續失敗 / 成功交替的頻率排序，比按失敗率排序更能抓到高噪音來源。</p>
<h3 id="隔離">隔離</h3>
<p>quarantine queue 是把已識別的 flaky test 從 gate-blocking path 移到 non-blocking path。quarantine 的目的是保護 gate 判讀可信度，同時維持 flaky 修復的追蹤壓力。quarantine 不是永久停靠 — 超過修復期限的 flaky test 必須決定是修復還是刪除。</p>
<h3 id="判讀門檻">判讀門檻</h3>
<p>flaky 率超過 5% 時，CI gate 的訊號開始失真：團隊無法確定 failure 是真回歸還是 flaky。超過 10% 時，CI pipeline 實質上失去 gate 功能 — retry 變成常態，failure 預設被忽略。此時應暫停新功能開發，集中修復 flaky backlog。這些門檻是基於中大型測試套件（500+ test cases）的經驗值。測試套件較小時，單一 flaky test 的比率衝擊更大，門檻應更低。</p>
<h2 id="environment-隔離">Environment 隔離</h2>
<p>CI 環境的隔離程度決定了測試結果的可信度下限。</p>
<h3 id="runner-隔離">Runner 隔離</h3>
<p>shared runner 會把不同 PR 的測試跑在同一台機器上。當 integration test 需要佔用 port、寫入 local state 或消耗大量記憶體，跨 job 干擾就會出現。ephemeral runner（每次 job 用乾淨環境）消除這類問題，但成本更高。判斷點是測試是否依賴 local state — 有依賴就用 ephemeral。</p>
<h3 id="secret-管理">Secret 管理</h3>
<p>CI secret（API key、DB credential、cloud token）需要按環境隔離。staging secret 不應該在 PR pipeline 可用，production secret 不應該在 staging pipeline 可用。secret 洩露的常見路徑是 CI log 輸出與 artifact 殘留 — 兩處都需要遮罩。</p>
<h3 id="load-test-資源池">Load test 資源池</h3>
<p>LinkedIn 的<a href="/blog/backend/06-reliability/cases/linkedin/capacity-headroom-and-oncall-tiering/" data-link-title="LinkedIn：Capacity Headroom 與 On-call 分層" data-link-desc="把容量預測與值班分層綁在一起，降低高峰時段的升級混亂與恢復延遲。">容量 headroom 實踐</a>把自動化壓測接進 CI。當 load test 跑在 CI 環境時，需要獨立資源池，避免壓測流量影響其他 pipeline job 的執行速度與穩定性。load test runner 的 quota 跟一般 CI runner 分開管理。</p>
<h2 id="ci-作為-release-gate-輸入">CI 作為 Release Gate 輸入</h2>
<p>CI 的最終產出不只是 pass/fail，而是一組可供 <a href="/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">release gate</a> 判讀的 evidence。</p>
<table>
  <thead>
      <tr>
          <th>產出</th>
          <th>判讀用途</th>
          <th>下游消費者</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>pipeline status</td>
          <td>所有 stage 是否通過</td>
          <td><a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate</a></td>
      </tr>
      <tr>
          <td>test coverage delta</td>
          <td>本次變更是否降低覆蓋率</td>
          <td><a href="/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13 perf regression gate</a></td>
      </tr>
      <tr>
          <td>artifact checksum</td>
          <td>部署產物是否與 CI 產出一致</td>
          <td><a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23 evidence handoff</a></td>
      </tr>
      <tr>
          <td>flaky rate snapshot</td>
          <td>gate 判讀可信度是否在可接受範圍</td>
          <td><a href="/blog/backend/06-reliability/reliability-metrics-governance/" data-link-title="6.18 Reliability Metrics Governance" data-link-desc="DORA / SPACE 指標的選用、量測陷阱、anti-gaming 與團隊階段適配">6.18 reliability metrics</a></td>
      </tr>
  </tbody>
</table>
<p>Google 的 <a href="/blog/backend/06-reliability/cases/google/error-budget-policy-and-release-gating/" data-link-title="Google：Error Budget 政策如何決定發布節奏" data-link-desc="把 SLO 消耗量轉成 release gate，讓可靠性與交付速度共用同一套決策語言。">error budget 政策</a>把 CI 定位成 release gate 的前置訊號來源：CI pipeline 產出的 evidence 直接進入 error budget 判讀流程。當 budget 消耗加速時，CI gate 的門檻隨之提高 — 從只需 fast path 通過，升級到要求 slow path 全部通過加人工 review。</p>
<h2 id="案例對照">案例對照</h2>
<ul>
<li><a href="/blog/backend/06-reliability/cases/google/error-budget-policy-and-release-gating/" data-link-title="Google：Error Budget 政策如何決定發布節奏" data-link-desc="把 SLO 消耗量轉成 release gate，讓可靠性與交付速度共用同一套決策語言。">Google</a>：CI pipeline status 是 error budget 政策的前置訊號，budget 消耗速度直接影響 CI gate 門檻高低。</li>
<li><a href="/blog/backend/06-reliability/cases/microsoft/change-management-and-reliability-governance/" data-link-title="Microsoft：變更治理與可靠性門檻" data-link-desc="透過分層變更管理與發布閘門，降低大型 SaaS 平台的系統性回歸風險。">Microsoft</a>：按變更風險分層走不同 CI path，高風險變更需要更完整的 slow path 驗證。</li>
<li><a href="/blog/backend/06-reliability/cases/linkedin/capacity-headroom-and-oncall-tiering/" data-link-title="LinkedIn：Capacity Headroom 與 On-call 分層" data-link-desc="把容量預測與值班分層綁在一起，降低高峰時段的升級混亂與恢復延遲。">LinkedIn L1</a>：容量 headroom 綁值班分層，CI 回饋是容量決策的輸入。</li>
<li><a href="/blog/backend/06-reliability/cases/linkedin/automated-load-testing-and-capacity-forecasting/" data-link-title="LinkedIn：Automated Load Testing 與 Capacity Forecasting" data-link-desc="持續壓測驅動容量預測：用自動化回饋取代一次性壓測的容量規劃。">LinkedIn L2</a>：自動化壓測接進 CI，load test 需要獨立資源池避免干擾其他 pipeline job。</li>
<li><a href="/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/" data-link-title="Stripe：Idempotency 與零停機遷移的交易安全設計" data-link-desc="把 API 重試與資料遷移放在同一套安全模型，維持支付交易的一致結果。">Stripe</a>：交易路徑的 idempotency 測試在 CI 跑，artifact 必須能重播到相同狀態。</li>
</ul>
<h2 id="判讀訊號">判讀訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>意義</th>
          <th>行動建議</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>CI 時長 &gt; 30 min</td>
          <td>fast path 混入了 slow path 測試</td>
          <td>重新分層，把 integration test 移到 merge gate</td>
      </tr>
      <tr>
          <td>fast / slow 沒分層</td>
          <td>每次 push 跑全部測試，回饋太慢</td>
          <td>拆 fast path（&lt; 5 min）與 slow path（&lt; 45 min）</td>
      </tr>
      <tr>
          <td>flaky 率 &gt; 5%</td>
          <td>gate 判讀可信度開始下降</td>
          <td>啟動 quarantine + 集中修復週期</td>
      </tr>
      <tr>
          <td>artifact 每次重建</td>
          <td>無法確認 staging 跟 production 同份</td>
          <td>改成 build once、deploy many</td>
      </tr>
      <tr>
          <td>env var 跨環境寫死</td>
          <td>staging 與 prod 行為不同</td>
          <td>改用 per-environment secret injection</td>
      </tr>
      <tr>
          <td>retry 成功率 &gt; 20% 且被視為 pipeline 通過</td>
          <td>真回歸被 flaky retry 遮蓋</td>
          <td>retry pass 不等於 gate pass，需人工確認</td>
      </tr>
      <tr>
          <td>flaky test 無 owner、修復靠志願者</td>
          <td>test 跟 team 責任未對齊</td>
          <td>建立 test ownership registry、每個 test file 或 suite 有明確 owner team</td>
      </tr>
  </tbody>
</table>
<h2 id="交接路由">交接路由</h2>
<ul>
<li><a href="/blog/backend/06-reliability/contract-testing/" data-link-title="6.10 Contract Testing 與 Schema 演進" data-link-desc="把跨服務 / API / event schema 的隱性期待變成可驗證契約，控制演進相容性">6.10 contract testing</a>：把跨服務契約納入 CI fast path</li>
<li><a href="/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13 perf regression gate</a>：把效能 baseline 變成 CI slow path gate</li>
<li><a href="/blog/backend/06-reliability/environment-parity/" data-link-title="6.15 Environment Parity 與漂移控制" data-link-desc="把 staging / preprod / prod 之間的差異視為一級風險，按漂移來源分類偵測與治理">6.15 environment parity</a>：CI 環境隔離是 parity 的前置條件</li>
<li><a href="/blog/backend/06-reliability/test-data-management/" data-link-title="6.16 Test Data Management" data-link-desc="把 fixture / seed / production-like data 作為跨模組共用 artifact，治理資料層次、遮罩策略與可重現性">6.16 test data</a>：把 fixture / seed 納入 CI artifact 管理</li>
<li><a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate</a>：CI evidence 是 release gate 的主要輸入</li>
<li><a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23 evidence handoff</a>：CI artifact checksum 進入證據交接</li>
</ul>
]]></content:encoded></item><item><title>GitHub Actions：Environment Protection 與 OIDC Cloud Auth</title><link>https://tarrragon.github.io/blog/backend/06-reliability/vendors/github-actions/environment-protection-and-oidc-cloud-auth/</link><pubDate>Tue, 23 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/vendors/github-actions/environment-protection-and-oidc-cloud-auth/</guid><description>&lt;h2 id="問題情境">問題情境&lt;/h2>
&lt;p>CI pipeline 的可靠性驗證在測試階段結束後，還需要兩道控制面才算完整。第一道是 deploy approval gate — 決定誰可以核准 production deploy、在什麼條件下放行。第二道是 credential 安全 — deploy 需要 cloud credential，但 long-lived secret 存在 CI 環境中會擴大洩漏面。&lt;/p>
&lt;p>GitHub Actions 用 environment protection rules 處理第一道，用 OIDC federation 處理第二道。兩者搭配讓 deploy 流程同時滿足 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate&lt;/a> 的放行控制與 &lt;a href="https://tarrragon.github.io/blog/backend/07-security-data-protection/" data-link-title="模組七：資安與資料保護" data-link-desc="以問題驅動方式擴充資安知識網：先定義服務環節問題，再以案例作為觸發式參考">07 資安&lt;/a> 的 credential 最小暴露原則。&lt;/p>
&lt;h2 id="environment-protection-rules">Environment Protection Rules&lt;/h2>
&lt;p>Environment 是 GitHub Actions 的 deploy 分層單位。每個 environment（staging / canary / production）可以獨立設定 protection rules，讓不同風險等級的 deploy 走不同的放行流程。&lt;/p>
&lt;h3 id="protection-rule-類型">Protection rule 類型&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>規則&lt;/th>
 &lt;th>責任&lt;/th>
 &lt;th>典型設定&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Required reviewers&lt;/td>
 &lt;td>指定人員核准後才能 deploy&lt;/td>
 &lt;td>production 需 2 人核准&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Wait timer&lt;/td>
 &lt;td>deploy 前強制等待，讓最後一刻能攔住&lt;/td>
 &lt;td>production 等 15 分鐘&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Deployment branch policy&lt;/td>
 &lt;td>只允許特定 branch deploy 到該 environment&lt;/td>
 &lt;td>production 只接受 main / release/*&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Required reviewers 是 deploy 層的 release gate。當 workflow job 標記 &lt;code>environment: production&lt;/code>，GitHub 會暫停 job 直到指定 reviewer 核准。reviewer 的選擇應對齊服務 ownership — 由該服務的 on-call lead 或 tech lead 核准，避免核准權過於集中或分散。&lt;/p>
&lt;p>Wait timer 提供一個緩衝窗口。deploy 前等待 N 分鐘讓團隊有時間檢查 staging 結果、確認沒有進行中的事故、或在發現問題時取消 deploy。timer 長度跟服務風險等級對齊 — 低風險服務可以 0 分鐘，交易路徑可以 15-30 分鐘。&lt;/p>
&lt;p>Deployment branch policy 限制哪些 branch 可以觸發特定 environment 的 deploy。這防止 feature branch 意外 deploy 到 production。production 通常只接受 main 或 release branch。&lt;/p>
&lt;h3 id="分層建議">分層建議&lt;/h3>
&lt;p>staging 用自動 deploy — push 到 staging branch 直接觸發 workflow，無需 approval，回饋速度最大化。production 用 required reviewer + wait timer — 確保每次 production deploy 都經過人工確認與緩衝。canary 介於兩者之間 — 可以自動 deploy 但加 wait timer，讓觀測指標有時間反映。&lt;/p>
&lt;h2 id="oidc-cloud-auth">OIDC Cloud Auth&lt;/h2>
&lt;h3 id="long-lived-credential-的風險">Long-lived credential 的風險&lt;/h3>
&lt;p>CI deploy 需要 cloud credential（AWS access key / GCP service account key / Azure service principal）。傳統做法是把這些 credential 存在 GitHub repository secret 或 environment secret 中。long-lived credential 的風險在於：洩漏後攻擊者可以長期使用、rotation 需要手動更新 CI 設定、credential scope 常設得比實際需求更大。&lt;/p></description><content:encoded><![CDATA[<h2 id="問題情境">問題情境</h2>
<p>CI pipeline 的可靠性驗證在測試階段結束後，還需要兩道控制面才算完整。第一道是 deploy approval gate — 決定誰可以核准 production deploy、在什麼條件下放行。第二道是 credential 安全 — deploy 需要 cloud credential，但 long-lived secret 存在 CI 環境中會擴大洩漏面。</p>
<p>GitHub Actions 用 environment protection rules 處理第一道，用 OIDC federation 處理第二道。兩者搭配讓 deploy 流程同時滿足 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate</a> 的放行控制與 <a href="/blog/backend/07-security-data-protection/" data-link-title="模組七：資安與資料保護" data-link-desc="以問題驅動方式擴充資安知識網：先定義服務環節問題，再以案例作為觸發式參考">07 資安</a> 的 credential 最小暴露原則。</p>
<h2 id="environment-protection-rules">Environment Protection Rules</h2>
<p>Environment 是 GitHub Actions 的 deploy 分層單位。每個 environment（staging / canary / production）可以獨立設定 protection rules，讓不同風險等級的 deploy 走不同的放行流程。</p>
<h3 id="protection-rule-類型">Protection rule 類型</h3>
<table>
  <thead>
      <tr>
          <th>規則</th>
          <th>責任</th>
          <th>典型設定</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Required reviewers</td>
          <td>指定人員核准後才能 deploy</td>
          <td>production 需 2 人核准</td>
      </tr>
      <tr>
          <td>Wait timer</td>
          <td>deploy 前強制等待，讓最後一刻能攔住</td>
          <td>production 等 15 分鐘</td>
      </tr>
      <tr>
          <td>Deployment branch policy</td>
          <td>只允許特定 branch deploy 到該 environment</td>
          <td>production 只接受 main / release/*</td>
      </tr>
  </tbody>
</table>
<p>Required reviewers 是 deploy 層的 release gate。當 workflow job 標記 <code>environment: production</code>，GitHub 會暫停 job 直到指定 reviewer 核准。reviewer 的選擇應對齊服務 ownership — 由該服務的 on-call lead 或 tech lead 核准，避免核准權過於集中或分散。</p>
<p>Wait timer 提供一個緩衝窗口。deploy 前等待 N 分鐘讓團隊有時間檢查 staging 結果、確認沒有進行中的事故、或在發現問題時取消 deploy。timer 長度跟服務風險等級對齊 — 低風險服務可以 0 分鐘，交易路徑可以 15-30 分鐘。</p>
<p>Deployment branch policy 限制哪些 branch 可以觸發特定 environment 的 deploy。這防止 feature branch 意外 deploy 到 production。production 通常只接受 main 或 release branch。</p>
<h3 id="分層建議">分層建議</h3>
<p>staging 用自動 deploy — push 到 staging branch 直接觸發 workflow，無需 approval，回饋速度最大化。production 用 required reviewer + wait timer — 確保每次 production deploy 都經過人工確認與緩衝。canary 介於兩者之間 — 可以自動 deploy 但加 wait timer，讓觀測指標有時間反映。</p>
<h2 id="oidc-cloud-auth">OIDC Cloud Auth</h2>
<h3 id="long-lived-credential-的風險">Long-lived credential 的風險</h3>
<p>CI deploy 需要 cloud credential（AWS access key / GCP service account key / Azure service principal）。傳統做法是把這些 credential 存在 GitHub repository secret 或 environment secret 中。long-lived credential 的風險在於：洩漏後攻擊者可以長期使用、rotation 需要手動更新 CI 設定、credential scope 常設得比實際需求更大。</p>
<h3 id="oidc-federation-的運作方式">OIDC federation 的運作方式</h3>
<p>GitHub Actions 支援作為 OIDC identity provider。workflow 在執行時可以向 GitHub 請求一個 short-lived OIDC token，cloud provider 信任這個 token 後發出 short-lived cloud credential。整個流程不需要在 CI 環境中存放任何 long-lived secret。</p>
<p>流程：workflow 啟動 → 向 GitHub OIDC provider 請求 token → token 帶有 repo / branch / environment 等 claim → cloud provider 的 trust policy 驗證 claim → 發出 short-lived credential（通常 1 小時有效期）。</p>
<h3 id="cloud-provider-配置">Cloud provider 配置</h3>
<p><strong>AWS</strong>：在 IAM 設定 OIDC identity provider（issuer: <code>token.actions.githubusercontent.com</code>）、建立 IAM role 並設定 trust policy 限制 repo + branch + environment。workflow 中用 <code>aws-actions/configure-aws-credentials</code> action 取得 session credential。</p>
<p><strong>GCP</strong>：設定 Workload Identity Federation pool + provider、建立 service account 並綁定 pool。workflow 中用 <code>google-github-actions/auth</code> action 取得 short-lived token。</p>
<p><strong>Azure</strong>：在 Azure AD 設定 federated credential 給 app registration、限制 repo + branch + environment。workflow 中用 <code>azure/login</code> action。</p>
<h3 id="trust-policy-的安全邊界">Trust policy 的安全邊界</h3>
<p>OIDC trust policy 必須限制到特定 repo、branch 與 environment。trust policy 寫成 wildcard（信任整個 GitHub org 的所有 repo）等於讓 org 內任何 repo 的 workflow 都能取得 cloud credential。最小權限原則：production environment 的 trust policy 只信任 <code>repo:org/service:environment:production</code>，不信任其他 environment 或 branch。</p>
<h2 id="實作範例">實作範例</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># .github/workflows/deploy.yml</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Deploy</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">  </span><span class="nt">push</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">main]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w"></span><span class="nt">permissions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">  </span><span class="nt">id-token</span><span class="p">:</span><span class="w"> </span><span class="l">write</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">  </span><span class="nt">contents</span><span class="p">:</span><span class="w"> </span><span class="l">read</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w"></span><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">  </span><span class="nt">deploy-staging</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w"> </span><span class="l">staging</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">aws-actions/configure-aws-credentials@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">          </span><span class="nt">role-to-assume</span><span class="p">:</span><span class="w"> </span><span class="l">arn:aws:iam::123456789012:role/staging-deploy</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">          </span><span class="nt">aws-region</span><span class="p">:</span><span class="w"> </span><span class="l">ap-northeast-1</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">      </span>- <span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">./scripts/deploy.sh staging</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">  </span><span class="nt">deploy-production</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">    </span><span class="nt">needs</span><span class="p">:</span><span class="w"> </span><span class="l">deploy-staging</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w"> </span><span class="l">production</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">aws-actions/configure-aws-credentials@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">          </span><span class="nt">role-to-assume</span><span class="p">:</span><span class="w"> </span><span class="l">arn:aws:iam::123456789012:role/production-deploy</span><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">          </span><span class="nt">aws-region</span><span class="p">:</span><span class="w"> </span><span class="l">ap-northeast-1</span><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">      </span>- <span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">./scripts/deploy.sh production</span></span></span></code></pre></div><p>staging job 自動觸發。production job 等 staging 完成後暫停，等待 environment protection rules 中設定的 reviewer 核准。兩個 job 各自用不同的 IAM role，scope 分離。</p>
<p>Environment secret 與 repository secret 的差異：environment secret 只在該 environment 的 job 中可用。把 production-only 的設定（如 database connection string）存在 production environment secret 而非 repository secret，避免 staging workflow 意外存取 production 資源。</p>
<h2 id="邊界與陷阱">邊界與陷阱</h2>
<p>Environment protection rules 在 private repo 上需要 GitHub Team 或 Enterprise 方案。Free 方案的 private repo 無法使用 required reviewers 與 wait timer，只有 public repo 或付費方案可用。</p>
<p>OIDC trust policy 的常見錯誤是 subject claim 設定太寬。<code>sub</code> claim 的格式是 <code>repo:{owner}/{repo}:environment:{name}</code>（使用 environment 時）或 <code>repo:{owner}/{repo}:ref:refs/heads/{branch}</code>（不使用 environment 時）。用 wildcard match 或省略 environment 限制會讓非預期的 workflow 取得 credential。</p>
<p>Wait timer 設定要跟服務風險等級對齊。所有服務統一用 30 分鐘 wait timer 會拖慢低風險服務的 deploy velocity。對齊方式：低風險服務 0 分鐘、中風險 5-10 分鐘、高風險（交易路徑）15-30 分鐘。</p>
<p>Required reviewer 數量跟團隊大小對齊。只有 1 個 reviewer 等於沒有四眼原則；需要 5 個 reviewer 會造成 approval 排隊。2-3 個 reviewer 是多數團隊的平衡點。</p>
<h2 id="整合路由">整合路由</h2>
<ul>
<li>上游：<a href="/blog/backend/06-reliability/ci-pipeline/" data-link-title="6.1 CI pipeline" data-link-desc="CI pipeline 的分層策略、artifact 管理、flaky 治理與 release gate 輸入">6.1 CI pipeline</a>（CI gate 通過後才進入 deploy 階段）</li>
<li>下游：<a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate</a>（environment protection 是 deploy 層的 release gate）</li>
<li>下游：<a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23 verification evidence handoff</a>（deploy 結果作為 release evidence）</li>
<li>平行：<a href="/blog/backend/06-reliability/vendors/circleci/" data-link-title="CircleCI" data-link-desc="CI/CD 平台、強 cache 與 parallelism">CircleCI</a> contexts + approval jobs（同類功能的不同實作）</li>
<li>案例回寫：<a href="/blog/backend/06-reliability/cases/microsoft/change-management-and-reliability-governance/" data-link-title="Microsoft：變更治理與可靠性門檻" data-link-desc="透過分層變更管理與發布閘門，降低大型 SaaS 平台的系統性回歸風險。">Microsoft 變更分層</a>（變更風險分層對應 environment 分層）、<a href="/blog/backend/06-reliability/cases/google/error-budget-policy-and-release-gating/" data-link-title="Google：Error Budget 政策如何決定發布節奏" data-link-desc="把 SLO 消耗量轉成 release gate，讓可靠性與交付速度共用同一套決策語言。">Google Error Budget</a>（error budget 消耗時提高 gate 門檻 → 可動態調整 required reviewer 數量）</li>
</ul>
]]></content:encoded></item><item><title>CircleCI</title><link>https://tarrragon.github.io/blog/backend/06-reliability/vendors/circleci/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/vendors/circleci/</guid><description>&lt;p>CircleCI 是獨立 CI/CD 平台、承擔三個責任：強進階 cache（layer-aware）+ parallelism（test splitting）、跨 VCS（GitHub / Bitbucket / GitLab）、resource class 彈性（含 macOS / ARM / GPU）。設計取捨偏向「進階 cache + 並行加速 + cross-VCS」、適合需要極致 build speed 跟 macOS runner 的團隊。&lt;/p>
&lt;h2 id="本章目標">本章目標&lt;/h2>
&lt;p>讀完本章後、你應該能：&lt;/p>
&lt;ol>
&lt;li>寫 .circleci/config.yml workflow&lt;/li>
&lt;li>設計 cache + workspace 加速 build&lt;/li>
&lt;li>用 parallelism + test splitting&lt;/li>
&lt;li>選 resource class（CPU / memory / macOS / GPU）&lt;/li>
&lt;li>評估 CircleCI vs GitHub Actions 的選用&lt;/li>
&lt;/ol>
&lt;h2 id="最短路徑5-分鐘把-circleci-跑起來">最短路徑：5 分鐘把 CircleCI 跑起來&lt;/h2>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c"># .circleci/config.yml&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">version&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">2.1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">jobs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">test&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">docker&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>{&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">cimg/node:20}]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">steps&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="l">checkout&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">run&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">npm test&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">workflows&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ci&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">jobs&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="l">test]&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="日常操作與決策形狀">日常操作與決策形狀&lt;/h2>
&lt;h3 id="pipeline--workflow--job-模型">Pipeline / workflow / job 模型&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>Pipeline（一次 trigger 的執行）&lt;/li>
&lt;li>Workflow（多 job 編排、DAG）&lt;/li>
&lt;li>Job（一組 step）&lt;/li>
&lt;li>對應指令範例：&lt;code>circleci local execute&lt;/code>（本地測 config）&lt;/li>
&lt;/ul>
&lt;h3 id="orb-重用">Orb 重用&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>Orb = package of reusable config（types / commands / jobs / executors）&lt;/li>
&lt;li>Public orb registry（circleci.com/developer/orbs）&lt;/li>
&lt;li>Private orb for company&lt;/li>
&lt;/ul>
&lt;h3 id="cache--workspace">Cache + workspace&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>Cache：跨 build 保留（dependency / build artifact）&lt;/li>
&lt;li>Workspace：同 workflow 內 job 之間傳遞&lt;/li>
&lt;li>Cache key 設計（與 GitHub Actions 類似）&lt;/li>
&lt;/ul>
&lt;h2 id="進階主題按需閱讀">進階主題（按需閱讀）&lt;/h2>
&lt;h3 id="parallelism--test-splitting">Parallelism + test splitting&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>Job parallelism N&lt;/li>
&lt;li>Test splitting by timing / name / class&lt;/li>
&lt;li>對應 test suite 加速&lt;/li>
&lt;/ul>
&lt;h3 id="resource-class">Resource class&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>small / medium / large / xlarge / 2xlarge&lt;/li>
&lt;li>macOS / Arm / GPU classes&lt;/li>
&lt;li>跟 cost 平衡&lt;/li>
&lt;/ul>
&lt;h3 id="self-hosted-runner">Self-hosted runner&lt;/h3>
&lt;p>子議題：&lt;/p></description><content:encoded><![CDATA[<p>CircleCI 是獨立 CI/CD 平台、承擔三個責任：強進階 cache（layer-aware）+ parallelism（test splitting）、跨 VCS（GitHub / Bitbucket / GitLab）、resource class 彈性（含 macOS / ARM / GPU）。設計取捨偏向「進階 cache + 並行加速 + cross-VCS」、適合需要極致 build speed 跟 macOS runner 的團隊。</p>
<h2 id="本章目標">本章目標</h2>
<p>讀完本章後、你應該能：</p>
<ol>
<li>寫 .circleci/config.yml workflow</li>
<li>設計 cache + workspace 加速 build</li>
<li>用 parallelism + test splitting</li>
<li>選 resource class（CPU / memory / macOS / GPU）</li>
<li>評估 CircleCI vs GitHub Actions 的選用</li>
</ol>
<h2 id="最短路徑5-分鐘把-circleci-跑起來">最短路徑：5 分鐘把 CircleCI 跑起來</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># .circleci/config.yml</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="m">2.1</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"></span><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">  </span><span class="nt">test</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="nt">docker</span><span class="p">:</span><span class="w"> </span><span class="p">[</span>{<span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">cimg/node:20}]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">      </span>- <span class="l">checkout</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">      </span>- <span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="l">npm test</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w"></span><span class="nt">workflows</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">  </span><span class="nt">ci</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="nt">jobs</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">test]</span></span></span></code></pre></div><h2 id="日常操作與決策形狀">日常操作與決策形狀</h2>
<h3 id="pipeline--workflow--job-模型">Pipeline / workflow / job 模型</h3>
<p>子議題：</p>
<ul>
<li>Pipeline（一次 trigger 的執行）</li>
<li>Workflow（多 job 編排、DAG）</li>
<li>Job（一組 step）</li>
<li>對應指令範例：<code>circleci local execute</code>（本地測 config）</li>
</ul>
<h3 id="orb-重用">Orb 重用</h3>
<p>子議題：</p>
<ul>
<li>Orb = package of reusable config（types / commands / jobs / executors）</li>
<li>Public orb registry（circleci.com/developer/orbs）</li>
<li>Private orb for company</li>
</ul>
<h3 id="cache--workspace">Cache + workspace</h3>
<p>子議題：</p>
<ul>
<li>Cache：跨 build 保留（dependency / build artifact）</li>
<li>Workspace：同 workflow 內 job 之間傳遞</li>
<li>Cache key 設計（與 GitHub Actions 類似）</li>
</ul>
<h2 id="進階主題按需閱讀">進階主題（按需閱讀）</h2>
<h3 id="parallelism--test-splitting">Parallelism + test splitting</h3>
<p>子議題：</p>
<ul>
<li>Job parallelism N</li>
<li>Test splitting by timing / name / class</li>
<li>對應 test suite 加速</li>
</ul>
<h3 id="resource-class">Resource class</h3>
<p>子議題：</p>
<ul>
<li>small / medium / large / xlarge / 2xlarge</li>
<li>macOS / Arm / GPU classes</li>
<li>跟 cost 平衡</li>
</ul>
<h3 id="self-hosted-runner">Self-hosted runner</h3>
<p>子議題：</p>
<ul>
<li>Runner agent</li>
<li>適合：內網 / 特殊環境</li>
</ul>
<h3 id="oidc-integration">OIDC integration</h3>
<p>子議題：</p>
<ul>
<li>OIDC token → AWS / GCP（無 long-lived secret）</li>
<li>跟 GitHub Actions 同 pattern</li>
</ul>
<h3 id="approval-job">Approval job</h3>
<p>子議題：</p>
<ul>
<li>type: approval job：人工介入</li>
<li>對應 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate</a></li>
</ul>
<h3 id="cross-vcs-support">Cross-VCS support</h3>
<p>子議題：</p>
<ul>
<li>GitHub / Bitbucket / GitLab</li>
<li>跟 GitHub Actions 只 GitHub 對比</li>
</ul>
<h2 id="排錯快速判讀">排錯快速判讀</h2>
<h3 id="build-慢">Build 慢</h3>
<p>操作原則：cache miss / test 沒 split / resource class 太小。</p>
<h3 id="cache-不命中">Cache 不命中</h3>
<p>操作原則：cache key 設計問題 / key change。</p>
<h3 id="parallelism-不均勻">Parallelism 不均勻</h3>
<p>操作原則：test split strategy（timing 最好但要 historical data）。</p>
<h3 id="approval-卡住">Approval 卡住</h3>
<p>操作原則：approval job 沒人按 / on-call 不在。</p>
<h2 id="何時改走其他服務">何時改走其他服務</h2>
<table>
  <thead>
      <tr>
          <th>需求形狀</th>
          <th>改走</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>GitHub-hosted</td>
          <td><a href="/blog/backend/06-reliability/vendors/github-actions/" data-link-title="GitHub Actions" data-link-desc="GitHub 原生 CI/CD、PR check、deploy gate">GitHub Actions</a></td>
      </tr>
      <tr>
          <td>Self-hosted enterprise</td>
          <td>Jenkins / Buildkite / Tekton</td>
      </tr>
      <tr>
          <td>GitLab-hosted</td>
          <td>GitLab CI</td>
      </tr>
      <tr>
          <td>複雜 DAG / K8s-native</td>
          <td>Tekton / Argo Workflows</td>
      </tr>
      <tr>
          <td>預算敏感</td>
          <td>GitHub Actions / self-hosted Jenkins</td>
      </tr>
  </tbody>
</table>
<h2 id="不在本頁內的主題">不在本頁內的主題</h2>
<ul>
<li>各 Orb 細節</li>
<li>CircleCI Server（self-host enterprise）</li>
<li>Pricing 細節</li>
</ul>
<h2 id="案例回寫">案例回寫</h2>
<table>
  <thead>
      <tr>
          <th>案例方向</th>
          <th>對應主題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/" data-link-title="Stripe：Idempotency 與零停機遷移的交易安全設計" data-link-desc="把 API 重試與資料遷移放在同一套安全模型，維持支付交易的一致結果。">Stripe：Idempotency 與零停機遷移</a></td>
          <td>canary deploy / approval job 的部署節奏</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/shopify/bfcm-capacity-and-game-day/" data-link-title="Shopify：BFCM 容量治理與 Game Day 驗證節奏" data-link-desc="把季節性流量峰值轉成年度可靠性流程，透過容量模型、演練與隔離策略提前吸收風險。">Shopify：BFCM 容量治理與 Game Day</a></td>
          <td>峰值前 CI workflow 跑 capacity test</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/microsoft/change-management-and-reliability-governance/" data-link-title="Microsoft：變更治理與可靠性門檻" data-link-desc="透過分層變更管理與發布閘門，降低大型 SaaS 平台的系統性回歸風險。">Microsoft：變更治理與可靠性門檻</a></td>
          <td>approval job 對應變更分層審查</td>
      </tr>
  </tbody>
</table>
<p><strong>待補 CircleCI customer case</strong>：大規模 CircleCI 採用、macOS / iOS CI 加速案例、CircleCI → GitHub Actions 遷移案例。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>上游概念：<a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate</a></li>
<li>平行 vendor：<a href="/blog/backend/06-reliability/vendors/github-actions/" data-link-title="GitHub Actions" data-link-desc="GitHub 原生 CI/CD、PR check、deploy gate">GitHub Actions</a></li>
<li>下游能力：<a href="/blog/backend/07-security-data-protection/" data-link-title="模組七：資安與資料保護" data-link-desc="以問題驅動方式擴充資安知識網：先定義服務環節問題，再以案例作為觸發式參考">07 security</a>、<a href="/blog/backend/05-deployment-platform/" data-link-title="模組五：部署平台與網路入口" data-link-desc="整理 Kubernetes、systemd、load balancer、container 與服務生命週期合約">5 deployment</a></li>
</ul>
]]></content:encoded></item><item><title>Netflix</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/netflix/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/netflix/</guid><description>&lt;p>Netflix 是 Chaos Engineering 的起源、Chaos Monkey 跟 Simian Army 是領域標準工具的概念來源、FIT（Failure Injection Testing）是大規模 production chaos 的實作範本。教學重點在「故障注入如何作為 first-class 工程實踐」。&lt;/p>
&lt;h2 id="規劃重點">規劃重點&lt;/h2>
&lt;ul>
&lt;li>Chaos Monkey 起點：在 production 隨機殺實例為何能改進架構&lt;/li>
&lt;li>Simian Army 工具鏈：Latency / Janitor / Conformity 等不同維度的 chaos&lt;/li>
&lt;li>FIT：把 chaos 從 instance 層升級到 request 層、攻擊更精細&lt;/li>
&lt;li>Chaos Maturity Model：團隊採用 chaos 的能力分級&lt;/li>
&lt;li>Steady state hypothesis：chaos 實驗的科學方法基礎&lt;/li>
&lt;/ul>
&lt;h2 id="預計收錄實踐">預計收錄實踐&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>議題&lt;/th>
 &lt;th>教學重點&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Chaos Monkey&lt;/td>
 &lt;td>起源、規則、為何在 weekday business hour&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Simian Army&lt;/td>
 &lt;td>多維度故障注入的設計&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>FIT&lt;/td>
 &lt;td>Request-level fault injection 的工程化&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Chaos Engineering Manifesto&lt;/td>
 &lt;td>hypothesis / scope / blast radius 控制&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Production chaos vs Staging&lt;/td>
 &lt;td>為何 production 才有真實價值&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="章節列表">章節列表&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>章節&lt;/th>
 &lt;th>主題&lt;/th>
 &lt;th>核心責任&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/netflix/steady-state-chaos-and-fit/" data-link-title="Netflix：Steady State、Chaos 與 FIT 的驗證路徑" data-link-desc="把故障注入從工具操作升級成可驗證流程：先定義穩態，再設計注入與回復條件。">N1&lt;/a>&lt;/td>
 &lt;td>Steady State、Chaos 與 FIT&lt;/td>
 &lt;td>把故障注入變成可證偽、可停止、可回寫的驗證流程&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/netflix/chaos-monkey-business-hours-guardrails/" data-link-title="Netflix：Business-Hours Chaos 與 Guardrails" data-link-desc="Chaos Monkey 為何刻意在 business hours 執行：把即時應變能力納入驗證，並用 guardrails 限制實驗風險。">N2&lt;/a>&lt;/td>
 &lt;td>Business-Hours Guardrails&lt;/td>
 &lt;td>把時段策略、風險邊界與應變能力整合進 chaos 驗證&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/netflix/fit-failure-injection-evidence-handoff/" data-link-title="Netflix：FIT 證據交接與 Release Gate 回寫" data-link-desc="用 Failure Injection Testing 產出的證據直接驅動 release gate：把實驗結果轉成可放行、可凍結、可回退的決策欄位。">N3&lt;/a>&lt;/td>
 &lt;td>FIT 證據交接&lt;/td>
 &lt;td>把故障注入結果轉成 release gate 可用證據&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="案例定位">案例定位&lt;/h2>
&lt;p>Netflix 這個案例在講的是故障注入如何從實驗變成工程制度。讀者要先分辨 steady state、hypothesis、blast radius 與回復條件各自扮演的角色，才能理解為什麼 chaos 是驗證服務韌性的方法，演示層面的價值是次要的。&lt;/p>
&lt;h2 id="判讀重點">判讀重點&lt;/h2>
&lt;p>當團隊只在 staging 做演練時，先看測試是否真的碰到生產流量的分布與依賴關係。當問題需要更細的干預時，再往 FIT 這種 request-level fault injection 移動，讓故障落在真正會被客戶碰到的路徑上。&lt;/p>
&lt;h2 id="可操作判準">可操作判準&lt;/h2>
&lt;ul>
&lt;li>能否先寫出 steady state，再設計實驗&lt;/li>
&lt;li>能否說清楚 blast radius 與 rollback 條件&lt;/li>
&lt;li>能否說明為何在 business hour 做 chaos 反而更安全&lt;/li>
&lt;li>能否判斷問題需要 instance-level 還是 request-level 注入&lt;/li>
&lt;/ul>
&lt;h2 id="與其他案例的關係">與其他案例的關係&lt;/h2>
&lt;p>Netflix 把「先驗證再承擔風險」這件事做成制度，和 AWS S3、Cloudflare 這類事故頁形成對照。前者是在可控條件下主動打破假設，後者是在失敗後回頭整理假設，因此兩者一起讀才能看懂 reliability 與 incident response 的分工。&lt;/p>
&lt;h2 id="代表樣本">代表樣本&lt;/h2>
&lt;ul>
&lt;li>Chaos Monkey 直接驗證實例被殺掉後，服務是否仍能維持 steady state。&lt;/li>
&lt;li>FIT 把故障注入從 instance 級推進到 request 級，讓實驗更貼近真實流量路徑。&lt;/li>
&lt;li>Simian Army 讓不同故障類型有各自的注入面。&lt;/li>
&lt;li>business-hour chaos 讓測試更接近真實營運節奏。&lt;/li>
&lt;li>chaos maturity model 讓團隊知道自己在採用故障注入的哪個階段。&lt;/li>
&lt;li>steady state hypothesis 讓實驗成為可證偽的工程判斷，超越單純演示。&lt;/li>
&lt;li>latency monkey 讓延遲問題成為可以主動驗證的故障型態。&lt;/li>
&lt;li>janitor / conformity 類工具把環境清理與架構規則也納入韌性管理。&lt;/li>
&lt;/ul>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://github.com/Netflix/chaosmonkey">Netflix/chaosmonkey&lt;/a>：Chaos Monkey 的現行開源實作。&lt;/li>
&lt;li>&lt;a href="https://github.com/Netflix/SimianArmy/wiki/Chaos-Monkey">Netflix/SimianArmy Wiki: Chaos Monkey&lt;/a>：Simian Army 舊版 wiki，說明 business-hours chaos 的基本規則。&lt;/li>
&lt;li>&lt;a href="https://github.com/Netflix/SimianArmy">Netflix/SimianArmy&lt;/a>：Simian Army 套件入口，補齊多種 monkey 的整體脈絡。&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Netflix 是 Chaos Engineering 的起源、Chaos Monkey 跟 Simian Army 是領域標準工具的概念來源、FIT（Failure Injection Testing）是大規模 production chaos 的實作範本。教學重點在「故障注入如何作為 first-class 工程實踐」。</p>
<h2 id="規劃重點">規劃重點</h2>
<ul>
<li>Chaos Monkey 起點：在 production 隨機殺實例為何能改進架構</li>
<li>Simian Army 工具鏈：Latency / Janitor / Conformity 等不同維度的 chaos</li>
<li>FIT：把 chaos 從 instance 層升級到 request 層、攻擊更精細</li>
<li>Chaos Maturity Model：團隊採用 chaos 的能力分級</li>
<li>Steady state hypothesis：chaos 實驗的科學方法基礎</li>
</ul>
<h2 id="預計收錄實踐">預計收錄實踐</h2>
<table>
  <thead>
      <tr>
          <th>議題</th>
          <th>教學重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Chaos Monkey</td>
          <td>起源、規則、為何在 weekday business hour</td>
      </tr>
      <tr>
          <td>Simian Army</td>
          <td>多維度故障注入的設計</td>
      </tr>
      <tr>
          <td>FIT</td>
          <td>Request-level fault injection 的工程化</td>
      </tr>
      <tr>
          <td>Chaos Engineering Manifesto</td>
          <td>hypothesis / scope / blast radius 控制</td>
      </tr>
      <tr>
          <td>Production chaos vs Staging</td>
          <td>為何 production 才有真實價值</td>
      </tr>
  </tbody>
</table>
<h2 id="章節列表">章節列表</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/netflix/steady-state-chaos-and-fit/" data-link-title="Netflix：Steady State、Chaos 與 FIT 的驗證路徑" data-link-desc="把故障注入從工具操作升級成可驗證流程：先定義穩態，再設計注入與回復條件。">N1</a></td>
          <td>Steady State、Chaos 與 FIT</td>
          <td>把故障注入變成可證偽、可停止、可回寫的驗證流程</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/netflix/chaos-monkey-business-hours-guardrails/" data-link-title="Netflix：Business-Hours Chaos 與 Guardrails" data-link-desc="Chaos Monkey 為何刻意在 business hours 執行：把即時應變能力納入驗證，並用 guardrails 限制實驗風險。">N2</a></td>
          <td>Business-Hours Guardrails</td>
          <td>把時段策略、風險邊界與應變能力整合進 chaos 驗證</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/netflix/fit-failure-injection-evidence-handoff/" data-link-title="Netflix：FIT 證據交接與 Release Gate 回寫" data-link-desc="用 Failure Injection Testing 產出的證據直接驅動 release gate：把實驗結果轉成可放行、可凍結、可回退的決策欄位。">N3</a></td>
          <td>FIT 證據交接</td>
          <td>把故障注入結果轉成 release gate 可用證據</td>
      </tr>
  </tbody>
</table>
<h2 id="案例定位">案例定位</h2>
<p>Netflix 這個案例在講的是故障注入如何從實驗變成工程制度。讀者要先分辨 steady state、hypothesis、blast radius 與回復條件各自扮演的角色，才能理解為什麼 chaos 是驗證服務韌性的方法，演示層面的價值是次要的。</p>
<h2 id="判讀重點">判讀重點</h2>
<p>當團隊只在 staging 做演練時，先看測試是否真的碰到生產流量的分布與依賴關係。當問題需要更細的干預時，再往 FIT 這種 request-level fault injection 移動，讓故障落在真正會被客戶碰到的路徑上。</p>
<h2 id="可操作判準">可操作判準</h2>
<ul>
<li>能否先寫出 steady state，再設計實驗</li>
<li>能否說清楚 blast radius 與 rollback 條件</li>
<li>能否說明為何在 business hour 做 chaos 反而更安全</li>
<li>能否判斷問題需要 instance-level 還是 request-level 注入</li>
</ul>
<h2 id="與其他案例的關係">與其他案例的關係</h2>
<p>Netflix 把「先驗證再承擔風險」這件事做成制度，和 AWS S3、Cloudflare 這類事故頁形成對照。前者是在可控條件下主動打破假設，後者是在失敗後回頭整理假設，因此兩者一起讀才能看懂 reliability 與 incident response 的分工。</p>
<h2 id="代表樣本">代表樣本</h2>
<ul>
<li>Chaos Monkey 直接驗證實例被殺掉後，服務是否仍能維持 steady state。</li>
<li>FIT 把故障注入從 instance 級推進到 request 級，讓實驗更貼近真實流量路徑。</li>
<li>Simian Army 讓不同故障類型有各自的注入面。</li>
<li>business-hour chaos 讓測試更接近真實營運節奏。</li>
<li>chaos maturity model 讓團隊知道自己在採用故障注入的哪個階段。</li>
<li>steady state hypothesis 讓實驗成為可證偽的工程判斷，超越單純演示。</li>
<li>latency monkey 讓延遲問題成為可以主動驗證的故障型態。</li>
<li>janitor / conformity 類工具把環境清理與架構規則也納入韌性管理。</li>
</ul>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://github.com/Netflix/chaosmonkey">Netflix/chaosmonkey</a>：Chaos Monkey 的現行開源實作。</li>
<li><a href="https://github.com/Netflix/SimianArmy/wiki/Chaos-Monkey">Netflix/SimianArmy Wiki: Chaos Monkey</a>：Simian Army 舊版 wiki，說明 business-hours chaos 的基本規則。</li>
<li><a href="https://github.com/Netflix/SimianArmy">Netflix/SimianArmy</a>：Simian Army 套件入口，補齊多種 monkey 的整體脈絡。</li>
</ul>
]]></content:encoded></item><item><title>6.2 load test</title><link>https://tarrragon.github.io/blog/backend/06-reliability/load-testing/</link><pubDate>Thu, 23 Apr 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/load-testing/</guid><description>&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>當系統需要回答「這個流量撐不撐得住」，&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/load-test/" data-link-title="Load Test" data-link-desc="說明在預期流量下驗證容量、延遲與降級策略的測試">load test&lt;/a> 把真實 workload model 變成可重播的壓力情境，找出吞吐、延遲與瓶頸轉折點。&lt;/p>
&lt;p>這一頁關心的是實際流量長什麼樣，不是把數字推高而已。模型若不接近 production shape，壓測結果就只是在驗證假場景。&lt;/p>
&lt;h2 id="核心判讀">核心判讀&lt;/h2>
&lt;p>Load test 的品質先看模型是否貼近流量結構，再看系統在 saturation 前後的行為。曲線在 saturation 前後如何變形才是關鍵，單點 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/throughput/" data-link-title="Throughput" data-link-desc="整理系統單位時間內可處理的工作量">throughput&lt;/a> 只是其中一個讀數。&lt;/p>
&lt;p>判讀時的關鍵面向：&lt;/p>
&lt;ul>
&lt;li>workload 是否包含尖峰、長尾與不同 cohort&lt;/li>
&lt;li>latency 是否在接近飽和時快速劣化&lt;/li>
&lt;li>bottleneck 是否能被定位到具體 resource&lt;/li>
&lt;li>load 結果是否能回寫到 capacity planning&lt;/li>
&lt;/ul>
&lt;h2 id="workload-model-設計">Workload model 設計&lt;/h2>
&lt;p>Workload model 的責任是把 production 流量結構轉成可重播的測試情境。模型越接近真實流量的形狀，壓測結果對容量決策的支撐力越高。&lt;/p>
&lt;p>設計 workload model 時先分析三個維度：&lt;/p>
&lt;p>&lt;strong>Traffic shape&lt;/strong>：production 流量很少是均勻的。峰值時段的 request rate 可能是均值的數倍到數十倍，而且峰值持續時間、上升斜率與衰退曲線各有差異。Shopify 的 BFCM 流量結構是短時間爆量加上高寫入比例；若模型只用日均流量推算，會漏掉峰值集中在數小時內的壓力集中度。模型需要把 peak / off-peak / burst 三種時段分開描述。&lt;/p>
&lt;p>&lt;strong>Cohort 拆分&lt;/strong>：讀與寫的資源消耗模式不同，混合比例會改變瓶頸位置。API gateway 層可能由讀主導，但 checkout 或 order-create 路徑的寫入比例明顯偏高。把不同 cohort（讀 / 寫 / 混合 / 背景任務）分開量測，才能判斷瓶頸是在哪個路徑上出現。&lt;/p>
&lt;p>&lt;strong>資料量對齊&lt;/strong>：staging 環境的資料量常與 production 差一到兩個數量級。query plan、index scan、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/connection-pool/" data-link-title="Connection Pool" data-link-desc="說明連線池如何限制下游資源並影響服務容量">connection pool&lt;/a> 飽和與 cache 行為都跟資料量高度相關。模型要盡可能用 production-like 資料量，或至少在結果判讀時標註資料量差異帶來的偏移。&lt;/p>
&lt;p>LinkedIn 的實踐揭露另一個面向：workload model 會隨時間漂移。流量結構、使用者行為與功能上線都會改變真實壓力形狀。當 load-test 模型不再定期校準，壓測結果與 production 壓力之間的差距會持續擴大。定期用 production traffic replay 或 access log 分析重建模型，是維持壓測可信度的必要動作。&lt;/p>
&lt;p>判斷 workload model 是否仍然有效的實務做法：把最近一次 load test 的 latency distribution 與 production 同時段的 latency distribution 對齊。若兩者的 p50 / p95 / p99 比率偏離超過 20%，模型已經需要校準。20% 是通用起點。latency 敏感的服務（交易、即時通訊）應使用更嚴格的門檻（10%），batch 類服務可適度放寬。偏離來源通常是三個之一：流量結構變了（新功能改變 read/write 比例）、資料量成長了（query plan 改變）、依賴行為變了（上游回應時間漂移）。&lt;/p>
&lt;h2 id="saturation-與瓶頸定位">Saturation 與瓶頸定位&lt;/h2>
&lt;p>Saturation 的轉折點決定了系統的實際容量上限 — 在什麼負載下，系統從線性擴展轉為劣化。&lt;/p>
&lt;p>判讀 saturation 先看 latency curve。在低負載時，latency 通常穩定；隨著負載上升，會出現一個 inflection point，之後 latency 開始加速上升。這個轉折點通常比 throughput ceiling 更早出現，是真正的容量邊界。&lt;/p>
&lt;p>在 inflection point 之後，系統行為會進入幾種退化模式。逐漸退化型的 latency 緩慢爬升，通常來自 queue 堆積或 GC 壓力加重；崩落型的 latency 在某個點突然跳升數倍，通常來自 connection pool 耗盡或 thread pool 飽和。兩種退化的應對策略不同：逐漸退化有 load shedding 的緩衝空間，崩落型需要提早在更低負載觸發限流。壓測結果需要標註系統屬於哪種退化模式，這個資訊直接影響 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/stop-condition/" data-link-title="Stop Condition" data-link-desc="說明變更、實驗或事故處理何時必須暫停、回退或改路線">stop condition&lt;/a> 的門檻設定。&lt;/p>
&lt;p>瓶頸定位需要對齊資源層。常見瓶頸包括 CPU saturation、memory pressure、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/connection-pool/" data-link-title="Connection Pool" data-link-desc="說明連線池如何限制下游資源並影響服務容量">connection pool&lt;/a> 耗盡、queue depth 堆積與 disk I/O。壓測時需要同步觀測這些資源指標，才能把 latency 劣化歸因到具體 resource。歸因的價值在於讓擴容或優化的投資方向可決策：CPU 瓶頸指向 compute scaling、connection pool 瓶頸指向 pool config 或 connection reuse、queue depth 瓶頸可能指向 consumer 吞吐不足。若只看 latency 劣化但不做歸因，團隊容易直覺式擴容，花了成本卻沒打到真正瓶頸。&lt;/p></description><content:encoded><![CDATA[<h2 id="概念定位">概念定位</h2>
<p>當系統需要回答「這個流量撐不撐得住」，<a href="/blog/backend/knowledge-cards/load-test/" data-link-title="Load Test" data-link-desc="說明在預期流量下驗證容量、延遲與降級策略的測試">load test</a> 把真實 workload model 變成可重播的壓力情境，找出吞吐、延遲與瓶頸轉折點。</p>
<p>這一頁關心的是實際流量長什麼樣，不是把數字推高而已。模型若不接近 production shape，壓測結果就只是在驗證假場景。</p>
<h2 id="核心判讀">核心判讀</h2>
<p>Load test 的品質先看模型是否貼近流量結構，再看系統在 saturation 前後的行為。曲線在 saturation 前後如何變形才是關鍵，單點 <a href="/blog/backend/knowledge-cards/throughput/" data-link-title="Throughput" data-link-desc="整理系統單位時間內可處理的工作量">throughput</a> 只是其中一個讀數。</p>
<p>判讀時的關鍵面向：</p>
<ul>
<li>workload 是否包含尖峰、長尾與不同 cohort</li>
<li>latency 是否在接近飽和時快速劣化</li>
<li>bottleneck 是否能被定位到具體 resource</li>
<li>load 結果是否能回寫到 capacity planning</li>
</ul>
<h2 id="workload-model-設計">Workload model 設計</h2>
<p>Workload model 的責任是把 production 流量結構轉成可重播的測試情境。模型越接近真實流量的形狀，壓測結果對容量決策的支撐力越高。</p>
<p>設計 workload model 時先分析三個維度：</p>
<p><strong>Traffic shape</strong>：production 流量很少是均勻的。峰值時段的 request rate 可能是均值的數倍到數十倍，而且峰值持續時間、上升斜率與衰退曲線各有差異。Shopify 的 BFCM 流量結構是短時間爆量加上高寫入比例；若模型只用日均流量推算，會漏掉峰值集中在數小時內的壓力集中度。模型需要把 peak / off-peak / burst 三種時段分開描述。</p>
<p><strong>Cohort 拆分</strong>：讀與寫的資源消耗模式不同，混合比例會改變瓶頸位置。API gateway 層可能由讀主導，但 checkout 或 order-create 路徑的寫入比例明顯偏高。把不同 cohort（讀 / 寫 / 混合 / 背景任務）分開量測，才能判斷瓶頸是在哪個路徑上出現。</p>
<p><strong>資料量對齊</strong>：staging 環境的資料量常與 production 差一到兩個數量級。query plan、index scan、<a href="/blog/backend/knowledge-cards/connection-pool/" data-link-title="Connection Pool" data-link-desc="說明連線池如何限制下游資源並影響服務容量">connection pool</a> 飽和與 cache 行為都跟資料量高度相關。模型要盡可能用 production-like 資料量，或至少在結果判讀時標註資料量差異帶來的偏移。</p>
<p>LinkedIn 的實踐揭露另一個面向：workload model 會隨時間漂移。流量結構、使用者行為與功能上線都會改變真實壓力形狀。當 load-test 模型不再定期校準，壓測結果與 production 壓力之間的差距會持續擴大。定期用 production traffic replay 或 access log 分析重建模型，是維持壓測可信度的必要動作。</p>
<p>判斷 workload model 是否仍然有效的實務做法：把最近一次 load test 的 latency distribution 與 production 同時段的 latency distribution 對齊。若兩者的 p50 / p95 / p99 比率偏離超過 20%，模型已經需要校準。20% 是通用起點。latency 敏感的服務（交易、即時通訊）應使用更嚴格的門檻（10%），batch 類服務可適度放寬。偏離來源通常是三個之一：流量結構變了（新功能改變 read/write 比例）、資料量成長了（query plan 改變）、依賴行為變了（上游回應時間漂移）。</p>
<h2 id="saturation-與瓶頸定位">Saturation 與瓶頸定位</h2>
<p>Saturation 的轉折點決定了系統的實際容量上限 — 在什麼負載下，系統從線性擴展轉為劣化。</p>
<p>判讀 saturation 先看 latency curve。在低負載時，latency 通常穩定；隨著負載上升，會出現一個 inflection point，之後 latency 開始加速上升。這個轉折點通常比 throughput ceiling 更早出現，是真正的容量邊界。</p>
<p>在 inflection point 之後，系統行為會進入幾種退化模式。逐漸退化型的 latency 緩慢爬升，通常來自 queue 堆積或 GC 壓力加重；崩落型的 latency 在某個點突然跳升數倍，通常來自 connection pool 耗盡或 thread pool 飽和。兩種退化的應對策略不同：逐漸退化有 load shedding 的緩衝空間，崩落型需要提早在更低負載觸發限流。壓測結果需要標註系統屬於哪種退化模式，這個資訊直接影響 <a href="/blog/backend/knowledge-cards/stop-condition/" data-link-title="Stop Condition" data-link-desc="說明變更、實驗或事故處理何時必須暫停、回退或改路線">stop condition</a> 的門檻設定。</p>
<p>瓶頸定位需要對齊資源層。常見瓶頸包括 CPU saturation、memory pressure、<a href="/blog/backend/knowledge-cards/connection-pool/" data-link-title="Connection Pool" data-link-desc="說明連線池如何限制下游資源並影響服務容量">connection pool</a> 耗盡、queue depth 堆積與 disk I/O。壓測時需要同步觀測這些資源指標，才能把 latency 劣化歸因到具體 resource。歸因的價值在於讓擴容或優化的投資方向可決策：CPU 瓶頸指向 compute scaling、connection pool 瓶頸指向 pool config 或 connection reuse、queue depth 瓶頸可能指向 consumer 吞吐不足。若只看 latency 劣化但不做歸因，團隊容易直覺式擴容，花了成本卻沒打到真正瓶頸。</p>
<p>Pinterest 的快取可靠性案例揭露一種不直覺的瓶頸類型：cache 命中率崩落時，瓶頸會從 compute 層移到 storage throughput。回源壓力瞬間上升，資料層的 I/O 成為新瓶頸。這種情境在純 compute 壓測中看不到，需要特別設計包含 cache miss 場景的 workload。實務上，cache miss 場景可以用兩種方式模擬：清空 cache 後立即打流量（cold start），或在壓測過程中讓部分 key 過期（partial eviction）。兩者暴露的瓶頸位置可能不同，cold start 偏向 storage 吞吐、partial eviction 偏向 connection pool 與 retry 放大。</p>
<h2 id="load-test-與容量規劃的接口">Load test 與容量規劃的接口</h2>
<p>Load test 的產出不只是 pass/fail，它是容量規劃的主要輸入。壓測結果要能轉成 headroom 計算與成本預測。</p>
<p><strong>Headroom 計算</strong>：peak load 佔 capacity ceiling 的比率決定安全緩衝。比率超過 70-80% 時，任何流量突增或依賴劣化都可能觸發 saturation。headroom 的安全值跟系統的退化模式綁在一起：崩落型退化的系統需要更大 headroom，因為從健康到故障的過渡窗口很短。LinkedIn 的做法是把 headroom 預算綁到值班分層，當 headroom 低於門檻時自動升級 <a href="/blog/backend/knowledge-cards/on-call/" data-link-title="On-Call" data-link-desc="說明值班制度如何承接告警、事故分級與升級流程">on-call</a> 層級，讓容量風險直接轉成團隊行動。</p>
<p><strong>成本曲線</strong>：擴容的邊際成本會在跨越 availability zone、region 或 tier 邊界時跳升。load test 結果要標註「容量到多少時需要跨越哪個擴容邊界」，讓容量規劃能把成本跳升點納入決策。這類資訊在高峰前特別有價值：團隊能提前決定是靠 load shedding 撐過峰值，還是提前擴容跨區，兩者的成本與風險完全不同。</p>
<p><strong>隔離單位的容量量測</strong>：全域容量規劃在多租戶或 cell-based 架構下會失真。Amazon 的做法是按 cell 獨立量測 saturation，每個隔離單位有自己的 headroom，避免一個 cell 的容量需求拖動全域擴容。這種設計讓 load test 的量測粒度從「整個服務」降到「每個隔離單位」，容量決策更精準。</p>
<p>load test 結果的完整路由是：壓測產出 saturation point 與 headroom ratio → 餵給 <a href="/blog/backend/06-reliability/capacity-cost/" data-link-title="6.9 容量與成本邊界" data-link-desc="把容量規劃跟成本約束變成驗證輸入">6.9 容量與成本邊界</a> 做容量預算 → 餵給 <a href="/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13 performance regression gate</a> 做持續守護。</p>
<h2 id="持續性-load-test-與事件性壓測">持續性 load test 與事件性壓測</h2>
<p>Load test 的執行模式依用途分兩類，兩者設計邏輯不同。</p>
<p><strong>持續性 load test</strong> 跑在 CI pipeline 中，用固定 workload 做 baseline regression 偵測。每次變更跑同一套 scenario，比較 latency 與 throughput 是否偏離 baseline。這類測試的 workload 不需要貼近峰值，但需要穩定到能偵測 5-10% 的 regression。連到 <a href="/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13 performance regression gate</a> 做自動化 gate。</p>
<p><strong>事件性壓測</strong> 針對特定事件（產品上線、促銷、峰值季節）做一次性或年度壓測。workload 設計要貼近該事件的流量形狀與資料量。Shopify 把 game day 做成年度制度化流程：每輪 BFCM 前跑容量驗證，演練結果回寫 resiliency matrix 與 runbook，讓下一輪從更高基準開始。事件性壓測的關鍵是結果留存與回寫，不是跑完就結束。</p>
<p>兩類測試的分工：持續性負責守住 baseline，事件性負責探索邊界。只跑持續性會漏掉峰值場景；只跑事件性會漏掉漸進退化。</p>
<p>判斷要用哪一類時，先問兩個問題。第一，這個服務是否有可預期的流量事件（促銷、賽季、發布日）？有的話，事件性壓測是必要的，因為峰值壓力的形狀跟日常完全不同。第二，這個服務的變更頻率是否超過每週一次？是的話，持續性 load test 是必要的，因為 regression 可能在任何一次 deploy 進入。多數生產系統兩類都需要。</p>
<h2 id="環境與工具考量">環境與工具考量</h2>
<p><strong>Staging vs production</strong>：staging 壓測控制成本低、風險低，但跟 production 的差異（資料量、網路拓撲、依賴行為）會讓結果偏移。Production load test（dark traffic、shadow read、canary traffic）結果更可信，但需要嚴格的 <a href="/blog/backend/knowledge-cards/blast-radius/" data-link-title="Blast Radius" data-link-desc="說明事故影響面如何估算與隔離">blast radius</a> 控制與 <a href="/blog/backend/knowledge-cards/stop-condition/" data-link-title="Stop Condition" data-link-desc="說明變更、實驗或事故處理何時必須暫停、回退或改路線">stop condition</a> 設計。選擇哪種環境取決於系統成熟度與風險承受能力。</p>
<p><strong>Synthetic traffic 的限制</strong>：synthetic 請求不帶真實 session、auth token 或 cache warm-up 狀態，行為與真實使用者不同。對 cache 敏感的系統，synthetic traffic 可能打出比真實流量更高的 miss rate，產生虛假瓶頸。對 auth 與 session 敏感的系統，synthetic 請求可能繞過 rate limit 或 WAF 路徑，壓測結果會低估 production 的真實負載。判讀時要標註 synthetic 與 real traffic 的行為差異，避免把假瓶頸或假安全當結論。</p>
<p><strong>資料隔離</strong>：production load test 需要確保測試流量不會污染真實資料。常見做法包括 shadow read（讀路徑複製、寫路徑丟棄）、test tenant 隔離（獨立資料空間）、與 feature flag 控制的 dark traffic。每種做法的隔離強度與實作成本不同，選擇時要對齊系統的資料敏感度。</p>
<p>工具選擇路由：CI-first 場景偏向 CLI 工具（<a href="/blog/backend/06-reliability/vendors/k6/" data-link-title="k6" data-link-desc="現代 load test、JS scripting、Grafana Labs">k6</a>）、JVM 生態偏向 <a href="/blog/backend/06-reliability/vendors/gatling/" data-link-title="Gatling" data-link-desc="JVM-based load test、Scala / Java / Kotlin DSL、強型別 scenario、HAR-driven recording">Gatling</a>、Python 團隊偏向 <a href="/blog/backend/06-reliability/vendors/locust/" data-link-title="Locust" data-link-desc="Python-based load test、distributed、易擴展">Locust</a>、既有 .jmx 資產偏向 <a href="/blog/backend/06-reliability/vendors/jmeter/" data-link-title="Apache JMeter" data-link-desc="老牌 load test 工具、GUI &#43; plugins">JMeter</a>。工具對照見 <a href="/blog/backend/06-reliability/vendors/" data-link-title="可靠性 Vendor 清單" data-link-desc="規劃 CI、壓測、chaos engineering 與 SLO 工具的服務頁撰寫順序與判準">vendors/</a>。</p>
<h2 id="load-test-結果的證據留存">Load test 結果的證據留存</h2>
<p>Load test 結果需要結構化留存，讓下游（容量規劃、release gate、事故決策）可以直接調用，而不是每次都要重跑或找人解釋。</p>
<p>留存的最小欄位：workload model 版本、測試環境、saturation point（latency inflection 的 RPS）、throughput ceiling、主要瓶頸歸因、headroom ratio、退化模式分類、測試日期。這些欄位讓 <a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23 verification evidence handoff</a> 可以把 load test 結論直接納入 release 決策，也讓 <a href="/blog/backend/06-reliability/capacity-cost/" data-link-title="6.9 容量與成本邊界" data-link-desc="把容量規劃跟成本約束變成驗證輸入">6.9 容量與成本邊界</a> 可以追蹤 saturation point 隨時間的變化趨勢。</p>
<p>若結果只以 dashboard 截圖或口頭摘要留存，下次壓測時團隊無法判斷「是系統變了還是模型變了」，校準失去基準。</p>
<h2 id="案例對照">案例對照</h2>
<ul>
<li><a href="/blog/backend/06-reliability/cases/shopify/bfcm-capacity-and-game-day/" data-link-title="Shopify：BFCM 容量治理與 Game Day 驗證節奏" data-link-desc="把季節性流量峰值轉成年度可靠性流程，透過容量模型、演練與隔離策略提前吸收風險。">Shopify H1</a>：高峰型流量要求 load model 涵蓋短時間爆量與高寫入比例，game day 把事件性壓測制度化。</li>
<li><a href="/blog/backend/06-reliability/cases/linkedin/capacity-headroom-and-oncall-tiering/" data-link-title="LinkedIn：Capacity Headroom 與 On-call 分層" data-link-desc="把容量預測與值班分層綁在一起，降低高峰時段的升級混亂與恢復延遲。">LinkedIn L1</a>：headroom 預算綁值班分層，load-test drift 需要定期校準模型。</li>
<li><a href="/blog/backend/06-reliability/cases/pinterest/cache-reliability-and-capacity-surprises/" data-link-title="Pinterest：快取可靠性與容量驚奇治理" data-link-desc="針對快取層失效與流量突增，建立容量緩衝、退化路徑與重建節奏。">Pinterest P1</a>：cache 命中率崩落改變瓶頸位置，壓測要涵蓋 cache miss 場景。</li>
<li><a href="/blog/backend/06-reliability/cases/amazon/shuffle-sharding-and-cell-boundary/" data-link-title="Amazon：Shuffle Sharding 與 Cell 邊界的失效局部化" data-link-desc="用 cell 與 shuffle sharding 將多租戶故障限制在局部，讓恢復策略可分批執行。">Amazon A1</a>：cell-based architecture 讓容量規劃按隔離單位量測，避免全域擴容失控。</li>
<li><a href="/blog/backend/06-reliability/cases/linkedin/automated-load-testing-and-capacity-forecasting/" data-link-title="LinkedIn：Automated Load Testing 與 Capacity Forecasting" data-link-desc="持續壓測驅動容量預測：用自動化回饋取代一次性壓測的容量規劃。">LinkedIn L2</a>：自動化壓測接入 CI pipeline，用 production traffic replay 定期更新 saturation point，讓容量預測的輸入持續校準。</li>
</ul>
<h2 id="產業情境電商與零售">產業情境：電商與零售</h2>
<p>電商流量的核心特徵是可預期的季節性峰值（雙十一、Black Friday、Prime Day）與不可預期的閃購爆量。兩者對 workload model 的需求不同，混用同一套模型會讓壓測結論對其中一種場景失真。</p>
<p>季節性峰值的 workload model 需要涵蓋三個電商特有維度：流量上升斜率（開賣瞬間的階梯式爆增 vs 活動期間的漸進增長）、讀寫比例變化（瀏覽階段讀為主 → 結帳階段寫入爆增）、庫存查詢的 cache miss 率（熱門商品快取因庫存變動頻繁失效）。<a href="/blog/backend/06-reliability/cases/shopify/bfcm-capacity-and-game-day/" data-link-title="Shopify：BFCM 容量治理與 Game Day 驗證節奏" data-link-desc="把季節性流量峰值轉成年度可靠性流程，透過容量模型、演練與隔離策略提前吸收風險。">Shopify 的 BFCM 容量治理</a>把這類峰值的容量驗證制度化為年度 game day。</p>
<p>閃購型流量的特徵是持續時間極短（分鐘級）但倍率極高（日常的 10-50 倍）。常規壓測用日均流量推算會完全漏掉這種尖峰，需要獨立的 burst scenario 模擬開賣瞬間的並發衝擊。</p>
<p>轉換率是電商特有的穩態指標。load test 的判讀不只看 latency 和 error rate，還要看結帳轉換率是否在壓力下劣化。研究顯示 latency 上升 100ms 可能讓轉換率下降 1-7%，這個商業影響在純技術指標中看不到。壓測結果要同時記錄技術指標與業務指標，容量決策才能對齊商業價值。</p>
<h2 id="操作判讀">操作判讀</h2>
<table>
  <thead>
      <tr>
          <th>觀察到的狀況</th>
          <th>可能原因</th>
          <th>下一步行動</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>壓測通過但 production peak 仍故障</td>
          <td>workload model 未涵蓋峰值形狀或 cohort 比例</td>
          <td>用 access log 重建 peak 時段模型</td>
      </tr>
      <tr>
          <td>latency 在低負載就開始劣化</td>
          <td>staging 資料量不足、query plan 與 production 不同</td>
          <td>用 production-like 資料量重測</td>
      </tr>
      <tr>
          <td>throughput ceiling 遠高於 production</td>
          <td>synthetic traffic 繞過 auth/cache 路徑</td>
          <td>加入 realistic session 與 cache miss scenario</td>
      </tr>
      <tr>
          <td>壓測結果每月差異大</td>
          <td>workload model drift</td>
          <td>建立定期校準流程、對比 p50/p95 偏移</td>
      </tr>
      <tr>
          <td>瓶頸定位不出來</td>
          <td>缺少資源層同步觀測</td>
          <td>壓測時同步收 CPU / memory / pool / queue 指標</td>
      </tr>
      <tr>
          <td>cache miss 場景未被覆蓋</td>
          <td>workload 只有 warm cache 情境</td>
          <td>參考 <a href="/blog/backend/06-reliability/cases/pinterest/cache-reliability-and-capacity-surprises/" data-link-title="Pinterest：快取可靠性與容量驚奇治理" data-link-desc="針對快取層失效與流量突增，建立容量緩衝、退化路徑與重建節奏。">Pinterest P1</a> 設計 cold start scenario</td>
      </tr>
  </tbody>
</table>
<h2 id="判讀訊號">判讀訊號</h2>
<ul>
<li>workload 是合成的、跟 production traffic shape 不同</li>
<li>壓測通過但 production peak 失敗、模型未涵蓋實際模式</li>
<li>只測 throughput、不測 saturation 與 cost curve</li>
<li>bottleneck 識別靠經驗、無系統定位流程</li>
<li>capacity 規劃靠一次性 load test 結論、無持續對齊</li>
<li>load-test 模型超過 6 個月未校準、drift 累積</li>
</ul>
<h2 id="交接路由">交接路由</h2>
<ul>
<li><a href="/blog/backend/06-reliability/capacity-cost/" data-link-title="6.9 容量與成本邊界" data-link-desc="把容量規劃跟成本約束變成驗證輸入">6.9 容量與成本邊界</a>：load test 餵給容量規劃輸入</li>
<li><a href="/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13 performance regression gate</a>：load baseline 升級為持續 gate</li>
<li><a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 experiment safety boundary</a>：production load test 的 blast radius 與 stop condition</li>
<li><a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22 steady state definition</a>：load test 驗證 saturation 前後的穩態維持</li>
<li><a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate</a>：load test 結果作為 release 放行的容量證據</li>
<li><a href="/blog/backend/06-reliability/reliability-metrics-governance/" data-link-title="6.18 Reliability Metrics Governance" data-link-desc="DORA / SPACE 指標的選用、量測陷阱、anti-gaming 與團隊階段適配">6.18 reliability metrics</a>：把流量與可靠性指標接起來</li>
</ul>
]]></content:encoded></item><item><title>Amazon</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/amazon/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/amazon/</guid><description>&lt;p>Amazon 是 cell-based architecture 與 shuffle sharding 的代表、AWS Builders&amp;rsquo; Library 是大規模分散式系統的工程實踐 SSoT。教學重點在「如何設計才能讓失效局部化」。&lt;/p>
&lt;h2 id="規劃重點">規劃重點&lt;/h2>
&lt;ul>
&lt;li>Cell-based Architecture：把服務切成獨立 cell、每個 cell 有完整 stack&lt;/li>
&lt;li>Shuffle Sharding：客戶請求映射到 cell 的隨機切分、讓單一壞客戶無法擊倒所有 cell&lt;/li>
&lt;li>Static Stability：control plane 失效時 data plane 仍能服務&lt;/li>
&lt;li>Constant Work Pattern：avoid scaling traffic in failure modes&lt;/li>
&lt;li>AWS Builders&amp;rsquo; Library：可重用 reliability patterns 的官方文件&lt;/li>
&lt;/ul>
&lt;h2 id="預計收錄實踐">預計收錄實踐&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>議題&lt;/th>
 &lt;th>教學重點&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Cell-based Architecture&lt;/td>
 &lt;td>DynamoDB / Route 53 / S3 的 cell 劃分原則&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Shuffle Sharding&lt;/td>
 &lt;td>數學上的 blast radius 量化&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Static Stability&lt;/td>
 &lt;td>control / data plane 分離的設計取捨&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Workload Isolation&lt;/td>
 &lt;td>tenancy / region / availability zone 的隔離層級&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Build with constant work&lt;/td>
 &lt;td>為何 push-based 比 pull-based 在 failure 時更穩定&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="章節列表">章節列表&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>章節&lt;/th>
 &lt;th>主題&lt;/th>
 &lt;th>核心責任&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/amazon/shuffle-sharding-and-cell-boundary/" data-link-title="Amazon：Shuffle Sharding 與 Cell 邊界的失效局部化" data-link-desc="用 cell 與 shuffle sharding 將多租戶故障限制在局部，讓恢復策略可分批執行。">A1&lt;/a>&lt;/td>
 &lt;td>Shuffle Sharding 與 Cell 邊界&lt;/td>
 &lt;td>用局部隔離限制多租戶擴散，讓恢復可以分批收斂&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/amazon/static-stability-and-constant-work/" data-link-title="Amazon：Static Stability 與 Constant Work Pattern" data-link-desc="控制面失效時資料面如何維持服務：用快取、預計算與固定工作量避免恢復放大。">A2&lt;/a>&lt;/td>
 &lt;td>Static Stability 與 Constant Work Pattern&lt;/td>
 &lt;td>控制面失效時資料面用快取與固定工作量維持服務&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="案例定位">案例定位&lt;/h2>
&lt;p>Amazon 這個案例在講的是可靠性如何靠隔離來守住擴散邊界。讀者先看懂 cell-based architecture 與 shuffle sharding 的責任，再把它們當成控制 blast radius 的設計語言，而不是單純的 AWS 名詞。&lt;/p>
&lt;h2 id="判讀重點">判讀重點&lt;/h2>
&lt;p>當多租戶系統出現資源爭用時，cell 邊界先決定故障能擴散到哪裡。當容量壓力開始拉高時，shuffle sharding 讓風險分散到不同子集合，避免單一熱點把整個服務拖進同一個失敗模式。&lt;/p>
&lt;h2 id="可操作判準">可操作判準&lt;/h2>
&lt;ul>
&lt;li>能否指出一個 workload 的 blast radius 邊界&lt;/li>
&lt;li>能否把共享基礎設施切成可獨立恢復的 cell&lt;/li>
&lt;li>能否說明 contention 會落在哪個 shard&lt;/li>
&lt;li>能否把 recovery 設計成分批恢復，而不是一次全開&lt;/li>
&lt;/ul>
&lt;h2 id="與其他案例的關係">與其他案例的關係&lt;/h2>
&lt;p>Amazon 的重點是把隔離變成架構語言，這和 Meta 的 region failover、Shopify 的 pod 架構、GCP 的控制面邊界都在同一條線上。差別只在於 Amazon 更早把 cell 與 shard 語言標準化，所以特別適合用來反推其他大型平台的設計選擇。&lt;/p>
&lt;h2 id="代表樣本">代表樣本&lt;/h2>
&lt;ul>
&lt;li>cell-based architecture 讓一個 cell 壞掉時，其他 cell 仍能維持服務。&lt;/li>
&lt;li>shuffle sharding 將多租戶請求分散到不同子集合，限制單一客戶或單一熱點的擴散範圍。&lt;/li>
&lt;li>static stability 讓 control plane 失效時 data plane 仍可服務。&lt;/li>
&lt;li>constant work pattern 避免失敗模式下的額外放大成本。&lt;/li>
&lt;li>workload isolation 讓 tenancy / region / AZ 的邊界能各自承擔風險。&lt;/li>
&lt;li>failure containment 讓擴散先停在 cell 或 shard 邊界。&lt;/li>
&lt;li>push-based recovery 讓恢復節奏不依賴大規模同步操作。&lt;/li>
&lt;li>fault isolation 讓局部失效不會拖垮整個 fleet。&lt;/li>
&lt;li>constant work 讓 failure mode 不會因為多做一件事而繼續放大。&lt;/li>
&lt;/ul>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://aws.amazon.com/about-aws/whats-new/2019/12/introducing-amazon-builders-library/">Introducing The Amazon Builders’ Library&lt;/a>：Builders&amp;rsquo; Library 的官方入口。&lt;/li>
&lt;li>&lt;a href="https://aws.amazon.com/builders-library/workload-isolation-using-shuffle-sharding/">Workload isolation using shuffle-sharding&lt;/a>：shuffle sharding 與 fault isolation 的官方文章。&lt;/li>
&lt;li>&lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/reducing-scope-of-impact-with-cell-based-architecture/faq.html">FAQ - Reducing the Scope of Impact with Cell-Based Architecture&lt;/a>：cell-based architecture 與 shuffle sharding 的關係說明。&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Amazon 是 cell-based architecture 與 shuffle sharding 的代表、AWS Builders&rsquo; Library 是大規模分散式系統的工程實踐 SSoT。教學重點在「如何設計才能讓失效局部化」。</p>
<h2 id="規劃重點">規劃重點</h2>
<ul>
<li>Cell-based Architecture：把服務切成獨立 cell、每個 cell 有完整 stack</li>
<li>Shuffle Sharding：客戶請求映射到 cell 的隨機切分、讓單一壞客戶無法擊倒所有 cell</li>
<li>Static Stability：control plane 失效時 data plane 仍能服務</li>
<li>Constant Work Pattern：avoid scaling traffic in failure modes</li>
<li>AWS Builders&rsquo; Library：可重用 reliability patterns 的官方文件</li>
</ul>
<h2 id="預計收錄實踐">預計收錄實踐</h2>
<table>
  <thead>
      <tr>
          <th>議題</th>
          <th>教學重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Cell-based Architecture</td>
          <td>DynamoDB / Route 53 / S3 的 cell 劃分原則</td>
      </tr>
      <tr>
          <td>Shuffle Sharding</td>
          <td>數學上的 blast radius 量化</td>
      </tr>
      <tr>
          <td>Static Stability</td>
          <td>control / data plane 分離的設計取捨</td>
      </tr>
      <tr>
          <td>Workload Isolation</td>
          <td>tenancy / region / availability zone 的隔離層級</td>
      </tr>
      <tr>
          <td>Build with constant work</td>
          <td>為何 push-based 比 pull-based 在 failure 時更穩定</td>
      </tr>
  </tbody>
</table>
<h2 id="章節列表">章節列表</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/amazon/shuffle-sharding-and-cell-boundary/" data-link-title="Amazon：Shuffle Sharding 與 Cell 邊界的失效局部化" data-link-desc="用 cell 與 shuffle sharding 將多租戶故障限制在局部，讓恢復策略可分批執行。">A1</a></td>
          <td>Shuffle Sharding 與 Cell 邊界</td>
          <td>用局部隔離限制多租戶擴散，讓恢復可以分批收斂</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/amazon/static-stability-and-constant-work/" data-link-title="Amazon：Static Stability 與 Constant Work Pattern" data-link-desc="控制面失效時資料面如何維持服務：用快取、預計算與固定工作量避免恢復放大。">A2</a></td>
          <td>Static Stability 與 Constant Work Pattern</td>
          <td>控制面失效時資料面用快取與固定工作量維持服務</td>
      </tr>
  </tbody>
</table>
<h2 id="案例定位">案例定位</h2>
<p>Amazon 這個案例在講的是可靠性如何靠隔離來守住擴散邊界。讀者先看懂 cell-based architecture 與 shuffle sharding 的責任，再把它們當成控制 blast radius 的設計語言，而不是單純的 AWS 名詞。</p>
<h2 id="判讀重點">判讀重點</h2>
<p>當多租戶系統出現資源爭用時，cell 邊界先決定故障能擴散到哪裡。當容量壓力開始拉高時，shuffle sharding 讓風險分散到不同子集合，避免單一熱點把整個服務拖進同一個失敗模式。</p>
<h2 id="可操作判準">可操作判準</h2>
<ul>
<li>能否指出一個 workload 的 blast radius 邊界</li>
<li>能否把共享基礎設施切成可獨立恢復的 cell</li>
<li>能否說明 contention 會落在哪個 shard</li>
<li>能否把 recovery 設計成分批恢復，而不是一次全開</li>
</ul>
<h2 id="與其他案例的關係">與其他案例的關係</h2>
<p>Amazon 的重點是把隔離變成架構語言，這和 Meta 的 region failover、Shopify 的 pod 架構、GCP 的控制面邊界都在同一條線上。差別只在於 Amazon 更早把 cell 與 shard 語言標準化，所以特別適合用來反推其他大型平台的設計選擇。</p>
<h2 id="代表樣本">代表樣本</h2>
<ul>
<li>cell-based architecture 讓一個 cell 壞掉時，其他 cell 仍能維持服務。</li>
<li>shuffle sharding 將多租戶請求分散到不同子集合，限制單一客戶或單一熱點的擴散範圍。</li>
<li>static stability 讓 control plane 失效時 data plane 仍可服務。</li>
<li>constant work pattern 避免失敗模式下的額外放大成本。</li>
<li>workload isolation 讓 tenancy / region / AZ 的邊界能各自承擔風險。</li>
<li>failure containment 讓擴散先停在 cell 或 shard 邊界。</li>
<li>push-based recovery 讓恢復節奏不依賴大規模同步操作。</li>
<li>fault isolation 讓局部失效不會拖垮整個 fleet。</li>
<li>constant work 讓 failure mode 不會因為多做一件事而繼續放大。</li>
</ul>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://aws.amazon.com/about-aws/whats-new/2019/12/introducing-amazon-builders-library/">Introducing The Amazon Builders’ Library</a>：Builders&rsquo; Library 的官方入口。</li>
<li><a href="https://aws.amazon.com/builders-library/workload-isolation-using-shuffle-sharding/">Workload isolation using shuffle-sharding</a>：shuffle sharding 與 fault isolation 的官方文章。</li>
<li><a href="https://docs.aws.amazon.com/wellarchitected/latest/reducing-scope-of-impact-with-cell-based-architecture/faq.html">FAQ - Reducing the Scope of Impact with Cell-Based Architecture</a>：cell-based architecture 與 shuffle sharding 的關係說明。</li>
</ul>
]]></content:encoded></item><item><title>k6</title><link>https://tarrragon.github.io/blog/backend/06-reliability/vendors/k6/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/vendors/k6/</guid><description>&lt;p>k6 是 Grafana Labs 出品的 load test 工具、承擔三個責任：CLI-first load test（Go 寫成、JS 寫測試 script）、threshold-based CI gate（pass/fail 直接接 CI）、Grafana Cloud k6 / k6 Operator on K8s 分散式。設計取捨偏向「CI-first + JS DX + 整合 Grafana 生態」、是現代 load test 主流選擇。&lt;/p>
&lt;h2 id="本章目標">本章目標&lt;/h2>
&lt;p>讀完本章後、你應該能：&lt;/p>
&lt;ol>
&lt;li>寫 k6 test script（VU / iteration / stages）&lt;/li>
&lt;li>設計 threshold + CI gate（pass/fail）&lt;/li>
&lt;li>用 xk6 extension 擴展（gRPC / Kafka / SQL）&lt;/li>
&lt;li>部署 k6 Operator 做 distributed load&lt;/li>
&lt;li>評估 k6 vs Gatling / Locust / JMeter 的選用&lt;/li>
&lt;/ol>
&lt;h2 id="最短路徑5-分鐘把-k6-跑起來">最短路徑：5 分鐘把 k6 跑起來&lt;/h2>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 1. 安裝&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: brew install k6 / docker run grafana/k6&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 2. 寫 test.js&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: import http from &amp;#39;k6/http&amp;#39;; export default function(){ http.get(...) }&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 3. 跑&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: k6 run --vus 10 --duration 30s test.js&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="日常操作與決策形狀">日常操作與決策形狀&lt;/h2>
&lt;h3 id="test-script-結構">Test script 結構&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>export default function（per-VU iteration）&lt;/li>
&lt;li>export const options（VU / duration / stages / thresholds）&lt;/li>
&lt;li>Setup / teardown&lt;/li>
&lt;li>對應指令範例：&lt;code>k6 run --vus 100 --duration 10m&lt;/code>&lt;/li>
&lt;/ul>
&lt;h3 id="threshold--ci-gate">Threshold + CI gate&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>thresholds: &lt;code>http_req_duration: ['p(95)&amp;lt;500']&lt;/code>&lt;/li>
&lt;li>Exit code 非 0 → CI fail&lt;/li>
&lt;li>Custom metric thresholds&lt;/li>
&lt;li>對應 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13 Performance Regression Gate&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="test-pattern">Test pattern&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>Smoke / Load / Stress / Spike / Soak / Breakpoint&lt;/li>
&lt;li>Stages（ramp-up / steady / ramp-down）&lt;/li>
&lt;li>VU vs iteration vs RPS-based&lt;/li>
&lt;/ul>
&lt;h2 id="進階主題按需閱讀">進階主題（按需閱讀）&lt;/h2>
&lt;h3 id="xk6-extensions">xk6 extensions&lt;/h3>
&lt;p>子議題：&lt;/p></description><content:encoded><![CDATA[<p>k6 是 Grafana Labs 出品的 load test 工具、承擔三個責任：CLI-first load test（Go 寫成、JS 寫測試 script）、threshold-based CI gate（pass/fail 直接接 CI）、Grafana Cloud k6 / k6 Operator on K8s 分散式。設計取捨偏向「CI-first + JS DX + 整合 Grafana 生態」、是現代 load test 主流選擇。</p>
<h2 id="本章目標">本章目標</h2>
<p>讀完本章後、你應該能：</p>
<ol>
<li>寫 k6 test script（VU / iteration / stages）</li>
<li>設計 threshold + CI gate（pass/fail）</li>
<li>用 xk6 extension 擴展（gRPC / Kafka / SQL）</li>
<li>部署 k6 Operator 做 distributed load</li>
<li>評估 k6 vs Gatling / Locust / JMeter 的選用</li>
</ol>
<h2 id="最短路徑5-分鐘把-k6-跑起來">最短路徑：5 分鐘把 k6 跑起來</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 1. 安裝</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># TODO: brew install k6 / docker run grafana/k6</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 2. 寫 test.js</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># TODO: import http from &#39;k6/http&#39;; export default function(){ http.get(...) }</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># 3. 跑</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c1"># TODO: k6 run --vus 10 --duration 30s test.js</span></span></span></code></pre></div><h2 id="日常操作與決策形狀">日常操作與決策形狀</h2>
<h3 id="test-script-結構">Test script 結構</h3>
<p>子議題：</p>
<ul>
<li>export default function（per-VU iteration）</li>
<li>export const options（VU / duration / stages / thresholds）</li>
<li>Setup / teardown</li>
<li>對應指令範例：<code>k6 run --vus 100 --duration 10m</code></li>
</ul>
<h3 id="threshold--ci-gate">Threshold + CI gate</h3>
<p>子議題：</p>
<ul>
<li>thresholds: <code>http_req_duration: ['p(95)&lt;500']</code></li>
<li>Exit code 非 0 → CI fail</li>
<li>Custom metric thresholds</li>
<li>對應 <a href="/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13 Performance Regression Gate</a></li>
</ul>
<h3 id="test-pattern">Test pattern</h3>
<p>子議題：</p>
<ul>
<li>Smoke / Load / Stress / Spike / Soak / Breakpoint</li>
<li>Stages（ramp-up / steady / ramp-down）</li>
<li>VU vs iteration vs RPS-based</li>
</ul>
<h2 id="進階主題按需閱讀">進階主題（按需閱讀）</h2>
<h3 id="xk6-extensions">xk6 extensions</h3>
<p>子議題：</p>
<ul>
<li>自訂 binary：xk6 build + import extension</li>
<li>內建：HTTP / WebSocket / gRPC</li>
<li>社群：Kafka / SQL / Redis / browser</li>
<li>對應 cross-protocol load test</li>
</ul>
<h3 id="k6-operator-on-k8s">k6 Operator on K8s</h3>
<p>子議題：</p>
<ul>
<li>TestRun CRD</li>
<li>Distributed load（多 pod 模擬高 VU）</li>
<li>Result aggregation</li>
<li>對應 <a href="/blog/backend/05-deployment-platform/vendors/kubernetes/" data-link-title="Kubernetes" data-link-desc="Container orchestration 主流、GKE / EKS / AKS / 自管">Kubernetes vendor 頁</a></li>
</ul>
<h3 id="grafana-cloud-k6">Grafana Cloud k6</h3>
<p>子議題：</p>
<ul>
<li>Managed runner（多 region load source）</li>
<li>跟 Grafana dashboard 整合</li>
<li>跟 Loki / Tempo trace 關聯（test → APM trace）</li>
</ul>
<h3 id="browser-testing">Browser testing</h3>
<p>子議題：</p>
<ul>
<li>k6 browser：Chromium-based browser testing</li>
<li>跟 Playwright 重疊但更聚焦 load</li>
<li>適合 frontend regression load test</li>
</ul>
<h3 id="ci-integration">CI integration</h3>
<p>子議題：</p>
<ul>
<li>GitHub Actions / GitLab CI / Jenkins 整合</li>
<li>Artifact + report upload</li>
<li>對應 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate</a></li>
</ul>
<h3 id="k6-vs-xk6-vs-cloud">k6 vs xk6 vs Cloud</h3>
<p>子議題：</p>
<ul>
<li>k6 OSS：CLI + local script</li>
<li>xk6：build custom binary with extensions</li>
<li>k6 Cloud / Grafana Cloud k6：managed + UI</li>
</ul>
<h2 id="排錯快速判讀">排錯快速判讀</h2>
<h3 id="test-結果差異大">Test 結果差異大</h3>
<p>操作原則：local network / VU saturation / target 處理能力。</p>
<h3 id="threshold-太鬆--太嚴">Threshold 太鬆 / 太嚴</h3>
<p>操作原則：baseline 不準 / production traffic pattern 沒模擬。</p>
<h3 id="distributed-load-不均勻">Distributed load 不均勻</h3>
<p>操作原則：k6 Operator 分配 VU 不均 / pod 規格差異。</p>
<h3 id="browser-testing-慢--不穩">Browser testing 慢 / 不穩</h3>
<p>操作原則：Chromium 啟動成本 / network condition / target 反應時間。</p>
<h2 id="何時改走其他服務">何時改走其他服務</h2>
<table>
  <thead>
      <tr>
          <th>需求形狀</th>
          <th>改走</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>JVM 生態</td>
          <td><a href="/blog/backend/06-reliability/vendors/gatling/" data-link-title="Gatling" data-link-desc="JVM-based load test、Scala / Java / Kotlin DSL、強型別 scenario、HAR-driven recording">Gatling</a></td>
      </tr>
      <tr>
          <td>GUI / 老牌</td>
          <td><a href="/blog/backend/06-reliability/vendors/jmeter/" data-link-title="Apache JMeter" data-link-desc="老牌 load test 工具、GUI &#43; plugins">JMeter</a></td>
      </tr>
      <tr>
          <td>Python</td>
          <td><a href="/blog/backend/06-reliability/vendors/locust/" data-link-title="Locust" data-link-desc="Python-based load test、distributed、易擴展">Locust</a></td>
      </tr>
      <tr>
          <td>純 browser flow</td>
          <td>Playwright / Cypress</td>
      </tr>
      <tr>
          <td>Cloud managed</td>
          <td>Grafana Cloud k6 / BlazeMeter / k6 Cloud</td>
      </tr>
      <tr>
          <td>Capacity planning（非 CI）</td>
          <td><a href="/blog/backend/09-performance-capacity/" data-link-title="模組九：效能工程與容量規劃" data-link-desc="把『目前配置能撐多少、要加多少機器』變成可量化、可驗證、可改進的工程流程">09 performance capacity 模組</a></td>
      </tr>
  </tbody>
</table>
<h2 id="不在本頁內的主題">不在本頁內的主題</h2>
<ul>
<li>JS 語言基礎</li>
<li>k6 完整 API</li>
<li>Grafana Cloud k6 pricing</li>
</ul>
<h2 id="案例回寫">案例回寫</h2>
<table>
  <thead>
      <tr>
          <th>案例方向</th>
          <th>對應主題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/shopify/bfcm-capacity-and-game-day/" data-link-title="Shopify：BFCM 容量治理與 Game Day 驗證節奏" data-link-desc="把季節性流量峰值轉成年度可靠性流程，透過容量模型、演練與隔離策略提前吸收風險。">Shopify：BFCM 容量治理與 Game Day</a></td>
          <td>峰值前 load test 對齊 capacity model + CI gate</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/linkedin/capacity-headroom-and-oncall-tiering/" data-link-title="LinkedIn：Capacity Headroom 與 On-call 分層" data-link-desc="把容量預測與值班分層綁在一起，降低高峰時段的升級混亂與恢復延遲。">LinkedIn：Capacity 與 On-call 分層</a></td>
          <td>automated load testing 變成日常流程的工程化做法</td>
      </tr>
  </tbody>
</table>
<p><strong>待補 k6 customer case</strong>：Grafana Labs / k6 customer engineering blog、企業遷移 JMeter → k6 案例。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>上游概念：<a href="/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13 Performance Regression Gate</a></li>
<li>平行 vendor：<a href="/blog/backend/06-reliability/vendors/gatling/" data-link-title="Gatling" data-link-desc="JVM-based load test、Scala / Java / Kotlin DSL、強型別 scenario、HAR-driven recording">Gatling</a>、<a href="/blog/backend/06-reliability/vendors/locust/" data-link-title="Locust" data-link-desc="Python-based load test、distributed、易擴展">Locust</a>、<a href="/blog/backend/06-reliability/vendors/jmeter/" data-link-title="Apache JMeter" data-link-desc="老牌 load test 工具、GUI &#43; plugins">JMeter</a></li>
<li>下游能力：<a href="/blog/backend/09-performance-capacity/" data-link-title="模組九：效能工程與容量規劃" data-link-desc="把『目前配置能撐多少、要加多少機器』變成可量化、可驗證、可改進的工程流程">09 performance capacity</a> load test 模組</li>
</ul>
]]></content:encoded></item><item><title>6.3 fuzz campaign</title><link>https://tarrragon.github.io/blog/backend/06-reliability/fuzz-campaign/</link><pubDate>Thu, 23 Apr 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/fuzz-campaign/</guid><description>&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/fuzz-test/" data-link-title="Fuzz Test" data-link-desc="說明用隨機與變異輸入驗證解析器與邊界處理健壯性">Fuzz test&lt;/a> 把沒想過的輸入轉成可重播、可修補的失敗案例，補齊人工列舉無法觸及的邊界盲區。&lt;/p>
&lt;p>這一頁處理的是輸入空間的盲區。當 API、parser、codec 或 schema 的邊界不清楚時，fuzz 比人工列案例更能覆蓋非預期路徑。&lt;/p>
&lt;h2 id="核心判讀">核心判讀&lt;/h2>
&lt;p>判讀 fuzz 的品質先看 target 選擇是否對準高風險輸入邊界，再看 corpus 是否持續收斂，最後看 crash 是否能轉成可回歸的修復。&lt;/p>
&lt;p>重點判斷：&lt;/p>
&lt;ul>
&lt;li>fuzz target 是否足夠小，能對準單一責任&lt;/li>
&lt;li>corpus 是否持續收斂，coverage delta 是否仍為正&lt;/li>
&lt;li>crash reproduction 是否可重播到同一條路徑&lt;/li>
&lt;li>修補後是否回寫成 regression test&lt;/li>
&lt;/ul>
&lt;h2 id="fuzz-target-設計">Fuzz target 設計&lt;/h2>
&lt;p>Fuzz target 是 fuzz campaign 的最小驗證單位，責任是把外部輸入導入一個可觀測邊界的函式。&lt;/p>
&lt;p>好的 target 對準單一 parser、codec、serializer 或 validation function，函式簽章接受原始位元組（如 &lt;code>func([]byte)&lt;/code> 或等效形式）。target 選擇的判準有三個：這個函式是否直接處理外部輸入、邊界行為是否不清楚、crash 是否有業務影響。&lt;/p>
&lt;p>target 粒度影響 fuzz 的效率與判讀價值。target 太大（整個 HTTP handler 含 auth / routing / DB 存取）會讓 crash 難以定位到具體邊界，因為 fuzz engine 需要同時探索太多分支，coverage 增長慢且 crash 歸因模糊。target 太小（單一 if 分支）會讓 coverage 增長無意義，因為分支行為已經被 unit test 覆蓋。&lt;/p>
&lt;p>常見的高價值 target 類型：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Target 類型&lt;/th>
 &lt;th>典型邊界風險&lt;/th>
 &lt;th>範例&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Protocol parser&lt;/td>
 &lt;td>畸形封包、長度溢位、巢狀深度&lt;/td>
 &lt;td>HTTP header parser、gRPC frame decoder&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Schema deserializer&lt;/td>
 &lt;td>型別不匹配、缺欄位、巢狀物件遞迴&lt;/td>
 &lt;td>JSON/Protobuf/MessagePack deserializer&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Image / media codec&lt;/td>
 &lt;td>buffer overflow、memory allocation&lt;/td>
 &lt;td>PNG decoder、PDF parser&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Validation function&lt;/td>
 &lt;td>邊界值、正則回溯、encoding 混淆&lt;/td>
 &lt;td>email validator、URL parser、SQL escaper&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Config parser&lt;/td>
 &lt;td>非預期組合、環境變數注入&lt;/td>
 &lt;td>YAML/TOML config loader&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="corpus-管理">Corpus 管理&lt;/h2>
&lt;p>Corpus 累積有效的輸入種子，讓 fuzz engine 能從已知邊界往外探索。corpus 品質直接決定 fuzz campaign 的探索效率。&lt;/p>
&lt;p>初始 corpus 從三個來源收集：unit test 的既有 fixture（已知的合法與邊界輸入）、production sample 脫敏後的真實請求（反映實際流量的輸入結構）、schema 範例與文件中的合法樣本。初始 corpus 的重點是涵蓋主要合法路徑，讓 fuzz engine 從合法輸入開始 mutation，更容易觸達邊界。&lt;/p>
&lt;p>持續擴充靠 coverage-guided mutation。fuzz engine 每次產生的 mutated input 若觸發了新的 code path（新分支、新呼叫），這個 input 會自動加入 corpus。隨著 campaign 進行，corpus 會累積越來越多能觸達深層分支的種子。&lt;/p>
&lt;p>corpus 品質的判讀指標是 coverage delta trend — 每個時段新增的 code path 數量。coverage delta 持續為正代表 corpus 仍在有效探索；coverage delta 趨近零代表當前 target 的探索接近飽和，應考慮三個方向：切換到新 target、調整 mutation dictionary（加入 domain-specific token）、或擴充初始 corpus 的多樣性。&lt;/p>
&lt;p>corpus 需要持久化管理。corpus 檔案應納入版本控制或 artifact storage，跨 CI job 保留。每次 fuzz campaign 結束時，新發現的有效種子合併回 corpus；crash input 在修復後轉成 regression fixture，從 fuzz corpus 移到 test fixture。&lt;/p></description><content:encoded><![CDATA[<h2 id="概念定位">概念定位</h2>
<p><a href="/blog/backend/knowledge-cards/fuzz-test/" data-link-title="Fuzz Test" data-link-desc="說明用隨機與變異輸入驗證解析器與邊界處理健壯性">Fuzz test</a> 把沒想過的輸入轉成可重播、可修補的失敗案例，補齊人工列舉無法觸及的邊界盲區。</p>
<p>這一頁處理的是輸入空間的盲區。當 API、parser、codec 或 schema 的邊界不清楚時，fuzz 比人工列案例更能覆蓋非預期路徑。</p>
<h2 id="核心判讀">核心判讀</h2>
<p>判讀 fuzz 的品質先看 target 選擇是否對準高風險輸入邊界，再看 corpus 是否持續收斂，最後看 crash 是否能轉成可回歸的修復。</p>
<p>重點判斷：</p>
<ul>
<li>fuzz target 是否足夠小，能對準單一責任</li>
<li>corpus 是否持續收斂，coverage delta 是否仍為正</li>
<li>crash reproduction 是否可重播到同一條路徑</li>
<li>修補後是否回寫成 regression test</li>
</ul>
<h2 id="fuzz-target-設計">Fuzz target 設計</h2>
<p>Fuzz target 是 fuzz campaign 的最小驗證單位，責任是把外部輸入導入一個可觀測邊界的函式。</p>
<p>好的 target 對準單一 parser、codec、serializer 或 validation function，函式簽章接受原始位元組（如 <code>func([]byte)</code> 或等效形式）。target 選擇的判準有三個：這個函式是否直接處理外部輸入、邊界行為是否不清楚、crash 是否有業務影響。</p>
<p>target 粒度影響 fuzz 的效率與判讀價值。target 太大（整個 HTTP handler 含 auth / routing / DB 存取）會讓 crash 難以定位到具體邊界，因為 fuzz engine 需要同時探索太多分支，coverage 增長慢且 crash 歸因模糊。target 太小（單一 if 分支）會讓 coverage 增長無意義，因為分支行為已經被 unit test 覆蓋。</p>
<p>常見的高價值 target 類型：</p>
<table>
  <thead>
      <tr>
          <th>Target 類型</th>
          <th>典型邊界風險</th>
          <th>範例</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Protocol parser</td>
          <td>畸形封包、長度溢位、巢狀深度</td>
          <td>HTTP header parser、gRPC frame decoder</td>
      </tr>
      <tr>
          <td>Schema deserializer</td>
          <td>型別不匹配、缺欄位、巢狀物件遞迴</td>
          <td>JSON/Protobuf/MessagePack deserializer</td>
      </tr>
      <tr>
          <td>Image / media codec</td>
          <td>buffer overflow、memory allocation</td>
          <td>PNG decoder、PDF parser</td>
      </tr>
      <tr>
          <td>Validation function</td>
          <td>邊界值、正則回溯、encoding 混淆</td>
          <td>email validator、URL parser、SQL escaper</td>
      </tr>
      <tr>
          <td>Config parser</td>
          <td>非預期組合、環境變數注入</td>
          <td>YAML/TOML config loader</td>
      </tr>
  </tbody>
</table>
<h2 id="corpus-管理">Corpus 管理</h2>
<p>Corpus 累積有效的輸入種子，讓 fuzz engine 能從已知邊界往外探索。corpus 品質直接決定 fuzz campaign 的探索效率。</p>
<p>初始 corpus 從三個來源收集：unit test 的既有 fixture（已知的合法與邊界輸入）、production sample 脫敏後的真實請求（反映實際流量的輸入結構）、schema 範例與文件中的合法樣本。初始 corpus 的重點是涵蓋主要合法路徑，讓 fuzz engine 從合法輸入開始 mutation，更容易觸達邊界。</p>
<p>持續擴充靠 coverage-guided mutation。fuzz engine 每次產生的 mutated input 若觸發了新的 code path（新分支、新呼叫），這個 input 會自動加入 corpus。隨著 campaign 進行，corpus 會累積越來越多能觸達深層分支的種子。</p>
<p>corpus 品質的判讀指標是 coverage delta trend — 每個時段新增的 code path 數量。coverage delta 持續為正代表 corpus 仍在有效探索；coverage delta 趨近零代表當前 target 的探索接近飽和，應考慮三個方向：切換到新 target、調整 mutation dictionary（加入 domain-specific token）、或擴充初始 corpus 的多樣性。</p>
<p>corpus 需要持久化管理。corpus 檔案應納入版本控制或 artifact storage，跨 CI job 保留。每次 fuzz campaign 結束時，新發現的有效種子合併回 corpus；crash input 在修復後轉成 regression fixture，從 fuzz corpus 移到 test fixture。</p>
<h2 id="crash-reproduction-與-minimization">Crash reproduction 與 minimization</h2>
<p>Fuzz 找到 crash 後的處理流程是 reproduce → minimize → fix → 回灌 regression test。</p>
<p><strong>Reproduce</strong>：用 fuzz engine 產出的 crash input 在相同環境重跑，確認 crash 可穩定觸發。不可穩定觸發的 crash 通常來自 race condition 或環境差異，需要額外的 concurrency 或環境控制才能定位。</p>
<p><strong>Minimize</strong>：minimization 把觸發 crash 的輸入縮到最小等效形式，讓 root cause 更容易定位。自動化 minimizer（如 Go 內建的 fuzz minimizer、libFuzzer 的 <code>-minimize_crash=1</code>）會反覆刪減 input 中的位元組，保留能觸發同一 crash 的最小子集。minimized input 通常比原始 input 短一到兩個數量級，讓開發者能直接看出觸發條件。</p>
<p><strong>Fix 與 regression test</strong>：修復 crash 後，用 minimized input 作為 fixture 寫成 regression test。這個 test 確保同類 bug 不再出現，也讓未來的 refactor 不會重新打開已修復的邊界。regression test 歸入 <a href="/blog/backend/06-reliability/ci-pipeline/" data-link-title="6.1 CI pipeline" data-link-desc="CI pipeline 的分層策略、artifact 管理、flaky 治理與 release gate 輸入">CI pipeline</a> 的 fast path，每次 push 都跑。</p>
<h2 id="ci-整合">CI 整合</h2>
<p>Fuzz 在 CI 的執行模式跟 unit test 不同。unit test 有明確的 pass/fail 結束條件，fuzz campaign 是開放式探索，執行時間越長覆蓋越廣。</p>
<p>CI 整合分兩種模式，對齊 <a href="/blog/backend/06-reliability/ci-pipeline/" data-link-title="6.1 CI pipeline" data-link-desc="CI pipeline 的分層策略、artifact 管理、flaky 治理與 release gate 輸入">6.1 CI pipeline</a> 的分層策略：</p>
<p><strong>Fast path regression</strong>（30 秒至 5 分鐘）：用既有 corpus 跑 fuzz，確認已知邊界沒退化。這個模式的目標是 regression 檢查，每次 push 觸發。corpus 裡的種子已經覆蓋了過去發現的邊界，短時間跑完可以確保修復沒被破壞、新變更沒引入已知類型的 crash。</p>
<p><strong>Scheduled exploration</strong>（小時級）：定期（每日或每週）跑長時間 fuzz，讓 engine 有足夠時間做深層 mutation 與路徑探索。新發現的種子合併回 corpus，crash input 產生 issue 或 alert。這個模式的 coverage delta 是判讀 campaign 價值的主要指標。</p>
<p>CI 整合的關鍵是 corpus 持久化。corpus 必須跨 job 保存（cache、artifact storage 或版本控制），每次 job 從上一次的 corpus 繼續探索。若 corpus 每次從零開始，fuzz engine 會重複探索已知路徑，浪費運算資源。</p>
<h2 id="coverage-門檻與收斂判讀">Coverage 門檻與收斂判讀</h2>
<p>Fuzz coverage 跟 unit test coverage 的意義不同。unit test coverage 衡量的是「多少行被執行過」，fuzz coverage 衡量的是「多少邊界被探索過」。同一個函式的 fuzz coverage 可以隨 corpus 擴充持續增長，因為 mutation 會觸發不同的分支組合。</p>
<p>判讀 fuzz campaign 是否仍有價值靠兩個指標：coverage delta trend（每小時新增多少 code path）與 corpus size growth（每小時新增多少有效種子）。兩者同時趨近零代表當前 target 的探索飽和。</p>
<p>飽和訊號指引兩個決策。第一，是否切換 target — 當前 target 的邊界已被充分探索，把 fuzz 資源移到另一個高風險 target 的邊際價值更高。第二，是否調整 mutation dictionary — 加入 domain-specific token（如 SQL keyword、JSON structure token、protocol magic bytes）可以讓 engine 更有效地觸達 domain-aware 的邊界。</p>
<h2 id="案例對照">案例對照</h2>
<ul>
<li><a href="/blog/backend/06-reliability/cases/google/" data-link-title="Google" data-link-desc="Google SRE 實踐原典：SLI / SLO / Error Budget / Postmortem 文化">Google</a>：OSS-Fuzz 對大量基礎元件（parser、codec、serializer）做持續 fuzz，corpus 跨版本累積，crash 自動提 issue 並追蹤修復。這個規模的 fuzz campaign 說明 corpus 持久化與自動化 crash 處理是可擴展的前提。</li>
<li><a href="/blog/backend/06-reliability/cases/stripe/" data-link-title="Stripe" data-link-desc="Stripe Deploy Strategy / Game Day / Idempotency 實踐">Stripe</a>：API 與 serialization 邊界的 fuzz 需要 domain-specific dictionary（支付欄位、currency code、idempotency key 格式），通用 mutation 難以觸達業務語意上的邊界 crash。</li>
<li><a href="/blog/backend/08-incident-response/cases/github/" data-link-title="GitHub" data-link-desc="GitHub 重大事故時間線與架構脈絡">GitHub</a>：webhook payload 與 schema 邊界的 fuzz 適合用 schema-aware fuzzer，從 OpenAPI / JSON Schema 產生結構化 mutation，覆蓋嵌套物件與型別邊界。</li>
</ul>
<h2 id="判讀訊號">判讀訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀條件</th>
          <th>行動建議</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>fuzz corpus 從未更新、覆蓋率停滯</td>
          <td>campaign 已失去探索價值 — 檢查是否需要換 target 或調整 mutation strategy</td>
          <td>換 target 或加 mutation dictionary</td>
      </tr>
      <tr>
          <td>crash 復現靠人工 minimization</td>
          <td>minimization 應自動化 — 手動 minimization 耗時且不可重複</td>
          <td>啟用 fuzzer 內建 minimizer 或接 CI 自動化</td>
      </tr>
      <tr>
          <td>fuzz 找到 bug 沒回灌成 regression test</td>
          <td>修復後邊界可能被再次打開 — regression fixture 應歸入 CI fast path</td>
          <td>把 minimized input 加入 CI regression 套件</td>
      </tr>
      <tr>
          <td>input boundary 無 spec、fuzz 範圍模糊</td>
          <td>target 選擇需要對齊 — 先定義哪些函式直接處理外部輸入</td>
          <td>盤點外部輸入函式、建立 target 清單</td>
      </tr>
      <tr>
          <td>production 出 crash 但 fuzz 沒抓到</td>
          <td>fuzz target 未覆蓋該輸入路徑 — 把 production crash input 加入 corpus</td>
          <td>補 target + 把 crash input 加入 seed</td>
      </tr>
      <tr>
          <td>coverage delta 持續為零但仍在跑長時間 fuzz</td>
          <td>資源浪費 — 飽和後應切換 target 或調整 dictionary</td>
          <td>停止當前 campaign、轉移資源到新 target</td>
      </tr>
  </tbody>
</table>
<h2 id="交接路由">交接路由</h2>
<ul>
<li><a href="/blog/backend/06-reliability/ci-pipeline/" data-link-title="6.1 CI pipeline" data-link-desc="CI pipeline 的分層策略、artifact 管理、flaky 治理與 release gate 輸入">6.1 CI pipeline</a>：fuzz regression 歸入 fast path、exploration 歸入 scheduled path</li>
<li><a href="/blog/backend/06-reliability/contract-testing/" data-link-title="6.10 Contract Testing 與 Schema 演進" data-link-desc="把跨服務 / API / event schema 的隱性期待變成可驗證契約，控制演進相容性">6.10 contract testing</a>：schema fuzz 與契約驗證互補，contract 定義已知邊界、fuzz 探索未知邊界</li>
<li><a href="/blog/backend/06-reliability/test-data-management/" data-link-title="6.16 Test Data Management" data-link-desc="把 fixture / seed / production-like data 作為跨模組共用 artifact，治理資料層次、遮罩策略與可重現性">6.16 test data</a>：fuzz 找到的 crash input 沉澱成 seed 與 fixture</li>
<li><a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 experiment safety boundary</a>：長時間 fuzz campaign 在 production-like 環境跑時需要資源邊界控制</li>
<li><a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate</a>：security-relevant fuzz crash 可作為 release 阻擋條件</li>
<li><a href="/blog/backend/08-incident-response/incident-pattern-library/" data-link-title="8.9 事故型態庫入口" data-link-desc="把跨服務的共通事故型態抽成型態卡，作為新事故的判讀錨點">8.9 事故型態庫</a>：recurrent crash pattern 抽象化成型態</li>
</ul>
]]></content:encoded></item><item><title>Flaky test 根因分類</title><link>https://tarrragon.github.io/blog/testing/05-test-design-judgment/flaky-test-root-cause/</link><pubDate>Fri, 19 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/testing/05-test-design-judgment/flaky-test-root-cause/</guid><description>&lt;p>Flaky test 是指在程式碼沒有改變的情況下，test 的結果在通過和失敗之間隨機切換。Flaky test 侵蝕團隊對 test suite 的信任 — 如果 test 經常「隨便」失敗，開發者會習慣性地 re-run 而非調查失敗原因，真正的 bug 可能在 re-run 中被忽略。&lt;/p>
&lt;h2 id="四類根因">四類根因&lt;/h2>
&lt;h3 id="計時依賴">計時依賴&lt;/h3>
&lt;p>Test 依賴特定的時間條件 — timeout、delay、animation duration。系統負載不同時，時間條件可能滿足也可能不滿足。&lt;/p>
&lt;p>常見模式：&lt;/p>
&lt;ul>
&lt;li>&lt;code>await Future.delayed(Duration(seconds: 2))&lt;/code> + assertion — 如果操作在 2 秒內完成，test 通過；如果 CI 機器負載高導致操作超過 2 秒，test 失敗&lt;/li>
&lt;li>&lt;code>expect(stopwatch.elapsed, lessThan(Duration(seconds: 1)))&lt;/code> — 效能斷言在不同機器上結果不同&lt;/li>
&lt;/ul>
&lt;p>處理策略：用事件驅動代替 timeout。等待 &lt;code>stream.first&lt;/code> 代替 &lt;code>delay(2s) + check&lt;/code>；用 completion signal 代替固定等待時間。如果必須用 timeout，設定寬裕的上限（10x 預期時間）而非精確的預期值。&lt;/p>
&lt;h3 id="環境差異">環境差異&lt;/h3>
&lt;p>Test 在不同環境下行為不同 — 作業系統、檔案系統、時區、locale、DNS 解析。&lt;/p>
&lt;p>常見模式：&lt;/p>
&lt;ul>
&lt;li>檔案路徑分隔符（&lt;code>/&lt;/code> vs &lt;code>\&lt;/code>）在不同 OS 下不同&lt;/li>
&lt;li>時間格式化結果依時區而定（UTC vs local）&lt;/li>
&lt;li>浮點數比較因 CPU 架構不同有微小差異&lt;/li>
&lt;/ul>
&lt;p>處理策略：用 &lt;code>path.join&lt;/code> 代替硬編碼路徑；時間操作用 UTC；浮點比較用 &lt;code>closeTo&lt;/code> 代替精確比較。在 CI 中固定環境變數（&lt;code>TZ=UTC&lt;/code>、&lt;code>LANG=en_US.UTF-8&lt;/code>）。&lt;/p>
&lt;h3 id="資源競爭">資源競爭&lt;/h3>
&lt;p>Test 依賴共享資源（port、暫存檔、資料庫行）— 平行執行時多個 test 同時存取同一資源，結果依賴執行順序。&lt;/p>
&lt;p>常見模式：&lt;/p>
&lt;ul>
&lt;li>多個 test 監聽同一個 port — 第二個綁定失敗&lt;/li>
&lt;li>多個 test 寫入同一個暫存檔 — 內容被覆蓋&lt;/li>
&lt;li>多個 test 操作同一個資料庫 table — 資料互相干擾&lt;/li>
&lt;/ul>
&lt;p>處理策略：每個 test 使用獨立的資源（隨機 port、唯一檔名、隔離的資料庫 schema）。如果資源無法隔離，sequential 執行相關 test（&lt;code>@sequential&lt;/code> 標註）。&lt;/p>
&lt;h3 id="非確定性輸出">非確定性輸出&lt;/h3>
&lt;p>程式碼的輸出本身不確定 — &lt;code>Set&lt;/code> 的迭代順序、&lt;code>Map&lt;/code> 的 key 順序、非同步操作的完成順序。&lt;/p>
&lt;p>常見模式：&lt;/p>
&lt;ul>
&lt;li>斷言 &lt;code>Set&lt;/code> 的 &lt;code>toString()&lt;/code> 結果等於特定字串 — &lt;code>Set&lt;/code> 的迭代順序不保證&lt;/li>
&lt;li>斷言 &lt;code>Future.wait([a, b]).then((results) =&amp;gt; results[0])&lt;/code> — &lt;code>a&lt;/code> 和 &lt;code>b&lt;/code> 的完成順序不固定&lt;/li>
&lt;li>斷言 JSON 序列化的 key 順序 — &lt;code>Map&lt;/code> 的 key 順序在不同實作中不同&lt;/li>
&lt;/ul>
&lt;p>處理策略：不斷言順序（用 &lt;code>containsAll&lt;/code> 代替 &lt;code>equals&lt;/code> 比較集合）；不斷言序列化格式（反序列化後比較值）；用 &lt;code>completion&lt;/code> matcher 代替順序假設。&lt;/p>
&lt;h2 id="診斷步驟">診斷步驟&lt;/h2>
&lt;p>發現疑似 flaky test 時的診斷步驟：&lt;/p>
&lt;ol>
&lt;li>&lt;strong>確認 flaky&lt;/strong>：在乾淨環境連續跑 20 次，確認失敗是隨機的（如果每次都失敗，是 bug 不是 flaky）&lt;/li>
&lt;li>&lt;strong>收集失敗訊息&lt;/strong>：記錄每次失敗的 assertion 訊息、stack trace、環境資訊（OS 版本、CI 機器 ID）&lt;/li>
&lt;li>&lt;strong>分類&lt;/strong>：失敗訊息指向時間（timeout）→ 計時依賴；指向值不同 → 非確定性或環境差異；指向連接失敗 → 資源競爭&lt;/li>
&lt;li>&lt;strong>修復&lt;/strong>：根據分類使用對應的處理策略&lt;/li>
&lt;/ol>
&lt;p>分類和修復之外，flaky test 的根因有時來自 assertion 本身的設計 — &lt;a href="https://tarrragon.github.io/blog/testing/05-test-design-judgment/assertion-quality/" data-link-title="Assertion 品質三問" data-link-desc="斷言的是行為嗎？能區分正確和錯誤嗎？會 flaky 嗎？— 三個問題判斷 assertion 是否有效">Assertion 品質三問&lt;/a>提供判斷 assertion 是否有效的框架。如果 flaky 的根因是 mock 和真實服務的行為差異，回到 &lt;a href="https://tarrragon.github.io/blog/testing/05-test-design-judgment/mock-boundary-decision/" data-link-title="Mock 邊界判斷決策表" data-link-desc="什麼時候 mock 夠用、什麼時候需要真實服務 — 從 API 層 / 協議層 / 環境層的斷裂點判斷 mock 的適用範圍">Mock 邊界判斷決策表&lt;/a>判斷 mock 是否還適用。Protocol integration test 在 CI 中的&lt;a href="https://tarrragon.github.io/blog/testing/03-protocol-integration-test/service-fixture-management/" data-link-title="CI 中的服務 fixture 管理" data-link-desc="在 CI 中啟動和停止真實服務的 test harness 設計 — Process.start / Docker / testcontainers 三種方案的適用場景">服務 fixture 管理&lt;/a>也是 flaky 的常見來源 — 服務啟動不完全就開始跑 test。&lt;/p></description><content:encoded><![CDATA[<p>Flaky test 是指在程式碼沒有改變的情況下，test 的結果在通過和失敗之間隨機切換。Flaky test 侵蝕團隊對 test suite 的信任 — 如果 test 經常「隨便」失敗，開發者會習慣性地 re-run 而非調查失敗原因，真正的 bug 可能在 re-run 中被忽略。</p>
<h2 id="四類根因">四類根因</h2>
<h3 id="計時依賴">計時依賴</h3>
<p>Test 依賴特定的時間條件 — timeout、delay、animation duration。系統負載不同時，時間條件可能滿足也可能不滿足。</p>
<p>常見模式：</p>
<ul>
<li><code>await Future.delayed(Duration(seconds: 2))</code> + assertion — 如果操作在 2 秒內完成，test 通過；如果 CI 機器負載高導致操作超過 2 秒，test 失敗</li>
<li><code>expect(stopwatch.elapsed, lessThan(Duration(seconds: 1)))</code> — 效能斷言在不同機器上結果不同</li>
</ul>
<p>處理策略：用事件驅動代替 timeout。等待 <code>stream.first</code> 代替 <code>delay(2s) + check</code>；用 completion signal 代替固定等待時間。如果必須用 timeout，設定寬裕的上限（10x 預期時間）而非精確的預期值。</p>
<h3 id="環境差異">環境差異</h3>
<p>Test 在不同環境下行為不同 — 作業系統、檔案系統、時區、locale、DNS 解析。</p>
<p>常見模式：</p>
<ul>
<li>檔案路徑分隔符（<code>/</code> vs <code>\</code>）在不同 OS 下不同</li>
<li>時間格式化結果依時區而定（UTC vs local）</li>
<li>浮點數比較因 CPU 架構不同有微小差異</li>
</ul>
<p>處理策略：用 <code>path.join</code> 代替硬編碼路徑；時間操作用 UTC；浮點比較用 <code>closeTo</code> 代替精確比較。在 CI 中固定環境變數（<code>TZ=UTC</code>、<code>LANG=en_US.UTF-8</code>）。</p>
<h3 id="資源競爭">資源競爭</h3>
<p>Test 依賴共享資源（port、暫存檔、資料庫行）— 平行執行時多個 test 同時存取同一資源，結果依賴執行順序。</p>
<p>常見模式：</p>
<ul>
<li>多個 test 監聽同一個 port — 第二個綁定失敗</li>
<li>多個 test 寫入同一個暫存檔 — 內容被覆蓋</li>
<li>多個 test 操作同一個資料庫 table — 資料互相干擾</li>
</ul>
<p>處理策略：每個 test 使用獨立的資源（隨機 port、唯一檔名、隔離的資料庫 schema）。如果資源無法隔離，sequential 執行相關 test（<code>@sequential</code> 標註）。</p>
<h3 id="非確定性輸出">非確定性輸出</h3>
<p>程式碼的輸出本身不確定 — <code>Set</code> 的迭代順序、<code>Map</code> 的 key 順序、非同步操作的完成順序。</p>
<p>常見模式：</p>
<ul>
<li>斷言 <code>Set</code> 的 <code>toString()</code> 結果等於特定字串 — <code>Set</code> 的迭代順序不保證</li>
<li>斷言 <code>Future.wait([a, b]).then((results) =&gt; results[0])</code> — <code>a</code> 和 <code>b</code> 的完成順序不固定</li>
<li>斷言 JSON 序列化的 key 順序 — <code>Map</code> 的 key 順序在不同實作中不同</li>
</ul>
<p>處理策略：不斷言順序（用 <code>containsAll</code> 代替 <code>equals</code> 比較集合）；不斷言序列化格式（反序列化後比較值）；用 <code>completion</code> matcher 代替順序假設。</p>
<h2 id="診斷步驟">診斷步驟</h2>
<p>發現疑似 flaky test 時的診斷步驟：</p>
<ol>
<li><strong>確認 flaky</strong>：在乾淨環境連續跑 20 次，確認失敗是隨機的（如果每次都失敗，是 bug 不是 flaky）</li>
<li><strong>收集失敗訊息</strong>：記錄每次失敗的 assertion 訊息、stack trace、環境資訊（OS 版本、CI 機器 ID）</li>
<li><strong>分類</strong>：失敗訊息指向時間（timeout）→ 計時依賴；指向值不同 → 非確定性或環境差異；指向連接失敗 → 資源競爭</li>
<li><strong>修復</strong>：根據分類使用對應的處理策略</li>
</ol>
<p>分類和修復之外，flaky test 的根因有時來自 assertion 本身的設計 — <a href="/blog/testing/05-test-design-judgment/assertion-quality/" data-link-title="Assertion 品質三問" data-link-desc="斷言的是行為嗎？能區分正確和錯誤嗎？會 flaky 嗎？— 三個問題判斷 assertion 是否有效">Assertion 品質三問</a>提供判斷 assertion 是否有效的框架。如果 flaky 的根因是 mock 和真實服務的行為差異，回到 <a href="/blog/testing/05-test-design-judgment/mock-boundary-decision/" data-link-title="Mock 邊界判斷決策表" data-link-desc="什麼時候 mock 夠用、什麼時候需要真實服務 — 從 API 層 / 協議層 / 環境層的斷裂點判斷 mock 的適用範圍">Mock 邊界判斷決策表</a>判斷 mock 是否還適用。Protocol integration test 在 CI 中的<a href="/blog/testing/03-protocol-integration-test/service-fixture-management/" data-link-title="CI 中的服務 fixture 管理" data-link-desc="在 CI 中啟動和停止真實服務的 test harness 設計 — Process.start / Docker / testcontainers 三種方案的適用場景">服務 fixture 管理</a>也是 flaky 的常見來源 — 服務啟動不完全就開始跑 test。</p>
]]></content:encoded></item><item><title>Gatling</title><link>https://tarrragon.github.io/blog/backend/06-reliability/vendors/gatling/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/vendors/gatling/</guid><description>&lt;p>Gatling 是 JVM 生態的 load test 工具、承擔三個責任：code-first 強型別 scenario DSL（Scala / Java / Kotlin、編譯期就抓 script bug）、async / non-blocking 引擎（單機高 VU 不靠 thread-per-VU）、Gatling Enterprise 分散式負載與企業 dashboard。設計取捨偏向「強型別 + 高單機 throughput + JVM 既有資產」、跟 k6（JS DX）跟 JMeter（GUI + plugins）的取捨在 dev workflow 跟團隊既有技能。&lt;/p>
&lt;h2 id="本章目標">本章目標&lt;/h2>
&lt;p>讀完本章後、你應該能：&lt;/p>
&lt;ol>
&lt;li>用 Scala / Java / Kotlin DSL 寫 simulation（scenario + injection profile）&lt;/li>
&lt;li>設計 assertion + threshold 接 CI&lt;/li>
&lt;li>用 HAR-driven recording 從瀏覽器抓真實 user flow 起 script&lt;/li>
&lt;li>評估 Gatling Enterprise 分散式 vs OSS 單機高 VU 的取捨&lt;/li>
&lt;li>評估 Gatling vs k6 / JMeter / Locust 的選用條件&lt;/li>
&lt;/ol>
&lt;h2 id="最短路徑5-分鐘把-gatling-跑起來">最短路徑：5 分鐘把 Gatling 跑起來&lt;/h2>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln"> 1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 1. 安裝&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 2&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: brew install gatling / 下載 bundle / Maven / sbt plugin&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 2. 寫 simulation&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 5&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: class MySim extends Simulation {&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 6&lt;/span>&lt;span class="cl">&lt;span class="c1"># val httpProtocol = http.baseUrl(&amp;#34;...&amp;#34;)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 7&lt;/span>&lt;span class="cl">&lt;span class="c1"># val scn = scenario(&amp;#34;...&amp;#34;).exec(http(&amp;#34;get&amp;#34;).get(&amp;#34;/&amp;#34;))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 8&lt;/span>&lt;span class="cl">&lt;span class="c1"># setUp(scn.inject(rampUsersPerSec(1).to(50).during(60))).protocols(httpProtocol)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln"> 9&lt;/span>&lt;span class="cl">&lt;span class="c1"># }&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">10&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">11&lt;/span>&lt;span class="cl">&lt;span class="c1"># 3. 跑&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">12&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: gatling.sh -s MySim / mvn gatling:test / sbt Gatling/test&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="日常操作與決策形狀">日常操作與決策形狀&lt;/h2>
&lt;h3 id="simulation-結構">Simulation 結構&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>&lt;code>Simulation&lt;/code> class（一個檔一個 simulation、整個 test 的根）&lt;/li>
&lt;li>&lt;code>scenario(...).exec(...)&lt;/code>（一條 user journey 的步驟序列）&lt;/li>
&lt;li>&lt;code>httpProtocol&lt;/code>（baseUrl / header / acceptedContent / proxy 共用配置）&lt;/li>
&lt;li>&lt;code>feeder&lt;/code>（CSV / JSON / JDBC 餵 data、配合 &lt;code>randomFeeder&lt;/code> / &lt;code>circular&lt;/code>）&lt;/li>
&lt;/ul>
&lt;h3 id="injection-profilevu-注入節奏">Injection profile（VU 注入節奏）&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>&lt;code>atOnceUsers(n)&lt;/code>、&lt;code>rampUsers(n).during(t)&lt;/code>、&lt;code>constantUsersPerSec(rate).during(t)&lt;/code>、&lt;code>rampUsersPerSec(a).to(b).during(t)&lt;/code>、&lt;code>heavisideUsers(n).during(t)&lt;/code>&lt;/li>
&lt;li>跟 k6 stages 對照：Gatling 用 injection step composition、k6 用 stages array — 概念近、語法不同&lt;/li>
&lt;li>Closed model（固定 VU）vs Open model（固定 rate）— Gatling 兩者都支援、production 流量多半 open model 更貼近&lt;/li>
&lt;/ul>
&lt;h3 id="assertion--threshold--ci">Assertion + threshold + CI&lt;/h3>
&lt;p>子議題：&lt;/p></description><content:encoded><![CDATA[<p>Gatling 是 JVM 生態的 load test 工具、承擔三個責任：code-first 強型別 scenario DSL（Scala / Java / Kotlin、編譯期就抓 script bug）、async / non-blocking 引擎（單機高 VU 不靠 thread-per-VU）、Gatling Enterprise 分散式負載與企業 dashboard。設計取捨偏向「強型別 + 高單機 throughput + JVM 既有資產」、跟 k6（JS DX）跟 JMeter（GUI + plugins）的取捨在 dev workflow 跟團隊既有技能。</p>
<h2 id="本章目標">本章目標</h2>
<p>讀完本章後、你應該能：</p>
<ol>
<li>用 Scala / Java / Kotlin DSL 寫 simulation（scenario + injection profile）</li>
<li>設計 assertion + threshold 接 CI</li>
<li>用 HAR-driven recording 從瀏覽器抓真實 user flow 起 script</li>
<li>評估 Gatling Enterprise 分散式 vs OSS 單機高 VU 的取捨</li>
<li>評估 Gatling vs k6 / JMeter / Locust 的選用條件</li>
</ol>
<h2 id="最短路徑5-分鐘把-gatling-跑起來">最短路徑：5 分鐘把 Gatling 跑起來</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># 1. 安裝</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"># TODO: brew install gatling / 下載 bundle / Maven / sbt plugin</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1"># 2. 寫 simulation</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"># TODO: class MySim extends Simulation {</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1">#         val httpProtocol = http.baseUrl(&#34;...&#34;)</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="c1">#         val scn = scenario(&#34;...&#34;).exec(http(&#34;get&#34;).get(&#34;/&#34;))</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="c1">#         setUp(scn.inject(rampUsersPerSec(1).to(50).during(60))).protocols(httpProtocol)</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1">#       }</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"># 3. 跑</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"># TODO: gatling.sh -s MySim / mvn gatling:test / sbt Gatling/test</span></span></span></code></pre></div><h2 id="日常操作與決策形狀">日常操作與決策形狀</h2>
<h3 id="simulation-結構">Simulation 結構</h3>
<p>子議題：</p>
<ul>
<li><code>Simulation</code> class（一個檔一個 simulation、整個 test 的根）</li>
<li><code>scenario(...).exec(...)</code>（一條 user journey 的步驟序列）</li>
<li><code>httpProtocol</code>（baseUrl / header / acceptedContent / proxy 共用配置）</li>
<li><code>feeder</code>（CSV / JSON / JDBC 餵 data、配合 <code>randomFeeder</code> / <code>circular</code>）</li>
</ul>
<h3 id="injection-profilevu-注入節奏">Injection profile（VU 注入節奏）</h3>
<p>子議題：</p>
<ul>
<li><code>atOnceUsers(n)</code>、<code>rampUsers(n).during(t)</code>、<code>constantUsersPerSec(rate).during(t)</code>、<code>rampUsersPerSec(a).to(b).during(t)</code>、<code>heavisideUsers(n).during(t)</code></li>
<li>跟 k6 stages 對照：Gatling 用 injection step composition、k6 用 stages array — 概念近、語法不同</li>
<li>Closed model（固定 VU）vs Open model（固定 rate）— Gatling 兩者都支援、production 流量多半 open model 更貼近</li>
</ul>
<h3 id="assertion--threshold--ci">Assertion + threshold + CI</h3>
<p>子議題：</p>
<ul>
<li><code>setUp(...).assertions(global.responseTime.percentile3.lt(500), global.successfulRequests.percent.gt(95))</code></li>
<li>Assertion 失敗時 process exit code 非 0、直接接 CI pass/fail gate</li>
<li>對應 <a href="/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13 Performance Regression Gate</a></li>
</ul>
<h2 id="進階主題按需閱讀">進階主題（按需閱讀）</h2>
<h3 id="har-driven-recording">HAR-driven recording</h3>
<p>子議題：</p>
<ul>
<li>Chrome DevTools 匯出 HAR、<code>gatling-recorder</code> 從 HAR 產 simulation skeleton</li>
<li>適合：複雜 user flow（multi-step checkout / form / login redirect）懶得手寫 script</li>
<li>邊界：recording 出來是 baseline、需手動補 dynamic correlation（CSRF token / session id / form state）</li>
</ul>
<h3 id="gatling-enterprise前-frontline">Gatling Enterprise（前 FrontLine）</h3>
<p>子議題：</p>
<ul>
<li>分散式 load（多 injector node 模擬 100k+ VU）、跨 region traffic source</li>
<li>Web UI 跑 test、看 dashboard、開 trend analysis</li>
<li>接 Git repo 自動 build simulation、跟 CI / Jenkins / GitLab 整合</li>
<li>對應 <a href="/blog/backend/05-deployment-platform/vendors/kubernetes/" data-link-title="Kubernetes" data-link-desc="Container orchestration 主流、GKE / EKS / AKS / 自管">Kubernetes vendor 頁</a> 的 on-K8s 部署</li>
</ul>
<h3 id="async-engine-跟單機高-vu">Async engine 跟單機高 VU</h3>
<p>子議題：</p>
<ul>
<li>引擎基於 Akka / Netty、non-blocking IO、單 thread 可驅動上千 VU</li>
<li>對比 JMeter thread-per-VU 模型、Gatling 單機 VU 上限可高 10x 起跳</li>
<li>邊界：target service 才是瓶頸時、單機更高 VU 也壓不出更多訊號、要走分散式</li>
</ul>
<h3 id="jvm-tuning">JVM tuning</h3>
<p>子議題：</p>
<ul>
<li>Heap size（<code>-Xms / -Xmx</code>）跟 GC 策略（G1 / ZGC）影響高 VU 穩定性</li>
<li>Connection pool / file descriptor ulimit 是常見卡關點</li>
<li>Container 跑 Gatling 要注意 CPU / memory request 給足</li>
</ul>
<h3 id="從-jmeter-遷移">從 JMeter 遷移</h3>
<p>子議題：</p>
<ul>
<li>JMeter <code>.jmx</code> 沒官方 converter、要人工 port</li>
<li>適合切點：新 simulation 寫 Gatling、舊 <code>.jmx</code> 維護收斂後再評估</li>
<li>對應 <a href="/blog/backend/06-reliability/vendors/jmeter/" data-link-title="Apache JMeter" data-link-desc="老牌 load test 工具、GUI &#43; plugins">JMeter</a> 「既有 .jmx 資產治理」段</li>
</ul>
<h2 id="排錯快速判讀">排錯快速判讀</h2>
<h3 id="單機-vu-上不去">單機 VU 上不去</h3>
<p>操作原則：JVM heap / ulimit / connection pool 三層先排、再看是不是 target service 已是瓶頸（latency 漲、VU 卻沒滿）。</p>
<h3 id="response-time-p99-不穩">Response time p99 不穩</h3>
<p>操作原則：GC pause（看 GC log）/ network jitter / target service warmup 沒做完。Steady-state 量測前要先 ramp-up + soak 5-10 分鐘。</p>
<h3 id="assertion-偶發-fail">Assertion 偶發 fail</h3>
<p>操作原則：threshold 設在 noise level 附近、把 baseline 重跑 3 次抓 p95 區間、再設 threshold 留 buffer。</p>
<h3 id="recording-出來的-script-跑不通">Recording 出來的 script 跑不通</h3>
<p>操作原則：HAR 沒抓到 dynamic value（CSRF / session）、要手動加 <code>check(regex(...).saveAs(...))</code> 把 response 抓出來餵後續 request。</p>
<h2 id="何時改走其他服務">何時改走其他服務</h2>
<table>
  <thead>
      <tr>
          <th>需求形狀</th>
          <th>改走</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>非 JVM 團隊 / JS DX</td>
          <td><a href="/blog/backend/06-reliability/vendors/k6/" data-link-title="k6" data-link-desc="現代 load test、JS scripting、Grafana Labs">k6</a></td>
      </tr>
      <tr>
          <td>Python + 動態 user behavior</td>
          <td><a href="/blog/backend/06-reliability/vendors/locust/" data-link-title="Locust" data-link-desc="Python-based load test、distributed、易擴展">Locust</a></td>
      </tr>
      <tr>
          <td>GUI 設計 / 既有資產</td>
          <td><a href="/blog/backend/06-reliability/vendors/jmeter/" data-link-title="Apache JMeter" data-link-desc="老牌 load test 工具、GUI &#43; plugins">JMeter</a></td>
      </tr>
      <tr>
          <td>Browser flow load</td>
          <td>k6 browser / Playwright + 自製 load harness</td>
      </tr>
      <tr>
          <td>Cloud managed</td>
          <td>Gatling Enterprise / BlazeMeter / k6 Cloud</td>
      </tr>
      <tr>
          <td>Capacity planning（非 CI）</td>
          <td><a href="/blog/backend/09-performance-capacity/" data-link-title="模組九：效能工程與容量規劃" data-link-desc="把『目前配置能撐多少、要加多少機器』變成可量化、可驗證、可改進的工程流程">09 performance capacity</a></td>
      </tr>
  </tbody>
</table>
<h2 id="不在本頁內的主題">不在本頁內的主題</h2>
<ul>
<li>Scala / Kotlin 語言基礎</li>
<li>Gatling DSL 完整 API reference</li>
<li>Gatling Enterprise pricing 跟 deployment model 細節</li>
</ul>
<h2 id="案例回寫">案例回寫</h2>
<table>
  <thead>
      <tr>
          <th>案例方向</th>
          <th>對應主題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/linkedin/capacity-headroom-and-oncall-tiering/" data-link-title="LinkedIn：Capacity Headroom 與 On-call 分層" data-link-desc="把容量預測與值班分層綁在一起，降低高峰時段的升級混亂與恢復延遲。">LinkedIn：Capacity 與 On-call 分層</a></td>
          <td>JVM 服務的 capacity headroom 與 automated load test</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/shopify/bfcm-capacity-and-game-day/" data-link-title="Shopify：BFCM 容量治理與 Game Day 驗證節奏" data-link-desc="把季節性流量峰值轉成年度可靠性流程，透過容量模型、演練與隔離策略提前吸收風險。">Shopify：BFCM 容量治理與 Game Day</a></td>
          <td>峰值準備期 scenario-driven load test 的對照組</td>
      </tr>
  </tbody>
</table>
<p><strong>待補 Gatling customer case</strong>：金融 / e-commerce 重度 JVM 生態採用 Gatling Enterprise、HAR-driven scenario recording 在 multi-step checkout flow 的實踐。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>上游概念：<a href="/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13 Performance Regression Gate</a></li>
<li>平行 vendor：<a href="/blog/backend/06-reliability/vendors/k6/" data-link-title="k6" data-link-desc="現代 load test、JS scripting、Grafana Labs">k6</a>、<a href="/blog/backend/06-reliability/vendors/locust/" data-link-title="Locust" data-link-desc="Python-based load test、distributed、易擴展">Locust</a>、<a href="/blog/backend/06-reliability/vendors/jmeter/" data-link-title="Apache JMeter" data-link-desc="老牌 load test 工具、GUI &#43; plugins">JMeter</a></li>
<li>下游能力：<a href="/blog/backend/09-performance-capacity/" data-link-title="模組九：效能工程與容量規劃" data-link-desc="把『目前配置能撐多少、要加多少機器』變成可量化、可驗證、可改進的工程流程">09 performance capacity</a> load test 模組</li>
</ul>
]]></content:encoded></item><item><title>Stripe</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/stripe/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/stripe/</guid><description>&lt;p>Stripe 是金流場景的可靠性教學標竿、deploy strategy 與 idempotency 設計是 API platform 的工程典範。教學重點在「金流不可重複扣款 / 不可漏扣款」如何透過工程實踐保證。&lt;/p>
&lt;h2 id="規劃重點">規劃重點&lt;/h2>
&lt;ul>
&lt;li>Deploy strategy：canary / staged rollout 的實作節奏&lt;/li>
&lt;li>Game Day：Stripe 公開的 game day 設計與運作&lt;/li>
&lt;li>Idempotency Key：API 設計層面的 retry safety&lt;/li>
&lt;li>Increasing reliability：從 99% 到 99.999% 的逐階段工程投資&lt;/li>
&lt;li>Capture the flag：內部紅藍演練（這是 Stripe 自有的、不是套 07 的紅藍）&lt;/li>
&lt;/ul>
&lt;h2 id="預計收錄實踐">預計收錄實踐&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>議題&lt;/th>
 &lt;th>教學重點&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Idempotency Key&lt;/td>
 &lt;td>API 重試安全的工程實作&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Game Day&lt;/td>
 &lt;td>演練設計、scope、後續 action items&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Canary Deploy&lt;/td>
 &lt;td>rollout 節奏、自動 rollback 條件&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Database online migration&lt;/td>
 &lt;td>高頻交易場景的 schema 變更&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Monitoring &amp;amp; Alerting&lt;/td>
 &lt;td>金流場景的訊號設計&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="章節列表">章節列表&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>章節&lt;/th>
 &lt;th>主題&lt;/th>
 &lt;th>核心責任&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/" data-link-title="Stripe：Idempotency 與零停機遷移的交易安全設計" data-link-desc="把 API 重試與資料遷移放在同一套安全模型，維持支付交易的一致結果。">S1&lt;/a>&lt;/td>
 &lt;td>Idempotency 與零停機遷移&lt;/td>
 &lt;td>把交易重試與資料遷移放在同一套一致性安全模型&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/stripe/canary-deploy-and-progressive-rollout/" data-link-title="Stripe：Canary Deploy 與 Progressive Rollout 治理" data-link-desc="金流場景如何用交易指標驅動放行節奏：延遲確認、duplicate 偵測與自動回退。">S2&lt;/a>&lt;/td>
 &lt;td>Canary Deploy 與 Progressive Rollout&lt;/td>
 &lt;td>用交易指標驅動放行節奏，延遲確認與自動回退&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="案例定位">案例定位&lt;/h2>
&lt;p>Stripe 這個案例在講的是交易系統如何把重試、遷移與部署都設計成可回復的操作。讀者先抓 idempotency 與 zero-downtime migration 這兩個原語，再看它們怎麼保護支付流程不被重試與變更放大。&lt;/p>
&lt;h2 id="判讀重點">判讀重點&lt;/h2>
&lt;p>當客戶端會重送請求時，idempotency key 讓 server 能把重試視為同一筆交易。當資料結構需要調整時，零停機遷移則把高風險變更拆成可驗證的小步驟，避免一次把整個 payment path 推到不可回復的狀態。&lt;/p>
&lt;h2 id="可操作判準">可操作判準&lt;/h2>
&lt;ul>
&lt;li>能否讓同一筆請求重送後仍得到同一個結果&lt;/li>
&lt;li>能否把 migration 拆成可觀察、可回滾的小階段&lt;/li>
&lt;li>能否區分 client retry 與 server duplicate processing&lt;/li>
&lt;li>能否把 deploy strategy 和交易一致性放在同一個判準下&lt;/li>
&lt;/ul>
&lt;h2 id="與其他案例的關係">與其他案例的關係&lt;/h2>
&lt;p>Stripe 的可靠性核心是把交易語義寫進系統邊界，這和 GitHub 的 replication、一樣都在處理「重複動作不能造成雙重結果」的問題。差別在於 Stripe 面對的是金流，容錯成本更高，所以 idempotency 與 zero-downtime migration 會比一般平台更早變成硬要求。&lt;/p>
&lt;h2 id="代表樣本">代表樣本&lt;/h2>
&lt;ul>
&lt;li>idempotency key 讓同一筆請求重送後，系統仍能回到相同交易結果。&lt;/li>
&lt;li>zero-downtime migration 把高風險資料變更拆成可驗證的小階段。&lt;/li>
&lt;li>canary deploy 讓交易流量先經過小範圍驗證。&lt;/li>
&lt;li>game day 讓支付與資料遷移的失效路徑先被演練。&lt;/li>
&lt;li>retry semantics 讓 client 重送不會變成雙重扣款。&lt;/li>
&lt;li>monitoring &amp;amp; alerting 讓支付路徑的異常先在訊號層浮出來。&lt;/li>
&lt;li>operational simplicity 讓流程越少分支，越容易守住交易正確性。&lt;/li>
&lt;li>safe deploy strategy 讓變更節奏和風險控制綁在一起。&lt;/li>
&lt;/ul>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://stripe.com/blog/idempotency">Designing robust and predictable APIs with idempotency&lt;/a>：idempotency key 與重試安全的官方文章。&lt;/li>
&lt;li>&lt;a href="https://stripe.com/blog/how-stripes-document-databases-supported-99.999-uptime-with-zero-downtime-data-migrations">How Stripe’s document databases supported 99.999% uptime with zero-downtime data migrations&lt;/a>：零停機資料遷移與可靠性投資的官方案例。&lt;/li>
&lt;li>&lt;a href="https://stripe.com/blog/engineering">Stripe Engineering&lt;/a>：Stripe Engineering 內容總入口，補 deploy / CI / reliability 的延伸脈絡。&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Stripe 是金流場景的可靠性教學標竿、deploy strategy 與 idempotency 設計是 API platform 的工程典範。教學重點在「金流不可重複扣款 / 不可漏扣款」如何透過工程實踐保證。</p>
<h2 id="規劃重點">規劃重點</h2>
<ul>
<li>Deploy strategy：canary / staged rollout 的實作節奏</li>
<li>Game Day：Stripe 公開的 game day 設計與運作</li>
<li>Idempotency Key：API 設計層面的 retry safety</li>
<li>Increasing reliability：從 99% 到 99.999% 的逐階段工程投資</li>
<li>Capture the flag：內部紅藍演練（這是 Stripe 自有的、不是套 07 的紅藍）</li>
</ul>
<h2 id="預計收錄實踐">預計收錄實踐</h2>
<table>
  <thead>
      <tr>
          <th>議題</th>
          <th>教學重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Idempotency Key</td>
          <td>API 重試安全的工程實作</td>
      </tr>
      <tr>
          <td>Game Day</td>
          <td>演練設計、scope、後續 action items</td>
      </tr>
      <tr>
          <td>Canary Deploy</td>
          <td>rollout 節奏、自動 rollback 條件</td>
      </tr>
      <tr>
          <td>Database online migration</td>
          <td>高頻交易場景的 schema 變更</td>
      </tr>
      <tr>
          <td>Monitoring &amp; Alerting</td>
          <td>金流場景的訊號設計</td>
      </tr>
  </tbody>
</table>
<h2 id="章節列表">章節列表</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/" data-link-title="Stripe：Idempotency 與零停機遷移的交易安全設計" data-link-desc="把 API 重試與資料遷移放在同一套安全模型，維持支付交易的一致結果。">S1</a></td>
          <td>Idempotency 與零停機遷移</td>
          <td>把交易重試與資料遷移放在同一套一致性安全模型</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/stripe/canary-deploy-and-progressive-rollout/" data-link-title="Stripe：Canary Deploy 與 Progressive Rollout 治理" data-link-desc="金流場景如何用交易指標驅動放行節奏：延遲確認、duplicate 偵測與自動回退。">S2</a></td>
          <td>Canary Deploy 與 Progressive Rollout</td>
          <td>用交易指標驅動放行節奏，延遲確認與自動回退</td>
      </tr>
  </tbody>
</table>
<h2 id="案例定位">案例定位</h2>
<p>Stripe 這個案例在講的是交易系統如何把重試、遷移與部署都設計成可回復的操作。讀者先抓 idempotency 與 zero-downtime migration 這兩個原語，再看它們怎麼保護支付流程不被重試與變更放大。</p>
<h2 id="判讀重點">判讀重點</h2>
<p>當客戶端會重送請求時，idempotency key 讓 server 能把重試視為同一筆交易。當資料結構需要調整時，零停機遷移則把高風險變更拆成可驗證的小步驟，避免一次把整個 payment path 推到不可回復的狀態。</p>
<h2 id="可操作判準">可操作判準</h2>
<ul>
<li>能否讓同一筆請求重送後仍得到同一個結果</li>
<li>能否把 migration 拆成可觀察、可回滾的小階段</li>
<li>能否區分 client retry 與 server duplicate processing</li>
<li>能否把 deploy strategy 和交易一致性放在同一個判準下</li>
</ul>
<h2 id="與其他案例的關係">與其他案例的關係</h2>
<p>Stripe 的可靠性核心是把交易語義寫進系統邊界，這和 GitHub 的 replication、一樣都在處理「重複動作不能造成雙重結果」的問題。差別在於 Stripe 面對的是金流，容錯成本更高，所以 idempotency 與 zero-downtime migration 會比一般平台更早變成硬要求。</p>
<h2 id="代表樣本">代表樣本</h2>
<ul>
<li>idempotency key 讓同一筆請求重送後，系統仍能回到相同交易結果。</li>
<li>zero-downtime migration 把高風險資料變更拆成可驗證的小階段。</li>
<li>canary deploy 讓交易流量先經過小範圍驗證。</li>
<li>game day 讓支付與資料遷移的失效路徑先被演練。</li>
<li>retry semantics 讓 client 重送不會變成雙重扣款。</li>
<li>monitoring &amp; alerting 讓支付路徑的異常先在訊號層浮出來。</li>
<li>operational simplicity 讓流程越少分支，越容易守住交易正確性。</li>
<li>safe deploy strategy 讓變更節奏和風險控制綁在一起。</li>
</ul>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://stripe.com/blog/idempotency">Designing robust and predictable APIs with idempotency</a>：idempotency key 與重試安全的官方文章。</li>
<li><a href="https://stripe.com/blog/how-stripes-document-databases-supported-99.999-uptime-with-zero-downtime-data-migrations">How Stripe’s document databases supported 99.999% uptime with zero-downtime data migrations</a>：零停機資料遷移與可靠性投資的官方案例。</li>
<li><a href="https://stripe.com/blog/engineering">Stripe Engineering</a>：Stripe Engineering 內容總入口，補 deploy / CI / reliability 的延伸脈絡。</li>
</ul>
]]></content:encoded></item><item><title>6.4 chaos testing</title><link>https://tarrragon.github.io/blog/backend/06-reliability/chaos-testing/</link><pubDate>Thu, 23 Apr 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/chaos-testing/</guid><description>&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/chaos-test/" data-link-title="Chaos Test" data-link-desc="說明透過受控故障注入驗證系統在異常條件下的恢復能力">Chaos test&lt;/a> 是在可控條件下主動注入故障，驗證系統是否能在真實依賴失效時維持 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/steady-state/" data-link-title="Steady State" data-link-desc="說明可靠性實驗與事故恢復如何定義系統應維持的可接受狀態">steady state&lt;/a> 與可接受的 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/blast-radius/" data-link-title="Blast Radius" data-link-desc="說明事故影響面如何估算與隔離">blast radius&lt;/a>。&lt;/p>
&lt;p>這一頁關心的是失效時系統怎麼退化。chaos 的價值在於判讀系統收到故障後的退化行為是否符合預期。沒有先定義 steady state，chaos 只會變成故障展示，不會變成判讀工具。&lt;/p>
&lt;h2 id="核心判讀">核心判讀&lt;/h2>
&lt;p>判讀 chaos 的重點是對控制面、資料面與依賴鏈的回復能力做驗證，而不是單純證明服務死過一次。&lt;/p>
&lt;p>重點訊號包括：&lt;/p>
&lt;ul>
&lt;li>是否先定義 steady state 與成功條件&lt;/li>
&lt;li>故障是否真的落在常見依賴與控制點&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/blast-radius/" data-link-title="Blast Radius" data-link-desc="說明事故影響面如何估算與隔離">blast radius&lt;/a> 是否可量測、可縮限&lt;/li>
&lt;li>recovery path 是否能在演練後被重播&lt;/li>
&lt;/ul>
&lt;h2 id="故障注入的設計流程">故障注入的設計流程&lt;/h2>
&lt;p>一輪有效的 chaos 驗證從穩態定義開始。先知道系統正常時應維持什麼行為，再設計注入去測試這個行為是否可持續。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>步驟&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>產出&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>定義穩態&lt;/td>
 &lt;td>服務正常時應維持什麼行為&lt;/td>
 &lt;td>穩態指標與門檻&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>設計假設&lt;/td>
 &lt;td>失效發生後系統仍應維持什麼&lt;/td>
 &lt;td>可證偽假設&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>限制 blast radius&lt;/td>
 &lt;td>實驗範圍怎麼控制&lt;/td>
 &lt;td>服務 / 區域 / 流量&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>設定停止條件&lt;/td>
 &lt;td>何時立即停止實驗&lt;/td>
 &lt;td>abort trigger&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>穩態定義是整個流程的錨點。Netflix 的 chaos 實踐把 steady state 放在驗證循環的第一步 — 先定義穩態指標（&lt;a href="https://tarrragon.github.io/blog/backend/04-observability/sli-slo-signal/" data-link-title="4.6 SLI 量測與 SLO 訊號設計" data-link-desc="把可靠性目標的訊號從 metric 端設計好、餵給 6.6 SLO 政策">SLI&lt;/a>、business KPI、queue lag），再用故障注入去測試這些指標是否能在壓力下維持。沒有穩態定義的故障注入只能產出「系統被打壞了」的結論，無法回答「系統是否按預期退化」。&lt;/p>
&lt;p>假設設計決定實驗能學到什麼。好的假設會說明「當 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/broker/" data-link-title="Broker" data-link-desc="說明 broker 在訊息傳遞系統中負責保存、路由與交付訊息">broker&lt;/a> 節點離線時，訊息消費延遲應在 30 秒內回線，checkout 成功率應維持在 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">SLO&lt;/a> 門檻內」，而不只是「關掉 broker 看看會怎樣」。假設越具體，實驗結果的判讀價值越高。&lt;/p>
&lt;p>Blast radius 需要同時包含技術範圍與客戶範圍。技術範圍是 service、region、cluster、dependency；客戶範圍是 tenant、plan、traffic percentage 或 internal-only cohort。從最小範圍開始，逐步放大，每一步都要確認停止條件仍可執行。&lt;/p>
&lt;p>停止條件讓實驗可控。當 SLO &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">burn rate&lt;/a> 超門檻、customer impact 出現或 cost 異常上升時，實驗應立即終止。停止條件要連到可觀測訊號，不能靠臨場討論決定是否繼續。&lt;/p>
&lt;h2 id="注入類型與層次">注入類型與層次&lt;/h2>
&lt;p>故障注入按依賴類型分層。不同依賴的失效模式不同，預期退化也不同，實驗設計需要對應調整。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>注入類型&lt;/th>
 &lt;th>打到的依賴&lt;/th>
 &lt;th>預期退化&lt;/th>
 &lt;th>結果可信條件&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Broker outage&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/broker/" data-link-title="Broker" data-link-desc="說明 broker 在訊息傳遞系統中負責保存、路由與交付訊息">broker&lt;/a> 節點或 partition&lt;/td>
 &lt;td>消費延遲上升、DLQ 累積&lt;/td>
 &lt;td>流量接近 production pattern&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>DB latency&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/database/" data-link-title="Database" data-link-desc="說明 database 在後端系統中如何承擔正式狀態、查詢與一致性責任">database&lt;/a> 連線或查詢&lt;/td>
 &lt;td>請求排隊、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/timeout/" data-link-title="Timeout" data-link-desc="說明等待外部操作的時間上限如何保護資源與使用者體驗">timeout&lt;/a> 觸發&lt;/td>
 &lt;td>connection pool 配置與 production 一致&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Node restart&lt;/td>
 &lt;td>應用節點&lt;/td>
 &lt;td>短暫不可用、load balancer 切流&lt;/td>
 &lt;td>readiness probe 與 graceful shutdown 配置一致&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Network jitter&lt;/td>
 &lt;td>跨服務通訊&lt;/td>
 &lt;td>latency 抖動、retry 上升&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/jitter/" data-link-title="Jitter" data-link-desc="說明重試或排程加入隨機偏移如何降低同步尖峰">jitter&lt;/a> 模式接近真實 ISP / cloud&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Broker outage 驗證的是非同步依賴的容錯能力。當 broker 節點或 partition 不可用時，生產端應有 retry 與 fallback，消費端應能在恢復後 drain backlog 而不是 replay storm。測試時需要確認 DLQ 設定正確、消費 lag 有監控、恢復後的 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/backpressure/" data-link-title="Backpressure" data-link-desc="說明下游處理速度不足時系統如何讓上游依下游能力送出工作">backpressure&lt;/a> 不會壓垮下游。&lt;/p></description><content:encoded><![CDATA[<h2 id="概念定位">概念定位</h2>
<p><a href="/blog/backend/knowledge-cards/chaos-test/" data-link-title="Chaos Test" data-link-desc="說明透過受控故障注入驗證系統在異常條件下的恢復能力">Chaos test</a> 是在可控條件下主動注入故障，驗證系統是否能在真實依賴失效時維持 <a href="/blog/backend/knowledge-cards/steady-state/" data-link-title="Steady State" data-link-desc="說明可靠性實驗與事故恢復如何定義系統應維持的可接受狀態">steady state</a> 與可接受的 <a href="/blog/backend/knowledge-cards/blast-radius/" data-link-title="Blast Radius" data-link-desc="說明事故影響面如何估算與隔離">blast radius</a>。</p>
<p>這一頁關心的是失效時系統怎麼退化。chaos 的價值在於判讀系統收到故障後的退化行為是否符合預期。沒有先定義 steady state，chaos 只會變成故障展示，不會變成判讀工具。</p>
<h2 id="核心判讀">核心判讀</h2>
<p>判讀 chaos 的重點是對控制面、資料面與依賴鏈的回復能力做驗證，而不是單純證明服務死過一次。</p>
<p>重點訊號包括：</p>
<ul>
<li>是否先定義 steady state 與成功條件</li>
<li>故障是否真的落在常見依賴與控制點</li>
<li><a href="/blog/backend/knowledge-cards/blast-radius/" data-link-title="Blast Radius" data-link-desc="說明事故影響面如何估算與隔離">blast radius</a> 是否可量測、可縮限</li>
<li>recovery path 是否能在演練後被重播</li>
</ul>
<h2 id="故障注入的設計流程">故障注入的設計流程</h2>
<p>一輪有效的 chaos 驗證從穩態定義開始。先知道系統正常時應維持什麼行為，再設計注入去測試這個行為是否可持續。</p>
<table>
  <thead>
      <tr>
          <th>步驟</th>
          <th>核心問題</th>
          <th>產出</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>定義穩態</td>
          <td>服務正常時應維持什麼行為</td>
          <td>穩態指標與門檻</td>
      </tr>
      <tr>
          <td>設計假設</td>
          <td>失效發生後系統仍應維持什麼</td>
          <td>可證偽假設</td>
      </tr>
      <tr>
          <td>限制 blast radius</td>
          <td>實驗範圍怎麼控制</td>
          <td>服務 / 區域 / 流量</td>
      </tr>
      <tr>
          <td>設定停止條件</td>
          <td>何時立即停止實驗</td>
          <td>abort trigger</td>
      </tr>
  </tbody>
</table>
<p>穩態定義是整個流程的錨點。Netflix 的 chaos 實踐把 steady state 放在驗證循環的第一步 — 先定義穩態指標（<a href="/blog/backend/04-observability/sli-slo-signal/" data-link-title="4.6 SLI 量測與 SLO 訊號設計" data-link-desc="把可靠性目標的訊號從 metric 端設計好、餵給 6.6 SLO 政策">SLI</a>、business KPI、queue lag），再用故障注入去測試這些指標是否能在壓力下維持。沒有穩態定義的故障注入只能產出「系統被打壞了」的結論，無法回答「系統是否按預期退化」。</p>
<p>假設設計決定實驗能學到什麼。好的假設會說明「當 <a href="/blog/backend/knowledge-cards/broker/" data-link-title="Broker" data-link-desc="說明 broker 在訊息傳遞系統中負責保存、路由與交付訊息">broker</a> 節點離線時，訊息消費延遲應在 30 秒內回線，checkout 成功率應維持在 <a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">SLO</a> 門檻內」，而不只是「關掉 broker 看看會怎樣」。假設越具體，實驗結果的判讀價值越高。</p>
<p>Blast radius 需要同時包含技術範圍與客戶範圍。技術範圍是 service、region、cluster、dependency；客戶範圍是 tenant、plan、traffic percentage 或 internal-only cohort。從最小範圍開始，逐步放大，每一步都要確認停止條件仍可執行。</p>
<p>停止條件讓實驗可控。當 SLO <a href="/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">burn rate</a> 超門檻、customer impact 出現或 cost 異常上升時，實驗應立即終止。停止條件要連到可觀測訊號，不能靠臨場討論決定是否繼續。</p>
<h2 id="注入類型與層次">注入類型與層次</h2>
<p>故障注入按依賴類型分層。不同依賴的失效模式不同，預期退化也不同，實驗設計需要對應調整。</p>
<table>
  <thead>
      <tr>
          <th>注入類型</th>
          <th>打到的依賴</th>
          <th>預期退化</th>
          <th>結果可信條件</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Broker outage</td>
          <td><a href="/blog/backend/knowledge-cards/broker/" data-link-title="Broker" data-link-desc="說明 broker 在訊息傳遞系統中負責保存、路由與交付訊息">broker</a> 節點或 partition</td>
          <td>消費延遲上升、DLQ 累積</td>
          <td>流量接近 production pattern</td>
      </tr>
      <tr>
          <td>DB latency</td>
          <td><a href="/blog/backend/knowledge-cards/database/" data-link-title="Database" data-link-desc="說明 database 在後端系統中如何承擔正式狀態、查詢與一致性責任">database</a> 連線或查詢</td>
          <td>請求排隊、<a href="/blog/backend/knowledge-cards/timeout/" data-link-title="Timeout" data-link-desc="說明等待外部操作的時間上限如何保護資源與使用者體驗">timeout</a> 觸發</td>
          <td>connection pool 配置與 production 一致</td>
      </tr>
      <tr>
          <td>Node restart</td>
          <td>應用節點</td>
          <td>短暫不可用、load balancer 切流</td>
          <td>readiness probe 與 graceful shutdown 配置一致</td>
      </tr>
      <tr>
          <td>Network jitter</td>
          <td>跨服務通訊</td>
          <td>latency 抖動、retry 上升</td>
          <td><a href="/blog/backend/knowledge-cards/jitter/" data-link-title="Jitter" data-link-desc="說明重試或排程加入隨機偏移如何降低同步尖峰">jitter</a> 模式接近真實 ISP / cloud</td>
      </tr>
  </tbody>
</table>
<p>Broker outage 驗證的是非同步依賴的容錯能力。當 broker 節點或 partition 不可用時，生產端應有 retry 與 fallback，消費端應能在恢復後 drain backlog 而不是 replay storm。測試時需要確認 DLQ 設定正確、消費 lag 有監控、恢復後的 <a href="/blog/backend/knowledge-cards/backpressure/" data-link-title="Backpressure" data-link-desc="說明下游處理速度不足時系統如何讓上游依下游能力送出工作">backpressure</a> 不會壓垮下游。</p>
<p>DB latency 驗證的是同步依賴在退化時的行為。延遲注入比完全斷線更接近真實故障 — production 常見的是 slow query、connection pool exhaustion 或 replica lag，而不是 database 完全離線。測試時需要確認 timeout 是否會級聯：一個慢查詢拖住連線，其他請求開始排隊，最終 thread pool 或 goroutine 耗盡。</p>
<p>Node restart 驗證的是服務在節點層級的恢復能力。graceful shutdown 是否正確 drain 連線、readiness probe 是否能阻止 load balancer 過早送流量、cold start 是否會因 cache miss 或 JIT warmup 造成短暫效能劣化。</p>
<p>Network jitter 驗證的是跨服務通訊的韌性。jitter 注入需要模擬真實的 latency distribution（長尾、間歇性），而不是固定延遲。測試時需要關注 retry 行為：固定 retry 在 jitter 環境下可能放大流量，需要搭配 <a href="/blog/backend/knowledge-cards/retry-budget/" data-link-title="Retry Budget" data-link-desc="說明重試次數如何受整體容量與錯誤預算限制">retry budget</a> 控制。</p>
<h3 id="注入粒度instance-level-vs-request-path">注入粒度：instance-level vs request-path</h3>
<p>故障注入有兩個主要粒度，適用場景不同。</p>
<p>Instance-level injection（如 Chaos Monkey）在節點層注入故障 — 關閉 instance、斷開網路、暫停程序。這個粒度驗證的是基礎設施韌性：load balancer 能否切流、auto-scaling 能否補位、graceful shutdown 能否完成。優點是簡單、接近真實硬體故障；缺點是粒度粗，無法精準驗證特定依賴路徑。</p>
<p>Request-path injection（如 FIT）在請求路徑層注入故障 — 對特定 API call、dependency request 或 service-to-service 通訊植入 timeout、error 或延遲。這個粒度驗證的是應用韌性：fallback 是否生效、circuit breaker 是否觸發、retry 是否安全。優點是精準、blast radius 小；缺點是需要更深的 instrumentation，建置成本較高。</p>
<p>兩者不互斥。instance-level injection 適合驗證基礎設施層的回復能力，request-path injection 適合驗證應用層的容錯邏輯。團隊可以從 instance-level 開始建立 chaos 習慣，再逐步引入 request-path injection 提升驗證精度。第三種粒度是 infrastructure-level injection（AZ failure / region failure），由 cloud provider 的 chaos 工具（如 AWS FIS、Azure Chaos Studio）支援，驗證的是跨 AZ 冗餘與 failover 路由。</p>
<h2 id="執行時段與環境">執行時段與環境</h2>
<p>故障注入的執行時段與環境直接影響驗證價值。</p>
<h3 id="business-hours-vs-off-peak">Business hours vs off-peak</h3>
<p>在 business hours 執行 chaos 能同時驗證系統韌性與團隊應變能力。人員在線可即時觀測、依賴流量接近真實、通訊鏈條（值班升級、跨團隊協作、內外部狀態更新）被完整測到。off-peak 雖然短期風險低，但測到的多是「工具可執行」，不是「服務在真實壓力下可承受」。</p>
<p>選擇 business hours 執行的前提是 guardrails 到位：時段限制在可支援的工作時間、blast radius 從小範圍開始、abort trigger 連到明確門檻、事後回寫進工程控制面。風險來自 guardrails 的缺失。</p>
<h3 id="staging-vs-production">Staging vs production</h3>
<p>Staging 適合驗證工具整合與基礎假設：注入能否生效、dashboard 能否呈現訊號、stop condition 能否觸發。但 staging 與 production 之間通常存在環境漂移 — traffic pattern 不同、dependency 配置不同、<a href="/blog/backend/knowledge-cards/connection-pool/" data-link-title="Connection Pool" data-link-desc="說明連線池如何限制下游資源並影響服務容量">connection pool</a> 大小不同、cache warmup 狀態不同。在 staging 通過的實驗，不能直接等同於 production 可承受。</p>
<p>Production chaos 的價值在於驗證真實依賴路徑。它需要從最小 cohort 開始（internal traffic、canary region、特定 tenant），搭配完整 stop condition 與 rollback path。Production chaos 需要 stop condition 作為安全網。團隊可以從簡單的 stop condition（如 error rate 超門檻就停止）起步，隨經驗累積逐步精細化。</p>
<h2 id="證據結構與回寫">證據結構與回寫</h2>
<p>Chaos 實驗的產出是可決策的證據。當實驗結果能直接回答「這個依賴的容錯能力是否足夠」，chaos 才從測試活動升級為可靠性控制面。</p>
<table>
  <thead>
      <tr>
          <th>證據欄位</th>
          <th>核心問題</th>
          <th>決策用途</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Steady-state impact</td>
          <td>注入後穩態指標是否維持</td>
          <td>判斷容錯能力是否符合預期</td>
      </tr>
      <tr>
          <td>Abort trigger record</td>
          <td>停止條件是否被觸發、何時觸發</td>
          <td>判斷是否需要凍結或回退</td>
      </tr>
      <tr>
          <td>Fallback result</td>
          <td>降級路徑是否可用、恢復是否收斂</td>
          <td>判斷事故時能否安全止血</td>
      </tr>
      <tr>
          <td>Dependency drift</td>
          <td>受影響依賴是否落在預期範圍</td>
          <td>判斷 blast radius 是否可接受</td>
      </tr>
  </tbody>
</table>
<p>Steady-state impact 是最核心的證據欄位。它回答的問題是「系統在故障期間是否維持了服務承諾」。若 <a href="/blog/backend/04-observability/sli-slo-signal/" data-link-title="4.6 SLI 量測與 SLO 訊號設計" data-link-desc="把可靠性目標的訊號從 metric 端設計好、餵給 6.6 SLO 政策">SLI</a> 維持在 <a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">SLO</a> 門檻內，代表容錯機制有效；若偏離，需要記錄偏離幅度、持續時間與影響範圍。</p>
<p>Abort trigger record 讓團隊知道 stop condition 是否可執行。若停止條件被觸發但執行延遲，代表觀測或通訊鏈條有缺口；若停止條件沒被觸發但影響已擴大，代表門檻設定需要校準。</p>
<p>這四個欄位接到 <a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23 verification evidence handoff</a> 後，可直接成為 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate</a> 的放行輸入。release decision 從「主觀討論」轉成「政策驅動」：有證據支持容錯能力 → 放行；abort 被觸發 → 凍結並修復；fallback 失敗 → 補 action item 再重驗。</p>
<h2 id="規模差異">規模差異</h2>
<p>Chaos 的設計在不同規模下差異顯著。單服務 chaos 與跨區 chaos 打到的系統層不同，blast radius 控制方式也不同。</p>
<h3 id="單服務-chaos">單服務 chaos</h3>
<p>單服務 chaos 驗證的是一個服務對其直接依賴的容錯能力。blast radius 限在該服務的 instance、replica 或 traffic cohort 內。適合驗證 circuit breaker、fallback、timeout、retry 與 graceful degradation。</p>
<h3 id="跨區-chaos-與-failure-localization">跨區 chaos 與 failure localization</h3>
<p>跨區 chaos 驗證的是故障在區域或依賴鏈上的擴散行為。Amazon 的 cell-based architecture 把多租戶服務的故障域限制在 cell 內 — 一個 cell 的異常不會擴散到其他 cell，恢復策略從全域搶救轉為分批收斂。Meta 的 region failover 實踐則關注控制面故障的跨區擴散 — 當核心網路或 BGP 配置異常跨越區域邊界，恢復動作本身可能成為新的放大器。</p>
<p>兩者共同的判讀重點是：故障是否被限制在預期邊界內。單服務 chaos 的邊界是 instance 與 dependency；跨區 chaos 的邊界是 region、cell 與 shared dependency。blast radius 越大，stop condition 與 rollback path 的設計要求越高。</p>
<h2 id="產業情境串流與媒體服務">產業情境：串流與媒體服務</h2>
<p>串流服務的故障注入需要考慮觀眾正在觀看的即時性。CDN 節點失效、origin server 延遲或 transcoding pipeline 中斷都會直接造成 buffering 或畫質降級，使用者的容忍窗口以秒計。</p>
<p>串流的 <a href="/blog/backend/knowledge-cards/steady-state/" data-link-title="Steady State" data-link-desc="說明可靠性實驗與事故恢復如何定義系統應維持的可接受狀態">steady state</a> 指標跟一般 web service 不同：buffering ratio（觀眾看到轉圈的時間比例）、bitrate stability（畫質是否頻繁跳動）、video start time（按下播放到第一幀的延遲）。這些指標直接反映觀看體驗，chaos 實驗的假設必須用這些指標定義穩態，而非只用 HTTP success rate。</p>
<p>CDN 有多層快取（edge / mid-tier / origin），某一層失效時流量會 fallback 到下一層。chaos 要驗證的是 fallback 路徑能否承受突增的回源流量，以及 adaptive bitrate 策略是否能平滑過渡到較低畫質，而非直接中斷播放。回源流量的放大倍數取決於該層的快取命中率 — 命中率越高的層失效，回源放大越劇烈。</p>
<p>直播事件的 chaos 約束更嚴格。VOD 內容可重試、可重播，直播沒有第二次機會。直播前的 chaos 演練需要模擬「直播進行中 CDN 節點失效」的場景，驗證備援路徑的切換速度是否在觀眾可感知門檻（通常 2-5 秒）內。Netflix 的 chaos 實踐原始動機即是保護串流觀看體驗，其 <a href="/blog/backend/06-reliability/cases/netflix/steady-state-chaos-and-fit/" data-link-title="Netflix：Steady State、Chaos 與 FIT 的驗證路徑" data-link-desc="把故障注入從工具操作升級成可驗證流程：先定義穩態，再設計注入與回復條件。">steady state hypothesis</a> 的設計直接適用於串流場景。</p>
<h2 id="案例對照">案例對照</h2>
<ul>
<li><a href="/blog/backend/06-reliability/cases/netflix/steady-state-chaos-and-fit/" data-link-title="Netflix：Steady State、Chaos 與 FIT 的驗證路徑" data-link-desc="把故障注入從工具操作升級成可驗證流程：先定義穩態，再設計注入與回復條件。">Netflix：Steady State、Chaos 與 FIT</a>：把故障注入變成科學化驗證循環，四元素（steady state / hypothesis / blast radius / abort condition）提供 chaos 設計的結構。FIT 把注入粒度推進到 request path，讓測試更接近真實依賴路徑。</li>
<li><a href="/blog/backend/06-reliability/cases/netflix/chaos-monkey-business-hours-guardrails/" data-link-title="Netflix：Business-Hours Chaos 與 Guardrails" data-link-desc="Chaos Monkey 為何刻意在 business hours 執行：把即時應變能力納入驗證，並用 guardrails 限制實驗風險。">Netflix：Business-Hours Chaos Guardrails</a>：business hours 執行的前提是 guardrails 到位（時段限制、範圍限制、abort trigger、事後回寫），驗證的不只是系統韌性，也包含團隊應變能力。</li>
<li><a href="/blog/backend/06-reliability/cases/netflix/fit-failure-injection-evidence-handoff/" data-link-title="Netflix：FIT 證據交接與 Release Gate 回寫" data-link-desc="用 Failure Injection Testing 產出的證據直接驅動 release gate：把實驗結果轉成可放行、可凍結、可回退的決策欄位。">Netflix：FIT 證據交接</a>：把 FIT 輸出結構化成四個決策欄位，讓實驗結果直接驅動 release gate。</li>
<li><a href="/blog/backend/06-reliability/cases/amazon/shuffle-sharding-and-cell-boundary/" data-link-title="Amazon：Shuffle Sharding 與 Cell 邊界的失效局部化" data-link-desc="用 cell 與 shuffle sharding 將多租戶故障限制在局部，讓恢復策略可分批執行。">Amazon：Shuffle Sharding 與 Cell 邊界</a>：cell-based architecture 讓恢復策略從全域搶救轉為分批收斂，是跨區 chaos 設計的前提。</li>
<li><a href="/blog/backend/06-reliability/cases/meta/region-failover-and-reliability-boundaries/" data-link-title="Meta：Region Failover 與可靠性邊界" data-link-desc="把跨區故障視為邊界治理問題，透過分區隔離與回復順序控制失效擴散。">Meta：Region Failover 邊界治理</a>：跨區依賴與控制面故障的回復順序，說明 blast radius 在大規模系統中的擴散治理。</li>
<li><a href="/blog/backend/06-reliability/cases/shopify/bfcm-capacity-and-game-day/" data-link-title="Shopify：BFCM 容量治理與 Game Day 驗證節奏" data-link-desc="把季節性流量峰值轉成年度可靠性流程，透過容量模型、演練與隔離策略提前吸收風險。">Shopify：BFCM 容量治理與 Game Day</a>：game day 把演練、壓測與隔離單位連成一條線，適合補充高峰型場景的 chaos 設計。</li>
</ul>
<h2 id="判讀訊號">判讀訊號</h2>
<p>判讀 chaos 的品質不只看實驗是否通過，要看實驗設計是否能產出可信結論。</p>
<ul>
<li><strong>chaos experiment 只測 happy path 的故障</strong>：只關掉不重要的服務、只在低流量時段跑，通過了也無法證明高價值路徑的容錯能力。判讀條件：注入目標是否對應服務的關鍵依賴路徑。行動：把注入目標對齊服務的 top-3 關鍵依賴。</li>
<li><strong>broker / DB / network 故障無自動演練、靠真事故學</strong>：沒有定期 chaos 的團隊只能從真實事故中學習，學習成本高且機會不可控。判讀條件：chaos 是否有固定節奏，而非只在事故後才啟動。行動：排入季度 chaos sprint、從最小 blast radius 開始。</li>
<li><strong>chaos 暴露問題沒修、紀錄堆積</strong>：實驗發現缺口但 action item 沒有 owner、沒有 deadline，同類問題反覆出現。判讀條件：action item 是否進入 <a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 reliability debt backlog</a> 並被追蹤。行動：每次 chaos 結束後 action item 指定 owner + deadline。</li>
<li><strong>production chaos 只在低流量時段跑、訊號失真</strong>：低流量時段的依賴行為、流量模式與團隊狀態都跟 production peak 不同，通過了不代表高峰時可承受。判讀條件：是否有 business-hours 或接近 peak 的驗證補充。行動：至少每季補一次 business-hours chaos 驗證。</li>
<li><strong>故障注入工具跟 production 不同 stack、結果不可信</strong>：staging 用不同的 broker、database 或 network 配置做 chaos，結果無法外推到 production。判讀條件：實驗環境與 production 的差異是否被記錄並納入結論限制。行動：在結論中標註環境差異、逐步推進 production chaos。</li>
<li><strong>chaos 結果沒進 runbook</strong>：值班人員不知道特定依賴失效後的預期退化行為，事故時仍靠臨場推理。判讀條件：chaos 結論是否已回寫到對應服務的 on-call runbook。行動：每次 chaos 完成後回寫 runbook 的「依賴失效預期行為」段。</li>
</ul>
<h2 id="交接路由">交接路由</h2>
<ul>
<li><a href="/blog/backend/06-reliability/dr-rollback-rehearsal/" data-link-title="6.7 DR 演練與 Rollback Rehearsal" data-link-desc="把回復路徑從紙面計畫變成定期可重播、可量測的驗證流程">6.7 DR / rollback rehearsal</a>：chaos 暴露的回復路徑問題進入 DR 演練</li>
<li><a href="/blog/backend/06-reliability/idempotency-replay/" data-link-title="6.12 Idempotency 與 Replay 驗證" data-link-desc="把重試、重播與冪等性從口頭約定變成可驗證屬性">6.12 idempotency / replay</a>：注入重複訊息驗證冪等能力</li>
<li><a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14 dependency budget</a>：對依賴注入故障驗證 reliability budget</li>
<li><a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 experiment safety boundary</a>：chaos 的 blast radius、stop condition 與權限約束</li>
<li><a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22 steady state definition</a>：chaos 開始前的穩態定義</li>
<li><a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23 verification evidence handoff</a>：chaos 證據接到 release gate</li>
<li><a href="/blog/backend/08-incident-response/drills-and-oncall-readiness/" data-link-title="8.6 演練與值班能力建設" data-link-desc="用演練與值班訓練提升事故反應品質">8.6 drills / on-call readiness</a>：chaos 結果回饋到值班訓練</li>
</ul>
]]></content:encoded></item><item><title>Apache JMeter</title><link>https://tarrragon.github.io/blog/backend/06-reliability/vendors/jmeter/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/vendors/jmeter/</guid><description>&lt;p>JMeter 是 Apache 出品的老牌 load test 工具、承擔三個責任：GUI-driven test plan 設計、多 protocol sampler（HTTP / JDBC / JMS / FTP / mail）、plugins 生態廣 + 企業環境普及。設計取捨偏向「GUI 易上手 + 既有測試資產治理 + 多 protocol」、跟 code-first（k6 / Gatling）的取捨在 dev workflow 跟 version control 友善度。&lt;/p>
&lt;h2 id="本章目標">本章目標&lt;/h2>
&lt;p>讀完本章後、你應該能：&lt;/p>
&lt;ol>
&lt;li>用 GUI 設計 test plan（thread group / sampler / listener / assertion）&lt;/li>
&lt;li>跑 non-GUI mode 給 CI&lt;/li>
&lt;li>用 Distributed mode（master / slave）擴張 VU&lt;/li>
&lt;li>用 JMeter Plugins Manager 加擴展&lt;/li>
&lt;li>評估 JMeter vs 現代 CLI-first（k6 / Gatling / Locust）的選用&lt;/li>
&lt;/ol>
&lt;h2 id="最短路徑5-分鐘把-jmeter-跑起來">最短路徑：5 分鐘把 JMeter 跑起來&lt;/h2>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 1. 安裝&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: brew install jmeter / 下載 zip&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 2. GUI 設計 .jmx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: 開 jmeter GUI、加 Thread Group / HTTP Sampler / Listener&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 3. CI 跑 non-GUI mode&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: jmeter -n -t test.jmx -l result.jtl -e -o report/&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="日常操作與決策形狀">日常操作與決策形狀&lt;/h2>
&lt;h3 id="test-plan-結構">Test plan 結構&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>Thread Group（VU + ramp-up + loop count）&lt;/li>
&lt;li>Sampler（HTTP / JDBC / JMS / FTP / Java Request）&lt;/li>
&lt;li>Listener（aggregate report / view tree / graph）&lt;/li>
&lt;li>Assertion（response / duration / size）&lt;/li>
&lt;/ul>
&lt;h3 id="non-gui-mode-for-ci">Non-GUI mode for CI&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>&lt;code>-n&lt;/code> non-GUI&lt;/li>
&lt;li>&lt;code>-t&lt;/code> test file / &lt;code>-l&lt;/code> log file&lt;/li>
&lt;li>&lt;code>-e -o&lt;/code> 產生 HTML dashboard&lt;/li>
&lt;li>Exit code 0 / 1（搭配 backend listener / assertion）&lt;/li>
&lt;/ul>
&lt;h3 id="distributed-testing">Distributed testing&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>Master / slave 配置&lt;/li>
&lt;li>RMI port 設定&lt;/li>
&lt;li>Result aggregation 在 master&lt;/li>
&lt;/ul>
&lt;h2 id="進階主題按需閱讀">進階主題（按需閱讀）&lt;/h2>
&lt;h3 id="plugins-manager">Plugins Manager&lt;/h3>
&lt;p>子議題：&lt;/p></description><content:encoded><![CDATA[<p>JMeter 是 Apache 出品的老牌 load test 工具、承擔三個責任：GUI-driven test plan 設計、多 protocol sampler（HTTP / JDBC / JMS / FTP / mail）、plugins 生態廣 + 企業環境普及。設計取捨偏向「GUI 易上手 + 既有測試資產治理 + 多 protocol」、跟 code-first（k6 / Gatling）的取捨在 dev workflow 跟 version control 友善度。</p>
<h2 id="本章目標">本章目標</h2>
<p>讀完本章後、你應該能：</p>
<ol>
<li>用 GUI 設計 test plan（thread group / sampler / listener / assertion）</li>
<li>跑 non-GUI mode 給 CI</li>
<li>用 Distributed mode（master / slave）擴張 VU</li>
<li>用 JMeter Plugins Manager 加擴展</li>
<li>評估 JMeter vs 現代 CLI-first（k6 / Gatling / Locust）的選用</li>
</ol>
<h2 id="最短路徑5-分鐘把-jmeter-跑起來">最短路徑：5 分鐘把 JMeter 跑起來</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 1. 安裝</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># TODO: brew install jmeter / 下載 zip</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 2. GUI 設計 .jmx</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># TODO: 開 jmeter GUI、加 Thread Group / HTTP Sampler / Listener</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># 3. CI 跑 non-GUI mode</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c1"># TODO: jmeter -n -t test.jmx -l result.jtl -e -o report/</span></span></span></code></pre></div><h2 id="日常操作與決策形狀">日常操作與決策形狀</h2>
<h3 id="test-plan-結構">Test plan 結構</h3>
<p>子議題：</p>
<ul>
<li>Thread Group（VU + ramp-up + loop count）</li>
<li>Sampler（HTTP / JDBC / JMS / FTP / Java Request）</li>
<li>Listener（aggregate report / view tree / graph）</li>
<li>Assertion（response / duration / size）</li>
</ul>
<h3 id="non-gui-mode-for-ci">Non-GUI mode for CI</h3>
<p>子議題：</p>
<ul>
<li><code>-n</code> non-GUI</li>
<li><code>-t</code> test file / <code>-l</code> log file</li>
<li><code>-e -o</code> 產生 HTML dashboard</li>
<li>Exit code 0 / 1（搭配 backend listener / assertion）</li>
</ul>
<h3 id="distributed-testing">Distributed testing</h3>
<p>子議題：</p>
<ul>
<li>Master / slave 配置</li>
<li>RMI port 設定</li>
<li>Result aggregation 在 master</li>
</ul>
<h2 id="進階主題按需閱讀">進階主題（按需閱讀）</h2>
<h3 id="plugins-manager">Plugins Manager</h3>
<p>子議題：</p>
<ul>
<li>jmeter-plugins.org plugins</li>
<li>常用：PerfMon / Dummy Sampler / Custom Thread Groups / WebSocket</li>
<li>安裝管理：Plugins Manager 安裝後可 UI 管</li>
</ul>
<h3 id="recording-controller">Recording controller</h3>
<p>子議題：</p>
<ul>
<li>HTTP(S) Test Script Recorder</li>
<li>Browser proxy 設定</li>
<li>適合：快速錄製 user flow</li>
</ul>
<h3 id="csv-data-set--parameterization">CSV data set / parameterization</h3>
<p>子議題：</p>
<ul>
<li>CSV Data Set Config</li>
<li>各 thread 取不同資料</li>
<li>適合 data-driven test</li>
</ul>
<h3 id="ci--jenkins-integration">CI / Jenkins integration</h3>
<p>子議題：</p>
<ul>
<li>Jenkins JMeter plugin</li>
<li>Performance plugin（trend analysis）</li>
<li>對應 <a href="/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13 Performance Regression Gate</a></li>
</ul>
<h3 id="既有-jmx-資產治理">既有 .jmx 資產治理</h3>
<p>子議題：</p>
<ul>
<li>XML 不友善 git diff</li>
<li>大 test plan 可讀性差</li>
<li>改用 module 拆 + Test Fragment</li>
<li>對應企業遷移到 k6 / Gatling 評估</li>
</ul>
<h2 id="排錯快速判讀">排錯快速判讀</h2>
<h3 id="high-vu-起不來">High VU 起不來</h3>
<p>操作原則：JVM heap 不夠 / GUI 模式有限制（永遠 non-GUI for production load）。</p>
<h3 id="listener-拖慢">Listener 拖慢</h3>
<p>操作原則：View Results Tree 記錄太多 → 改 simple data writer / disable detail。</p>
<h3 id="distributed-rmi-連不上">Distributed RMI 連不上</h3>
<p>操作原則：firewall + RMI port 不對。</p>
<h3 id="assertion-noise">Assertion noise</h3>
<p>操作原則：assertion failed 多但實際 OK → response time / size 設過嚴。</p>
<h2 id="何時改走其他服務">何時改走其他服務</h2>
<table>
  <thead>
      <tr>
          <th>需求形狀</th>
          <th>改走</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Code-first / CI-first</td>
          <td><a href="/blog/backend/06-reliability/vendors/k6/" data-link-title="k6" data-link-desc="現代 load test、JS scripting、Grafana Labs">k6</a> / <a href="/blog/backend/06-reliability/vendors/gatling/" data-link-title="Gatling" data-link-desc="JVM-based load test、Scala / Java / Kotlin DSL、強型別 scenario、HAR-driven recording">Gatling</a></td>
      </tr>
      <tr>
          <td>Python</td>
          <td><a href="/blog/backend/06-reliability/vendors/locust/" data-link-title="Locust" data-link-desc="Python-based load test、distributed、易擴展">Locust</a></td>
      </tr>
      <tr>
          <td>Cloud managed</td>
          <td>BlazeMeter / Octoperf / Tricentis NeoLoad</td>
      </tr>
      <tr>
          <td>Browser flow</td>
          <td>Playwright / Cypress / k6 browser</td>
      </tr>
      <tr>
          <td>Capacity planning</td>
          <td><a href="/blog/backend/09-performance-capacity/" data-link-title="模組九：效能工程與容量規劃" data-link-desc="把『目前配置能撐多少、要加多少機器』變成可量化、可驗證、可改進的工程流程">09 performance capacity</a></td>
      </tr>
  </tbody>
</table>
<h2 id="不在本頁內的主題">不在本頁內的主題</h2>
<ul>
<li>完整 plugins 列表</li>
<li>BeanShell / Groovy scripting</li>
<li>JMeter internal architecture</li>
</ul>
<h2 id="案例回寫">案例回寫</h2>
<table>
  <thead>
      <tr>
          <th>案例方向</th>
          <th>對應主題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/linkedin/capacity-headroom-and-oncall-tiering/" data-link-title="LinkedIn：Capacity Headroom 與 On-call 分層" data-link-desc="把容量預測與值班分層綁在一起，降低高峰時段的升級混亂與恢復延遲。">LinkedIn：Capacity 與 On-call 分層</a></td>
          <td>企業內部 load test pipeline + headroom 驗證</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/shopify/bfcm-capacity-and-game-day/" data-link-title="Shopify：BFCM 容量治理與 Game Day 驗證節奏" data-link-desc="把季節性流量峰值轉成年度可靠性流程，透過容量模型、演練與隔離策略提前吸收風險。">Shopify：BFCM 容量治理與 Game Day</a></td>
          <td>峰值前 load test scenario 與 capacity baseline 的對照組</td>
      </tr>
  </tbody>
</table>
<p><strong>待補 JMeter customer case</strong>：企業內部 JMeter 大規模採用案例、JMeter → k6 遷移案例。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>上游概念：<a href="/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13 Performance Regression Gate</a></li>
<li>平行 vendor：<a href="/blog/backend/06-reliability/vendors/k6/" data-link-title="k6" data-link-desc="現代 load test、JS scripting、Grafana Labs">k6</a>、<a href="/blog/backend/06-reliability/vendors/gatling/" data-link-title="Gatling" data-link-desc="JVM-based load test、Scala / Java / Kotlin DSL、強型別 scenario、HAR-driven recording">Gatling</a></li>
<li>下游能力：<a href="/blog/backend/09-performance-capacity/" data-link-title="模組九：效能工程與容量規劃" data-link-desc="把『目前配置能撐多少、要加多少機器』變成可量化、可驗證、可改進的工程流程">09 performance capacity</a></li>
</ul>
]]></content:encoded></item><item><title>Shopify</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/shopify/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/shopify/</guid><description>&lt;p>Shopify 是 BFCM（Black Friday / Cyber Monday）流量峰值的可靠性教學標竿、pod-based architecture 是 multi-tenant SaaS 的隔離典範。教學重點在「年度可預期峰值如何透過架構與演練準備」。&lt;/p>
&lt;h2 id="規劃重點">規劃重點&lt;/h2>
&lt;ul>
&lt;li>Pod-based Architecture：多租戶切分、商家隔離設計&lt;/li>
&lt;li>BFCM 準備：年度峰值的 capacity planning 流程&lt;/li>
&lt;li>Resiliency Matrix：列舉服務與失效模式的對照表&lt;/li>
&lt;li>Toxiproxy / Resiliency tooling：Shopify 開源的 chaos 工具&lt;/li>
&lt;li>Database sharding：MySQL 分片策略與 online resharding&lt;/li>
&lt;/ul>
&lt;h2 id="預計收錄實踐">預計收錄實踐&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>議題&lt;/th>
 &lt;th>教學重點&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>BFCM Capacity Planning&lt;/td>
 &lt;td>容量預測、load test 設計、實際峰值對照&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Pod Architecture&lt;/td>
 &lt;td>多租戶切分、failure isolation&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Resiliency Matrix&lt;/td>
 &lt;td>失效模式對照表的維護方法&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Toxiproxy&lt;/td>
 &lt;td>TCP-level 故障注入的工程實作&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Database resharding&lt;/td>
 &lt;td>線上 schema 與 sharding 變更&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="章節列表">章節列表&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>章節&lt;/th>
 &lt;th>主題&lt;/th>
 &lt;th>核心責任&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/shopify/bfcm-capacity-and-game-day/" data-link-title="Shopify：BFCM 容量治理與 Game Day 驗證節奏" data-link-desc="把季節性流量峰值轉成年度可靠性流程，透過容量模型、演練與隔離策略提前吸收風險。">H1&lt;/a>&lt;/td>
 &lt;td>BFCM 容量治理與 Game Day&lt;/td>
 &lt;td>把季節性峰值壓力轉成可預演、可回寫的年度可靠性節奏&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/shopify/pod-architecture-and-resiliency-matrix/" data-link-title="Shopify：Pod Architecture 與 Resiliency Matrix" data-link-desc="多租戶隔離與系統化失敗模式盤點：pod 邊界控制擴散、resiliency matrix 驅動演練。">H2&lt;/a>&lt;/td>
 &lt;td>Pod Architecture 與 Resiliency Matrix&lt;/td>
 &lt;td>多租戶隔離與系統化失敗模式盤點&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="案例定位">案例定位&lt;/h2>
&lt;p>Shopify 這個案例在講的是峰值流量如何被提前吸收，而不是在事故當下硬扛。讀者先抓 capacity planning、performance testing 與 pods architecture 的分工，再看它們怎麼把 BFCM 這種季節性壓力轉成可管理的工程節奏。&lt;/p>
&lt;h2 id="判讀重點">判讀重點&lt;/h2>
&lt;p>當流量會在短時間內暴增時，先做容量模型與壓測，再確認 pods 邊界能否切住故障擴散。當資料平台也在同一波壓力下成長時，重點不只在擴容，而在是否能保住查詢、寫入與回放的穩定節奏。&lt;/p>
&lt;h2 id="可操作判準">可操作判準&lt;/h2>
&lt;ul>
&lt;li>能否在 peak 之前說出容量上限與安全緩衝&lt;/li>
&lt;li>能否把壓測結果對應到真實流量模型&lt;/li>
&lt;li>能否讓 pods 邊界成為故障隔離單位&lt;/li>
&lt;li>能否在高峰前完成演練與當日指揮節奏對齊&lt;/li>
&lt;/ul>
&lt;h2 id="與其他案例的關係">與其他案例的關係&lt;/h2>
&lt;p>Shopify 的價值在於它把峰值準備寫成年度節奏，這和 LinkedIn 的 capacity planning、AWS S3 的區域風險、Discord 的流量驚奇都能互相對照。讀這頁時要抓的是「先把峰值變成可預測問題」，而不是等事故來了才補救。&lt;/p>
&lt;h2 id="代表樣本">代表樣本&lt;/h2>
&lt;ul>
&lt;li>BFCM 前的 capacity planning 讓峰值壓力先被模型吸收，而不是直接落在事故當下。&lt;/li>
&lt;li>pods architecture 把多租戶流量切成較小隔離單位，限制故障擴散。&lt;/li>
&lt;li>performance testing 讓真實峰值在演練階段就可見。&lt;/li>
&lt;li>resiliency tooling 讓團隊能在高峰前驗證失效模式。&lt;/li>
&lt;li>database resharding 讓高峰下的 stateful 系統仍能持續擴容。&lt;/li>
&lt;li>incident rehearsal 讓當日指揮與復原節奏先對齊。&lt;/li>
&lt;li>resiliency matrix 讓每個服務與失效模式都有明確對照。&lt;/li>
&lt;li>Toxiproxy 讓 TCP 層故障注入成為可重用工具。&lt;/li>
&lt;/ul>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://shopify.engineering/capacity-planning-shopify">Capacity Planning at Scale&lt;/a>：BFCM 前的容量規劃與驗證方法。&lt;/li>
&lt;li>&lt;a href="https://shopify.engineering/scale-performance-testing">Performance Testing At Scale—for BFCM and Beyond&lt;/a>：BFCM scale testing 與壓測節奏。&lt;/li>
&lt;li>&lt;a href="https://shopify.engineering/a-pods-architecture-to-allow-shopify-to-scale">A Pods Architecture To Allow Shopify To Scale&lt;/a>：pods 架構與隔離設計。&lt;/li>
&lt;li>&lt;a href="https://shopify.engineering/blogs/engineering/reliably-scale-data-platform">How to Reliably Scale Your Data Platform for High Volumes&lt;/a>：資料平台在高流量下的可靠性方法。&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Shopify 是 BFCM（Black Friday / Cyber Monday）流量峰值的可靠性教學標竿、pod-based architecture 是 multi-tenant SaaS 的隔離典範。教學重點在「年度可預期峰值如何透過架構與演練準備」。</p>
<h2 id="規劃重點">規劃重點</h2>
<ul>
<li>Pod-based Architecture：多租戶切分、商家隔離設計</li>
<li>BFCM 準備：年度峰值的 capacity planning 流程</li>
<li>Resiliency Matrix：列舉服務與失效模式的對照表</li>
<li>Toxiproxy / Resiliency tooling：Shopify 開源的 chaos 工具</li>
<li>Database sharding：MySQL 分片策略與 online resharding</li>
</ul>
<h2 id="預計收錄實踐">預計收錄實踐</h2>
<table>
  <thead>
      <tr>
          <th>議題</th>
          <th>教學重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>BFCM Capacity Planning</td>
          <td>容量預測、load test 設計、實際峰值對照</td>
      </tr>
      <tr>
          <td>Pod Architecture</td>
          <td>多租戶切分、failure isolation</td>
      </tr>
      <tr>
          <td>Resiliency Matrix</td>
          <td>失效模式對照表的維護方法</td>
      </tr>
      <tr>
          <td>Toxiproxy</td>
          <td>TCP-level 故障注入的工程實作</td>
      </tr>
      <tr>
          <td>Database resharding</td>
          <td>線上 schema 與 sharding 變更</td>
      </tr>
  </tbody>
</table>
<h2 id="章節列表">章節列表</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/shopify/bfcm-capacity-and-game-day/" data-link-title="Shopify：BFCM 容量治理與 Game Day 驗證節奏" data-link-desc="把季節性流量峰值轉成年度可靠性流程，透過容量模型、演練與隔離策略提前吸收風險。">H1</a></td>
          <td>BFCM 容量治理與 Game Day</td>
          <td>把季節性峰值壓力轉成可預演、可回寫的年度可靠性節奏</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/shopify/pod-architecture-and-resiliency-matrix/" data-link-title="Shopify：Pod Architecture 與 Resiliency Matrix" data-link-desc="多租戶隔離與系統化失敗模式盤點：pod 邊界控制擴散、resiliency matrix 驅動演練。">H2</a></td>
          <td>Pod Architecture 與 Resiliency Matrix</td>
          <td>多租戶隔離與系統化失敗模式盤點</td>
      </tr>
  </tbody>
</table>
<h2 id="案例定位">案例定位</h2>
<p>Shopify 這個案例在講的是峰值流量如何被提前吸收，而不是在事故當下硬扛。讀者先抓 capacity planning、performance testing 與 pods architecture 的分工，再看它們怎麼把 BFCM 這種季節性壓力轉成可管理的工程節奏。</p>
<h2 id="判讀重點">判讀重點</h2>
<p>當流量會在短時間內暴增時，先做容量模型與壓測，再確認 pods 邊界能否切住故障擴散。當資料平台也在同一波壓力下成長時，重點不只在擴容，而在是否能保住查詢、寫入與回放的穩定節奏。</p>
<h2 id="可操作判準">可操作判準</h2>
<ul>
<li>能否在 peak 之前說出容量上限與安全緩衝</li>
<li>能否把壓測結果對應到真實流量模型</li>
<li>能否讓 pods 邊界成為故障隔離單位</li>
<li>能否在高峰前完成演練與當日指揮節奏對齊</li>
</ul>
<h2 id="與其他案例的關係">與其他案例的關係</h2>
<p>Shopify 的價值在於它把峰值準備寫成年度節奏，這和 LinkedIn 的 capacity planning、AWS S3 的區域風險、Discord 的流量驚奇都能互相對照。讀這頁時要抓的是「先把峰值變成可預測問題」，而不是等事故來了才補救。</p>
<h2 id="代表樣本">代表樣本</h2>
<ul>
<li>BFCM 前的 capacity planning 讓峰值壓力先被模型吸收，而不是直接落在事故當下。</li>
<li>pods architecture 把多租戶流量切成較小隔離單位，限制故障擴散。</li>
<li>performance testing 讓真實峰值在演練階段就可見。</li>
<li>resiliency tooling 讓團隊能在高峰前驗證失效模式。</li>
<li>database resharding 讓高峰下的 stateful 系統仍能持續擴容。</li>
<li>incident rehearsal 讓當日指揮與復原節奏先對齊。</li>
<li>resiliency matrix 讓每個服務與失效模式都有明確對照。</li>
<li>Toxiproxy 讓 TCP 層故障注入成為可重用工具。</li>
</ul>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://shopify.engineering/capacity-planning-shopify">Capacity Planning at Scale</a>：BFCM 前的容量規劃與驗證方法。</li>
<li><a href="https://shopify.engineering/scale-performance-testing">Performance Testing At Scale—for BFCM and Beyond</a>：BFCM scale testing 與壓測節奏。</li>
<li><a href="https://shopify.engineering/a-pods-architecture-to-allow-shopify-to-scale">A Pods Architecture To Allow Shopify To Scale</a>：pods 架構與隔離設計。</li>
<li><a href="https://shopify.engineering/blogs/engineering/reliably-scale-data-platform">How to Reliably Scale Your Data Platform for High Volumes</a>：資料平台在高流量下的可靠性方法。</li>
</ul>
]]></content:encoded></item><item><title>6.5 失敗模式預判（Pre-mortem 與 FMEA）</title><link>https://tarrragon.github.io/blog/backend/06-reliability/failure-mode-pre-mortem/</link><pubDate>Fri, 24 Apr 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/failure-mode-pre-mortem/</guid><description>&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>失敗模式預判是在變更上線前，主動尋找驗證覆蓋的缺口。責任是把「我們漏掉了什麼」從事後驚訝變成事前盤點。&lt;/p>
&lt;p>這一頁處理的是驗證邊界。當某個環節一旦失效就會放大事故，pre-mortem 與 FMEA 的工作是提前把那個環節標出來，讓團隊能在上線前決定是補驗證、收窄範圍還是延後變更。&lt;/p>
&lt;h2 id="核心判讀">核心判讀&lt;/h2>
&lt;p>驗證缺口的核心問題是變更是否被差異化控制、回復路徑是否經過驗證。&lt;/p>
&lt;p>重點訊號包括：&lt;/p>
&lt;ul>
&lt;li>高風險變更是否有獨立 gate&lt;/li>
&lt;li>負載模型是否包含失敗流量特徵&lt;/li>
&lt;li>故障演練是否覆蓋 partial failure 與連鎖失效&lt;/li>
&lt;li>rollback 與 runbook 是否有時限驗證&lt;/li>
&lt;/ul>
&lt;h2 id="pre-mortem-流程">Pre-mortem 流程&lt;/h2>
&lt;p>Pre-mortem 的核心假設是「這個變更已經在 production 造成事故」，然後反向推導可能的失敗路徑。這個方法的價值在於成本極低（只需要一次結構化討論）但能暴露驗證盲區。&lt;/p>
&lt;p>流程分四步：&lt;/p>
&lt;p>&lt;strong>列出依賴與資料路徑&lt;/strong>：把變更涉及的服務依賴、資料寫入路徑與外部呼叫畫出來。重點是找出「變更直接或間接觸及的系統邊界」，包括 schema、config、依賴服務版本與流量路由。&lt;/p>
&lt;p>&lt;strong>對每條路徑問失敗影響&lt;/strong>：對每條路徑假設失敗，判斷影響範圍。問的是「如果這條路徑斷了 / 慢了 / 回傳錯誤，影響會擴散到哪裡」。影響範圍包含直接依賴方、上游呼叫者、使用者可見行為與資料一致性。&lt;/p>
&lt;p>&lt;strong>判斷現有驗證覆蓋&lt;/strong>：對每條失敗路徑，檢查現有 CI、load test、chaos experiment、contract test 是否能攔住這個失敗。重點是找出「我們認為有覆蓋但實際沒覆蓋」的路徑 — 例如 CI 有 unit test 但沒有 integration test 覆蓋跨服務呼叫，或 load test 有 throughput 驗證但沒有 retry storm 場景。&lt;/p>
&lt;p>&lt;strong>識別驗證缺口並路由&lt;/strong>：未覆蓋的失敗路徑進入兩條路由。上線前能補的缺口回寫到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/reliability-readiness-review/" data-link-title="6.19 Reliability Readiness Review" data-link-desc="把上線前、重大變更前與高風險操作前的可靠性準備度變成可檢查門檻">6.19 reliability readiness review&lt;/a>，作為上線前檢查項目。上線前補不了的缺口回寫到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 reliability debt backlog&lt;/a>，作為可排序的改善項目。&lt;/p>
&lt;p>Pre-mortem 的常見失效是流程走了但結論沒路由。當缺口被列出但沒有 owner、沒有 deadline、沒有連到 readiness review 或 debt backlog，pre-mortem 就只是會議紀錄。&lt;/p>
&lt;h2 id="fmea-分類軸">FMEA 分類軸&lt;/h2>
&lt;p>Failure Mode and Effects Analysis 按失效模式分類驗證缺口。按模式分類的好處是讓團隊能判斷「缺口屬於哪一類」，然後沿對應章節的路由去補。&lt;/p>
&lt;h3 id="gate-failure">Gate failure&lt;/h3>
&lt;p>Release gate 缺少高風險變更的差異化控制。當所有變更走同一條 CI pipeline、同一套 gate 門檻，高風險變更（schema migration、payment path、config rollout）的驗證強度跟日常小改動相同，gate 實質上對高風險變更無效。&lt;/p>
&lt;p>判讀條件：高風險變更是否有獨立的 gate 流程；gate 門檻是否隨變更風險等級調整。&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/microsoft/change-management-and-reliability-governance/" data-link-title="Microsoft：變更治理與可靠性門檻" data-link-desc="透過分層變更管理與發布閘門，降低大型 SaaS 平台的系統性回歸風險。">Microsoft 的變更治理實踐&lt;/a>把變更按風險分層，高風險變更需要更嚴的放行條件與更完整的驗證路徑。回到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate&lt;/a> 補差異化門檻。&lt;/p>
&lt;h3 id="load-failure">Load failure&lt;/h3>
&lt;p>Workload model 沒覆蓋失敗流量特徵。壓測模型通常反映正常流量，但事故時的流量形狀完全不同：retry storm 放大請求量、cascade &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/timeout/" data-link-title="Timeout" data-link-desc="說明等待外部操作的時間上限如何保護資源與使用者體驗">timeout&lt;/a> 佔住連線、queue backlog 堆積改變消費節奏。當壓測模型只包含正常流量，通過壓測不代表系統能承受失敗流量。&lt;/p>
&lt;p>判讀條件：workload model 是否包含 retry 放大、timeout cascade 與 queue 堆積場景。回到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2 load test&lt;/a> 補失敗流量模型。&lt;/p>
&lt;h3 id="recovery-failure">Recovery failure&lt;/h3>
&lt;p>Rollback 或 DR 路徑在事故前沒被驗證過。團隊假設 rollback 可用，但 schema 已經不向下相容；團隊假設 failover 可用，但 failover config 跟 production 已經漂移。recovery failure 的特徵是「有計畫但沒跑過」。&lt;/p>
&lt;p>判讀條件：rollback 是否在過去 90 天被 rehearsal 驗證過；DR failover config 是否跟 production 同步。回到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/dr-rollback-rehearsal/" data-link-title="6.7 DR 演練與 Rollback Rehearsal" data-link-desc="把回復路徑從紙面計畫變成定期可重播、可量測的驗證流程">6.7 DR / rollback rehearsal&lt;/a> 建立定期驗證節奏。&lt;/p>
&lt;h3 id="detection-failure">Detection failure&lt;/h3>
&lt;p>告警延遲或缺失，問題被使用者先發現。當 SLO alert 覆蓋不足、dashboard 缺少關鍵路徑的訊號、或告警門檻設定過寬，團隊的 MTTD（mean time to detect）會拉長到使用者回報之後。detection failure 讓所有下游反應（止血、升級、溝通）都延遲。&lt;/p></description><content:encoded><![CDATA[<h2 id="概念定位">概念定位</h2>
<p>失敗模式預判是在變更上線前，主動尋找驗證覆蓋的缺口。責任是把「我們漏掉了什麼」從事後驚訝變成事前盤點。</p>
<p>這一頁處理的是驗證邊界。當某個環節一旦失效就會放大事故，pre-mortem 與 FMEA 的工作是提前把那個環節標出來，讓團隊能在上線前決定是補驗證、收窄範圍還是延後變更。</p>
<h2 id="核心判讀">核心判讀</h2>
<p>驗證缺口的核心問題是變更是否被差異化控制、回復路徑是否經過驗證。</p>
<p>重點訊號包括：</p>
<ul>
<li>高風險變更是否有獨立 gate</li>
<li>負載模型是否包含失敗流量特徵</li>
<li>故障演練是否覆蓋 partial failure 與連鎖失效</li>
<li>rollback 與 runbook 是否有時限驗證</li>
</ul>
<h2 id="pre-mortem-流程">Pre-mortem 流程</h2>
<p>Pre-mortem 的核心假設是「這個變更已經在 production 造成事故」，然後反向推導可能的失敗路徑。這個方法的價值在於成本極低（只需要一次結構化討論）但能暴露驗證盲區。</p>
<p>流程分四步：</p>
<p><strong>列出依賴與資料路徑</strong>：把變更涉及的服務依賴、資料寫入路徑與外部呼叫畫出來。重點是找出「變更直接或間接觸及的系統邊界」，包括 schema、config、依賴服務版本與流量路由。</p>
<p><strong>對每條路徑問失敗影響</strong>：對每條路徑假設失敗，判斷影響範圍。問的是「如果這條路徑斷了 / 慢了 / 回傳錯誤，影響會擴散到哪裡」。影響範圍包含直接依賴方、上游呼叫者、使用者可見行為與資料一致性。</p>
<p><strong>判斷現有驗證覆蓋</strong>：對每條失敗路徑，檢查現有 CI、load test、chaos experiment、contract test 是否能攔住這個失敗。重點是找出「我們認為有覆蓋但實際沒覆蓋」的路徑 — 例如 CI 有 unit test 但沒有 integration test 覆蓋跨服務呼叫，或 load test 有 throughput 驗證但沒有 retry storm 場景。</p>
<p><strong>識別驗證缺口並路由</strong>：未覆蓋的失敗路徑進入兩條路由。上線前能補的缺口回寫到 <a href="/blog/backend/06-reliability/reliability-readiness-review/" data-link-title="6.19 Reliability Readiness Review" data-link-desc="把上線前、重大變更前與高風險操作前的可靠性準備度變成可檢查門檻">6.19 reliability readiness review</a>，作為上線前檢查項目。上線前補不了的缺口回寫到 <a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 reliability debt backlog</a>，作為可排序的改善項目。</p>
<p>Pre-mortem 的常見失效是流程走了但結論沒路由。當缺口被列出但沒有 owner、沒有 deadline、沒有連到 readiness review 或 debt backlog，pre-mortem 就只是會議紀錄。</p>
<h2 id="fmea-分類軸">FMEA 分類軸</h2>
<p>Failure Mode and Effects Analysis 按失效模式分類驗證缺口。按模式分類的好處是讓團隊能判斷「缺口屬於哪一類」，然後沿對應章節的路由去補。</p>
<h3 id="gate-failure">Gate failure</h3>
<p>Release gate 缺少高風險變更的差異化控制。當所有變更走同一條 CI pipeline、同一套 gate 門檻，高風險變更（schema migration、payment path、config rollout）的驗證強度跟日常小改動相同，gate 實質上對高風險變更無效。</p>
<p>判讀條件：高風險變更是否有獨立的 gate 流程；gate 門檻是否隨變更風險等級調整。<a href="/blog/backend/06-reliability/cases/microsoft/change-management-and-reliability-governance/" data-link-title="Microsoft：變更治理與可靠性門檻" data-link-desc="透過分層變更管理與發布閘門，降低大型 SaaS 平台的系統性回歸風險。">Microsoft 的變更治理實踐</a>把變更按風險分層，高風險變更需要更嚴的放行條件與更完整的驗證路徑。回到 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate</a> 補差異化門檻。</p>
<h3 id="load-failure">Load failure</h3>
<p>Workload model 沒覆蓋失敗流量特徵。壓測模型通常反映正常流量，但事故時的流量形狀完全不同：retry storm 放大請求量、cascade <a href="/blog/backend/knowledge-cards/timeout/" data-link-title="Timeout" data-link-desc="說明等待外部操作的時間上限如何保護資源與使用者體驗">timeout</a> 佔住連線、queue backlog 堆積改變消費節奏。當壓測模型只包含正常流量，通過壓測不代表系統能承受失敗流量。</p>
<p>判讀條件：workload model 是否包含 retry 放大、timeout cascade 與 queue 堆積場景。回到 <a href="/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2 load test</a> 補失敗流量模型。</p>
<h3 id="recovery-failure">Recovery failure</h3>
<p>Rollback 或 DR 路徑在事故前沒被驗證過。團隊假設 rollback 可用，但 schema 已經不向下相容；團隊假設 failover 可用，但 failover config 跟 production 已經漂移。recovery failure 的特徵是「有計畫但沒跑過」。</p>
<p>判讀條件：rollback 是否在過去 90 天被 rehearsal 驗證過；DR failover config 是否跟 production 同步。回到 <a href="/blog/backend/06-reliability/dr-rollback-rehearsal/" data-link-title="6.7 DR 演練與 Rollback Rehearsal" data-link-desc="把回復路徑從紙面計畫變成定期可重播、可量測的驗證流程">6.7 DR / rollback rehearsal</a> 建立定期驗證節奏。</p>
<h3 id="detection-failure">Detection failure</h3>
<p>告警延遲或缺失，問題被使用者先發現。當 SLO alert 覆蓋不足、dashboard 缺少關鍵路徑的訊號、或告警門檻設定過寬，團隊的 MTTD（mean time to detect）會拉長到使用者回報之後。detection failure 讓所有下游反應（止血、升級、溝通）都延遲。</p>
<p>判讀條件：關鍵路徑的 MTTD 是否在可接受範圍；SLO alert 是否覆蓋使用者可見的服務承諾。<a href="/blog/backend/06-reliability/cases/netflix/steady-state-chaos-and-fit/" data-link-title="Netflix：Steady State、Chaos 與 FIT 的驗證路徑" data-link-desc="把故障注入從工具操作升級成可驗證流程：先定義穩態，再設計注入與回復條件。">Netflix 的 chaos 實踐</a>把 <a href="/blog/backend/knowledge-cards/steady-state/" data-link-title="Steady State" data-link-desc="說明可靠性實驗與事故恢復如何定義系統應維持的可接受狀態">steady state</a> 定義放在驗證的第一步 — 沒有穩態定義，告警就無法判斷系統是否偏離正常，detection 變成盲目。回到 <a href="/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">04 可觀測性</a> 補訊號覆蓋。</p>
<h2 id="失敗模式嚴重度評估">失敗模式嚴重度評估</h2>
<p>FMEA 傳統用 severity × probability × detectability 三軸評估風險優先序。在可靠性驗證的語境中，這三軸可以簡化為可操作判讀：</p>
<table>
  <thead>
      <tr>
          <th>軸</th>
          <th>判讀問題</th>
          <th>量測方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Severity</td>
          <td>失效的 <a href="/blog/backend/knowledge-cards/blast-radius/" data-link-title="Blast Radius" data-link-desc="說明事故影響面如何估算與隔離">blast radius</a> 有多大</td>
          <td>單服務 / 跨服務 / 跨區 / 跨租戶</td>
      </tr>
      <tr>
          <td>Probability</td>
          <td>這個失效路徑多常被觸及</td>
          <td>變更頻率、歷史事故率、依賴穩定度</td>
      </tr>
      <tr>
          <td>Detectability</td>
          <td>問題被發現需要多久</td>
          <td>MTTD、alert 覆蓋率、synthetic probe 頻率</td>
      </tr>
  </tbody>
</table>
<p>三軸的交叉決定驗證投資順序：high severity + high probability + low detectability 的缺口最先處理。反過來，low severity + low probability 的缺口可以先記錄在 <a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 reliability debt</a>，不需要立即補驗證。</p>
<p>嚴重度評估的陷阱是把評分當目標。三軸的責任是排序驗證投資，讓團隊在有限時間內先補最危險的缺口。當評分本身變成需要維護的文件，評估的維護成本會超過它帶來的判讀價值。</p>
<h2 id="服務環節問題地圖">服務環節問題地圖</h2>
<table>
  <thead>
      <tr>
          <th>環節</th>
          <th>失效分類</th>
          <th>主要問題</th>
          <th>案例</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Release Gate</td>
          <td>Gate</td>
          <td>高風險變更缺少差異化 gate</td>
          <td><a href="/blog/backend/07-security-data-protection/red-team/cases/supply-chain/teamcity-cve-2023-42793-ci-entrypoint/" data-link-title="7.R7.2.5 TeamCity 2023：CI 入口漏洞與交付鏈風險" data-link-desc="CI 平台入口被利用後，如何沿著建置與發佈流程擴散供應鏈風險">TeamCity 2023</a></td>
      </tr>
      <tr>
          <td>負載驗證模型</td>
          <td>Load</td>
          <td>測試流量與實際失敗節奏脫鉤</td>
          <td><a href="/blog/backend/07-security-data-protection/red-team/cases/data-exfiltration/progress-wsftp-2023-file-service-breach/" data-link-title="7.R7.4.6 Progress WS_FTP 2023：檔案服務入口與資料外送" data-link-desc="對外檔案服務漏洞在企業環境常直接轉為資料外送風險">WS_FTP 2023</a></td>
      </tr>
      <tr>
          <td>失敗模式演練</td>
          <td>Recovery</td>
          <td>partial failure 與連鎖失效覆蓋不足</td>
          <td><a href="/blog/backend/07-security-data-protection/red-team/cases/data-exfiltration/change-healthcare-2024-ops-impact/" data-link-title="7.R7.4.3 Change Healthcare 2024：資料事件轉為營運中斷" data-link-desc="醫療支付中樞事件如何同時衝擊資料安全與業務連續性">Change Healthcare 2024</a></td>
      </tr>
      <tr>
          <td>回復路徑驗證</td>
          <td>Recovery</td>
          <td>rollback 與 runbook 缺少時限驗證</td>
          <td><a href="/blog/backend/07-security-data-protection/red-team/cases/data-exfiltration/vmware-esxiargs-2023-ransomware-recovery-pressure/" data-link-title="7.R7.4.5 VMware ESXiArgs 2023：虛擬化平台勒索回復壓力" data-link-desc="虛擬化平台漏洞被利用後，回復策略與營運連續性會面臨同步壓力">VMware ESXiArgs 2023</a></td>
      </tr>
  </tbody>
</table>
<p>TeamCity 案例暴露的是 gate failure：CI 入口本身被繞過時，後續所有 gate 都失效。判讀條件是 CI pipeline 的存取控制是否被納入驗證範圍，而不只是 pipeline 內容。</p>
<p>Change Healthcare 案例暴露的是 recovery failure：事故影響擴散到營運層面時，技術回復完成不代表服務恢復。判讀條件是 DR plan 是否涵蓋跨系統依賴的恢復順序，而不只是單一服務的 rollback。</p>
<h2 id="案例對照">案例對照</h2>
<table>
  <thead>
      <tr>
          <th>情境</th>
          <th>失效分類</th>
          <th>判讀</th>
          <th>路由章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>CI 綠燈但線上回滾率上升</td>
          <td>Gate</td>
          <td>gate 覆蓋與實際風險未對齊</td>
          <td><a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate</a></td>
      </tr>
      <tr>
          <td>壓測通過但事故時連鎖降速</td>
          <td>Load</td>
          <td>負載模型缺少失敗流量特徵</td>
          <td><a href="/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2 load test</a></td>
      </tr>
      <tr>
          <td>演練記錄完整但回復時間偏長</td>
          <td>Recovery</td>
          <td>演練內容與實戰決策節奏不一致</td>
          <td><a href="/blog/backend/06-reliability/dr-rollback-rehearsal/" data-link-title="6.7 DR 演練與 Rollback Rehearsal" data-link-desc="把回復路徑從紙面計畫變成定期可重播、可量測的驗證流程">6.7 DR rehearsal</a></td>
      </tr>
      <tr>
          <td>使用者先於告警發現問題</td>
          <td>Detection</td>
          <td>訊號覆蓋不足或門檻過寬</td>
          <td><a href="/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">04 可觀測性</a></td>
      </tr>
  </tbody>
</table>
<p><a href="/blog/backend/06-reliability/cases/google/error-budget-policy-and-release-gating/" data-link-title="Google：Error Budget 政策如何決定發布節奏" data-link-desc="把 SLO 消耗量轉成 release gate，讓可靠性與交付速度共用同一套決策語言。">Google 的 error budget 政策</a>把 gate 門檻跟 budget 消耗綁在一起：budget 健康時走正常 gate，budget 快速消耗時提高門檻。這種做法讓 gate failure 的偵測從「事後觀察回滾率」轉成「事前看 budget 消耗趨勢」。</p>
<p><a href="/blog/backend/06-reliability/cases/shopify/pod-architecture-and-resiliency-matrix/" data-link-title="Shopify：Pod Architecture 與 Resiliency Matrix" data-link-desc="多租戶隔離與系統化失敗模式盤點：pod 邊界控制擴散、resiliency matrix 驅動演練。">Shopify 的 resiliency matrix</a> 是 FMEA 的制度化形式：service × failure mode 的矩陣，每格填入防護狀態（covered / gap / in-progress），gap 欄直接成為 game day 的演練題目。這種做法讓 FMEA 從一次性盤點變成持續維護的驗證清單。</p>
<h2 id="跟其他章節的整合">跟其他章節的整合</h2>
<p>Pre-mortem 與 FMEA 的產出需要路由到三個下游：</p>
<ul>
<li><a href="/blog/backend/06-reliability/reliability-readiness-review/" data-link-title="6.19 Reliability Readiness Review" data-link-desc="把上線前、重大變更前與高風險操作前的可靠性準備度變成可檢查門檻">6.19 reliability readiness review</a>：上線前能補的缺口進入 readiness checklist</li>
<li><a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 experiment safety boundary</a>：需要驗證的失敗假設轉成 chaos / load test 的實驗設計</li>
<li><a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 reliability debt backlog</a>：上線前補不了的缺口進入可排序的改善 backlog</li>
</ul>
<p>路由清晰度決定 pre-mortem 的實際價值。當缺口被識別但沒有路由到具體章節的具體動作，pre-mortem 就只是風險清單。</p>
<h2 id="判讀訊號">判讀訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀條件</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>高風險變更走一般 gate、無差異化控制</td>
          <td>gate failure — 回到 6.8 確認是否有風險分層</td>
      </tr>
      <tr>
          <td>壓測通過但 production 事故來自 retry/queue</td>
          <td>load failure — workload model 是否涵蓋失敗流量</td>
      </tr>
      <tr>
          <td>rollback 路徑上次驗證超過 90 天</td>
          <td>recovery failure — 回到 6.7 確認 rehearsal 節奏</td>
      </tr>
      <tr>
          <td>事故 MTTD 超過 SLO window</td>
          <td>detection failure — 回到 04 確認 alert 覆蓋與門檻</td>
      </tr>
      <tr>
          <td>pre-mortem 有做但缺口無 owner</td>
          <td>流程失效 — 結論沒路由到 6.19 或 6.21</td>
      </tr>
      <tr>
          <td>FMEA 評分定期更新但驗證沒跟著動</td>
          <td>評估與行動脫鉤 — 評分的責任是排序投資，改完要回寫驗證狀態</td>
      </tr>
  </tbody>
</table>
<h2 id="交接路由">交接路由</h2>
<ul>
<li><a href="/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2 load test</a>：補失敗流量模型（retry / timeout / queue）</li>
<li><a href="/blog/backend/06-reliability/dr-rollback-rehearsal/" data-link-title="6.7 DR 演練與 Rollback Rehearsal" data-link-desc="把回復路徑從紙面計畫變成定期可重播、可量測的驗證流程">6.7 DR / rollback rehearsal</a>：補回復路徑驗證</li>
<li><a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate</a>：補高風險變更的差異化 gate</li>
<li><a href="/blog/backend/06-reliability/reliability-readiness-review/" data-link-title="6.19 Reliability Readiness Review" data-link-desc="把上線前、重大變更前與高風險操作前的可靠性準備度變成可檢查門檻">6.19 reliability readiness review</a>：pre-mortem 缺口轉成上線前檢查</li>
<li><a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 experiment safety boundary</a>：失敗假設轉成實驗設計</li>
<li><a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 reliability debt backlog</a>：未修缺口進入可排序 backlog</li>
<li><a href="/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">04 可觀測性</a>：detection failure 回到訊號覆蓋</li>
<li><a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23 verification evidence handoff</a>：FMEA 結論作為 readiness 證據</li>
<li><a href="/blog/backend/08-incident-response/" data-link-title="模組八：事故處理與復盤" data-link-desc="用 IR 領域詞彙建問題節點、以服務級案例庫累積事故脈絡，先建概念與案例庫再進實作交接">08 事故處理</a>：pre-mortem 假設在事故中被驗證時回寫</li>
</ul>
]]></content:encoded></item><item><title>6.6 SLO 與 Error Budget 政策</title><link>https://tarrragon.github.io/blog/backend/06-reliability/slo-error-budget/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/slo-error-budget/</guid><description>&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>SLO 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/error-budget/" data-link-title="Error Budget" data-link-desc="說明 SLO 允許的失敗額度如何影響發版與可靠性投入">error budget&lt;/a> 是把可靠性從口號變成政策的工具。SLO 定義的是服務要對哪個使用者旅程負責，error budget 定義的是這個責任在一段時間內可以承受多少退化。當這兩個條件被寫清楚，可靠性就能從「感覺上應該穩」變成「超過哪個門檻就要暫停、降風險或修復」。&lt;/p>
&lt;p>這個節點先處理目標，再處理門檻。先問服務要守住什麼體驗，再問這個體驗要用哪些訊號衡量，最後才決定 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">burn rate&lt;/a> 到多少時要 freeze。這樣寫的好處是，讀者會先理解政策責任，再理解數字本身。&lt;/p>
&lt;h2 id="大綱">大綱&lt;/h2>
&lt;ul>
&lt;li>SLI 選型：user-journey-centric vs system-metric&lt;/li>
&lt;li>SLO 目標訂定：可達性、商業意義、頻率窗&lt;/li>
&lt;li>error budget：burn rate、policy、freeze 條件&lt;/li>
&lt;li>跟 &lt;a href="https://tarrragon.github.io/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">04 觀測&lt;/a> 的訊號交接&lt;/li>
&lt;li>跟 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate&lt;/a> 的凍結觸發&lt;/li>
&lt;li>跟 &lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/incident-severity-trigger/" data-link-title="8.1 事故分級與啟動條件" data-link-desc="建立統一分級標準與事故啟動門檻">8.1 事故分級&lt;/a> 的門檻對齊&lt;/li>
&lt;li>反模式：cargo-cult 99.99%、SLO 無人擁有、burn rate 無 alert&lt;/li>
&lt;/ul>
&lt;h2 id="核心判讀">核心判讀&lt;/h2>
&lt;p>SLO 的責任是讓團隊知道自己到底在保護什麼。當讀者看到一個 SLO 時，第一個問題是這個數字是否對應使用者行為、商業風險與回復成本；數字高低要放在這個脈絡中判讀。&lt;/p>
&lt;p>error budget 的責任是把風險傳導成決策。當 burn rate 開始上升時，團隊先確認 budget 還剩多少、目前的變更是否會放大風險、freeze 條件是否已經被觸發。這裡的重點是路由清楚，數字只是路由的輸入。&lt;/p>
&lt;h2 id="sli-選型">SLI 選型&lt;/h2>
&lt;p>SLI 選型的責任是把使用者旅程轉成可量測訊號。好的 SLI 先描述使用者能否完成重要任務，再選擇最能代表該任務的 log、metric、trace 或 client-side signal。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>SLI 類型&lt;/th>
 &lt;th>適用旅程&lt;/th>
 &lt;th>常見訊號&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Availability&lt;/td>
 &lt;td>request、checkout、login 是否成功&lt;/td>
 &lt;td>success rate、valid response&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Latency&lt;/td>
 &lt;td>使用者等待是否在可接受範圍&lt;/td>
 &lt;td>latency histogram、p95 / p99&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Freshness&lt;/td>
 &lt;td>資料是否足夠新&lt;/td>
 &lt;td>replication lag、index delay&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Correctness&lt;/td>
 &lt;td>回應是否符合業務語意&lt;/td>
 &lt;td>reconciliation error、mismatch&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Durability&lt;/td>
 &lt;td>寫入是否可保留與回復&lt;/td>
 &lt;td>write success、replay validation&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Availability 適合描述同步 API 與 user-facing request。它需要清楚定義分母與分子，例如只計算有效請求、排除客戶端取消，或把 timeout、5xx 與 business failure 分開。&lt;/p>
&lt;p>Latency 適合描述體驗壓力。平均值容易掩蓋長尾，可靠性政策通常需要 percentile 或 histogram，並且要對應使用者旅程，再用單一 process 的 handler time 作為診斷輔助。&lt;/p>
&lt;p>Freshness 適合描述資料管線、search index、cache projection 與 read model。這類服務即使 API 回應成功，資料過舊仍會破壞使用者體驗。&lt;/p>
&lt;p>Correctness 適合描述金流、帳務、庫存、資料同步與 migration。這類可靠性目標需要資料校驗與 reconciliation，而不只看 request 成功率。&lt;/p>
&lt;p>Durability 適合描述 queue、event log、object storage 與資料寫入。它關心寫入後能否找回、重播、備份與回復，常和 RPO / RTO 一起定義。&lt;/p>
&lt;h2 id="slo-政策">SLO 政策&lt;/h2>
&lt;p>SLO 政策的責任是把可靠性目標轉成團隊行為。數字本身只是門檻，政策要說明目標的 owner、時間窗、例外條件、檢視頻率與觸發後動作。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>政策欄位&lt;/th>
 &lt;th>責任&lt;/th>
 &lt;th>判讀用途&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>User journey&lt;/td>
 &lt;td>定義受保護體驗&lt;/td>
 &lt;td>避免 SLO 停在系統資源層&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>SLI formula&lt;/td>
 &lt;td>定義分母、分子與資料來源&lt;/td>
 &lt;td>保護 SLO 可重算與可解釋&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Objective&lt;/td>
 &lt;td>定義目標值與時間窗&lt;/td>
 &lt;td>連接可靠性承諾與風險預算&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Owner&lt;/td>
 &lt;td>指定維護與決策責任&lt;/td>
 &lt;td>讓 policy 能被檢視與調整&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Burn alert&lt;/td>
 &lt;td>定義消耗速度與通知條件&lt;/td>
 &lt;td>讓風險在 budget 耗盡前被看見&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Freeze action&lt;/td>
 &lt;td>定義暫停發布或限制變更的條件&lt;/td>
 &lt;td>把可靠性風險接到 release gate&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Review cadence&lt;/td>
 &lt;td>定義檢視頻率與調整機制&lt;/td>
 &lt;td>避免目標跟服務現況脫節&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>User journey 是 SLO 的錨點。checkout、login、message delivery、search freshness、invoice generation 都比 CPU 或 memory 更適合承載可靠性承諾，因為它們能直接對應使用者結果。&lt;/p></description><content:encoded><![CDATA[<h2 id="概念定位">概念定位</h2>
<p>SLO 與 <a href="/blog/backend/knowledge-cards/error-budget/" data-link-title="Error Budget" data-link-desc="說明 SLO 允許的失敗額度如何影響發版與可靠性投入">error budget</a> 是把可靠性從口號變成政策的工具。SLO 定義的是服務要對哪個使用者旅程負責，error budget 定義的是這個責任在一段時間內可以承受多少退化。當這兩個條件被寫清楚，可靠性就能從「感覺上應該穩」變成「超過哪個門檻就要暫停、降風險或修復」。</p>
<p>這個節點先處理目標，再處理門檻。先問服務要守住什麼體驗，再問這個體驗要用哪些訊號衡量，最後才決定 <a href="/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">burn rate</a> 到多少時要 freeze。這樣寫的好處是，讀者會先理解政策責任，再理解數字本身。</p>
<h2 id="大綱">大綱</h2>
<ul>
<li>SLI 選型：user-journey-centric vs system-metric</li>
<li>SLO 目標訂定：可達性、商業意義、頻率窗</li>
<li>error budget：burn rate、policy、freeze 條件</li>
<li>跟 <a href="/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">04 觀測</a> 的訊號交接</li>
<li>跟 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate</a> 的凍結觸發</li>
<li>跟 <a href="/blog/backend/08-incident-response/incident-severity-trigger/" data-link-title="8.1 事故分級與啟動條件" data-link-desc="建立統一分級標準與事故啟動門檻">8.1 事故分級</a> 的門檻對齊</li>
<li>反模式：cargo-cult 99.99%、SLO 無人擁有、burn rate 無 alert</li>
</ul>
<h2 id="核心判讀">核心判讀</h2>
<p>SLO 的責任是讓團隊知道自己到底在保護什麼。當讀者看到一個 SLO 時，第一個問題是這個數字是否對應使用者行為、商業風險與回復成本；數字高低要放在這個脈絡中判讀。</p>
<p>error budget 的責任是把風險傳導成決策。當 burn rate 開始上升時，團隊先確認 budget 還剩多少、目前的變更是否會放大風險、freeze 條件是否已經被觸發。這裡的重點是路由清楚，數字只是路由的輸入。</p>
<h2 id="sli-選型">SLI 選型</h2>
<p>SLI 選型的責任是把使用者旅程轉成可量測訊號。好的 SLI 先描述使用者能否完成重要任務，再選擇最能代表該任務的 log、metric、trace 或 client-side signal。</p>
<table>
  <thead>
      <tr>
          <th>SLI 類型</th>
          <th>適用旅程</th>
          <th>常見訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Availability</td>
          <td>request、checkout、login 是否成功</td>
          <td>success rate、valid response</td>
      </tr>
      <tr>
          <td>Latency</td>
          <td>使用者等待是否在可接受範圍</td>
          <td>latency histogram、p95 / p99</td>
      </tr>
      <tr>
          <td>Freshness</td>
          <td>資料是否足夠新</td>
          <td>replication lag、index delay</td>
      </tr>
      <tr>
          <td>Correctness</td>
          <td>回應是否符合業務語意</td>
          <td>reconciliation error、mismatch</td>
      </tr>
      <tr>
          <td>Durability</td>
          <td>寫入是否可保留與回復</td>
          <td>write success、replay validation</td>
      </tr>
  </tbody>
</table>
<p>Availability 適合描述同步 API 與 user-facing request。它需要清楚定義分母與分子，例如只計算有效請求、排除客戶端取消，或把 timeout、5xx 與 business failure 分開。</p>
<p>Latency 適合描述體驗壓力。平均值容易掩蓋長尾，可靠性政策通常需要 percentile 或 histogram，並且要對應使用者旅程，再用單一 process 的 handler time 作為診斷輔助。</p>
<p>Freshness 適合描述資料管線、search index、cache projection 與 read model。這類服務即使 API 回應成功，資料過舊仍會破壞使用者體驗。</p>
<p>Correctness 適合描述金流、帳務、庫存、資料同步與 migration。這類可靠性目標需要資料校驗與 reconciliation，而不只看 request 成功率。</p>
<p>Durability 適合描述 queue、event log、object storage 與資料寫入。它關心寫入後能否找回、重播、備份與回復，常和 RPO / RTO 一起定義。</p>
<h2 id="slo-政策">SLO 政策</h2>
<p>SLO 政策的責任是把可靠性目標轉成團隊行為。數字本身只是門檻，政策要說明目標的 owner、時間窗、例外條件、檢視頻率與觸發後動作。</p>
<table>
  <thead>
      <tr>
          <th>政策欄位</th>
          <th>責任</th>
          <th>判讀用途</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>User journey</td>
          <td>定義受保護體驗</td>
          <td>避免 SLO 停在系統資源層</td>
      </tr>
      <tr>
          <td>SLI formula</td>
          <td>定義分母、分子與資料來源</td>
          <td>保護 SLO 可重算與可解釋</td>
      </tr>
      <tr>
          <td>Objective</td>
          <td>定義目標值與時間窗</td>
          <td>連接可靠性承諾與風險預算</td>
      </tr>
      <tr>
          <td>Owner</td>
          <td>指定維護與決策責任</td>
          <td>讓 policy 能被檢視與調整</td>
      </tr>
      <tr>
          <td>Burn alert</td>
          <td>定義消耗速度與通知條件</td>
          <td>讓風險在 budget 耗盡前被看見</td>
      </tr>
      <tr>
          <td>Freeze action</td>
          <td>定義暫停發布或限制變更的條件</td>
          <td>把可靠性風險接到 release gate</td>
      </tr>
      <tr>
          <td>Review cadence</td>
          <td>定義檢視頻率與調整機制</td>
          <td>避免目標跟服務現況脫節</td>
      </tr>
  </tbody>
</table>
<p>User journey 是 SLO 的錨點。checkout、login、message delivery、search freshness、invoice generation 都比 CPU 或 memory 更適合承載可靠性承諾，因為它們能直接對應使用者結果。</p>
<p>SLI formula 需要可重算。分母包含哪些 request、分子如何判定成功、資料來源來自 server-side 還是 client-side、sampling 有哪些限制，都需要寫進政策。</p>
<p>Objective 需要結合商業風險與回復成本。99.9% 與 99.99% 的差異不只是小數點，而是代表可接受 downtime、工程投資、成本與變更節奏的差異。</p>
<p>Freeze action 讓 error budget 進入工程決策。當 budget 消耗過快時，團隊需要知道哪些變更暫停、哪些修復可繼續、哪些例外需要 owner 核准。</p>
<h2 id="error-budget-與-burn-rate">Error Budget 與 Burn Rate</h2>
<p>Error budget 的責任是把可靠性退化轉成可管理的風險餘額。它讓團隊在「追求穩定」與「持續變更」之間有共同語言。</p>
<table>
  <thead>
      <tr>
          <th>狀態</th>
          <th>判讀訊號</th>
          <th>常見動作</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Budget healthy</td>
          <td>burn rate 低於門檻</td>
          <td>維持正常發布節奏</td>
      </tr>
      <tr>
          <td>Budget warning</td>
          <td>短窗 burn rate 上升</td>
          <td>檢查近期變更與高風險發布</td>
      </tr>
      <tr>
          <td>Budget critical</td>
          <td>多窗口 burn rate 同時超門檻</td>
          <td>暫停高風險變更，優先修復可靠性</td>
      </tr>
      <tr>
          <td>Budget exhausted</td>
          <td>error budget 用盡或接近用盡</td>
          <td>啟動 freeze、復盤與可靠性改善</td>
      </tr>
      <tr>
          <td>Policy mismatch</td>
          <td>SLO 長期過鬆或過緊</td>
          <td>調整 SLI、objective 或時間窗</td>
      </tr>
  </tbody>
</table>
<p>Burn rate 要看短窗與長窗。短窗能捕捉快速事故，長窗能避免一次性尖峰造成過度反應；兩者一起使用，才適合觸發 page、ticket 或 release freeze。</p>
<p>Budget warning 適合做風險整理。團隊可以檢查近期 deploy、feature flag、migration、capacity、dependency 與 incident review action item，判斷是否需要降低變更速度。</p>
<p>Budget critical 適合觸發 release gate。此時可靠性風險已經從觀測層進入決策層，團隊需要把發布、rollback、capacity 與 incident readiness 放在同一張表中判讀。</p>
<p>Budget exhausted 適合觸發可靠性改善。改善內容可能是修 bug、補 capacity、降低 alert noise、補 runbook、重設 SLO 或清理 reliability debt。</p>
<h2 id="判讀訊號">判讀訊號</h2>
<ul>
<li>SLO 數字無 owner、過半年沒檢視</li>
<li>burn rate 無 alert、只有 monthly review</li>
<li>error budget 耗盡但 deployment 節奏不變</li>
<li>SLI 用 system metric（CPU / memory）、不對應 user journey</li>
<li>目標數字是抄來的（99.9 / 99.99）、無商業 anchor</li>
</ul>
<h2 id="案例對照">案例對照</h2>
<p>Google 提供的是制度原點，因為它把 SLO、<a href="/blog/backend/knowledge-cards/post-incident-review/" data-link-title="Post-Incident Review" data-link-desc="說明事故後如何完成復盤、學習與改進閉環">post-incident review</a> 與 toil budget 串成可管理的可靠性文化。Honeycomb 提供的是訊號層的延伸，因為 high-cardinality 與 burn rate alert 讓 SLO 可以在真實流量下被看見。Stripe 則把 SLO 風格的決策壓到交易語義上，讓 idempotency 與 migration 不會因為重試而失真。</p>
<p>當讀者把這三個案例放在一起，就會看見 SLO 不只是「填一個百分比」，而是把不同層級的風險接到同一條路由：制度、訊號與交易正確性。這也是本節章節要建立的核心能力。</p>
<h2 id="error-budget-三對齊跟-release-gating">Error Budget 三對齊跟 Release Gating</h2>
<p>Error budget 三對齊是把「SLI 範圍」「SLO 目標」「Budget gate 觸發點」分別跟「使用者價值 / 可接受承諾 / 交付節奏」綁定的設計練習。任一條未對齊、policy 就會跟團隊行為脫鉤 — SLI 不對齊使用者價值、policy 就保護錯的東西；SLO 不對齊承諾、團隊就追錯目標；Gate 不對齊交付節奏、政策就無人遵循。</p>
<p>對應 <a href="/blog/backend/06-reliability/cases/google/error-budget-policy-and-release-gating/" data-link-title="Google：Error Budget 政策如何決定發布節奏" data-link-desc="把 SLO 消耗量轉成 release gate，讓可靠性與交付速度共用同一套決策語言。">G1 Google Error Budget Policy</a>：揭露 SLO policy 設計的三個對齊 — 使用者行為對齊（哪些 journey 直接反映服務價值 → SLI 範圍）、可靠性承諾對齊（什麼水準算服務仍可接受 → SLO 目標）、交付節奏對齊（可靠性消耗到哪裡要改變發布策略 → Budget gate）。</p>
<p>三對齊完成後、release gate 可從「主觀風險判斷」轉成「政策驅動」：</p>
<ul>
<li>budget 健康：正常發版</li>
<li>budget 快速消耗：啟用變更限速、提高驗證門檻</li>
<li>budget 透支：凍結非必要變更、先修復與回補訊號</li>
</ul>
<p>把 budget gate 跟 <a href="/blog/backend/06-reliability/release-gate/#%e8%ae%8a%e6%9b%b4%e5%88%86%e5%b1%a4%e8%b7%9f-gate-%e6%94%bf%e7%ad%96" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release-gate 變更分層段</a> 綁定、讓「budget 三階段」對應「release gate 三層放行決策」。</p>
<p>Error budget 是「可靠性 vs 交付節奏」的平衡工具、不是被追求的固定分數。當 budget 被 KPI 化、SLI 範圍會被縮小、告警會被延後、例外條件會被擴張 — 三者都降低 budget 的判讀可信度。</p>
<h2 id="burn-rate-雙窗監控">Burn Rate 雙窗監控</h2>
<p>Burn rate 雙窗監控是把「budget 消耗速率」拆成短窗（急性事故）跟長窗（慢性退化）兩個 channel、各自觸發不同回應的設計。比固定閾值告警更接近使用者體感、且能區分「需立即頁」跟「需排修復節奏」。</p>
<p>對應 <a href="/blog/backend/06-reliability/cases/honeycomb/burn-rate-driven-reliability-operations/" data-link-title="Honeycomb：以 Burn Rate 驅動的可靠性操作" data-link-desc="把 SLO burn rate 直接連到值班決策與改善優先序，降低高噪音告警造成的判讀失真。">HC1 Honeycomb Burn Rate 驅動可靠性操作</a>：揭露 fast burn / slow burn 雙窗監控的價值 — 固定閾值告警在高變化流量下容易失真、burn rate 提供比固定閾值更接近使用者體感的判讀方式。</p>
<p>雙窗監控的設計：</p>
<ul>
<li><strong>Fast burn</strong>（短窗、高消耗率）：捕捉急性事故、觸發 page 立即響應</li>
<li><strong>Slow burn</strong>（長窗、低消耗率持續累積）：捕捉慢性退化、觸發 ticket 排入修復節奏</li>
</ul>
<p>兩窗一起用、避免單一閾值在不同流量型態下失真。Honeycomb 自家平台展示 burn rate 訊號可以跟 trace outlier path 對接 — 看到 burn rate 上升、能直接跳到具體退化 trace（這是 Honeycomb 的產品特色、tracing-first 對 burn rate 的補強）。vendor-neutral 的同類概念見 <a href="/blog/backend/04-observability/tracing-context/" data-link-title="4.3 tracing 與 context link" data-link-desc="整理 trace id、span 與跨服務 context propagation">4.3 tracing-context</a> 跟 <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> 的訊號設計。</p>
<h2 id="控制面">控制面</h2>
<p>SLO 與 error budget 的控制面是把可靠性訊號接到發布、事故與改善流程。SLO 只有在能改變團隊行為時，才會成為政策。</p>
<ol>
<li>SLI 設計回到 <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 訊號設計</a>。</li>
<li>資料品質限制回到 <a href="/blog/backend/04-observability/telemetry-data-quality/" data-link-title="4.17 Telemetry Data Quality" data-link-desc="把 missing signal、schema drift、sampling bias 與 timestamp skew 變成資料品質問題">4.17 Telemetry Data Quality</a>。</li>
<li>Budget warning 進入 release risk review。</li>
<li>Budget critical 進入 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate</a>。</li>
<li>事故觸發與復盤回寫進入 <a href="/blog/backend/08-incident-response/incident-severity-trigger/" data-link-title="8.1 事故分級與啟動條件" data-link-desc="建立統一分級標準與事故啟動門檻">8.1 事故分級</a> 與 <a href="/blog/backend/08-incident-response/post-incident-review/" data-link-title="8.5 復盤與改進追蹤" data-link-desc="把 RCA 與 action items 轉成可驗證閉環">8.5 復盤</a>。</li>
</ol>
<p>SLO policy 需要定期校準。服務規模、使用者旅程、依賴型態與商業風險變化後，原本的 SLI、objective 與 freeze 條件也要重新檢視。</p>
<p>SLO policy 也需要例外流程。重大資安修補、合規變更、資料修復或客戶承諾可能需要在 budget 緊張時繼續推進；例外應記錄 owner、理由、風險與回退條件。</p>
<h2 id="產業情境金融科技">產業情境：金融科技</h2>
<p>金融服務的 error budget 治理需要把合規週期納入凍結條件。交易關鍵路徑（payment / settlement / reconciliation）的 SLO 破壞可能直接觸發監管通報義務，budget 消耗到門檻時的升級路徑必須包含合規人員。</p>
<p>交易路徑的 SLI 選型需要涵蓋 correctness（reconciliation error rate），availability 和 latency 通過但對帳失敗仍然是 SLO 破壞。correctness SLI 的量測來源通常是日終或即時的 reconciliation pipeline，跟 availability SLI 的即時 request-level 量測有不同的時間粒度。</p>
<p>Budget 凍結的觸發條件除了 burn rate，還要對齊監管報告週期。若 budget 在季末報告前已消耗過多，凍結應提前啟動，因為報告期間內的可靠性退化會被放大審視。這個提前量取決於報告週期長度與修復節奏 — 月報制的提前量比季報制短。</p>
<p>Error budget 政策的升級路徑需要跟 compliance team 對齊。budget warning 階段通知工程 owner；budget critical 階段同時通知合規人員；budget exhausted 階段啟動合規審查流程。這個分層讓合規介入的時機跟工程介入同步，避免事後才發現可靠性退化已觸發通報義務。</p>
<p>金融場景的 budget 恢復比一般 SaaS 慢。恢復期間需要額外的 reconciliation 驗證（確認退化期間無交易錯漏）才能宣告 budget 回補。若 reconciliation 發現差異，budget 恢復會被延後直到差異被解決。這個約束讓金融服務的 freeze 持續時間通常比一般服務長。</p>
<h2 id="常見反模式">常見反模式</h2>
<p>SLO 反模式通常來自把目標數字當成可靠性制度本身。數字需要對應旅程、資料、owner 與決策，才有工程意義。</p>
<table>
  <thead>
      <tr>
          <th>反模式</th>
          <th>表面現象</th>
          <th>修正方向</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Cargo-cult 99.99%</td>
          <td>目標抄自外部範例</td>
          <td>從 user journey 與商業風險回推</td>
      </tr>
      <tr>
          <td>System metric SLO</td>
          <td>SLO 看 CPU / memory</td>
          <td>改用成功率、延遲、freshness</td>
      </tr>
      <tr>
          <td>SLO 無 owner</td>
          <td>目標存在但無人調整</td>
          <td>指定 policy owner 與 review</td>
      </tr>
      <tr>
          <td>Burn rate 無 alert</td>
          <td>budget 耗盡後才開會</td>
          <td>建立短窗 / 長窗 burn alert</td>
      </tr>
      <tr>
          <td>Freeze 無路由</td>
          <td>可靠性風險不影響發布</td>
          <td>接到 release gate 與例外流程</td>
      </tr>
  </tbody>
</table>
<p>Cargo-cult 99.99% 的問題在於缺少服務脈絡。高可用目標會增加架構、成本、演練與值班負擔；低可用目標則會增加使用者與商業風險。合理目標要從服務承諾回推。</p>
<p>System metric SLO 會讓可靠性偏向基礎設施視角。CPU 健康不代表 checkout 成功，pod running 不代表資料新鮮；系統指標適合支援 diagnosis，user journey 指標適合承載 SLO。</p>
<h2 id="交接路由">交接路由</h2>
<ul>
<li>04 訊號治理：SLI / burn rate metric 設計</li>
<li>06.8 release gate：error budget 耗盡觸發 freeze</li>
<li>06.9 capacity / cost：容量不足傳導為 SLO 風險</li>
<li>06.14 dependency budget：依賴可靠性納入 SLO 算式</li>
<li>08 事故閉環：burn rate alert 啟動條件</li>
<li>08.13 repeated / toil：error budget 撥用 toil reduction</li>
<li>06.18 reliability metrics：SLO 跟 DORA / SPACE 的指標分層</li>
</ul>
]]></content:encoded></item><item><title>Locust</title><link>https://tarrragon.github.io/blog/backend/06-reliability/vendors/locust/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/vendors/locust/</guid><description>&lt;p>Locust 是 Python-based load test 工具、承擔三個責任：Python class-based test 設計（user behavior 表達力強）、distributed mode（master / worker 內建）、Web UI 即時觀察。設計取捨偏向「Python DX + 高度自訂邏輯 + 任何 Python lib 都可用」、適合 Python 團隊與需要極高自訂邏輯的場景。&lt;/p>
&lt;h2 id="本章目標">本章目標&lt;/h2>
&lt;p>讀完本章後、你應該能：&lt;/p>
&lt;ol>
&lt;li>寫 Locust user class + task&lt;/li>
&lt;li>跑 standalone + distributed mode&lt;/li>
&lt;li>自訂 client（非 HTTP、如 gRPC / WebSocket）&lt;/li>
&lt;li>設計 task weight + on_start / on_stop hook&lt;/li>
&lt;li>評估 Locust vs k6 / Gatling 的選用&lt;/li>
&lt;/ol>
&lt;h2 id="最短路徑5-分鐘把-locust-跑起來">最短路徑：5 分鐘把 Locust 跑起來&lt;/h2>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 1. 安裝&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: pip install locust&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 2. 寫 locustfile.py&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: class User(HttpUser): wait_time = ..., @task def hello(self): ...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 3. 跑&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: locust -f locustfile.py --host=http://target&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: 瀏覽器 http://localhost:8089 操作&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="日常操作與決策形狀">日常操作與決策形狀&lt;/h2>
&lt;h3 id="user-class--task">User class + task&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>HttpUser / FastHttpUser（FastHttpUser 用 geventhttpclient、效能高）&lt;/li>
&lt;li>@task decorator + weight&lt;/li>
&lt;li>on_start / on_stop（per-VU setup / teardown）&lt;/li>
&lt;li>對應 Python class inheritance&lt;/li>
&lt;/ul>
&lt;h3 id="distributed-mode">Distributed mode&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>master：協調 + 收集 metric&lt;/li>
&lt;li>worker：實際發送 request&lt;/li>
&lt;li>&lt;code>locust --master&lt;/code> / &lt;code>locust --worker --master-host=...&lt;/code>&lt;/li>
&lt;li>多 worker 突破 Python GIL 限制&lt;/li>
&lt;/ul>
&lt;h3 id="web-ui-vs-headless">Web UI vs headless&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>Web UI（dev / interactive）&lt;/li>
&lt;li>Headless（&lt;code>--headless --users N --spawn-rate N --run-time T&lt;/code>）&lt;/li>
&lt;li>對應 CI 整合：CSV report&lt;/li>
&lt;/ul>
&lt;h2 id="進階主題按需閱讀">進階主題（按需閱讀）&lt;/h2>
&lt;h3 id="自訂-client非-http">自訂 client（非 HTTP）&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>任何 Python lib 都可包成 user&lt;/li>
&lt;li>gRPC / WebSocket / database / queue 都行&lt;/li>
&lt;li>request event 手動 fire&lt;/li>
&lt;/ul>
&lt;h3 id="custom-request">Custom request&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>self.client.get/post（HTTP）&lt;/li>
&lt;li>自訂 event emission&lt;/li>
&lt;li>Custom statistics&lt;/li>
&lt;/ul>
&lt;h3 id="locust-plugins-生態">locust-plugins 生態&lt;/h3>
&lt;p>子議題：&lt;/p></description><content:encoded><![CDATA[<p>Locust 是 Python-based load test 工具、承擔三個責任：Python class-based test 設計（user behavior 表達力強）、distributed mode（master / worker 內建）、Web UI 即時觀察。設計取捨偏向「Python DX + 高度自訂邏輯 + 任何 Python lib 都可用」、適合 Python 團隊與需要極高自訂邏輯的場景。</p>
<h2 id="本章目標">本章目標</h2>
<p>讀完本章後、你應該能：</p>
<ol>
<li>寫 Locust user class + task</li>
<li>跑 standalone + distributed mode</li>
<li>自訂 client（非 HTTP、如 gRPC / WebSocket）</li>
<li>設計 task weight + on_start / on_stop hook</li>
<li>評估 Locust vs k6 / Gatling 的選用</li>
</ol>
<h2 id="最短路徑5-分鐘把-locust-跑起來">最短路徑：5 分鐘把 Locust 跑起來</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 1. 安裝</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># TODO: pip install locust</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 2. 寫 locustfile.py</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># TODO: class User(HttpUser): wait_time = ..., @task def hello(self): ...</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># 3. 跑</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c1"># TODO: locust -f locustfile.py --host=http://target</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="c1"># TODO: 瀏覽器 http://localhost:8089 操作</span></span></span></code></pre></div><h2 id="日常操作與決策形狀">日常操作與決策形狀</h2>
<h3 id="user-class--task">User class + task</h3>
<p>子議題：</p>
<ul>
<li>HttpUser / FastHttpUser（FastHttpUser 用 geventhttpclient、效能高）</li>
<li>@task decorator + weight</li>
<li>on_start / on_stop（per-VU setup / teardown）</li>
<li>對應 Python class inheritance</li>
</ul>
<h3 id="distributed-mode">Distributed mode</h3>
<p>子議題：</p>
<ul>
<li>master：協調 + 收集 metric</li>
<li>worker：實際發送 request</li>
<li><code>locust --master</code> / <code>locust --worker --master-host=...</code></li>
<li>多 worker 突破 Python GIL 限制</li>
</ul>
<h3 id="web-ui-vs-headless">Web UI vs headless</h3>
<p>子議題：</p>
<ul>
<li>Web UI（dev / interactive）</li>
<li>Headless（<code>--headless --users N --spawn-rate N --run-time T</code>）</li>
<li>對應 CI 整合：CSV report</li>
</ul>
<h2 id="進階主題按需閱讀">進階主題（按需閱讀）</h2>
<h3 id="自訂-client非-http">自訂 client（非 HTTP）</h3>
<p>子議題：</p>
<ul>
<li>任何 Python lib 都可包成 user</li>
<li>gRPC / WebSocket / database / queue 都行</li>
<li>request event 手動 fire</li>
</ul>
<h3 id="custom-request">Custom request</h3>
<p>子議題：</p>
<ul>
<li>self.client.get/post（HTTP）</li>
<li>自訂 event emission</li>
<li>Custom statistics</li>
</ul>
<h3 id="locust-plugins-生態">locust-plugins 生態</h3>
<p>子議題：</p>
<ul>
<li>locust-plugins：第三方 plugin（CSV report enhanced / Postgres / Kafka / etc）</li>
<li>Custom shape（dynamic load profile）</li>
<li>TaskSet / SequentialTaskSet</li>
</ul>
<h3 id="ci-integration">CI integration</h3>
<p>子議題：</p>
<ul>
<li>Headless mode + exit code</li>
<li>CSV / JSON report</li>
<li>對應 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate</a></li>
</ul>
<h3 id="distributed-scaling">Distributed scaling</h3>
<p>子議題：</p>
<ul>
<li>Kubernetes 部署</li>
<li>多 region load source</li>
<li>Result aggregation</li>
</ul>
<h2 id="排錯快速判讀">排錯快速判讀</h2>
<h3 id="high-vu-跑不上去">High VU 跑不上去</h3>
<p>操作原則：Python GIL + 單 worker 限制、用 distributed mode。判讀：CPU / network bottleneck？</p>
<h3 id="worker-disconnect">Worker disconnect</h3>
<p>操作原則：master / worker network 不通、heartbeat timeout。判讀：log + master UI。</p>
<h3 id="custom-protocol-報告不正確">Custom protocol 報告不正確</h3>
<p>操作原則：手動 event fire 缺 / metric name 不對。</p>
<h3 id="memory-leak">Memory leak</h3>
<p>操作原則：long run test、user state accumulate。判讀：on_stop cleanup。</p>
<h2 id="何時改走其他服務">何時改走其他服務</h2>
<table>
  <thead>
      <tr>
          <th>需求形狀</th>
          <th>改走</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>編譯後分發 / 高 VU 單機</td>
          <td><a href="/blog/backend/06-reliability/vendors/k6/" data-link-title="k6" data-link-desc="現代 load test、JS scripting、Grafana Labs">k6</a></td>
      </tr>
      <tr>
          <td>JVM 生態</td>
          <td><a href="/blog/backend/06-reliability/vendors/gatling/" data-link-title="Gatling" data-link-desc="JVM-based load test、Scala / Java / Kotlin DSL、強型別 scenario、HAR-driven recording">Gatling</a></td>
      </tr>
      <tr>
          <td>GUI / 老牌</td>
          <td><a href="/blog/backend/06-reliability/vendors/jmeter/" data-link-title="Apache JMeter" data-link-desc="老牌 load test 工具、GUI &#43; plugins">JMeter</a></td>
      </tr>
      <tr>
          <td>Cloud managed</td>
          <td>k6 Cloud / BlazeMeter / Locust 自管 K8s</td>
      </tr>
      <tr>
          <td>Capacity planning</td>
          <td><a href="/blog/backend/09-performance-capacity/" data-link-title="模組九：效能工程與容量規劃" data-link-desc="把『目前配置能撐多少、要加多少機器』變成可量化、可驗證、可改進的工程流程">09 performance capacity 模組</a></td>
      </tr>
  </tbody>
</table>
<h2 id="不在本頁內的主題">不在本頁內的主題</h2>
<ul>
<li>Python 語言基礎</li>
<li>gevent / asyncio 內部</li>
<li>locust-plugins 完整列表</li>
</ul>
<h2 id="案例回寫">案例回寫</h2>
<table>
  <thead>
      <tr>
          <th>案例方向</th>
          <th>對應主題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/linkedin/capacity-headroom-and-oncall-tiering/" data-link-title="LinkedIn：Capacity Headroom 與 On-call 分層" data-link-desc="把容量預測與值班分層綁在一起，降低高峰時段的升級混亂與恢復延遲。">LinkedIn：Capacity 與 On-call 分層</a></td>
          <td>automated load testing 對齊 headroom 預測（Python 場景）</td>
      </tr>
  </tbody>
</table>
<p><strong>Case 庫稀薄</strong>：本 cases/ 目錄目前沒有以 Locust 為主軸的案例。可參考候選方向：</p>
<ul>
<li><strong>待補 Locust customer case</strong>：Python-heavy 團隊 load test 採用案例、distributed Locust 大規模部署案例</li>
<li><strong>候選 case</strong>：Pinterest（ML serving / 推薦系統壓測場景）、Spotify（squad-based 各團隊自管壓測）— 若未來收錄需先在 cases/ 補正文，本欄再寫實際 link</li>
</ul>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>上游概念：<a href="/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13 Performance Regression Gate</a></li>
<li>平行 vendor：<a href="/blog/backend/06-reliability/vendors/k6/" data-link-title="k6" data-link-desc="現代 load test、JS scripting、Grafana Labs">k6</a>、<a href="/blog/backend/06-reliability/vendors/gatling/" data-link-title="Gatling" data-link-desc="JVM-based load test、Scala / Java / Kotlin DSL、強型別 scenario、HAR-driven recording">Gatling</a></li>
<li>下游能力：<a href="/blog/backend/09-performance-capacity/" data-link-title="模組九：效能工程與容量規劃" data-link-desc="把『目前配置能撐多少、要加多少機器』變成可量化、可驗證、可改進的工程流程">09 performance capacity</a></li>
</ul>
]]></content:encoded></item><item><title>模組六：可靠性驗證流程</title><link>https://tarrragon.github.io/blog/backend/06-reliability/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/</guid><description>&lt;p>可靠性驗證模組的核心目標是說明測試如何從單一函式擴展到整個後端系統。語言教材會處理 unit test、table-driven / parameterized test、race / async test 與 integration test；本模組負責 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/ci-pipeline/" data-link-title="CI Pipeline" data-link-desc="說明持續整合流程如何在合併前驗證品質與相容性">CI pipeline&lt;/a>、壓力測試、fuzz campaign、chaos testing、SLO 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">Release Gate&lt;/a>。&lt;/p>
&lt;p>本輪規劃採問題驅動方法、用 SRE 領域 first-class 詞彙（&lt;a href="https://tarrragon.github.io/blog/backend/04-observability/sli-slo-signal/" data-link-title="4.6 SLI 量測與 SLO 訊號設計" data-link-desc="把可靠性目標的訊號從 metric 端設計好、餵給 6.6 SLO 政策">SLI&lt;/a> / &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">SLO&lt;/a> / &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/error-budget/" data-link-title="Error Budget" data-link-desc="說明 SLO 允許的失敗額度如何影響發版與可靠性投入">Error Budget&lt;/a> / Failure Mode / Chaos Hypothesis），把驗證議題拆成問題節點，蒐集公開 SRE 實踐作為服務級案例庫，再把控制面交接到可觀測性、部署平台與事故處理模組落地。&lt;/p>
&lt;h2 id="驗證角色">驗證角色&lt;/h2>
&lt;p>可靠性驗證的角色是把「系統會不會在真實壓力下失敗」變成可預演的工程問題。這一層不負責寫測試語法，也不負責定義服務功能，而是負責定義哪些失效值得被主動打破、哪一種訊號可以證明風險存在、哪一種門檻可以阻止變更往下流。&lt;/p>
&lt;p>當讀者把驗證看成流程，就會自然分出三個層次。第一層是訊號，先知道要看什麼。第二層是演練，先知道要怎麼打。第三層是放行，先知道什麼情況需要暫停或退回。這三層分別對應可觀測性、可靠性驗證與交付平台的責任。&lt;/p>
&lt;h2 id="問題節點">問題節點&lt;/h2>
&lt;p>問題節點先描述失效風險，再描述驗證手段。這樣寫的好處是，讀者能先理解「為什麼要驗證」，再看到「怎麼驗證」，讓工具名回到解題手段的位置。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>節點&lt;/th>
 &lt;th>驗證問題&lt;/th>
 &lt;th>常見訊號&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>CI pipeline&lt;/td>
 &lt;td>測試是否真的攔住回歸、artifact 是否可重播&lt;/td>
 &lt;td>flaky rate、test duration、build queue&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Load test&lt;/td>
 &lt;td>真實負載是否被模型覆蓋、瓶頸是否被提早暴露&lt;/td>
 &lt;td>latency curve、throughput ceiling、error rate&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Fuzz campaign&lt;/td>
 &lt;td>邊界輸入是否能觸發 crash、corpus 是否持續擴充&lt;/td>
 &lt;td>crash reproduction、coverage delta&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Chaos testing&lt;/td>
 &lt;td>依賴失效後系統是否仍能維持服務、回復路徑是否可執行&lt;/td>
 &lt;td>steady state drift、rollback success rate&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>SLO / Error Budget&lt;/td>
 &lt;td>可靠性是否已經被消耗、變更是否還能繼續推進&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">burn rate&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/error-budget/" data-link-title="Error Budget" data-link-desc="說明 SLO 允許的失敗額度如何影響發版與可靠性投入">error budget&lt;/a> remaining&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這張表的責任是提供路由。每一列都要回到服務案例庫，從公開實踐找出真實世界的樣本，把問題節點和失效模式綁在一起。&lt;/p>
&lt;h2 id="案例庫讀法">案例庫讀法&lt;/h2>
&lt;p>案例庫的責任是提供幾種反覆出現的失效與驗證模式。Google、Netflix、Amazon、Stripe 與 Shopify 這五個 T1 案例，分別對應量化門檻、主動故障注入、隔離邊界、交易正確性與峰值準備。&lt;/p>
&lt;p>當讀者遇到某個驗證節點卡住時，可以先問三個問題。第一，現在缺的是訊號還是門檻。第二，失敗是在單一服務內還是在依賴鏈上。第三，這種風險更像回歸、容量、變更還是恢復問題。這三個問題會把讀者導向不同案例頁，也會把讀者導回可觀測性、部署平台或事故處理的交接節點。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>案例&lt;/th>
 &lt;th>主要用途&lt;/th>
 &lt;th>常見回扣節點&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Google&lt;/td>
 &lt;td>把可靠性制度化&lt;/td>
 &lt;td>SLO、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/post-incident-review/" data-link-title="Post-Incident Review" data-link-desc="說明事故後如何完成復盤、學習與改進閉環">post-incident review&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/toil/" data-link-title="Toil" data-link-desc="說明重複、手動、無永久價值的工作如何成為工程治理對象">toil&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Netflix&lt;/td>
 &lt;td>把故障注入制度化&lt;/td>
 &lt;td>chaos、steady state、FIT&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Amazon&lt;/td>
 &lt;td>把隔離與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/blast-radius/" data-link-title="Blast Radius" data-link-desc="說明事故影響面如何估算與隔離">blast radius&lt;/a> 制度化&lt;/td>
 &lt;td>cell、shard、static stability&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Stripe&lt;/td>
 &lt;td>把交易正確性制度化&lt;/td>
 &lt;td>idempotency、canary、migration&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Shopify&lt;/td>
 &lt;td>把峰值準備與演練制度化&lt;/td>
 &lt;td>capacity planning、resiliency matrix&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="vendor--platform-清單">Vendor / Platform 清單&lt;/h2>
&lt;p>實作工具見 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/vendors/" data-link-title="可靠性 Vendor 清單" data-link-desc="規劃 CI、壓測、chaos engineering 與 SLO 工具的服務頁撰寫順序與判準">vendors&lt;/a> — T1 收錄 CI（GitHub Actions / CircleCI）、Load test（k6 / Gatling / JMeter / Locust）、Chaos（Chaos Mesh / LitmusChaos / Gremlin / Toxiproxy）、SLO（Nobl9 / Sloth）共 12 個 vendor 骨架。跟 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/" data-link-title="可靠性服務案例庫" data-link-desc="按服務組織的 SRE 實踐案例庫，累積架構脈絡與工程文化">cases/&lt;/a> 是不同維度（cases 是教學案例來源、vendors 是實作工具）。&lt;/p></description><content:encoded><![CDATA[<p>可靠性驗證模組的核心目標是說明測試如何從單一函式擴展到整個後端系統。語言教材會處理 unit test、table-driven / parameterized test、race / async test 與 integration test；本模組負責 <a href="/blog/backend/knowledge-cards/ci-pipeline/" data-link-title="CI Pipeline" data-link-desc="說明持續整合流程如何在合併前驗證品質與相容性">CI pipeline</a>、壓力測試、fuzz campaign、chaos testing、SLO 與 <a href="/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">Release Gate</a>。</p>
<p>本輪規劃採問題驅動方法、用 SRE 領域 first-class 詞彙（<a href="/blog/backend/04-observability/sli-slo-signal/" data-link-title="4.6 SLI 量測與 SLO 訊號設計" data-link-desc="把可靠性目標的訊號從 metric 端設計好、餵給 6.6 SLO 政策">SLI</a> / <a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">SLO</a> / <a href="/blog/backend/knowledge-cards/error-budget/" data-link-title="Error Budget" data-link-desc="說明 SLO 允許的失敗額度如何影響發版與可靠性投入">Error Budget</a> / Failure Mode / Chaos Hypothesis），把驗證議題拆成問題節點，蒐集公開 SRE 實踐作為服務級案例庫，再把控制面交接到可觀測性、部署平台與事故處理模組落地。</p>
<h2 id="驗證角色">驗證角色</h2>
<p>可靠性驗證的角色是把「系統會不會在真實壓力下失敗」變成可預演的工程問題。這一層不負責寫測試語法，也不負責定義服務功能，而是負責定義哪些失效值得被主動打破、哪一種訊號可以證明風險存在、哪一種門檻可以阻止變更往下流。</p>
<p>當讀者把驗證看成流程，就會自然分出三個層次。第一層是訊號，先知道要看什麼。第二層是演練，先知道要怎麼打。第三層是放行，先知道什麼情況需要暫停或退回。這三層分別對應可觀測性、可靠性驗證與交付平台的責任。</p>
<h2 id="問題節點">問題節點</h2>
<p>問題節點先描述失效風險，再描述驗證手段。這樣寫的好處是，讀者能先理解「為什麼要驗證」，再看到「怎麼驗證」，讓工具名回到解題手段的位置。</p>
<table>
  <thead>
      <tr>
          <th>節點</th>
          <th>驗證問題</th>
          <th>常見訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>CI pipeline</td>
          <td>測試是否真的攔住回歸、artifact 是否可重播</td>
          <td>flaky rate、test duration、build queue</td>
      </tr>
      <tr>
          <td>Load test</td>
          <td>真實負載是否被模型覆蓋、瓶頸是否被提早暴露</td>
          <td>latency curve、throughput ceiling、error rate</td>
      </tr>
      <tr>
          <td>Fuzz campaign</td>
          <td>邊界輸入是否能觸發 crash、corpus 是否持續擴充</td>
          <td>crash reproduction、coverage delta</td>
      </tr>
      <tr>
          <td>Chaos testing</td>
          <td>依賴失效後系統是否仍能維持服務、回復路徑是否可執行</td>
          <td>steady state drift、rollback success rate</td>
      </tr>
      <tr>
          <td>SLO / Error Budget</td>
          <td>可靠性是否已經被消耗、變更是否還能繼續推進</td>
          <td><a href="/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">burn rate</a>、<a href="/blog/backend/knowledge-cards/error-budget/" data-link-title="Error Budget" data-link-desc="說明 SLO 允許的失敗額度如何影響發版與可靠性投入">error budget</a> remaining</td>
      </tr>
  </tbody>
</table>
<p>這張表的責任是提供路由。每一列都要回到服務案例庫，從公開實踐找出真實世界的樣本，把問題節點和失效模式綁在一起。</p>
<h2 id="案例庫讀法">案例庫讀法</h2>
<p>案例庫的責任是提供幾種反覆出現的失效與驗證模式。Google、Netflix、Amazon、Stripe 與 Shopify 這五個 T1 案例，分別對應量化門檻、主動故障注入、隔離邊界、交易正確性與峰值準備。</p>
<p>當讀者遇到某個驗證節點卡住時，可以先問三個問題。第一，現在缺的是訊號還是門檻。第二，失敗是在單一服務內還是在依賴鏈上。第三，這種風險更像回歸、容量、變更還是恢復問題。這三個問題會把讀者導向不同案例頁，也會把讀者導回可觀測性、部署平台或事故處理的交接節點。</p>
<table>
  <thead>
      <tr>
          <th>案例</th>
          <th>主要用途</th>
          <th>常見回扣節點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Google</td>
          <td>把可靠性制度化</td>
          <td>SLO、<a href="/blog/backend/knowledge-cards/post-incident-review/" data-link-title="Post-Incident Review" data-link-desc="說明事故後如何完成復盤、學習與改進閉環">post-incident review</a>、<a href="/blog/backend/knowledge-cards/toil/" data-link-title="Toil" data-link-desc="說明重複、手動、無永久價值的工作如何成為工程治理對象">toil</a></td>
      </tr>
      <tr>
          <td>Netflix</td>
          <td>把故障注入制度化</td>
          <td>chaos、steady state、FIT</td>
      </tr>
      <tr>
          <td>Amazon</td>
          <td>把隔離與 <a href="/blog/backend/knowledge-cards/blast-radius/" data-link-title="Blast Radius" data-link-desc="說明事故影響面如何估算與隔離">blast radius</a> 制度化</td>
          <td>cell、shard、static stability</td>
      </tr>
      <tr>
          <td>Stripe</td>
          <td>把交易正確性制度化</td>
          <td>idempotency、canary、migration</td>
      </tr>
      <tr>
          <td>Shopify</td>
          <td>把峰值準備與演練制度化</td>
          <td>capacity planning、resiliency matrix</td>
      </tr>
  </tbody>
</table>
<h2 id="vendor--platform-清單">Vendor / Platform 清單</h2>
<p>實作工具見 <a href="/blog/backend/06-reliability/vendors/" data-link-title="可靠性 Vendor 清單" data-link-desc="規劃 CI、壓測、chaos engineering 與 SLO 工具的服務頁撰寫順序與判準">vendors</a> — T1 收錄 CI（GitHub Actions / CircleCI）、Load test（k6 / Gatling / JMeter / Locust）、Chaos（Chaos Mesh / LitmusChaos / Gremlin / Toxiproxy）、SLO（Nobl9 / Sloth）共 12 個 vendor 骨架。跟 <a href="/blog/backend/06-reliability/cases/" data-link-title="可靠性服務案例庫" data-link-desc="按服務組織的 SRE 實踐案例庫，累積架構脈絡與工程文化">cases/</a> 是不同維度（cases 是教學案例來源、vendors 是實作工具）。</p>
<p>進入工具比較前，先回到 <a href="/blog/backend/00-service-selection/operations-control-service-selection/" data-link-title="0.12 觀測、可靠性與事故服務選型" data-link-desc="從訊號、驗證與響應三層能力判斷操作控制服務的選型順序">觀測、可靠性與事故服務選型</a> 判斷目前缺的是驗證層能力，還是缺少可觀測性的訊號 baseline 或事故處理的接手流程。可靠性工具選型要以「能否安全驗證失敗」為主軸，CI、load、chaos 或 SLO 工具名稱只是落地選項。</p>
<p>Deep article（工具自身的配置、故障、容量）跟 migration playbook（跨工具遷移流程）的撰寫進度見 <a href="/blog/backend/06-reliability/vendors/" data-link-title="可靠性 Vendor 清單" data-link-desc="規劃 CI、壓測、chaos engineering 與 SLO 工具的服務頁撰寫順序與判準">vendors/</a> 的「內容覆蓋進度」段。</p>
<h2 id="規劃方向">規劃方向</h2>
<p>本輪規劃的核心是把模組從「驗證手段列表」升級成「失敗風險節點 + 服務級案例庫」兩層結構：</p>
<ol>
<li><strong>問題節點先行</strong>：6.1-6.5 主章已建立、補 6.6（SLO/Error Budget）/ 6.7（DR &amp; Rollback Rehearsal）/ 6.8（Release Gate &amp; Change Cadence）/ 6.9（Capacity &amp; Cost）等節點，不綁特定框架。</li>
<li><strong>服務級案例庫</strong>：以公開 SRE 實踐（Google / Netflix / Amazon / Stripe / Shopify 等）作 cases，每個服務一個資料夾、累積架構脈絡與多次驗證案例。</li>
<li><strong>資安驗證是其中一類</strong>：跟 07 的交接點維持，但 07 的紅藍隊框架不外推到本模組 — SRE 自有 Failure Mode / Pre-mortem / FMEA / Chaos Hypothesis 等 first-class 詞彙、不需要藉攻防隱喻表達。</li>
</ol>
<p>不經實作即可推進的理由：可靠性的價值在「失敗模式預判與驗證設計」，這層跟具體框架解耦，SRE 公開素材成熟，符合先建概念層的條件。</p>
<h2 id="模組方法">模組方法</h2>
<p>問題驅動方法的核心是讓案例退到證據角色，讓知識網以失敗風險為主體。</p>
<ol>
<li>先定義驗證環節問題與失敗風險邊界。</li>
<li>再定義判讀訊號（容量門檻、退化曲線、依賴失效模式）與門檻條件。</li>
<li>接著定義交接路由與前置控制面。</li>
<li>最後在問題觸發時引用對應服務的 SRE 案例。</li>
</ol>
<h2 id="模組分工定位">模組分工定位</h2>
<p>本模組提供觀念、判讀與路由。實作細節由對應模組承接，確保概念層與實作層分工清晰。</p>
<ul>
<li><code>backend/04-observability</code>：可觀測性模組，負責訊號定義、SLO 量測與 alert 治理實作。</li>
<li><code>backend/05-deployment-platform</code>：rollout、rollback、流量切換與環境管理實作。</li>
<li><code>backend/07-security-data-protection</code>：權限、稽核與高風險演練約束實作。</li>
<li><code>backend/08-incident-response</code>：事故處理模組，負責事故指揮、分級與復盤的事中事後流程。</li>
</ul>
<h2 id="從章節到實作的-chain">從章節到實作的 chain</h2>
<p>各章節交付三樣：問題節點清單、判讀訊號、控制面 link。判讀完成後沿兩條 chain 進入 implementation：</p>
<ol>
<li><strong>Mechanism chain</strong>：點問題節點表的 <code>[control-name]</code> link 進 <a href="/blog/backend/knowledge-cards/" data-link-title="Knowledge Cards" data-link-desc="用原子化卡片整理後端服務選型前需要理解的 domain knowhow">knowledge-cards</a>、那層展開機制 / 邊界 / context-dependence。例：<code>[circuit-breaker]</code> 的 knowledge-card 是該 control 的 mechanism SSoT。</li>
<li><strong>Delivery chain</strong>：章節「交接路由」欄位指向下游模組，包括可觀測性（訊號 / SLO）、部署平台（rollout / rollback）、資安與資料保護（權限約束）與事故處理（事故閉環）。</li>
</ol>
<p>兩條 chain 走完，控制面交付完整。Implementation 強度取決於兩條 chain 的完成度，章節閱讀本身完成 routing 階段。</p>
<h2 id="跟既有模組的串接">跟既有模組的串接</h2>
<p>本模組是「觀測 → 驗證 → 事故」閉環的中段、承接資安概念判讀、同時餵給事故處理閉環。資安驗證僅是驗證的一個子集、其他多數驗證是容量 / 變更 / 依賴類。</p>
<p><strong>觀測、驗證與事故閉環交接基線</strong>：</p>
<ul>
<li><strong>來自 <a href="/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">可觀測性平台</a></strong>：SLO / SLI 量測 baseline、production 訊號是 chaos hypothesis 與 SLO 政策的依據。沒有可信訊號就沒有可信驗證。</li>
<li><strong>餵給 <a href="/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">可觀測性平台</a></strong>：驗證需求驅動訊號設計 — chaos experiment 需要新 metric、load test 需要新 dashboard、SLO 政策需要新 alert rule。</li>
<li><strong>餵給 <a href="/blog/backend/08-incident-response/" data-link-title="模組八：事故處理與復盤" data-link-desc="用 IR 領域詞彙建問題節點、以服務級案例庫累積事故脈絡，先建概念與案例庫再進實作交接">事故處理與復盤</a></strong>：把事前演練結果作為事中決策素材、game day 暴露的 runbook 缺口直接補進值班與演練能力建設。</li>
<li><strong>來自 <a href="/blog/backend/08-incident-response/" data-link-title="模組八：事故處理與復盤" data-link-desc="用 IR 領域詞彙建問題節點、以服務級案例庫累積事故脈絡，先建概念與案例庫再進實作交接">事故處理與復盤</a></strong>：事故 <a href="/blog/backend/knowledge-cards/post-incident-review/" data-link-title="Post-Incident Review" data-link-desc="說明事故後如何完成復盤、學習與改進閉環">post-incident review</a> action items 回寫成新 chaos / DR 演練題目。</li>
<li><strong>詳細閉環說明</strong>：見 <a href="/blog/backend/08-incident-response/observability-reliability-incident-loop/" data-link-title="8.11 Observability / Reliability / Incident Response 閉環" data-link-desc="把 04 / 06 / 08 三個模組的雙向反饋串成可判讀循環，定義閉環健康度判讀訊號">Observability / Reliability / Incident Response 閉環</a>。</li>
</ul>
<p><strong>07 資安交接基線</strong>：</p>
<ul>
<li>來自 <a href="/blog/backend/07-security-data-protection/data-protection-and-masking-governance/" data-link-title="7.4 資料保護與遮罩治理" data-link-desc="以問題驅動方式整理資料分級、遮罩、匯出與備份治理">7.4 資料保護與遮罩治理</a>：承接資料外送與回復排序的驗證場景。</li>
<li>來自 <a href="/blog/backend/07-security-data-protection/audit-trail-and-accountability-boundary/" data-link-title="7.7 稽核追蹤與責任邊界" data-link-desc="以問題驅動方式整理高風險操作追蹤、可回查與責任切分">7.7 稽核追蹤與責任邊界</a>：承接事件證據完整性與回查演練。</li>
<li>來自紅隊 <a href="/blog/backend/07-security-data-protection/red-team/resource-abuse/" data-link-title="7.R4 資源濫用與可用性破壞" data-link-desc="說明攻擊者如何把合法操作放大成容量壓力或服務退化">7.R4 資源濫用與可用性破壞</a>：承接壓力放大路徑與降級回復驗證。</li>
<li>來自 <a href="/blog/backend/07-security-data-protection/security-and-reliability-shared-controls/" data-link-title="7.23 資安與可靠性的共同控制面" data-link-desc="建立資安與可靠性共同控制面的交集，整合 rollback、containment、degradation 與 evidence">7.23 資安與可靠性的共同控制面</a>：承接 rollback、containment、degradation 共用語意。</li>
</ul>
<h2 id="與語言教材的分工">與語言教材的分工</h2>
<p>語言教材處理測試程式如何寫得可讀、可重現、可定位。Backend reliability 模組處理測試如何在 CI、環境、資料庫、broker、網路與部署流程中被執行。</p>
<h2 id="企業案例補充">企業案例補充</h2>
<p>可靠性案例補充的重點是「驗證機制如何被制度化」。閱讀時先抓它在保護哪一種風險，再對照本模組的驗證節點與放行門檻。</p>
<table>
  <thead>
      <tr>
          <th>企業案例</th>
          <th>主要可靠性選型問題</th>
          <th>優先回讀章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="https://shopify.engineering/four-steps-creating-effective-game-day-tests">Four Steps to Creating Effective Game Day Tests</a></td>
          <td>Game Day 如何從想法變成可執行驗證流程</td>
          <td><a href="/blog/backend/06-reliability/chaos-testing/" data-link-title="6.4 chaos testing" data-link-desc="把故障注入從工具操作升級成可驗證流程：先定義穩態，再按依賴類型設計注入、控制 blast radius 與收集證據">6.4</a>、<a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20</a></td>
      </tr>
      <tr>
          <td><a href="https://shopify.engineering/resiliency-planning-for-high-traffic-events">Resiliency Planning for High-Traffic Events</a></td>
          <td>高流量活動前如何做風險建模與演練</td>
          <td><a href="/blog/backend/06-reliability/capacity-cost/" data-link-title="6.9 容量與成本邊界" data-link-desc="把容量規劃跟成本約束變成驗證輸入">6.9</a>、<a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22</a></td>
      </tr>
      <tr>
          <td><a href="https://aws.amazon.com/builders-library/workload-isolation-using-shuffle-sharding/">Workload isolation using shuffle-sharding</a></td>
          <td>多租戶系統如何把故障影響限制在局部</td>
          <td><a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14</a>、<a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20</a></td>
      </tr>
      <tr>
          <td><a href="https://sre.google/workbook/error-budget-policy/">Google SRE Workbook: Example Error Budget Policy</a></td>
          <td>Error budget 如何直接影響 release 節奏</td>
          <td><a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6</a>、<a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8</a></td>
      </tr>
  </tbody>
</table>
<p>若要延續案例擴充，先從 <a href="/blog/backend/00-service-selection/enterprise-selection-case-atlas/" data-link-title="0.14 企業選型案例圖譜" data-link-desc="蒐集不同類型與不同規模企業的技術選型案例，作為後端選型判讀的跨情境補充。">0.14 企業選型案例圖譜</a> 找到對應規模與產業，再回到本模組決定要補哪一類驗證節點（6.6、6.19、6.20、6.22、6.24）。案例頁與主章的關係是「案例提供壓力樣本，主章提供放行規則」。</p>
<p>產業情境回寫覆蓋 7 個產業，每個產業回寫到 2 個最相關的章節。不同產業的約束類型不同（監管 / 即時互動 / 合規 / 季節峰值 / 多租戶 / 即時交付 / 邊緣可靠性），通用章節覆蓋不到的差異由產業情境段補齊。</p>
<table>
  <thead>
      <tr>
          <th>產業案例類型</th>
          <th>約束類型</th>
          <th>驗證回寫重點</th>
          <th>章節路由</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>FinTech</td>
          <td>監管 + 交易正確性</td>
          <td>error budget 觸發凍結、交易關鍵路徑的 release gate</td>
          <td><a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6</a>、<a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8</a></td>
      </tr>
      <tr>
          <td>Gaming</td>
          <td>即時互動 + 使用者體驗</td>
          <td>高峰事件前穩態定義、規則推送回退與停止條件</td>
          <td><a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22</a>、<a href="/blog/backend/06-reliability/rule-rollout-safety-gate/" data-link-title="6.24 規則推送安全閘門" data-link-desc="把規則、策略與控制面配置推送從部署步驟升級為可靠性 gate，避免小變更在秒級擴散成全網事故。">6.24</a></td>
      </tr>
      <tr>
          <td>Healthcare</td>
          <td>合規 + 臨床安全</td>
          <td>DR rehearsal 節奏、合規約束下的恢復驗證與 readiness</td>
          <td><a href="/blog/backend/06-reliability/dr-rollback-rehearsal/" data-link-title="6.7 DR 演練與 Rollback Rehearsal" data-link-desc="把回復路徑從紙面計畫變成定期可重播、可量測的驗證流程">6.7</a>、<a href="/blog/backend/06-reliability/reliability-readiness-review/" data-link-title="6.19 Reliability Readiness Review" data-link-desc="把上線前、重大變更前與高風險操作前的可靠性準備度變成可檢查門檻">6.19</a></td>
      </tr>
      <tr>
          <td>電商 / 零售</td>
          <td>季節峰值 + 轉換率</td>
          <td>峰值 workload model、容量成本與降級邊界</td>
          <td><a href="/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2</a>、<a href="/blog/backend/06-reliability/capacity-cost/" data-link-title="6.9 容量與成本邊界" data-link-desc="把容量規劃跟成本約束變成驗證輸入">6.9</a></td>
      </tr>
      <tr>
          <td>SaaS / B2B</td>
          <td>多租戶 SLA + 隔離</td>
          <td>依賴 budget 按 SLA 分配、租戶級穩態定義</td>
          <td><a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14</a>、<a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22</a></td>
      </tr>
      <tr>
          <td>串流 / 媒體</td>
          <td>即時交付 + 直播事件</td>
          <td>CDN chaos 與媒體品質 regression</td>
          <td><a href="/blog/backend/06-reliability/chaos-testing/" data-link-title="6.4 chaos testing" data-link-desc="把故障注入從工具操作升級成可驗證流程：先定義穩態，再按依賴類型設計注入、控制 blast radius 與收集證據">6.4</a>、<a href="/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13</a></td>
      </tr>
      <tr>
          <td>IoT / 製造</td>
          <td>邊緣可靠性 + OTA 安全</td>
          <td>firmware rollback 與裝置碎片化 release gate</td>
          <td><a href="/blog/backend/06-reliability/dr-rollback-rehearsal/" data-link-title="6.7 DR 演練與 Rollback Rehearsal" data-link-desc="把回復路徑從紙面計畫變成定期可重播、可量測的驗證流程">6.7</a>、<a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8</a></td>
      </tr>
  </tbody>
</table>
<h2 id="跨語言適配評估">跨語言適配評估</h2>
<p>可靠性驗證使用方式會受語言的測試框架、fixture 生態、並發測試能力、型別系統、fuzz 支援與容器化工具影響。同步 runtime 要測 thread pool、<a href="/blog/backend/knowledge-cards/connection-pool/" data-link-title="Connection Pool" data-link-desc="說明連線池如何限制下游資源並影響服務容量">connection pool</a> 與 <a href="/blog/backend/knowledge-cards/timeout/" data-link-title="Timeout" data-link-desc="說明等待外部操作的時間上限如何保護資源與使用者體驗">timeout</a>；async runtime 要測 event loop blocking、task cancellation 與 <a href="/blog/backend/knowledge-cards/backpressure/" data-link-title="Backpressure" data-link-desc="說明下游處理速度不足時系統如何讓上游依下游能力送出工作">backpressure</a>；動態語言要用 <a href="/blog/backend/knowledge-cards/contract/" data-link-title="Boundary Contract" data-link-desc="說明跨邊界約定如何維持相容與可驗證">contract</a> test 與 runtime validation 補足 schema 風險；強型別語言要把型別安全延伸到外部 payload 與 migration 相容性。</p>
<h2 id="主章規劃">主章規劃</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/ci-pipeline/" data-link-title="6.1 CI pipeline" data-link-desc="CI pipeline 的分層策略、artifact 管理、flaky 治理與 release gate 輸入">6.1 CI pipeline</a></td>
          <td>CI Pipeline</td>
          <td>分層測試、快慢測試與 artifact 管理</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2 Load test</a></td>
          <td>Load Test</td>
          <td>定義 workload、吞吐與延遲基準</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/fuzz-campaign/" data-link-title="6.3 fuzz campaign" data-link-desc="用自動化輸入探索覆蓋未知邊界：target 設計、corpus 管理、crash reproduction 與 CI 整合">6.3 Fuzz campaign</a></td>
          <td>Fuzz Campaign</td>
          <td>建立輸入邊界、corpus 與 crash reproduction</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/chaos-testing/" data-link-title="6.4 chaos testing" data-link-desc="把故障注入從工具操作升級成可驗證流程：先定義穩態，再按依賴類型設計注入、控制 blast radius 與收集證據">6.4 Chaos testing</a></td>
          <td>Chaos Testing</td>
          <td>模擬 broker、DB、network 與節點故障</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/failure-mode-pre-mortem/" data-link-title="6.5 失敗模式預判（Pre-mortem 與 FMEA）" data-link-desc="用 pre-mortem 反向推導失敗路徑、用 FMEA 分類軸評估驗證缺口，把可靠性盲區變成可排序的改善輸入">6.5 失敗模式預判（Pre-mortem 與 FMEA）</a></td>
          <td>Failure Mode Pre-mortem</td>
          <td>用驗證盲區、演練缺口與門檻失真檢查 release 風險用 SRE first-class 詞彙定義失敗模式預判</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6 SLO 與 Error Budget 政策</a></td>
          <td>SLO &amp; Error Budget</td>
          <td>把可靠性目標轉成可驗證量測與凍結條件</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/dr-rollback-rehearsal/" data-link-title="6.7 DR 演練與 Rollback Rehearsal" data-link-desc="把回復路徑從紙面計畫變成定期可重播、可量測的驗證流程">6.7 DR 演練與 Rollback Rehearsal</a></td>
          <td>DR &amp; Rollback Rehearsal</td>
          <td>把回復路徑變成定期可重播流程</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate 與變更節奏</a></td>
          <td>Release Gate</td>
          <td>把驗證、migration、相容性納入放行判準</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/capacity-cost/" data-link-title="6.9 容量與成本邊界" data-link-desc="把容量規劃跟成本約束變成驗證輸入">6.9 容量與成本邊界</a></td>
          <td>Capacity &amp; Cost</td>
          <td>把容量規劃跟成本約束變成驗證輸入</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/contract-testing/" data-link-title="6.10 Contract Testing 與 Schema 演進" data-link-desc="把跨服務 / API / event schema 的隱性期待變成可驗證契約，控制演進相容性">6.10 Contract Testing 與 Schema 演進</a></td>
          <td>Contract Testing</td>
          <td>把跨服務 / API / event schema 契約變成可驗證 artifact</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/migration-safety/" data-link-title="6.11 Migration Safety 與 DB Rollout" data-link-desc="把 schema migration 從一次性事件變成可逆、可漸進的 rollout 流程">6.11 Migration Safety 與 DB Rollout</a></td>
          <td>Migration Safety</td>
          <td>把 schema migration 變成可逆、可漸進的 rollout 流程</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/idempotency-replay/" data-link-title="6.12 Idempotency 與 Replay 驗證" data-link-desc="把重試、重播與冪等性從口頭約定變成可驗證屬性">6.12 Idempotency 與 Replay 驗證</a></td>
          <td>Idempotency &amp; Replay</td>
          <td>把重試 / 重播 / 冪等從口頭約定變成可驗證屬性</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13 Performance Regression Gate</a></td>
          <td>Perf Regression Gate</td>
          <td>把效能 baseline 從一次性壓測變成持續 release gate</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14 Dependency Reliability Budget</a></td>
          <td>Dependency Budget</td>
          <td>把內外依賴可靠性納入 SLO 計算與設計約束</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/environment-parity/" data-link-title="6.15 Environment Parity 與漂移控制" data-link-desc="把 staging / preprod / prod 之間的差異視為一級風險，按漂移來源分類偵測與治理">6.15 Environment Parity 與漂移控制</a></td>
          <td>Environment Parity</td>
          <td>把 staging / preprod / prod 差異作為一級風險治理</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/test-data-management/" data-link-title="6.16 Test Data Management" data-link-desc="把 fixture / seed / production-like data 作為跨模組共用 artifact，治理資料層次、遮罩策略與可重現性">6.16 Test Data Management</a></td>
          <td>Test Data Management</td>
          <td>把 fixture / seed / production-like data 作為跨模組共用 artifact</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/feature-flag-governance/" data-link-title="6.17 Feature Flag Governance" data-link-desc="把 feature flag 從上線開關升級為有角色分類、lifecycle 管理與 debt 治理的 runtime artifact">6.17 Feature Flag Governance</a></td>
          <td>Feature Flag Governance</td>
          <td>把 feature flag 從上線工具升級為有 lifecycle / debt 治理的 artifact</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/reliability-metrics-governance/" data-link-title="6.18 Reliability Metrics Governance" data-link-desc="DORA / SPACE 指標的選用、量測陷阱、anti-gaming 與團隊階段適配">6.18 Reliability Metrics Governance</a></td>
          <td>Reliability Metrics</td>
          <td>DORA / SPACE / CFR 等可靠性指標的選用、量測與治理</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/reliability-readiness-review/" data-link-title="6.19 Reliability Readiness Review" data-link-desc="把上線前、重大變更前與高風險操作前的可靠性準備度變成可檢查門檻">6.19 Reliability Readiness Review</a></td>
          <td>Reliability Readiness Review</td>
          <td>把上線前、重大變更前與高風險操作前的可靠性準備度變成可檢查門檻</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 Experiment Safety Boundary</a></td>
          <td>Experiment Safety Boundary</td>
          <td>定義 chaos、load test、DR drill 的 blast radius、停止條件與權限約束</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 Reliability Debt Backlog</a></td>
          <td>Reliability Debt Backlog</td>
          <td>把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22 Steady State Definition</a></td>
          <td>Steady State Definition</td>
          <td>在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23 Verification Evidence Handoff</a></td>
          <td>Verification Evidence Handoff</td>
          <td>把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/rule-rollout-safety-gate/" data-link-title="6.24 規則推送安全閘門" data-link-desc="把規則、策略與控制面配置推送從部署步驟升級為可靠性 gate，避免小變更在秒級擴散成全網事故。">6.24 規則推送安全閘門</a></td>
          <td>Rule Rollout Safety Gate</td>
          <td>把規則、策略與控制面配置推送變更納入高擴散風險 gate</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/provider-dependency-release-gate/" data-link-title="6.25 Provider Dependency Release Gate 實作示範" data-link-desc="以 payment provider 變更示範 release gate 如何結合 evidence、stop condition 與 rollback window。">6.25 Provider Dependency Release Gate</a></td>
          <td>Provider Dependency Release Gate 實作示範</td>
          <td>以 payment provider 變更示範 gate、stop condition 與 <a href="/blog/backend/knowledge-cards/rollback-window/" data-link-title="Rollback Window" data-link-desc="說明變更進入 production 後還能用哪種方式回退或改路線的時間與條件">rollback window</a> 的實作交接</td>
      </tr>
  </tbody>
</table>
<blockquote>
<p>註：6.1-6.25 已完成概念層與第一篇實作示範正文，案例庫可支援 SLO、readiness、experiment boundary、evidence handoff 與 release gate 實作路由。後續工作重點是案例深挖與主章回寫密度，不是章節補齊。</p></blockquote>
<h2 id="個案前拓展空間">個案前拓展空間</h2>
<p>個案前拓展的責任是先建立驗證判準，再讓服務案例成為證據。可靠性驗證適合補「怎麼安全地驗證失敗」這類跨服務流程，不適合先把 Google / Netflix / Amazon 的故事直接展開。</p>
<table>
  <thead>
      <tr>
          <th>拓展方向</th>
          <th>補充理由</th>
          <th>先放位置</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Reliability Readiness Review</td>
          <td>服務進入 production 前需要有可檢查的可靠性門檻</td>
          <td>6.19</td>
      </tr>
      <tr>
          <td>Experiment Safety Boundary</td>
          <td>故障注入與壓測需要明確 blast radius 與停止條件</td>
          <td>6.20</td>
      </tr>
      <tr>
          <td>Reliability Debt Backlog</td>
          <td>復盤與演練缺口需要形成可排序的改善 backlog</td>
          <td>6.21</td>
      </tr>
      <tr>
          <td>Steady State Definition</td>
          <td>chaos 與 DR drill 需要先知道什麼狀態算穩定</td>
          <td>6.22</td>
      </tr>
  </tbody>
</table>
<p>本輪先完成其中三個前置章節：Reliability Readiness Review、Experiment Safety Boundary 與 Steady State Definition，並補強 6.6 SLO / Error Budget 政策。服務案例完成後，若教訓是「上線前準備不足」，回寫 Reliability Readiness Review；若是「實驗本身造成過大影響」，回寫 Experiment Safety Boundary；若是「反覆事故沒有被工程化」，回寫 Reliability Debt Backlog；若是「chaos 沒有穩態定義」，回寫 Steady State Definition。</p>
<h2 id="後續深化方向">後續深化方向</h2>
<p>06 後續深化以「多事件案例鏈、驗證證據欄位統一、事故路由回寫」為主。可靠性驗證承接 04 的訊號可信度，並把結果穩定交給 08 的 incident 決策流程。</p>
<table>
  <thead>
      <tr>
          <th>深化方向</th>
          <th>主要責任</th>
          <th>回寫路由</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>多事件案例鏈</td>
          <td>同服務補第二、第三事件，提升 longitudinal 判讀</td>
          <td><a href="/blog/backend/06-reliability/cases/" data-link-title="可靠性服務案例庫" data-link-desc="按服務組織的 SRE 實踐案例庫，累積架構脈絡與工程文化">cases/</a></td>
      </tr>
      <tr>
          <td>證據欄位統一</td>
          <td>把 SLO / chaos / rollout 證據變成同一決策格式</td>
          <td><a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23</a>、<a href="/blog/backend/08-incident-response/incident-decision-log/" data-link-title="8.19 Incident Decision Log" data-link-desc="把事中假設、決策、證據、回退條件與責任人留下可復盤紀錄">8.19</a></td>
      </tr>
      <tr>
          <td>風險回寫治理</td>
          <td>把 repeated incident 與手動補救回寫 backlog</td>
          <td><a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21</a>、<a href="/blog/backend/08-incident-response/incident-evidence-write-back/" data-link-title="8.22 Incident Evidence Write-back" data-link-desc="把事故證據、決策與復盤結論回寫到 observability、reliability 與 runbook">8.22</a></td>
      </tr>
  </tbody>
</table>
<h2 id="實作探討入口">實作探討入口</h2>
<p>進入實作層時，06 建議先做一條最小 release gate：同一個變更同時具備 <code>SLO 狀態、readiness 結論、experiment 證據、rollback 條件</code> 四欄，並寫入 <a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23</a> 供 <a href="/blog/backend/08-incident-response/incident-decision-log/" data-link-title="8.19 Incident Decision Log" data-link-desc="把事中假設、決策、證據、回退條件與責任人留下可復盤紀錄">8.19</a> 直接調用。</p>
<p>首篇示範已完成： <a href="/blog/backend/06-reliability/provider-dependency-release-gate/" data-link-title="6.25 Provider Dependency Release Gate 實作示範" data-link-desc="以 payment provider 變更示範 release gate 如何結合 evidence、stop condition 與 rollback window。">6.25 Provider Dependency Release Gate 實作示範</a>。</p>
<p>完成條件是每篇都能回答四件事：可靠性目標、驗證訊號、停止或凍結條件、事故或發布路由。這樣可靠性章節才會成為「觀測 → 驗證 → 事故」閉環的中段，而不是測試工具清單。</p>
<h2 id="服務案例庫規劃">服務案例庫規劃</h2>
<p>服務作為案例單位、累積架構脈絡與多次驗證實踐。每個服務一個資料夾、收錄該服務的 SRE 實踐、failure mode 與 chaos / DR 案例。資料夾位置：<code>content/backend/06-reliability/cases/{vendor-service}/</code>。</p>
<h3 id="t1必寫sre-教學標竿">T1（必寫、SRE 教學標竿）</h3>
<table>
  <thead>
      <tr>
          <th>服務</th>
          <th>教學重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/google/" data-link-title="Google" data-link-desc="Google SRE 實踐原典：SLI / SLO / Error Budget / Postmortem 文化">google</a></td>
          <td>SRE Book 原典 / SLI-SLO / <a href="/blog/backend/knowledge-cards/post-incident-review/" data-link-title="Post-Incident Review" data-link-desc="說明事故後如何完成復盤、學習與改進閉環">post-incident review</a> culture / error budget</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/netflix/" data-link-title="Netflix" data-link-desc="Netflix Chaos Engineering 起源：Simian Army / FIT / 規模化故障注入">netflix</a></td>
          <td>Chaos Monkey / Simian Army / FIT 故障注入工具鏈</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/amazon/" data-link-title="Amazon" data-link-desc="Amazon Cell-based Architecture / Shuffle Sharding / Blast Radius 設計">amazon</a></td>
          <td>Cell-based architecture / shuffle sharding / blast radius</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/stripe/" data-link-title="Stripe" data-link-desc="Stripe Deploy Strategy / Game Day / Idempotency 實踐">stripe</a></td>
          <td>Deploy strategy / Game day / canary 與 idempotency</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/shopify/" data-link-title="Shopify" data-link-desc="Shopify BFCM Scaling / Pod-based Isolation / Capacity Planning">shopify</a></td>
          <td>BFCM scaling / pod-based isolation / capacity planning</td>
      </tr>
  </tbody>
</table>
<h3 id="t2補不同視角">T2（補不同視角）</h3>
<table>
  <thead>
      <tr>
          <th>服務</th>
          <th>教學重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/linkedin/" data-link-title="LinkedIn" data-link-desc="LinkedIn Capacity Planning 與 On-call 結構">linkedin</a></td>
          <td>Capacity planning / <a href="/blog/backend/knowledge-cards/on-call/" data-link-title="On-Call" data-link-desc="說明值班制度如何承接告警、事故分級與升級流程">on-call</a> structure</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/honeycomb/" data-link-title="Honeycomb" data-link-desc="Honeycomb Observability-driven SRE 與 SLO 實作">honeycomb</a></td>
          <td>Observability-driven SRE / SLO 實作</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/08-incident-response/cases/cloudflare/" data-link-title="Cloudflare" data-link-desc="Cloudflare 全球 edge 事故時間線與架構脈絡">cloudflare</a></td>
          <td>Edge reliability engineering / 公開實踐（住於 08）</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/microsoft/" data-link-title="Microsoft / Azure SRE" data-link-desc="Microsoft Azure SRE Practices 與 Resilience Patterns">microsoft</a></td>
          <td>Azure SRE / Resilience patterns</td>
      </tr>
  </tbody>
</table>
<h3 id="t3補完">T3（補完）</h3>
<table>
  <thead>
      <tr>
          <th>服務</th>
          <th>教學重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/spotify/" data-link-title="Spotify" data-link-desc="Spotify Chaos Engineering 與 Squad-based SRE">spotify</a></td>
          <td>Squad-based SRE / Backstage</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/pinterest/" data-link-title="Pinterest" data-link-desc="Pinterest Capacity Planning 與儲存架構可靠性">pinterest</a></td>
          <td>Storage capacity / cache reliability</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/meta/" data-link-title="Meta / Facebook" data-link-desc="Meta Reliability Engineering 與超大規模事故學習">meta</a></td>
          <td>2021-10 BGP / Region failover / cell arch</td>
      </tr>
  </tbody>
</table>
<h2 id="模組完成狀態">模組完成狀態</h2>
<p>主章 6.1-6.25 已完成首輪正文，服務案例庫第一批正文已補齊（T1：Google / Netflix / Amazon / Stripe / Shopify；T2/T3：LinkedIn / Honeycomb / Microsoft / Spotify / Pinterest / Meta）。目前重點從「補章節骨架」轉為「補案例深度與跨章節回寫」。</p>
<p>案例正文入口見 <a href="/blog/backend/06-reliability/cases/" data-link-title="可靠性服務案例庫" data-link-desc="按服務組織的 SRE 實踐案例庫，累積架構脈絡與工程文化">可靠性案例庫</a>。每篇案例至少要能回寫一個章節判準（例如 6.6、6.19、6.20、6.22、6.23、6.24），避免案例只停留在事件敘事。</p>
<p>第二批案例深挖已補 Google 與 Netflix 的第二篇正文： <a href="/blog/backend/06-reliability/cases/google/postmortem-action-item-closure-governance/" data-link-title="Google：Postmortem Action Item Closure 治理" data-link-desc="把 blameless postmortem 從會議文件變成可追蹤的可靠性治理機制：action item 分級、完成條件與回寫節奏。">Google Postmortem Closure 治理</a> 與 <a href="/blog/backend/06-reliability/cases/netflix/chaos-monkey-business-hours-guardrails/" data-link-title="Netflix：Business-Hours Chaos 與 Guardrails" data-link-desc="Chaos Monkey 為何刻意在 business hours 執行：把即時應變能力納入驗證，並用 guardrails 限制實驗風險。">Netflix Business-Hours Chaos Guardrails</a>。兩者分別對應 <code>6.21 / 8.5 / 8.22</code> 與 <code>6.19 / 6.20 / 6.22 / 8.6</code> 的制度化回寫。</p>
<p>深挖批次 B 已補 Google 第三篇制度案例： <a href="/blog/backend/06-reliability/cases/google/toil-budget-and-automation-investment-policy/" data-link-title="Google：Toil Budget 與 Automation 投資政策" data-link-desc="把 toil 從感受問題轉成預算問題：用時間配比與自動化回報機制，避免 on-call 壓力長期侵蝕可靠性工程。">Google Toil Budget 與 Automation 投資政策</a>。這篇把 toil ratio 直接接到 <code>6.8 / 6.21 / 8.22</code>，補齊「值班壓力 → 工程投資 → release gate」的決策鏈。</p>
<p>第三批案例補強已補 <code>Netflix</code> 第三篇： <a href="/blog/backend/06-reliability/cases/netflix/fit-failure-injection-evidence-handoff/" data-link-title="Netflix：FIT 證據交接與 Release Gate 回寫" data-link-desc="用 Failure Injection Testing 產出的證據直接驅動 release gate：把實驗結果轉成可放行、可凍結、可回退的決策欄位。">FIT 證據交接與 Release Gate 回寫</a>。這篇把故障注入結果直接接到 <code>6.23 / 6.24 / 8.19 / 8.22</code>，補齊「實驗結果 → 放行決策 → 事故調用」的鏈路。</p>
<h2 id="case-first-第四批8-章-stage-2-擴充">Case-First 第四批：8 章 stage 2 擴充</h2>
<p>依 <a href="/blog/posts/case-first--agent-team-review%E6%95%99%E5%AD%B8%E5%85%A7%E5%AE%B9%E7%9A%84%E7%94%9F%E7%94%A2%E6%B5%81%E7%A8%8B/" data-link-title="Case-First &#43; Agent Team Review：教學內容的生產流程" data-link-desc="Case-first &#43; agent team review 的教學內容生產流程：讀案例庫抽 findings、專責 reviewer 平行審查、polish pass 收系統性殘留。防止通用 best practice 被誤包裝成案例揭露。">Case-First + Agent Team Review 流程</a> 完成 8 個章節的 case-driven 擴充（commit 3c33ea9 / 41c0101）、覆蓋全部 15 個 content case：</p>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>擴充內容</th>
          <th>Case 對應</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release-gate</a></td>
          <td>變更分層 + Release Gate 政策、交易類變更的 gate 設計</td>
          <td>MS1 / G2 / S1</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14 dependency-reliability-budget</a></td>
          <td>失效局部化、跨區故障與回復順序、跨團隊 reliability 契約</td>
          <td>A1 / M1 / SP1</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 reliability-debt-backlog</a></td>
          <td>Action Item 分級跟 Release Gate 綁定、Toil Budget 預算治理</td>
          <td>G2 / G3</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/capacity-cost/" data-link-title="6.9 容量與成本邊界" data-link-desc="把容量規劃跟成本約束變成驗證輸入">6.9 capacity-cost</a></td>
          <td>高峰型容量治理、容量值班分層協同、快取容量特殊性</td>
          <td>H1 / L1 / P1</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/migration-safety/" data-link-title="6.11 Migration Safety 與 DB Rollout" data-link-desc="把 schema migration 從一次性事件變成可逆、可漸進的 rollout 流程">6.11 migration-safety</a></td>
          <td>交易類 migration 的特殊性</td>
          <td>S1</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/idempotency-replay/" data-link-title="6.12 Idempotency 與 Replay 驗證" data-link-desc="把重試、重播與冪等性從口頭約定變成可驗證屬性">6.12 idempotency-replay</a></td>
          <td>支付類 Idempotency 的設計約束</td>
          <td>S1</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6 slo-error-budget</a></td>
          <td>Error Budget 三對齊跟 Release Gating、Burn Rate 雙窗監控</td>
          <td>G1 / HC1</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 experiment-safety-boundary</a></td>
          <td>案例對照：Chaos / FIT 的安全邊界設計</td>
          <td>N1 / N2 / N3</td>
      </tr>
  </tbody>
</table>
<p>擴充紀律對應 <a href="/blog/posts/case-first--agent-team-review%E6%95%99%E5%AD%B8%E5%85%A7%E5%AE%B9%E7%9A%84%E7%94%9F%E7%94%A2%E6%B5%81%E7%A8%8B/" data-link-title="Case-First &#43; Agent Team Review：教學內容的生產流程" data-link-desc="Case-first &#43; agent team review 的教學內容生產流程：讀案例庫抽 findings、專責 reviewer 平行審查、polish pass 收系統性殘留。防止通用 best practice 被誤包裝成案例揭露。">Case-First Module Workflow</a> 的五階段流程、用 agent team review 三維度（寫作規範 / 案例引用 / 跨章一致性）驗證、case fidelity 達 88%。</p>
<h2 id="下一輪推演大綱">下一輪推演大綱</h2>
<table>
  <thead>
      <tr>
          <th>階段</th>
          <th>產出</th>
          <th>責任</th>
          <th>回寫位置</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>1</td>
          <td>案例深挖批次 A</td>
          <td>針對 T1 案例補第二篇以上正文，強化同一服務的多次驗證脈絡</td>
          <td><code>cases/google/</code>、<code>cases/netflix/</code></td>
      </tr>
      <tr>
          <td>2</td>
          <td>案例深挖批次 B</td>
          <td>針對 T2/T3 案例補跨規模對照，避免只描述單一事件</td>
          <td><code>cases/{service}/</code></td>
      </tr>
      <tr>
          <td>3</td>
          <td>章節回寫補強</td>
          <td>把案例中的 policy、gate、readiness 與 evidence 直接回寫主章段落</td>
          <td><code>6.6</code>、<code>6.19</code>、<code>6.20</code>、<code>6.22</code>、<code>6.23</code></td>
      </tr>
      <tr>
          <td>4</td>
          <td>跨模組路由校正</td>
          <td>補齊 04/05/07/08 的交接連結，讓讀者可從案例直接跳到對應控制面</td>
          <td>各章節「交接路由」段</td>
      </tr>
  </tbody>
</table>
<p>推演資產化的完成條件是讓讀者能從一個失敗風險出發，找到驗證節點、服務 case 與回寫章節。完成後可靠性模組才進入穩定維護狀態。</p>
<h3 id="本輪全面推進2026-06-23">本輪全面推進（2026-06-23）</h3>
<p>主章 6.1-6.25 全部從骨架擴充到完整內容（最小 75 行、中位 113 行、最大 176 行），覆蓋概念定位、判讀訊號、案例回寫與交接路由。案例庫補齊 T1/T2/T3 第二批正文共 9 篇（Amazon A2 / Stripe S2 / Shopify H2 / LinkedIn L2 / Meta M2 / Honeycomb HC2 / Microsoft MS2 / Spotify SP2 / Pinterest P2），11 個 vendor 各有 2+ 篇案例。Vendor deep article 新增 4 篇（k6 / Chaos Mesh / Sloth / GitHub Actions）。產業情境回寫 3 組（FinTech → 6.6+6.8 / Gaming → 6.22+6.24 / Healthcare → 6.7+6.19）。經過三輪多輪審查（寫作規範 / cadence 同質化 / steelman reality test）修法。</p>
<p>目前模組處於穩定維護狀態。剩餘 backlog：8 個 vendor 的 deep article（CircleCI / Gatling / JMeter / Locust / LitmusChaos / Gremlin / Toxiproxy / Nobl9）、08 模組的 06 反向引用補齊、判讀訊號表格補行動建議欄。</p>
<h2 id="tripwire">Tripwire</h2>
<ul>
<li>寫 T1 服務第 3 個時、若 case 之間無共通分類軸 → 改用單服務獨立檔，不開資料夾。</li>
<li>寫到第 9 主章發現章節覆蓋 60%+ → 軸線過於相似、合併或重切。</li>
<li>進服務實作模組時 routing chain 走不通 → 回頭補對應主章。</li>
</ul>
<h2 id="既有可引用卡片">既有可引用卡片</h2>
<ul>
<li><a href="/blog/backend/knowledge-cards/load-test/" data-link-title="Load Test" data-link-desc="說明在預期流量下驗證容量、延遲與降級策略的測試">load test</a></li>
<li><a href="/blog/backend/knowledge-cards/chaos-test/" data-link-title="Chaos Test" data-link-desc="說明透過受控故障注入驗證系統在異常條件下的恢復能力">chaos test</a></li>
<li><a href="/blog/backend/knowledge-cards/fuzz-test/" data-link-title="Fuzz Test" data-link-desc="說明用隨機與變異輸入驗證解析器與邊界處理健壯性">fuzz test</a></li>
<li><a href="/blog/backend/knowledge-cards/idempotency/" data-link-title="Idempotency" data-link-desc="說明同一操作執行多次時如何保持結果一致">idempotency</a></li>
<li><a href="/blog/backend/knowledge-cards/schema-migration/" data-link-title="Schema Migration" data-link-desc="說明資料庫結構如何隨應用程式版本安全演進">schema migration</a></li>
</ul>
]]></content:encoded></item><item><title>6.7 DR 演練與 Rollback Rehearsal</title><link>https://tarrragon.github.io/blog/backend/06-reliability/dr-rollback-rehearsal/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/dr-rollback-rehearsal/</guid><description>&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>DR 演練與 rollback rehearsal 是把回復能力從「有計畫」變成「經過驗證」的工具。DR 關心的是系統在災難後能不能回來，rollback rehearsal 關心的是變更失敗時能不能退回安全狀態。兩者的責任是把回復路徑變成可驗證流程。&lt;/p>
&lt;p>這個節點先處理路徑，再處理速度。先確認資料能不能回來、服務能不能切回來、回復後會不會再掉回去，然後才談 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/rto/" data-link-title="RTO" data-link-desc="說明恢復時間目標如何約束事故回復策略">RTO&lt;/a> / &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/rpo/" data-link-title="RPO" data-link-desc="說明恢復點目標如何定義可接受資料損失範圍">RPO&lt;/a>。這樣讀，會比直接背指標更接近真實系統的恢復成本。&lt;/p>
&lt;h2 id="核心判讀">核心判讀&lt;/h2>
&lt;p>DR 的責任是證明回復路徑存在，而且可實際走通。只要 backup 還沒被 restore 驗證過，它就只是備份，不是復原能力。只要 failover config 沒跟 production 對齊，它就只是文件，不是操作路由。&lt;/p>
&lt;p>rollback rehearsal 的責任是把失敗變更的退路先跑過。當 deployment 出現問題時，團隊需要知道自己是能回退、必須 roll forward，還是必須先止血再處理資料。這個判斷來自平常 rehearsal 的累積，臨場才不會陷入猜測。&lt;/p>
&lt;h2 id="rollback-vs-roll-forward-的判斷條件">Rollback vs Roll-forward 的判斷條件&lt;/h2>
&lt;p>變更失敗時的第一個決策是退回還是往前修。這個判斷取決於變更是否可逆，以及新資料是否已經依賴新版結構。&lt;/p>
&lt;p>rollback 的前提是變更可逆：schema 仍向下相容、feature flag 可關閉、routing 可切回前一版。當這些條件成立時，rollback 通常比 roll-forward 更快收斂，因為退回的行為已經被驗證過（它就是前一版的 production 狀態）。&lt;/p>
&lt;p>roll-forward 的前提是修復比退版快且安全。當新版已經寫入不可回退的資料（新欄位被使用、新格式被下游消費、交易已在新路徑完成），退版會造成資料遺失或不一致，此時 roll-forward 是被迫的選擇，不是偏好。&lt;/p>
&lt;p>兩者之間存在灰色地帶：schema migration 已執行但流量尚未切換、feature flag 已開啟但影響範圍有限。這類情境需要事前在 rehearsal 中定義判斷條件，而不是事中討論。第三種常見路徑是先 rollback 止血（降低 customer impact），確認穩定後再推出修復版 roll-forward。這個 hybrid 策略的前提是 rollback 安全且修復方案已知。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/" data-link-title="Stripe：Idempotency 與零停機遷移的交易安全設計" data-link-desc="把 API 重試與資料遷移放在同一套安全模型，維持支付交易的一致結果。">Stripe 的 expand/contract migration 模型&lt;/a> 說明交易系統的 rollback 需要同時處理 schema 相容與冪等重播。當 idempotency key 與業務操作邊界一致時，rollback 後的重試才能產生正確結果。這個案例揭露的判讀條件是：rollback 安全性不只看部署層，還要看資料語義層。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>條件&lt;/th>
 &lt;th>傾向 rollback&lt;/th>
 &lt;th>傾向 roll-forward&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Schema 相容性&lt;/td>
 &lt;td>舊版可讀新版資料、無破壞性變更&lt;/td>
 &lt;td>新欄位已被寫入、舊版無法解析&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>資料狀態&lt;/td>
 &lt;td>新版尚未產生不可回退的資料&lt;/td>
 &lt;td>交易、訂單或事件已在新路徑完成&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>修復時間&lt;/td>
 &lt;td>問題根因不明、修復時間不可預測&lt;/td>
 &lt;td>根因明確、修復可在分鐘內完成&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Feature flag&lt;/td>
 &lt;td>flag 可關閉且影響範圍已知&lt;/td>
 &lt;td>flag 關閉會觸發另一組問題&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>下游依賴&lt;/td>
 &lt;td>下游未消費新版輸出&lt;/td>
 &lt;td>下游已開始處理新格式資料&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="restore-驗證">Restore 驗證&lt;/h2>
&lt;p>備份的價值在還原時才能被證明。restore drill 的責任是證明備份能在需要時變成可用的服務狀態。&lt;/p>
&lt;p>restore 驗證分三個層次，每一層回答不同的問題。&lt;/p>
&lt;p>&lt;strong>資料完整性&lt;/strong>：還原後的資料是否完整。驗證手段包含 row count 比對、checksum 校驗、reconciliation query。這一層的失敗模式通常是 backup 時段選擇不當（跨越 batch job 執行期）或 incremental backup 鏈條斷裂。&lt;/p>
&lt;p>&lt;strong>服務可用性&lt;/strong>：還原後的系統是否能正常回應。資料完整不代表服務可用 — config、secret、schema version、connection pool 設定都可能在 restore 後失效。這一層需要在 restore 完成後跑 smoke test 與 health check，確認服務能處理請求。&lt;/p>
&lt;p>&lt;strong>恢復時間量測&lt;/strong>：實際 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/rto/" data-link-title="RTO" data-link-desc="說明恢復時間目標如何約束事故回復策略">RTO&lt;/a> 是否符合承諾。如果承諾 4 小時 RTO 但 restore 本身需要 6 小時，這個承諾就是空的。量測要包含從決策啟動到服務恢復的完整時間，不只是資料還原時間。Roblox 2021 的 73 小時 outage 說明 recovery 不是切回流量就結束 — 資料一致性重建、快取預熱與依賴服務的啟動順序都會拉長實際恢復時間。&lt;/p>
&lt;h2 id="演練類型">演練類型&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>類型&lt;/th>
 &lt;th>目的&lt;/th>
 &lt;th>典型輸出&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>tabletop&lt;/td>
 &lt;td>檢查決策路由與角色分工&lt;/td>
 &lt;td>角色清單、決策順序、通訊模板&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>partial failover&lt;/td>
 &lt;td>驗證局部區域或子系統能否切換&lt;/td>
 &lt;td>切換結果、回復時間、手動步驟&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>full region failover&lt;/td>
 &lt;td>驗證整個區域是否能從災難中回來&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/rto/" data-link-title="RTO" data-link-desc="說明恢復時間目標如何約束事故回復策略">RTO&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/rpo/" data-link-title="RPO" data-link-desc="說明恢復點目標如何定義可接受資料損失範圍">RPO&lt;/a>、資料一致性檢查&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>data restore drill&lt;/td>
 &lt;td>驗證備份是否能真的還原資料&lt;/td>
 &lt;td>restore log、校驗結果、缺口清單&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這些演練的共同點是：演練本身要留下證據。沒有輸出，就沒有辦法判斷回復能力到底有沒有被建立。&lt;/p></description><content:encoded><![CDATA[<h2 id="概念定位">概念定位</h2>
<p>DR 演練與 rollback rehearsal 是把回復能力從「有計畫」變成「經過驗證」的工具。DR 關心的是系統在災難後能不能回來，rollback rehearsal 關心的是變更失敗時能不能退回安全狀態。兩者的責任是把回復路徑變成可驗證流程。</p>
<p>這個節點先處理路徑，再處理速度。先確認資料能不能回來、服務能不能切回來、回復後會不會再掉回去，然後才談 <a href="/blog/backend/knowledge-cards/rto/" data-link-title="RTO" data-link-desc="說明恢復時間目標如何約束事故回復策略">RTO</a> / <a href="/blog/backend/knowledge-cards/rpo/" data-link-title="RPO" data-link-desc="說明恢復點目標如何定義可接受資料損失範圍">RPO</a>。這樣讀，會比直接背指標更接近真實系統的恢復成本。</p>
<h2 id="核心判讀">核心判讀</h2>
<p>DR 的責任是證明回復路徑存在，而且可實際走通。只要 backup 還沒被 restore 驗證過，它就只是備份，不是復原能力。只要 failover config 沒跟 production 對齊，它就只是文件，不是操作路由。</p>
<p>rollback rehearsal 的責任是把失敗變更的退路先跑過。當 deployment 出現問題時，團隊需要知道自己是能回退、必須 roll forward，還是必須先止血再處理資料。這個判斷來自平常 rehearsal 的累積，臨場才不會陷入猜測。</p>
<h2 id="rollback-vs-roll-forward-的判斷條件">Rollback vs Roll-forward 的判斷條件</h2>
<p>變更失敗時的第一個決策是退回還是往前修。這個判斷取決於變更是否可逆，以及新資料是否已經依賴新版結構。</p>
<p>rollback 的前提是變更可逆：schema 仍向下相容、feature flag 可關閉、routing 可切回前一版。當這些條件成立時，rollback 通常比 roll-forward 更快收斂，因為退回的行為已經被驗證過（它就是前一版的 production 狀態）。</p>
<p>roll-forward 的前提是修復比退版快且安全。當新版已經寫入不可回退的資料（新欄位被使用、新格式被下游消費、交易已在新路徑完成），退版會造成資料遺失或不一致，此時 roll-forward 是被迫的選擇，不是偏好。</p>
<p>兩者之間存在灰色地帶：schema migration 已執行但流量尚未切換、feature flag 已開啟但影響範圍有限。這類情境需要事前在 rehearsal 中定義判斷條件，而不是事中討論。第三種常見路徑是先 rollback 止血（降低 customer impact），確認穩定後再推出修復版 roll-forward。這個 hybrid 策略的前提是 rollback 安全且修復方案已知。</p>
<p><a href="/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/" data-link-title="Stripe：Idempotency 與零停機遷移的交易安全設計" data-link-desc="把 API 重試與資料遷移放在同一套安全模型，維持支付交易的一致結果。">Stripe 的 expand/contract migration 模型</a> 說明交易系統的 rollback 需要同時處理 schema 相容與冪等重播。當 idempotency key 與業務操作邊界一致時，rollback 後的重試才能產生正確結果。這個案例揭露的判讀條件是：rollback 安全性不只看部署層，還要看資料語義層。</p>
<table>
  <thead>
      <tr>
          <th>條件</th>
          <th>傾向 rollback</th>
          <th>傾向 roll-forward</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Schema 相容性</td>
          <td>舊版可讀新版資料、無破壞性變更</td>
          <td>新欄位已被寫入、舊版無法解析</td>
      </tr>
      <tr>
          <td>資料狀態</td>
          <td>新版尚未產生不可回退的資料</td>
          <td>交易、訂單或事件已在新路徑完成</td>
      </tr>
      <tr>
          <td>修復時間</td>
          <td>問題根因不明、修復時間不可預測</td>
          <td>根因明確、修復可在分鐘內完成</td>
      </tr>
      <tr>
          <td>Feature flag</td>
          <td>flag 可關閉且影響範圍已知</td>
          <td>flag 關閉會觸發另一組問題</td>
      </tr>
      <tr>
          <td>下游依賴</td>
          <td>下游未消費新版輸出</td>
          <td>下游已開始處理新格式資料</td>
      </tr>
  </tbody>
</table>
<h2 id="restore-驗證">Restore 驗證</h2>
<p>備份的價值在還原時才能被證明。restore drill 的責任是證明備份能在需要時變成可用的服務狀態。</p>
<p>restore 驗證分三個層次，每一層回答不同的問題。</p>
<p><strong>資料完整性</strong>：還原後的資料是否完整。驗證手段包含 row count 比對、checksum 校驗、reconciliation query。這一層的失敗模式通常是 backup 時段選擇不當（跨越 batch job 執行期）或 incremental backup 鏈條斷裂。</p>
<p><strong>服務可用性</strong>：還原後的系統是否能正常回應。資料完整不代表服務可用 — config、secret、schema version、connection pool 設定都可能在 restore 後失效。這一層需要在 restore 完成後跑 smoke test 與 health check，確認服務能處理請求。</p>
<p><strong>恢復時間量測</strong>：實際 <a href="/blog/backend/knowledge-cards/rto/" data-link-title="RTO" data-link-desc="說明恢復時間目標如何約束事故回復策略">RTO</a> 是否符合承諾。如果承諾 4 小時 RTO 但 restore 本身需要 6 小時，這個承諾就是空的。量測要包含從決策啟動到服務恢復的完整時間，不只是資料還原時間。Roblox 2021 的 73 小時 outage 說明 recovery 不是切回流量就結束 — 資料一致性重建、快取預熱與依賴服務的啟動順序都會拉長實際恢復時間。</p>
<h2 id="演練類型">演練類型</h2>
<table>
  <thead>
      <tr>
          <th>類型</th>
          <th>目的</th>
          <th>典型輸出</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>tabletop</td>
          <td>檢查決策路由與角色分工</td>
          <td>角色清單、決策順序、通訊模板</td>
      </tr>
      <tr>
          <td>partial failover</td>
          <td>驗證局部區域或子系統能否切換</td>
          <td>切換結果、回復時間、手動步驟</td>
      </tr>
      <tr>
          <td>full region failover</td>
          <td>驗證整個區域是否能從災難中回來</td>
          <td><a href="/blog/backend/knowledge-cards/rto/" data-link-title="RTO" data-link-desc="說明恢復時間目標如何約束事故回復策略">RTO</a>、<a href="/blog/backend/knowledge-cards/rpo/" data-link-title="RPO" data-link-desc="說明恢復點目標如何定義可接受資料損失範圍">RPO</a>、資料一致性檢查</td>
      </tr>
      <tr>
          <td>data restore drill</td>
          <td>驗證備份是否能真的還原資料</td>
          <td>restore log、校驗結果、缺口清單</td>
      </tr>
  </tbody>
</table>
<p>這些演練的共同點是：演練本身要留下證據。沒有輸出，就沒有辦法判斷回復能力到底有沒有被建立。</p>
<p><strong>Tabletop</strong> 的重點是決策路由清晰度。參與者在紙上走一遍事故情境，回答「誰負責決定切換」「什麼條件觸發升級」「通訊延遲多長可接受」。這個類型成本最低、頻率應最高，適合用來發現流程漏洞與角色模糊。</p>
<p><strong>Partial failover</strong> 的重點是切換腳本與監控覆蓋。選擇一個子系統或單一 availability zone 做真實切換，驗證自動化腳本是否可執行、監控是否能在切換過程中保持可見性。這個階段常暴露的問題是：腳本假設的前提條件在 production 不成立，或監控在切換過程中產生大量 false positive。</p>
<p><strong>Full region failover</strong> 的重點是資料一致性與恢復順序。<a href="/blog/backend/06-reliability/cases/meta/region-failover-and-reliability-boundaries/" data-link-title="Meta：Region Failover 與可靠性邊界" data-link-desc="把跨區故障視為邊界治理問題，透過分區隔離與回復順序控制失效擴散。">Meta 的 2021 年事故</a>顯示，跨區 failover 的最大風險在恢復順序 — 控制面與資料面共用路徑時，先恢復哪條路徑會直接決定整體恢復時間。當恢復動作本身依賴尚未恢復的控制面服務，恢復會陷入循環等待。</p>
<h2 id="演練節奏與升級">演練節奏與升級</h2>
<p>演練是按風險層級安排的循環流程。</p>
<table>
  <thead>
      <tr>
          <th>類型</th>
          <th>建議節奏</th>
          <th>升級條件</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>tabletop</td>
          <td>季度</td>
          <td>新增關鍵依賴、組織結構變更、重大事故後</td>
      </tr>
      <tr>
          <td>partial failover</td>
          <td>半年</td>
          <td>tabletop 暴露切換路徑疑慮</td>
      </tr>
      <tr>
          <td>full region failover</td>
          <td>年度</td>
          <td>partial 驗證通過、業務需求（合規、審計）</td>
      </tr>
      <tr>
          <td>data restore drill</td>
          <td>季度</td>
          <td>備份策略變更、資料量跳升、新增資料源</td>
      </tr>
  </tbody>
</table>
<p>每輪演練產出的缺口應回寫到 <a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 reliability debt backlog</a>，成為下一輪演練的驗證目標。<a href="/blog/backend/06-reliability/cases/google/postmortem-action-item-closure-governance/" data-link-title="Google：Postmortem Action Item Closure 治理" data-link-desc="把 blameless postmortem 從會議文件變成可追蹤的可靠性治理機制：action item 分級、完成條件與回寫節奏。">Google 的 postmortem action item closure 治理</a>說明把事故教訓轉成有 owner 與完成條件的改進項，這個機制同樣適用於演練缺口：P0 缺口應在下個 release 週期前修復，P1 缺口應排入固定追蹤。</p>
<p><a href="/blog/backend/06-reliability/cases/shopify/bfcm-capacity-and-game-day/" data-link-title="Shopify：BFCM 容量治理與 Game Day 驗證節奏" data-link-desc="把季節性流量峰值轉成年度可靠性流程，透過容量模型、演練與隔離策略提前吸收風險。">Shopify 的 BFCM 準備流程</a>把年度高峰前的 game day 當作 DR 演練的自然觸發點。容量模型、隔離邊界與 failover 路徑在 game day 中一起驗證，每輪暴露的缺口回寫成下一輪的準備 checklist。這種做法讓演練節奏跟業務節奏對齊，不是額外負擔。</p>
<h2 id="dr-與-chaos-的邊界">DR 與 chaos 的邊界</h2>
<p>DR 演練與 <a href="/blog/backend/06-reliability/chaos-testing/" data-link-title="6.4 chaos testing" data-link-desc="把故障注入從工具操作升級成可驗證流程：先定義穩態，再按依賴類型設計注入、控制 blast radius 與收集證據">chaos testing</a> 都涉及故障情境，但驗證目標不同。</p>
<p>Chaos 驗證的是系統在故障持續期間能否維持服務。它的成功條件是 <a href="/blog/backend/knowledge-cards/steady-state/" data-link-title="Steady State" data-link-desc="說明可靠性實驗與事故恢復如何定義系統應維持的可接受狀態">steady state</a> 不被破壞，停止條件是 steady state breach。chaos 實驗結束後，系統應該仍在運作。</p>
<p>DR 驗證的是系統在災難發生後能否回來。它的成功條件是恢復路徑可執行且符合 <a href="/blog/backend/knowledge-cards/rto/" data-link-title="RTO" data-link-desc="說明恢復時間目標如何約束事故回復策略">RTO</a> / <a href="/blog/backend/knowledge-cards/rpo/" data-link-title="RPO" data-link-desc="說明恢復點目標如何定義可接受資料損失範圍">RPO</a> 承諾，停止條件是恢復時間超過 RTO 或資料遺失超過 RPO。DR 演練結束時，系統經歷了一次完整的失效與恢復循環。</p>
<p>兩者的交集是 failover drill：chaos 關心切換期間的服務退化程度，DR 關心切換完成後的恢復品質。在實務上，成熟團隊會把 chaos experiment 的結果作為 DR 演練的輸入 — chaos 發現的弱點變成 DR 演練的測試案例。<a href="/blog/backend/06-reliability/cases/amazon/shuffle-sharding-and-cell-boundary/" data-link-title="Amazon：Shuffle Sharding 與 Cell 邊界的失效局部化" data-link-desc="用 cell 與 shuffle sharding 將多租戶故障限制在局部，讓恢復策略可分批執行。">Amazon 的 cell boundary 與 static stability 設計</a>讓恢復可分批執行，同時服務 chaos 驗證（局部故障不擴散）與 DR 驗證（分批恢復可預測）。</p>
<h2 id="產業情境醫療系統">產業情境：醫療系統</h2>
<p>醫療系統的 DR 演練受合規（HIPAA / GDPR health data）和臨床連續性的雙重約束。演練設計需要同時滿足技術恢復目標與臨床安全要求。</p>
<p>演練排程需要跟臨床作業週期對齊。手術高峰、急診高峰與夜班交接時段都應避免做 failover 演練，因為演練造成的短暫服務中斷可能直接影響臨床決策。可執行窗口通常是週末凌晨或排定的維護時段。</p>
<p>恢復順序由臨床風險決定。EMR（電子病歷）系統優先於醫囑系統、PACS（影像系統）與行政系統。這個順序跟技術依賴不完全重疊 — 技術上 PACS 可能先恢復更快，但臨床上 EMR 的中斷風險更高。恢復順序的設計需要臨床代表參與，技術團隊單獨決定會漏掉臨床優先級。</p>
<p>Restore 驗證需要額外的 audit trail 完整性檢查。HIPAA 要求能追蹤誰在什麼時間存取了哪些病患資料，恢復後的資料若 audit trail 斷裂，即使資料本身完整也不符合合規要求。restore drill 的校驗清單需要把 audit trail 連續性納入必檢項。</p>
<p>醫療紀錄的 <a href="/blog/backend/knowledge-cards/rpo/" data-link-title="RPO" data-link-desc="說明恢復點目標如何定義可接受資料損失範圍">RPO</a> 通常比一般 SaaS 更嚴格，接近零資料遺失。遺失的醫療紀錄可能直接影響用藥決策或手術判斷，RPO 設定需要對齊臨床風險而非技術方便性。</p>
<p>演練證據本身也需要合規留存。DR 演練紀錄、恢復時間量測、缺口清單與改善追蹤都是合規審計的輸入。沒有留存的演練在審計視角等同未演練。</p>
<h2 id="產業情境iot-與製造系統">產業情境：IoT 與製造系統</h2>
<p>IoT 裝置的 rollback 成本遠高於雲端服務。雲端服務的 rollback 是 deploy 前一版 container image，秒級生效；IoT 裝置的 rollback 需要 OTA（Over-the-Air）推送，受限於裝置連線狀態、頻寬、電量與儲存空間。部分裝置可能在 rollback 過程中斷線，進入新舊版本混合的不一致狀態。</p>
<p>DR 演練需要包含「裝置不在線」場景。工業場景的裝置可能在偏遠地點、離線數天到數週。DR 計畫需要回答「離線裝置重新上線後，如何安全地同步到正確版本」，以及混合版本期間的相容性處理。</p>
<p>安全關鍵系統（製造產線控制、醫療設備、車載系統）的回退約束比一般軟體更嚴格。firmware 缺陷可能造成物理傷害，rollback 後需要跑功能安全測試（IEC 61508 等級的驗證），確認回退版本在目標硬體上的行為符合安全規格。</p>
<p>A/B firmware partition 是 IoT 的 DR 基礎設計。裝置保留兩個 firmware slot（active / inactive），更新寫入 inactive slot，驗證通過後切換到新 slot。失敗時切回原 active slot，整個過程在裝置本地完成，不需要額外 OTA 推送。這個設計讓裝置的 rollback 路徑跟 <a href="/blog/backend/06-reliability/cases/amazon/static-stability-and-constant-work/" data-link-title="Amazon：Static Stability 與 Constant Work Pattern" data-link-desc="控制面失效時資料面如何維持服務：用快取、預計算與固定工作量避免恢復放大。">Amazon A2 的 static stability</a> 概念對齊 — 即使控制面（OTA server）不可用，裝置仍能用本地 slot 切換完成回退。</p>
<h2 id="案例對照">案例對照</h2>
<table>
  <thead>
      <tr>
          <th>案例</th>
          <th>DR 視角的教訓</th>
          <th>回讀章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Meta M1</td>
          <td>控制面與資料面共用路徑時，恢復順序決定整體恢復時間</td>
          <td><a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14</a>、<a href="/blog/backend/08-incident-response/multi-incident-coordination/" data-link-title="8.14 Multi-incident Coordination" data-link-desc="把同時多事故的優先序、資源分配與 incident command system pool 協調變成可執行流程">8.14</a></td>
      </tr>
      <tr>
          <td>Amazon A1</td>
          <td>cell boundary 讓恢復可分批，不需要全域同步恢復</td>
          <td><a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20</a></td>
      </tr>
      <tr>
          <td>Stripe S1</td>
          <td>交易系統 rollback 需要同時驗證 schema 相容與冪等重播</td>
          <td><a href="/blog/backend/06-reliability/migration-safety/" data-link-title="6.11 Migration Safety 與 DB Rollout" data-link-desc="把 schema migration 從一次性事件變成可逆、可漸進的 rollout 流程">6.11</a>、<a href="/blog/backend/06-reliability/idempotency-replay/" data-link-title="6.12 Idempotency 與 Replay 驗證" data-link-desc="把重試、重播與冪等性從口頭約定變成可驗證屬性">6.12</a></td>
      </tr>
      <tr>
          <td>Shopify H1</td>
          <td>年度高峰前的 game day 是 DR 演練的自然觸發點</td>
          <td><a href="/blog/backend/06-reliability/capacity-cost/" data-link-title="6.9 容量與成本邊界" data-link-desc="把容量規劃跟成本約束變成驗證輸入">6.9</a>、<a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22</a></td>
      </tr>
      <tr>
          <td>Google G2</td>
          <td>postmortem action item 轉成下一輪 DR 演練題目</td>
          <td><a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21</a>、<a href="/blog/backend/08-incident-response/post-incident-review/" data-link-title="8.5 復盤與改進追蹤" data-link-desc="把 RCA 與 action items 轉成可驗證閉環">8.5</a></td>
      </tr>
      <tr>
          <td>Netflix N1</td>
          <td><a href="/blog/backend/knowledge-cards/steady-state/" data-link-title="Steady State" data-link-desc="說明可靠性實驗與事故恢復如何定義系統應維持的可接受狀態">steady state</a> 定義同時作為 DR recovery complete 的判準</td>
          <td><a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22</a></td>
      </tr>
      <tr>
          <td>Amazon A2</td>
          <td>static stability 讓資料面在控制面失效時仍能服務，恢復路徑不依賴已故障的控制面</td>
          <td><a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14</a>、<a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22</a></td>
      </tr>
      <tr>
          <td>Meta M2</td>
          <td>回復工具依賴已故障的系統（BGP / DNS / 遠端存取），恢復陷入循環等待</td>
          <td><a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14</a>、<a href="/blog/backend/08-incident-response/multi-incident-coordination/" data-link-title="8.14 Multi-incident Coordination" data-link-desc="把同時多事故的優先序、資源分配與 incident command system pool 協調變成可執行流程">8.14</a></td>
      </tr>
  </tbody>
</table>
<h2 id="判讀訊號">判讀訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀條件</th>
          <th>行動建議</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>DR plan 寫在 wiki、過去 12 個月未演練</td>
          <td>回復能力不可信 — plan 與 production 可能已漂移</td>
          <td>排入下季 tabletop + partial failover 演練</td>
      </tr>
      <tr>
          <td>backup 有排程、restore 從未跑過</td>
          <td>備份完整性未知 — restore 是唯一能證明備份可用的手段</td>
          <td>安排 restore drill、量測實際 RTO</td>
      </tr>
      <tr>
          <td>failover 配置與 production 漂移</td>
          <td>failover 路徑不可靠 — 任何 infra 變更都可能讓 failover 腳本失效</td>
          <td>建 failover config diff 定期掃描</td>
      </tr>
      <tr>
          <td>RTO / RPO 是估值、不是量值</td>
          <td>恢復承諾不可信 — 未被演練量測過的數字只是猜測</td>
          <td>用 restore drill 量測實際值、更新承諾</td>
      </tr>
      <tr>
          <td>rollback 需要手動 SQL 或脫離部署流程</td>
          <td>rollback 路徑高風險 — 手動操作在壓力下容易出錯</td>
          <td>把 rollback 步驟自動化進 deploy pipeline</td>
      </tr>
      <tr>
          <td>演練缺口未回寫到 backlog</td>
          <td>演練價值流失 — 發現問題但不追蹤等同未發現</td>
          <td>每次演練產出寫入 6.21 reliability debt + owner</td>
      </tr>
  </tbody>
</table>
<h2 id="交接路由">交接路由</h2>
<ul>
<li><a href="/blog/backend/05-deployment-platform/" data-link-title="模組五：部署平台與網路入口" data-link-desc="整理 Kubernetes、systemd、load balancer、container 與服務生命週期合約">05 部署平台</a>：blue-green / region failover 實作</li>
<li><a href="/blog/backend/06-reliability/chaos-testing/" data-link-title="6.4 chaos testing" data-link-desc="把故障注入從工具操作升級成可驗證流程：先定義穩態，再按依賴類型設計注入、控制 blast radius 與收集證據">6.4 chaos testing</a>：chaos 暴露的弱點變成 DR 演練題目</li>
<li><a href="/blog/backend/06-reliability/migration-safety/" data-link-title="6.11 Migration Safety 與 DB Rollout" data-link-desc="把 schema migration 從一次性事件變成可逆、可漸進的 rollout 流程">6.11 migration safety</a>：migration rollback 演練</li>
<li><a href="/blog/backend/06-reliability/idempotency-replay/" data-link-title="6.12 Idempotency 與 Replay 驗證" data-link-desc="把重試、重播與冪等性從口頭約定變成可驗證屬性">6.12 idempotency / replay</a>：replay 是 DR 回復的前提</li>
<li><a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 reliability debt backlog</a>：演練缺口回寫</li>
<li><a href="/blog/backend/06-reliability/provider-dependency-release-gate/" data-link-title="6.25 Provider Dependency Release Gate 實作示範" data-link-desc="以 payment provider 變更示範 release gate 如何結合 evidence、stop condition 與 rollback window。">6.25 provider dependency release gate</a>：provider 變更的 rollback 實作示範</li>
<li><a href="/blog/backend/08-incident-response/containment-recovery-strategy/" data-link-title="8.3 止血、降級與回復策略" data-link-desc="把短期止血與正式回復拆成可執行步驟">8.3 止血回復</a>：演練結果作為事中決策素材</li>
<li><a href="/blog/backend/08-incident-response/drills-and-oncall-readiness/" data-link-title="8.6 演練與值班能力建設" data-link-desc="用演練與值班訓練提升事故反應品質">8.6 演練與值班</a>：DR 結果回饋到團隊技能建設</li>
<li><a href="/blog/backend/08-incident-response/vendor-dependency-incident/" data-link-title="8.15 Vendor / 第三方依賴事故處理" data-link-desc="依賴方掛掉、自己無 control 時的決策模型">8.15 vendor 事故</a>：多 vendor / 多區 failover 路徑</li>
</ul>
]]></content:encoded></item><item><title>Chaos Mesh</title><link>https://tarrragon.github.io/blog/backend/06-reliability/vendors/chaos-mesh/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/vendors/chaos-mesh/</guid><description>&lt;p>Chaos Mesh 是 PingCAP 開源、CNCF incubating 的 Kubernetes-native chaos engineering 平台、承擔三個責任：CRD-driven fault injection（PodChaos / NetworkChaos / IOChaos / StressChaos）、Chaos Workflow（多步驟編排）、Chaos Dashboard 視覺化 + experiment scope 控制。設計取捨偏向「K8s-native + GitOps-friendly + multi-fault types」、適合 K8s 為主的 chaos engineering。&lt;/p>
&lt;h2 id="本章目標">本章目標&lt;/h2>
&lt;p>讀完本章後、你應該能：&lt;/p>
&lt;ol>
&lt;li>部署 Chaos Mesh 到 K8s cluster&lt;/li>
&lt;li>設計 PodChaos / NetworkChaos / IOChaos experiment&lt;/li>
&lt;li>用 Chaos Workflow 編排多步驟實驗 + steady state probe&lt;/li>
&lt;li>控制 blast radius（namespace / labelSelector / mode）&lt;/li>
&lt;li>跟 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 Experiment Safety Boundary&lt;/a> 對齊 chaos 實驗審批&lt;/li>
&lt;/ol>
&lt;h2 id="最短路徑5-分鐘把-chaos-mesh-跑起來">最短路徑：5 分鐘把 Chaos Mesh 跑起來&lt;/h2>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 1. 安裝&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: curl -sSL https://mirrors.chaos-mesh.org/v2.7.0/install.sh | bash&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 2. 跑第一個 PodChaos&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: 寫 podchaos.yaml、kubectl apply&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: action: pod-kill / selector / mode&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="c1"># 3. Dashboard&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: kubectl port-forward svc/chaos-dashboard 2333:2333&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="日常操作與決策形狀">日常操作與決策形狀&lt;/h2>
&lt;h3 id="crd-設計">CRD 設計&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>PodChaos：pod-kill / pod-failure / container-kill&lt;/li>
&lt;li>NetworkChaos：delay / loss / duplicate / corrupt / partition&lt;/li>
&lt;li>IOChaos：delay / errno / mistake / attrOverride&lt;/li>
&lt;li>StressChaos：CPU / memory pressure&lt;/li>
&lt;li>對應 GitOps：Helm / Kustomize 管 experiment&lt;/li>
&lt;/ul>
&lt;h3 id="chaos-workflow">Chaos Workflow&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>多步驟 chaos 編排（serial / parallel）&lt;/li>
&lt;li>Suspend / resume 控制&lt;/li>
&lt;li>Probe（steady state validation）&lt;/li>
&lt;li>對應 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 Experiment Safety Boundary&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="chaos-dashboard">Chaos Dashboard&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>視覺化 experiment timeline&lt;/li>
&lt;li>Experiment archive&lt;/li>
&lt;li>Event log&lt;/li>
&lt;li>RBAC&lt;/li>
&lt;/ul>
&lt;h2 id="進階主題按需閱讀">進階主題（按需閱讀）&lt;/h2>
&lt;h3 id="blast-radius-控制">Blast radius 控制&lt;/h3>
&lt;p>子議題：&lt;/p></description><content:encoded><![CDATA[<p>Chaos Mesh 是 PingCAP 開源、CNCF incubating 的 Kubernetes-native chaos engineering 平台、承擔三個責任：CRD-driven fault injection（PodChaos / NetworkChaos / IOChaos / StressChaos）、Chaos Workflow（多步驟編排）、Chaos Dashboard 視覺化 + experiment scope 控制。設計取捨偏向「K8s-native + GitOps-friendly + multi-fault types」、適合 K8s 為主的 chaos engineering。</p>
<h2 id="本章目標">本章目標</h2>
<p>讀完本章後、你應該能：</p>
<ol>
<li>部署 Chaos Mesh 到 K8s cluster</li>
<li>設計 PodChaos / NetworkChaos / IOChaos experiment</li>
<li>用 Chaos Workflow 編排多步驟實驗 + steady state probe</li>
<li>控制 blast radius（namespace / labelSelector / mode）</li>
<li>跟 <a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 Experiment Safety Boundary</a> 對齊 chaos 實驗審批</li>
</ol>
<h2 id="最短路徑5-分鐘把-chaos-mesh-跑起來">最短路徑：5 分鐘把 Chaos Mesh 跑起來</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 1. 安裝</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># TODO: curl -sSL https://mirrors.chaos-mesh.org/v2.7.0/install.sh | bash</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 2. 跑第一個 PodChaos</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># TODO: 寫 podchaos.yaml、kubectl apply</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="c1"># TODO: action: pod-kill / selector / mode</span>
</span></span><span class="line"><span class="ln">7</span><span class="cl">
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c1"># 3. Dashboard</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="c1"># TODO: kubectl port-forward svc/chaos-dashboard 2333:2333</span></span></span></code></pre></div><h2 id="日常操作與決策形狀">日常操作與決策形狀</h2>
<h3 id="crd-設計">CRD 設計</h3>
<p>子議題：</p>
<ul>
<li>PodChaos：pod-kill / pod-failure / container-kill</li>
<li>NetworkChaos：delay / loss / duplicate / corrupt / partition</li>
<li>IOChaos：delay / errno / mistake / attrOverride</li>
<li>StressChaos：CPU / memory pressure</li>
<li>對應 GitOps：Helm / Kustomize 管 experiment</li>
</ul>
<h3 id="chaos-workflow">Chaos Workflow</h3>
<p>子議題：</p>
<ul>
<li>多步驟 chaos 編排（serial / parallel）</li>
<li>Suspend / resume 控制</li>
<li>Probe（steady state validation）</li>
<li>對應 <a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 Experiment Safety Boundary</a></li>
</ul>
<h3 id="chaos-dashboard">Chaos Dashboard</h3>
<p>子議題：</p>
<ul>
<li>視覺化 experiment timeline</li>
<li>Experiment archive</li>
<li>Event log</li>
<li>RBAC</li>
</ul>
<h2 id="進階主題按需閱讀">進階主題（按需閱讀）</h2>
<h3 id="blast-radius-控制">Blast radius 控制</h3>
<p>子議題：</p>
<ul>
<li>namespace 限制</li>
<li>labelSelector / value mode（one / all / fixed / fixed-percent / random-max-percent）</li>
<li>annotationSelector</li>
<li>Pause / resume 緊急中止</li>
</ul>
<h3 id="schedule-與-gitops">Schedule 與 GitOps</h3>
<p>子議題：</p>
<ul>
<li>Schedule CRD 定期 chaos</li>
<li>ArgoCD / Flux 整合</li>
<li>Experiment as code review</li>
</ul>
<h3 id="跟-litmuschaos--gremlin-對比">跟 LitmusChaos / Gremlin 對比</h3>
<p>子議題：</p>
<ul>
<li>Chaos Mesh：CRD-driven、PingCAP 主導</li>
<li>LitmusChaos：ChaosHub experiment / CNCF graduated</li>
<li>Gremlin：商業 SaaS、跨平台</li>
<li>選擇判讀：K8s OSS first → Chaos Mesh / Litmus；商業跨平台 → Gremlin</li>
</ul>
<h3 id="steady-state-驗證">Steady state 驗證</h3>
<p>子議題：</p>
<ul>
<li>HTTP / TCP / Pod / podHTTPChaos</li>
<li>Probe success threshold</li>
<li>跟 <a href="/blog/backend/09-performance-capacity/" data-link-title="模組九：效能工程與容量規劃" data-link-desc="把『目前配置能撐多少、要加多少機器』變成可量化、可驗證、可改進的工程流程">9.13 SLO</a> 對應 burn rate</li>
</ul>
<h2 id="排錯快速判讀">排錯快速判讀</h2>
<h3 id="experiment-沒生效">Experiment 沒生效</h3>
<p>操作原則：先 <code>kubectl describe podchaos</code> 看 status、再看 webhook + RBAC。</p>
<h3 id="blast-radius-過大">Blast radius 過大</h3>
<p>操作原則：mode 設 all 或 percent 設太高、影響超出預期。預防：先 dry-run / staging 測試。</p>
<h3 id="pause-不及時">Pause 不及時</h3>
<p>操作原則：experiment running 中要 pause、不是 delete（delete 不會 cleanup state）。</p>
<h3 id="dashboard-連不上">Dashboard 連不上</h3>
<p>操作原則：service 沒暴露、RBAC 不對。</p>
<h2 id="何時改走其他服務">何時改走其他服務</h2>
<table>
  <thead>
      <tr>
          <th>需求形狀</th>
          <th>改走</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>非 K8s 環境</td>
          <td><a href="/blog/backend/06-reliability/vendors/gremlin/" data-link-title="Gremlin" data-link-desc="商業 chaos engineering 平台、跨平台與 GameDay">Gremlin</a> / <a href="/blog/backend/06-reliability/vendors/toxiproxy/" data-link-title="Toxiproxy" data-link-desc="TCP-level fault injection proxy（Shopify 開源）">Toxiproxy</a></td>
      </tr>
      <tr>
          <td>AWS-native chaos</td>
          <td>AWS Fault Injection Service</td>
      </tr>
      <tr>
          <td>K8s + ChaosHub experiment</td>
          <td><a href="/blog/backend/06-reliability/vendors/litmuschaos/" data-link-title="LitmusChaos" data-link-desc="Kubernetes chaos engineering 平台（CNCF graduated）">LitmusChaos</a></td>
      </tr>
      <tr>
          <td>Integration test 模擬故障</td>
          <td><a href="/blog/backend/06-reliability/vendors/toxiproxy/" data-link-title="Toxiproxy" data-link-desc="TCP-level fault injection proxy（Shopify 開源）">Toxiproxy</a></td>
      </tr>
      <tr>
          <td>商業 + GameDay 設計</td>
          <td><a href="/blog/backend/06-reliability/vendors/gremlin/" data-link-title="Gremlin" data-link-desc="商業 chaos engineering 平台、跨平台與 GameDay">Gremlin</a></td>
      </tr>
  </tbody>
</table>
<h2 id="不在本頁內的主題">不在本頁內的主題</h2>
<ul>
<li>完整 CRD spec</li>
<li>Chaos Mesh internal architecture</li>
<li>各 fault type 詳細 parameter</li>
</ul>
<h2 id="案例回寫">案例回寫</h2>
<table>
  <thead>
      <tr>
          <th>案例方向</th>
          <th>對應主題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/netflix/steady-state-chaos-and-fit/" data-link-title="Netflix：Steady State、Chaos 與 FIT 的驗證路徑" data-link-desc="把故障注入從工具操作升級成可驗證流程：先定義穩態，再設計注入與回復條件。">Netflix：Steady State、Chaos 與 FIT</a></td>
          <td>steady state hypothesis 對應 Chaos Workflow Probe</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/netflix/chaos-monkey-business-hours-guardrails/" data-link-title="Netflix：Business-Hours Chaos 與 Guardrails" data-link-desc="Chaos Monkey 為何刻意在 business hours 執行：把即時應變能力納入驗證，並用 guardrails 限制實驗風險。">Netflix：Business-Hours Guardrails</a></td>
          <td>blast radius / pause / mode 控制對應時段策略</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/pinterest/cache-reliability-and-capacity-surprises/" data-link-title="Pinterest：快取可靠性與容量驚奇治理" data-link-desc="針對快取層失效與流量突增，建立容量緩衝、退化路徑與重建節奏。">Pinterest：快取可靠性與容量驚奇</a></td>
          <td>NetworkChaos / StressChaos 模擬熱點與 cache failure mode</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/google/error-budget-policy-and-release-gating/" data-link-title="Google：Error Budget 政策如何決定發布節奏" data-link-desc="把 SLO 消耗量轉成 release gate，讓可靠性與交付速度共用同一套決策語言。">Google：Error Budget 與 Release Gating</a></td>
          <td>chaos finding 對應 SLO burn rate 的回寫</td>
      </tr>
  </tbody>
</table>
<p><strong>待補 Chaos Mesh customer case</strong>：PingCAP / TiDB 客戶 Chaos Mesh 案例、CNCF Chaos Mesh adopters。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>上游概念：<a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 Experiment Safety Boundary</a></li>
<li>平行 vendor：<a href="/blog/backend/06-reliability/vendors/litmuschaos/" data-link-title="LitmusChaos" data-link-desc="Kubernetes chaos engineering 平台（CNCF graduated）">LitmusChaos</a>、<a href="/blog/backend/06-reliability/vendors/gremlin/" data-link-title="Gremlin" data-link-desc="商業 chaos engineering 平台、跨平台與 GameDay">Gremlin</a></li>
<li>下游能力：<a href="/blog/backend/08-incident-response/" data-link-title="模組八：事故處理與復盤" data-link-desc="用 IR 領域詞彙建問題節點、以服務級案例庫累積事故脈絡，先建概念與案例庫再進實作交接">8 incident response</a>（chaos finding 進 IR 流程）</li>
</ul>
]]></content:encoded></item><item><title>6.8 Release Gate 與變更節奏</title><link>https://tarrragon.github.io/blog/backend/06-reliability/release-gate/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/release-gate/</guid><description>&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">Release gate&lt;/a> 是把放行決策從「看起來可以」變成「條件已經達成」的控制面。它的責任是把哪些變更可以進、哪些變更要等、哪些變更必須先補證據說清楚，擋住所有變更從來不是目標。當 gate 被寫成政策，團隊就能用同一套條件判斷 CI、SLO、migration、相容性與高風險時段。&lt;/p>
&lt;p>這個節點先處理節奏，再處理工具。先問變更是否應該放行，再問這次放行需要哪些訊號與檢查。當 gate 被看成節奏控制，讀者就會明白為什麼 freeze 是可靠性政策的一部分，視為例外會弱化整套節奏控制。&lt;/p>
&lt;h2 id="大綱">大綱&lt;/h2>
&lt;ul>
&lt;li>release gate 的核心責任：把放行決策從個人判斷變成可驗證條件&lt;/li>
&lt;li>gate 類別：CI 通過、SLO 健康、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/error-budget/" data-link-title="Error Budget" data-link-desc="說明 SLO 允許的失敗額度如何影響發版與可靠性投入">error budget&lt;/a> 餘額、migration 可逆、相容性檢查&lt;/li>
&lt;li>變更節奏：deploy frequency、batch size、change failure rate（DORA 四指標）&lt;/li>
&lt;li>freeze 條件：error budget 耗盡、事故進行中、高風險時段&lt;/li>
&lt;li>跟 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6 SLO&lt;/a> 的耦合：error budget 是 gate 的一個條件&lt;/li>
&lt;li>跟 &lt;a href="https://tarrragon.github.io/blog/backend/05-deployment-platform/" data-link-title="模組五：部署平台與網路入口" data-link-desc="整理 Kubernetes、systemd、load balancer、container 與服務生命週期合約">05 部署&lt;/a> 的交接：gate 通過後 rollout 策略接手&lt;/li>
&lt;li>反模式：gate 流於形式、freeze 無 owner、緊急修復繞過 gate 變常態&lt;/li>
&lt;/ul>
&lt;h2 id="核心判讀">核心判讀&lt;/h2>
&lt;p>gate 的責任是把放行條件具體化。CI green 只代表測試通過，不代表服務可以安全進 production；SLO 健康只代表目前風險可接受，不代表任何變更都能繼續推；migration 可逆只代表退路存在，不代表已經證明回退完全無害。這些條件要一起看，才知道 gate 有沒有真的在做事。&lt;/p>
&lt;p>資料庫 migration 的 gate 要把 evidence 放回 rollout 階段判讀。Expand、backfill、cutover 與 contract 需要不同 checks：compatibility result、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/validation-query/" data-link-title="Validation Query" data-link-desc="說明遷移、回填與修復期間如何用查詢證明資料語意是否一致">validation query&lt;/a>、mismatch rate、replication lag、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/rollback-window/" data-link-title="Rollback Window" data-link-desc="說明變更進入 production 後還能用哪種方式回退或改路線的時間與條件">rollback window&lt;/a> 與 owner。完整欄位形狀可接到 &lt;a href="https://tarrragon.github.io/blog/backend/01-database/schema-migration-rollout-evidence/" data-link-title="1.7 Schema Migration Rollout 證據（Schema Migration Rollout Evidence）實作示範" data-link-desc="以訂單付款狀態欄位演進示範 schema migration 如何產出 evidence、release gate 與 incident decision log。">1.7 Schema Migration Rollout 證據&lt;/a>。&lt;/p>
&lt;p>freeze 的責任是把風險攔住。當 error budget 耗盡、事故正在進行、或高風險時段已到時，freeze 不應該被視為拖延，而應該被視為維持可靠性的一種放行決策。這樣的政策，會比只看 CI 更接近真實的部署世界。&lt;/p>
&lt;h2 id="判讀訊號">判讀訊號&lt;/h2>
&lt;ul>
&lt;li>gate 只看 CI green、不看 SLO / error budget / migration 可逆性&lt;/li>
&lt;li>emergency bypass 從例外變週常&lt;/li>
&lt;li>freeze 條件無 owner、沒人知道誰能解凍&lt;/li>
&lt;li>change failure rate 沒量、無法評估 gate 是否有效&lt;/li>
&lt;li>migration 沒做向後相容檢查、rollback 後資料不一致&lt;/li>
&lt;/ul>
&lt;h2 id="案例對照">案例對照&lt;/h2>
&lt;p>Google 很適合用來看 gate 需要什麼政策語言，因為它把 SLO、error budget 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/post-incident-review/" data-link-title="Post-Incident Review" data-link-desc="說明事故後如何完成復盤、學習與改進閉環">post-incident review&lt;/a> 連成一套治理系統。Stripe 則適合用來看交易場景下的 gate，因為 idempotency、canary 與 migration safety 會把放行和交易正確性綁在一起。Shopify 可以補峰值節奏，因為 BFCM 前的 gate 不只是測試通過，而是要確定高峰時仍能守住容量與隔離。&lt;/p>
&lt;p>Amazon 和 Meta 則提供更偏架構層的 gate 視角。前者告訴我們隔離邊界與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/blast-radius/" data-link-title="Blast Radius" data-link-desc="說明事故影響面如何估算與隔離">blast radius&lt;/a> 會直接影響哪些變更可以放行，後者則顯示 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/control-plane/" data-link-title="Control Plane" data-link-desc="負責下發策略、配置與路由決策的控制層">control plane&lt;/a> 變更如果沒有足夠的 gate，可能直接把整個區域或整個公司拖進事故。把這些案例一起看，gate 就不再只是 CI 的最後一步，而是整個變更節奏的控制面。&lt;/p></description><content:encoded><![CDATA[<h2 id="概念定位">概念定位</h2>
<p><a href="/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">Release gate</a> 是把放行決策從「看起來可以」變成「條件已經達成」的控制面。它的責任是把哪些變更可以進、哪些變更要等、哪些變更必須先補證據說清楚，擋住所有變更從來不是目標。當 gate 被寫成政策，團隊就能用同一套條件判斷 CI、SLO、migration、相容性與高風險時段。</p>
<p>這個節點先處理節奏，再處理工具。先問變更是否應該放行，再問這次放行需要哪些訊號與檢查。當 gate 被看成節奏控制，讀者就會明白為什麼 freeze 是可靠性政策的一部分，視為例外會弱化整套節奏控制。</p>
<h2 id="大綱">大綱</h2>
<ul>
<li>release gate 的核心責任：把放行決策從個人判斷變成可驗證條件</li>
<li>gate 類別：CI 通過、SLO 健康、<a href="/blog/backend/knowledge-cards/error-budget/" data-link-title="Error Budget" data-link-desc="說明 SLO 允許的失敗額度如何影響發版與可靠性投入">error budget</a> 餘額、migration 可逆、相容性檢查</li>
<li>變更節奏：deploy frequency、batch size、change failure rate（DORA 四指標）</li>
<li>freeze 條件：error budget 耗盡、事故進行中、高風險時段</li>
<li>跟 <a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6 SLO</a> 的耦合：error budget 是 gate 的一個條件</li>
<li>跟 <a href="/blog/backend/05-deployment-platform/" data-link-title="模組五：部署平台與網路入口" data-link-desc="整理 Kubernetes、systemd、load balancer、container 與服務生命週期合約">05 部署</a> 的交接：gate 通過後 rollout 策略接手</li>
<li>反模式：gate 流於形式、freeze 無 owner、緊急修復繞過 gate 變常態</li>
</ul>
<h2 id="核心判讀">核心判讀</h2>
<p>gate 的責任是把放行條件具體化。CI green 只代表測試通過，不代表服務可以安全進 production；SLO 健康只代表目前風險可接受，不代表任何變更都能繼續推；migration 可逆只代表退路存在，不代表已經證明回退完全無害。這些條件要一起看，才知道 gate 有沒有真的在做事。</p>
<p>資料庫 migration 的 gate 要把 evidence 放回 rollout 階段判讀。Expand、backfill、cutover 與 contract 需要不同 checks：compatibility result、<a href="/blog/backend/knowledge-cards/validation-query/" data-link-title="Validation Query" data-link-desc="說明遷移、回填與修復期間如何用查詢證明資料語意是否一致">validation query</a>、mismatch rate、replication lag、<a href="/blog/backend/knowledge-cards/rollback-window/" data-link-title="Rollback Window" data-link-desc="說明變更進入 production 後還能用哪種方式回退或改路線的時間與條件">rollback window</a> 與 owner。完整欄位形狀可接到 <a href="/blog/backend/01-database/schema-migration-rollout-evidence/" data-link-title="1.7 Schema Migration Rollout 證據（Schema Migration Rollout Evidence）實作示範" data-link-desc="以訂單付款狀態欄位演進示範 schema migration 如何產出 evidence、release gate 與 incident decision log。">1.7 Schema Migration Rollout 證據</a>。</p>
<p>freeze 的責任是把風險攔住。當 error budget 耗盡、事故正在進行、或高風險時段已到時，freeze 不應該被視為拖延，而應該被視為維持可靠性的一種放行決策。這樣的政策，會比只看 CI 更接近真實的部署世界。</p>
<h2 id="判讀訊號">判讀訊號</h2>
<ul>
<li>gate 只看 CI green、不看 SLO / error budget / migration 可逆性</li>
<li>emergency bypass 從例外變週常</li>
<li>freeze 條件無 owner、沒人知道誰能解凍</li>
<li>change failure rate 沒量、無法評估 gate 是否有效</li>
<li>migration 沒做向後相容檢查、rollback 後資料不一致</li>
</ul>
<h2 id="案例對照">案例對照</h2>
<p>Google 很適合用來看 gate 需要什麼政策語言，因為它把 SLO、error budget 與 <a href="/blog/backend/knowledge-cards/post-incident-review/" data-link-title="Post-Incident Review" data-link-desc="說明事故後如何完成復盤、學習與改進閉環">post-incident review</a> 連成一套治理系統。Stripe 則適合用來看交易場景下的 gate，因為 idempotency、canary 與 migration safety 會把放行和交易正確性綁在一起。Shopify 可以補峰值節奏，因為 BFCM 前的 gate 不只是測試通過，而是要確定高峰時仍能守住容量與隔離。</p>
<p>Amazon 和 Meta 則提供更偏架構層的 gate 視角。前者告訴我們隔離邊界與 <a href="/blog/backend/knowledge-cards/blast-radius/" data-link-title="Blast Radius" data-link-desc="說明事故影響面如何估算與隔離">blast radius</a> 會直接影響哪些變更可以放行，後者則顯示 <a href="/blog/backend/knowledge-cards/control-plane/" data-link-title="Control Plane" data-link-desc="負責下發策略、配置與路由決策的控制層">control plane</a> 變更如果沒有足夠的 gate，可能直接把整個區域或整個公司拖進事故。把這些案例一起看，gate 就不再只是 CI 的最後一步，而是整個變更節奏的控制面。</p>
<p><a href="/blog/backend/06-reliability/cases/stripe/canary-deploy-and-progressive-rollout/" data-link-title="Stripe：Canary Deploy 與 Progressive Rollout 治理" data-link-desc="金流場景如何用交易指標驅動放行節奏：延遲確認、duplicate 偵測與自動回退。">Stripe 的 canary deploy 實踐</a>把金流場景的 progressive rollout 跟交易指標綁在一起：每一批放量用 checkout success rate、duplicate charge、退款率判斷是否安全。金流的 feedback loop 比一般功能長（結帳 → 確認 → 對帳 → 退款），觀察窗必須對齊這個延遲。</p>
<h2 id="gate-類別">gate 類別</h2>
<table>
  <thead>
      <tr>
          <th>類別</th>
          <th>作用</th>
          <th>常見例子</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>CI 通過</td>
          <td>確認基礎測試與 artifact 可重播</td>
          <td>unit / integration / lint</td>
      </tr>
      <tr>
          <td>SLO 健康</td>
          <td>確認服務健康仍在可接受區間</td>
          <td><a href="/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">burn rate</a>、error budget</td>
      </tr>
      <tr>
          <td>Migration 可逆</td>
          <td>確認 schema / data 變更有退路</td>
          <td>forward / backward compatibility</td>
      </tr>
      <tr>
          <td>相容性檢查</td>
          <td>確認上下游協議與資料不會互相打架</td>
          <td>contract / schema checks</td>
      </tr>
      <tr>
          <td>高風險時段凍結</td>
          <td>確認人在、窗在、風險可控</td>
          <td>freeze window、<a href="/blog/backend/knowledge-cards/on-call/" data-link-title="On-Call" data-link-desc="說明值班制度如何承接告警、事故分級與升級流程">on-call</a> presence</td>
      </tr>
  </tbody>
</table>
<p>這張表的重點是每一類都要對應 owner 與回退條件，分類只是組織方式。沒有回退條件的 gate，只是心理安慰。</p>
<h2 id="變更分層跟-gate-政策">變更分層跟 gate 政策</h2>
<p>變更分層是把變更依失敗代價跟回退成本切成不同 gate 政策的控制面。讓高風險變更承受高 gate 成本、低風險變更不被高成本拖累、是分層治理的核心責任。可重複套用的做法是先做變更分層、再對應分層 gate 政策。</p>
<p>對應 <a href="/blog/backend/06-reliability/cases/microsoft/change-management-and-reliability-governance/" data-link-title="Microsoft：變更治理與可靠性門檻" data-link-desc="透過分層變更管理與發布閘門，降低大型 SaaS 平台的系統性回歸風險。">MS1 Microsoft 變更治理與可靠性門檻</a>：揭露「變更分層 + 漸進發布 + 復盤回寫」三個機制、適用大型 SaaS 高頻變更累積回歸的場景。對應 <a href="/blog/backend/06-reliability/cases/google/postmortem-action-item-closure-governance/" data-link-title="Google：Postmortem Action Item Closure 治理" data-link-desc="把 blameless postmortem 從會議文件變成可追蹤的可靠性治理機制：action item 分級、完成條件與回寫節奏。">G2 Google Postmortem AI Closure</a>：揭露 P0/P1 action item 必須綁定 release gate、未完成不得放行關聯變更（這層綁定讓 gate 從 release 工具升級為事故治理工具）。詳見 <a href="/blog/backend/06-reliability/reliability-debt-backlog/#action-item-%e5%88%86%e7%b4%9a%e8%b7%9f-release-gate-%e7%b6%81%e5%ae%9a" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 Action Item 分級跟 Release Gate 綁定</a>。</p>
<p>可操作的分層方法：</p>
<ul>
<li>低風險變更（配置微調、文案、UI 細節）：CI green + SLO 健康 即可放行</li>
<li>中風險變更（新 feature、依賴升級）：加 canary + per-version SLI 偏差檢查</li>
<li>高風險變更（schema migration、payment / auth 路徑、跨 region rollout）：加 evidence package + 高風險時段 freeze + P0 action item closure 檢查</li>
</ul>
<p>高風險層的三類變更要拆開治理、彼此 gate 機制不同：schema migration 的 gate 重點是 expand/contract 階段對齊跟 rollback 路徑（詳見 <a href="/blog/backend/06-reliability/migration-safety/" data-link-title="6.11 Migration Safety 與 DB Rollout" data-link-desc="把 schema migration 從一次性事件變成可逆、可漸進的 rollout 流程">6.11 migration-safety</a>）；跨 region rollout 的 gate 重點是 ordered failover 跟 blast radius 限制（詳見 <a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14 dependency-reliability-budget</a>）；payment / auth 路徑的 gate 重點在交易一致性跟 idempotency（詳見後段「交易類變更的 gate 設計」跟 <a href="/blog/backend/06-reliability/idempotency-replay/" data-link-title="6.12 Idempotency 與 Replay 驗證" data-link-desc="把重試、重播與冪等性從口頭約定變成可驗證屬性">6.12 idempotency-replay</a>）。三者皆屬高風險、但失敗模式跟回退路徑完全不同。</p>
<p>分層後高風險變更得到匹配的 gate 強度、低風險變更不被拖累、整體交付節奏跟可靠性同步提升。</p>
<h2 id="交易類變更的-gate-設計">交易類變更的 gate 設計</h2>
<p>交易類變更的 gate 同時承擔可用性跟正確性兩條軸。除了服務健康（一般 gate 已覆蓋）、還要守住交易結果一致性；回退條件也要多看一層：rollback 是否會觸發資料不一致。</p>
<p>對應 <a href="/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/" data-link-title="Stripe：Idempotency 與零停機遷移的交易安全設計" data-link-desc="把 API 重試與資料遷移放在同一套安全模型，維持支付交易的一致結果。">S1 Stripe Idempotency 與零停機遷移</a>：揭露 idempotency key + expand/contract migration + canary + rollback gate + transaction observability 四機制組合、適用支付類「可用性 + 正確性同時守住」的場景。</p>
<p>交易類變更的 gate 跟一般 release gate 差別在：</p>
<ul>
<li>一般 release gate 看「服務是否健康」、交易類 gate 還要看「交易結果是否一致」</li>
<li>一般 release gate 看「回退是否可行」、交易類 gate 要看「回退是否會引發資料不一致」</li>
<li>一般 release gate 看「per-version SLI 偏差」、交易類 gate 要看「duplicate request collapse ratio」「migration phase error drift」「canary transaction anomaly」這類交易專屬訊號</li>
</ul>
<p>把交易類變更的 gate 從一般 release gate 分出來、寫進獨立 checklist、由 <a href="/blog/backend/06-reliability/idempotency-replay/" data-link-title="6.12 Idempotency 與 Replay 驗證" data-link-desc="把重試、重播與冪等性從口頭約定變成可驗證屬性">6.12 idempotency-replay</a> 跟 <a href="/blog/backend/06-reliability/migration-safety/" data-link-title="6.11 Migration Safety 與 DB Rollout" data-link-desc="把 schema migration 從一次性事件變成可逆、可漸進的 rollout 流程">6.11 migration-safety</a> 提供具體欄位。</p>
<h2 id="產業情境金融科技">產業情境：金融科技</h2>
<p>金融服務的 release gate 需要把交易正確性放在跟可用性同等的位置。一般 SaaS 的 gate 主要看 error rate 和 latency；金融服務的 gate 需要加上 duplicate detection、settlement 一致性與 compliance audit trail。</p>
<p>變更風險分層跟交易路徑綁定。碰到 payment path 的變更（provider 切換、timeout 調整、retry 策略、settlement 流程）自動升級到高風險 gate，不論變更看起來多小。payment path 的變更即使只改一個 timeout 值，也可能影響交易成功率、重試行為與對帳結果。</p>
<p>Gate 通過條件需要包含交易專屬欄位。<a href="/blog/backend/knowledge-cards/idempotency/" data-link-title="Idempotency" data-link-desc="說明同一操作執行多次時如何保持結果一致">idempotency</a> 驗證確認重試不會產生重複扣款；reconciliation 通過確認結算數字一致；audit trail 完整確認每個決策都可追溯。這三項跟一般的 CI green / SLO healthy 是不同維度的檢查，需要獨立 checklist。</p>
<p>高風險變更的 canary 觀察窗需要涵蓋結算週期。一般 feature rollout 的觀察窗是分鐘到小時級；金融變更的觀察窗需要涵蓋 T+1（隔日結算）甚至 T+2，因為交易確認延遲、退款申請與對帳差異可能在數小時到數天後才暴露。觀察窗太短會讓問題在全量放行後才被發現。</p>
<p>Rollback 決策需要考慮已完成交易的一致性。當新版已處理交易且交易已進入結算流程，rollback 可能比繼續 roll-forward 更危險 — 退回舊版的 schema / 邏輯可能無法正確處理新版產生的交易紀錄。這個判斷跟 <a href="/blog/backend/06-reliability/dr-rollback-rehearsal/" data-link-title="6.7 DR 演練與 Rollback Rehearsal" data-link-desc="把回復路徑從紙面計畫變成定期可重播、可量測的驗證流程">6.7 rollback vs roll-forward 的判斷條件</a> 對齊，但金融場景的資料不可逆性更高。</p>
<h2 id="產業情境iot-與製造系統">產業情境：IoT 與製造系統</h2>
<p>IoT 的 release gate 需要處理「一旦推出就難以全面回收」的不可逆壓力。雲端服務的 deploy 可以秒級 rollback；IoT firmware 一旦推送到裝置，回收需要每台裝置個別 OTA，受限於連線狀態與頻寬。</p>
<p>裝置碎片化要求 gate 按硬體版本分群驗證。同一產品線可能有多個硬體版本（rev A / B / C），每個版本的 firmware 相容性不同。release gate 需要按硬體版本群組各自跑 checks，通過的群組才放行推送，不能全域一次放行。</p>
<p>IoT 的 canary 是按裝置群組分批推送，而非按流量百分比分流。推送順序通常是：內部測試裝置 → beta 用戶 → 特定區域 → 全域。每批的觀察窗需要比雲端更長（天到週），因為裝置的 failure mode 可能在特定環境條件下才觸發 — 溫度、濕度、網路品質、電力穩定度都是變數。</p>
<p>OTA 推送一旦開始，中途停止意味著部分裝置已更新、部分未更新。stop condition 需要同時監控「已更新裝置的健康度」和「混合版本之間的相容性」。若新舊版本的通訊協議不相容，部分更新的裝置群可能會觸發新的 failure mode。</p>
<p>安全關鍵系統（車載、醫療設備、工業控制）的 gate 需要額外的功能安全驗證（IEC 61508 / ISO 26262 等），通過合規驗證是放行的前置條件。這類 gate 的 owner 通常跨越工程與合規兩個團隊。</p>
<p><a href="/blog/backend/06-reliability/cases/amazon/static-stability-and-constant-work/" data-link-title="Amazon：Static Stability 與 Constant Work Pattern" data-link-desc="控制面失效時資料面如何維持服務：用快取、預計算與固定工作量避免恢復放大。">Amazon A2 的 static stability</a> 跟 IoT 的離線運作需求對齊 — 裝置在控制面（OTA server）不可用時，用本地快取的配置繼續運作，回復路徑不依賴已故障的控制面。</p>
<h2 id="交接路由">交接路由</h2>
<ul>
<li><a href="/blog/backend/06-reliability/ci-pipeline/" data-link-title="6.1 CI pipeline" data-link-desc="CI pipeline 的分層策略、artifact 管理、flaky 治理與 release gate 輸入">6.1 CI pipeline</a>：CI evidence 是 release gate 的主要輸入</li>
<li>05 部署：canary / progressive delivery 實作</li>
<li>06.6 SLO：error budget 餘額查詢</li>
<li>06.10 contract testing：契約通過作為放行條件</li>
<li>06.11 migration safety：可逆性檢查</li>
<li>01.7 Schema Migration Rollout 證據：把 migration evidence 轉成 <a href="/blog/backend/knowledge-cards/gate-decision/" data-link-title="Gate Decision" data-link-desc="說明 release gate 如何把證據轉成放行、暫停、回退或補證據的決策">gate decision</a>、checks、<a href="/blog/backend/knowledge-cards/stop-condition/" data-link-title="Stop Condition" data-link-desc="說明變更、實驗或事故處理何時必須暫停、回退或改路線">stop condition</a> 與 <a href="/blog/backend/knowledge-cards/rollback-window/" data-link-title="Rollback Window" data-link-desc="說明變更進入 production 後還能用哪種方式回退或改路線的時間與條件">rollback window</a></li>
<li>06.13 perf regression gate：退化作為 freeze 條件</li>
<li>07 資安：高風險變更的權限約束</li>
<li>08 事故閉環：事故進行中 freeze 觸發</li>
<li>06.17 feature flag：rollout 的細粒度控制層</li>
<li>06.18 reliability metrics：CFR 是 gate 健康度</li>
</ul>
]]></content:encoded></item><item><title>LitmusChaos</title><link>https://tarrragon.github.io/blog/backend/06-reliability/vendors/litmuschaos/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/vendors/litmuschaos/</guid><description>&lt;p>LitmusChaos 是 CNCF graduated 的 Kubernetes chaos engineering 平台、承擔三個責任：ChaosHub experiment marketplace（現成 experiment 直接用）、ChaosWorkflow 編排多步驟實驗、Probe-based steady state validation。設計取捨偏向「現成 experiment 庫 + workflow-centric + CNCF graduated 治理」、是 Chaos Mesh 的近競品、Harness 提供商業版（ChaosNative）。&lt;/p>
&lt;h2 id="本章目標">本章目標&lt;/h2>
&lt;p>讀完本章後、你應該能：&lt;/p>
&lt;ol>
&lt;li>部署 Litmus 到 K8s&lt;/li>
&lt;li>從 ChaosHub 引用現成 experiment&lt;/li>
&lt;li>寫 ChaosWorkflow（多步驟 + probe）&lt;/li>
&lt;li>設計 Probe（HTTP / Cmd / K8s / Prometheus）做 steady state&lt;/li>
&lt;li>評估 Litmus vs Chaos Mesh vs Gremlin 的選用&lt;/li>
&lt;/ol>
&lt;h2 id="最短路徑5-分鐘把-litmus-跑起來">最短路徑：5 分鐘把 Litmus 跑起來&lt;/h2>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 1. 安裝&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: helm install litmus litmus/litmus -n litmus --create-namespace&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 2. 從 ChaosHub 引用 experiment&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: kubectl apply -f https://hub.litmuschaos.io/...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 3. 跑 experiment + 看 ChaosResult&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: kubectl apply -f chaosengine.yaml&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: kubectl describe chaosresult &amp;lt;name&amp;gt;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="日常操作與決策形狀">日常操作與決策形狀&lt;/h2>
&lt;h3 id="crd-設計">CRD 設計&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>ChaosExperiment：experiment 定義&lt;/li>
&lt;li>ChaosEngine：bind experiment 到 target&lt;/li>
&lt;li>ChaosResult：執行結果&lt;/li>
&lt;/ul>
&lt;h3 id="chaoshub-experiment">ChaosHub experiment&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>現成 experiment marketplace&lt;/li>
&lt;li>Generic / Kafka / Cassandra / GCP / AWS / VMware experiments&lt;/li>
&lt;li>自訂 experiment 上傳 Hub&lt;/li>
&lt;/ul>
&lt;h3 id="chaosworkflow">ChaosWorkflow&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>Argo Workflow-based&lt;/li>
&lt;li>多步驟 chaos 編排&lt;/li>
&lt;li>Schedule trigger&lt;/li>
&lt;/ul>
&lt;h2 id="進階主題按需閱讀">進階主題（按需閱讀）&lt;/h2>
&lt;h3 id="probe-based-steady-state">Probe-based steady state&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>HTTP probe / Cmd probe / K8s probe / Prometheus probe&lt;/li>
&lt;li>跟 chaos 同步 / 序列執行&lt;/li>
&lt;li>Success threshold 設計&lt;/li>
&lt;/ul>
&lt;h3 id="chaoscentercontrol-plane">ChaosCenter（control plane）&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>跨 cluster chaos 管理&lt;/li>
&lt;li>ChaosResult dashboard&lt;/li>
&lt;li>RBAC 控制&lt;/li>
&lt;/ul>
&lt;h3 id="harness-chaosnative商業">Harness ChaosNative（商業）&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>商業支援版本&lt;/li>
&lt;li>跟 Harness CD 整合&lt;/li>
&lt;li>Enterprise governance&lt;/li>
&lt;/ul>
&lt;h3 id="跟-chaos-mesh-對照">跟 Chaos Mesh 對照&lt;/h3>
&lt;p>子議題：&lt;/p></description><content:encoded><![CDATA[<p>LitmusChaos 是 CNCF graduated 的 Kubernetes chaos engineering 平台、承擔三個責任：ChaosHub experiment marketplace（現成 experiment 直接用）、ChaosWorkflow 編排多步驟實驗、Probe-based steady state validation。設計取捨偏向「現成 experiment 庫 + workflow-centric + CNCF graduated 治理」、是 Chaos Mesh 的近競品、Harness 提供商業版（ChaosNative）。</p>
<h2 id="本章目標">本章目標</h2>
<p>讀完本章後、你應該能：</p>
<ol>
<li>部署 Litmus 到 K8s</li>
<li>從 ChaosHub 引用現成 experiment</li>
<li>寫 ChaosWorkflow（多步驟 + probe）</li>
<li>設計 Probe（HTTP / Cmd / K8s / Prometheus）做 steady state</li>
<li>評估 Litmus vs Chaos Mesh vs Gremlin 的選用</li>
</ol>
<h2 id="最短路徑5-分鐘把-litmus-跑起來">最短路徑：5 分鐘把 Litmus 跑起來</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 1. 安裝</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># TODO: helm install litmus litmus/litmus -n litmus --create-namespace</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 2. 從 ChaosHub 引用 experiment</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># TODO: kubectl apply -f https://hub.litmuschaos.io/...</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># 3. 跑 experiment + 看 ChaosResult</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c1"># TODO: kubectl apply -f chaosengine.yaml</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="c1"># TODO: kubectl describe chaosresult &lt;name&gt;</span></span></span></code></pre></div><h2 id="日常操作與決策形狀">日常操作與決策形狀</h2>
<h3 id="crd-設計">CRD 設計</h3>
<p>子議題：</p>
<ul>
<li>ChaosExperiment：experiment 定義</li>
<li>ChaosEngine：bind experiment 到 target</li>
<li>ChaosResult：執行結果</li>
</ul>
<h3 id="chaoshub-experiment">ChaosHub experiment</h3>
<p>子議題：</p>
<ul>
<li>現成 experiment marketplace</li>
<li>Generic / Kafka / Cassandra / GCP / AWS / VMware experiments</li>
<li>自訂 experiment 上傳 Hub</li>
</ul>
<h3 id="chaosworkflow">ChaosWorkflow</h3>
<p>子議題：</p>
<ul>
<li>Argo Workflow-based</li>
<li>多步驟 chaos 編排</li>
<li>Schedule trigger</li>
</ul>
<h2 id="進階主題按需閱讀">進階主題（按需閱讀）</h2>
<h3 id="probe-based-steady-state">Probe-based steady state</h3>
<p>子議題：</p>
<ul>
<li>HTTP probe / Cmd probe / K8s probe / Prometheus probe</li>
<li>跟 chaos 同步 / 序列執行</li>
<li>Success threshold 設計</li>
</ul>
<h3 id="chaoscentercontrol-plane">ChaosCenter（control plane）</h3>
<p>子議題：</p>
<ul>
<li>跨 cluster chaos 管理</li>
<li>ChaosResult dashboard</li>
<li>RBAC 控制</li>
</ul>
<h3 id="harness-chaosnative商業">Harness ChaosNative（商業）</h3>
<p>子議題：</p>
<ul>
<li>商業支援版本</li>
<li>跟 Harness CD 整合</li>
<li>Enterprise governance</li>
</ul>
<h3 id="跟-chaos-mesh-對照">跟 Chaos Mesh 對照</h3>
<p>子議題：</p>
<ul>
<li>Litmus：workflow-centric、ChaosHub</li>
<li>Chaos Mesh：CRD-driven、Dashboard 友善</li>
<li>選擇判讀：現成 experiment 庫 → Litmus；fault types 多樣 → Chaos Mesh</li>
</ul>
<h3 id="chaos-as-code">Chaos as Code</h3>
<p>子議題：</p>
<ul>
<li>ChaosWorkflow YAML version control</li>
<li>GitOps integration</li>
<li>PR-based chaos review</li>
</ul>
<h2 id="排錯快速判讀">排錯快速判讀</h2>
<h3 id="experiment-fail-to-start">Experiment fail to start</h3>
<p>操作原則：ServiceAccount + RBAC 不對、experiment image pull 失敗。判讀：<code>kubectl describe chaosengine</code>。</p>
<h3 id="probe-失敗">Probe 失敗</h3>
<p>操作原則：probe 條件設錯 / target 沒準備好。判讀：ChaosResult 看 probe verdict。</p>
<h3 id="hub-experiment-引用版本不對">Hub experiment 引用版本不對</h3>
<p>操作原則：experiment.yaml 跟 Litmus version 不對齊。判讀：Litmus version + experiment compatibility。</p>
<h3 id="workflow-卡住">Workflow 卡住</h3>
<p>操作原則：Argo Workflow 卡 → 看 Argo pod log。</p>
<h2 id="何時改走其他服務">何時改走其他服務</h2>
<table>
  <thead>
      <tr>
          <th>需求形狀</th>
          <th>改走</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>多 fault types / Dashboard</td>
          <td><a href="/blog/backend/06-reliability/vendors/chaos-mesh/" data-link-title="Chaos Mesh" data-link-desc="Kubernetes-native chaos engineering（CNCF incubating）">Chaos Mesh</a></td>
      </tr>
      <tr>
          <td>非 K8s / 商業</td>
          <td><a href="/blog/backend/06-reliability/vendors/gremlin/" data-link-title="Gremlin" data-link-desc="商業 chaos engineering 平台、跨平台與 GameDay">Gremlin</a></td>
      </tr>
      <tr>
          <td>Integration test</td>
          <td><a href="/blog/backend/06-reliability/vendors/toxiproxy/" data-link-title="Toxiproxy" data-link-desc="TCP-level fault injection proxy（Shopify 開源）">Toxiproxy</a></td>
      </tr>
      <tr>
          <td>AWS-native</td>
          <td>AWS Fault Injection Service</td>
      </tr>
  </tbody>
</table>
<h2 id="不在本頁內的主題">不在本頁內的主題</h2>
<ul>
<li>ChaosHub 各 experiment 詳細 parameter</li>
<li>Argo Workflow 內部</li>
<li>Litmus 商業版本 detail</li>
</ul>
<h2 id="案例回寫">案例回寫</h2>
<table>
  <thead>
      <tr>
          <th>案例方向</th>
          <th>對應主題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/netflix/steady-state-chaos-and-fit/" data-link-title="Netflix：Steady State、Chaos 與 FIT 的驗證路徑" data-link-desc="把故障注入從工具操作升級成可驗證流程：先定義穩態，再設計注入與回復條件。">Netflix：Steady State、Chaos 與 FIT</a></td>
          <td>hypothesis-driven experiment 對應 ChaosHub workflow</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/spotify/platform-engineering-and-reliability-contracts/" data-link-title="Spotify：平台工程與可靠性契約" data-link-desc="用平台契約統一服務團隊的可靠性最低標準，降低跨團隊變更造成的隱性風險。">Spotify：平台工程與可靠性契約</a></td>
          <td>squad-based 採用 chaos 的平台化路徑</td>
      </tr>
  </tbody>
</table>
<p><strong>Case 庫稀薄</strong>：本 cases/ 目錄目前沒有以 LitmusChaos 為主軸的案例。</p>
<ul>
<li><strong>待補 LitmusChaos customer case</strong>：CNCF graduated 後客戶採用案例、Harness ChaosNative 客戶</li>
<li><strong>候選 case</strong>：Meta（K8s-native region failover chaos）、Microsoft（Chaos Studio 對照組）— 若未來收錄需先在 cases/ 補正文</li>
</ul>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>上游概念：<a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 Experiment Safety Boundary</a></li>
<li>平行 vendor：<a href="/blog/backend/06-reliability/vendors/chaos-mesh/" data-link-title="Chaos Mesh" data-link-desc="Kubernetes-native chaos engineering（CNCF incubating）">Chaos Mesh</a>、<a href="/blog/backend/06-reliability/vendors/gremlin/" data-link-title="Gremlin" data-link-desc="商業 chaos engineering 平台、跨平台與 GameDay">Gremlin</a></li>
<li>下游能力：<a href="/blog/backend/08-incident-response/" data-link-title="模組八：事故處理與復盤" data-link-desc="用 IR 領域詞彙建問題節點、以服務級案例庫累積事故脈絡，先建概念與案例庫再進實作交接">8 incident response</a></li>
</ul>
]]></content:encoded></item><item><title>6.9 容量與成本邊界</title><link>https://tarrragon.github.io/blog/backend/06-reliability/capacity-cost/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/capacity-cost/</guid><description>&lt;h2 id="大綱">大綱&lt;/h2>
&lt;ul>
&lt;li>容量規劃的核心：peak demand × headroom × growth curve&lt;/li>
&lt;li>headroom 訂定：成本 vs 突發承載 tradeoff&lt;/li>
&lt;li>capacity test 跟 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2 load test&lt;/a> 的差異：load 看 throughput、capacity 看 saturation 與 cost curve&lt;/li>
&lt;li>成本作為驗證輸入：autoscaling 上限、預算告警、queue lag 跟成本的關係&lt;/li>
&lt;li>跨層容量：DB connection、queue、cache、CDN、第三方 API rate limit&lt;/li>
&lt;li>跟 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6 SLO&lt;/a> 的耦合：SLO 達成的容量代價&lt;/li>
&lt;li>反模式：容量規劃只看 CPU、autoscaling 無上限、成本失控用降級掩蓋&lt;/li>
&lt;/ul>
&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>Capacity 與成本邊界是把容量規劃跟成本約束一起看，責任是讓系統能承載預期負載，同時不把成本曲線推到不可接受區域。&lt;/p>
&lt;p>這一頁處理的是規模化之後的 trade-off。容量不是越高越好，真正的目標是找到能維持 SLO、又不浪費資源的區間。&lt;/p>
&lt;h2 id="核心判讀">核心判讀&lt;/h2>
&lt;p>判讀 capacity 時，先看 saturation 點，再看成本曲線是不是隨之失控。&lt;/p>
&lt;p>重點訊號包括：&lt;/p>
&lt;ul>
&lt;li>autoscaling 是否有清楚上限與成本門檻&lt;/li>
&lt;li>依賴層是否先於應用層成為瓶頸&lt;/li>
&lt;li>peak forecast 是否涵蓋活動、季節性與推廣事件&lt;/li>
&lt;li>降級是否被當成例外策略，而不是常態容量替代&lt;/li>
&lt;/ul>
&lt;h2 id="案例對照">案例對照&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/shopify/" data-link-title="Shopify" data-link-desc="Shopify BFCM Scaling / Pod-based Isolation / Capacity Planning">Shopify&lt;/a>：高峰型流量把容量與成本的邊界推得很清楚。&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/linkedin/" data-link-title="LinkedIn" data-link-desc="LinkedIn Capacity Planning 與 On-call 結構">LinkedIn&lt;/a>：互動型服務常先在某個依賴層出現瓶頸。&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/amazon/" data-link-title="Amazon" data-link-desc="Amazon Cell-based Architecture / Shuffle Sharding / Blast Radius 設計">Amazon&lt;/a>：大規模系統常把成本與可靠性一起做優化。&lt;/li>
&lt;/ul>
&lt;h2 id="高峰型容量治理game-day--capacity-planning">高峰型容量治理：Game Day + Capacity Planning&lt;/h2>
&lt;p>高峰型容量治理是把「可預期的非典型流量」當獨立治理面操作的能力。涵蓋 baseline 預估、邊界隔離、game day 驗證跟 resiliency matrix 對齊四個面向。日常擴容靠 autoscaling、高峰需要的是預先驗證跟邊界控制 — 峰值期間擴容延遲跟依賴抖動會疊加放大成事故。&lt;/p>
&lt;p>對應 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/shopify/bfcm-capacity-and-game-day/" data-link-title="Shopify：BFCM 容量治理與 Game Day 驗證節奏" data-link-desc="把季節性流量峰值轉成年度可靠性流程，透過容量模型、演練與隔離策略提前吸收風險。">H1 Shopify BFCM 容量治理與 Game Day&lt;/a>：揭露四個機制對應上述四個面向 — capacity planning baseline（高峰前可承受上限是多少）、pod/isolation boundary（故障影響如何限制在局部）、game day（高峰前如何驗證假設）、resiliency matrix（服務與失效模式如何對齊）。&lt;/p>
&lt;p>可重複套用的做法：&lt;/p>
&lt;p>三個治理面性質不同、不是同一個時間軸的三步驟：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Capacity planning&lt;/strong>（forecast + headroom 模型）：高峰前 N 週開始、整合 forecast、headroom、依賴 quota — 不只看單一 CPU 數字、要看整條依賴鏈的瓶頸層&lt;/li>
&lt;li>&lt;strong>Game day&lt;/strong>（production-like 假設驗證）：高峰前 N 天執行、把 runbook、matrix、驗證腳本、放行門檻當固定資產輸出、不是「跑完就好」&lt;/li>
&lt;li>&lt;strong>Isolation boundary&lt;/strong>（runtime 故障擴散控制）：高峰當下持續運作、cell 邊界跟 graceful degradation 把故障限制在最小可影響範圍、補強 autoscaling 來不及的延遲段&lt;/li>
&lt;/ul>
&lt;p>把每輪活動輸出的缺口回寫成固定資產（不只是「一次性專案」），下一輪準備就能從更高基準開始。&lt;/p>
&lt;h2 id="容量跟值班分層的協同">容量跟值班分層的協同&lt;/h2>
&lt;p>容量跟值班分層的綁定責任是讓「容量門檻」跟「升級路徑」在同一個 trigger 觸發：接近 headroom 限制時值班自動分層、避免事故發生才升級。這個綁定需要三件事配合：runtime 訊號（headroom 預算）、接手機制（三層值班）、模型校準（壓測驗證）。&lt;/p>
&lt;p>對應 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/linkedin/capacity-headroom-and-oncall-tiering/" data-link-title="LinkedIn：Capacity Headroom 與 On-call 分層" data-link-desc="把容量預測與值班分層綁在一起，降低高峰時段的升級混亂與恢復延遲。">L1 LinkedIn Capacity Headroom 與 On-call 分層&lt;/a>：揭露三個機制對應上述三件事 — headroom 預算（何時進入風險區）、primary/secondary/SME 三層值班（何時由誰接手）、自動化壓測（模型是否貼近現況）。前兩個是 runtime 治理、後者是 model 校準、屬於不同邏輯位階。&lt;/p></description><content:encoded><![CDATA[<h2 id="大綱">大綱</h2>
<ul>
<li>容量規劃的核心：peak demand × headroom × growth curve</li>
<li>headroom 訂定：成本 vs 突發承載 tradeoff</li>
<li>capacity test 跟 <a href="/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2 load test</a> 的差異：load 看 throughput、capacity 看 saturation 與 cost curve</li>
<li>成本作為驗證輸入：autoscaling 上限、預算告警、queue lag 跟成本的關係</li>
<li>跨層容量：DB connection、queue、cache、CDN、第三方 API rate limit</li>
<li>跟 <a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6 SLO</a> 的耦合：SLO 達成的容量代價</li>
<li>反模式：容量規劃只看 CPU、autoscaling 無上限、成本失控用降級掩蓋</li>
</ul>
<h2 id="概念定位">概念定位</h2>
<p>Capacity 與成本邊界是把容量規劃跟成本約束一起看，責任是讓系統能承載預期負載，同時不把成本曲線推到不可接受區域。</p>
<p>這一頁處理的是規模化之後的 trade-off。容量不是越高越好，真正的目標是找到能維持 SLO、又不浪費資源的區間。</p>
<h2 id="核心判讀">核心判讀</h2>
<p>判讀 capacity 時，先看 saturation 點，再看成本曲線是不是隨之失控。</p>
<p>重點訊號包括：</p>
<ul>
<li>autoscaling 是否有清楚上限與成本門檻</li>
<li>依賴層是否先於應用層成為瓶頸</li>
<li>peak forecast 是否涵蓋活動、季節性與推廣事件</li>
<li>降級是否被當成例外策略，而不是常態容量替代</li>
</ul>
<h2 id="案例對照">案例對照</h2>
<ul>
<li><a href="/blog/backend/06-reliability/cases/shopify/" data-link-title="Shopify" data-link-desc="Shopify BFCM Scaling / Pod-based Isolation / Capacity Planning">Shopify</a>：高峰型流量把容量與成本的邊界推得很清楚。</li>
<li><a href="/blog/backend/06-reliability/cases/linkedin/" data-link-title="LinkedIn" data-link-desc="LinkedIn Capacity Planning 與 On-call 結構">LinkedIn</a>：互動型服務常先在某個依賴層出現瓶頸。</li>
<li><a href="/blog/backend/06-reliability/cases/amazon/" data-link-title="Amazon" data-link-desc="Amazon Cell-based Architecture / Shuffle Sharding / Blast Radius 設計">Amazon</a>：大規模系統常把成本與可靠性一起做優化。</li>
</ul>
<h2 id="高峰型容量治理game-day--capacity-planning">高峰型容量治理：Game Day + Capacity Planning</h2>
<p>高峰型容量治理是把「可預期的非典型流量」當獨立治理面操作的能力。涵蓋 baseline 預估、邊界隔離、game day 驗證跟 resiliency matrix 對齊四個面向。日常擴容靠 autoscaling、高峰需要的是預先驗證跟邊界控制 — 峰值期間擴容延遲跟依賴抖動會疊加放大成事故。</p>
<p>對應 <a href="/blog/backend/06-reliability/cases/shopify/bfcm-capacity-and-game-day/" data-link-title="Shopify：BFCM 容量治理與 Game Day 驗證節奏" data-link-desc="把季節性流量峰值轉成年度可靠性流程，透過容量模型、演練與隔離策略提前吸收風險。">H1 Shopify BFCM 容量治理與 Game Day</a>：揭露四個機制對應上述四個面向 — capacity planning baseline（高峰前可承受上限是多少）、pod/isolation boundary（故障影響如何限制在局部）、game day（高峰前如何驗證假設）、resiliency matrix（服務與失效模式如何對齊）。</p>
<p>可重複套用的做法：</p>
<p>三個治理面性質不同、不是同一個時間軸的三步驟：</p>
<ul>
<li><strong>Capacity planning</strong>（forecast + headroom 模型）：高峰前 N 週開始、整合 forecast、headroom、依賴 quota — 不只看單一 CPU 數字、要看整條依賴鏈的瓶頸層</li>
<li><strong>Game day</strong>（production-like 假設驗證）：高峰前 N 天執行、把 runbook、matrix、驗證腳本、放行門檻當固定資產輸出、不是「跑完就好」</li>
<li><strong>Isolation boundary</strong>（runtime 故障擴散控制）：高峰當下持續運作、cell 邊界跟 graceful degradation 把故障限制在最小可影響範圍、補強 autoscaling 來不及的延遲段</li>
</ul>
<p>把每輪活動輸出的缺口回寫成固定資產（不只是「一次性專案」），下一輪準備就能從更高基準開始。</p>
<h2 id="容量跟值班分層的協同">容量跟值班分層的協同</h2>
<p>容量跟值班分層的綁定責任是讓「容量門檻」跟「升級路徑」在同一個 trigger 觸發：接近 headroom 限制時值班自動分層、避免事故發生才升級。這個綁定需要三件事配合：runtime 訊號（headroom 預算）、接手機制（三層值班）、模型校準（壓測驗證）。</p>
<p>對應 <a href="/blog/backend/06-reliability/cases/linkedin/capacity-headroom-and-oncall-tiering/" data-link-title="LinkedIn：Capacity Headroom 與 On-call 分層" data-link-desc="把容量預測與值班分層綁在一起，降低高峰時段的升級混亂與恢復延遲。">L1 LinkedIn Capacity Headroom 與 On-call 分層</a>：揭露三個機制對應上述三件事 — headroom 預算（何時進入風險區）、primary/secondary/SME 三層值班（何時由誰接手）、自動化壓測（模型是否貼近現況）。前兩個是 runtime 治理、後者是 model 校準、屬於不同邏輯位階。</p>
<p>容量規劃要回答「擴容門檻是多少」、值班分層要回答「接近門檻時誰接手」。兩者綁定後、高峰期值班分層自動觸發、不需等事故發生才升級。詳見 <a href="/blog/backend/08-incident-response/ic-handoff-long-incident/" data-link-title="8.12 IC Handoff 與長事故跨班次協調" data-link-desc="把 24h&#43; / 跨 timezone 事故的接班節奏變成可重複流程">8.12 IC handoff for long incident</a>。</p>
<h2 id="快取容量的特殊性">快取容量的特殊性</h2>
<p>快取容量治理的核心責任是失溫時資料層仍可承受。headroom 不是看快取 QPS、是看命中率下滑後的回源放大係數 — 快取本身可能耐 10x 流量、資料層可能撐不到 1.5x。</p>
<p>對應 <a href="/blog/backend/06-reliability/cases/pinterest/cache-reliability-and-capacity-surprises/" data-link-title="Pinterest：快取可靠性與容量驚奇治理" data-link-desc="針對快取層失效與流量突增，建立容量緩衝、退化路徑與重建節奏。">P1 Pinterest 快取可靠性與容量驚奇治理</a>：揭露三個機制 — cache headroom（命中率下滑能承受多久）、graceful degradation（快取失效時如何降級）、rewarm strategy（熱資料如何有序回填）。</p>
<p>快取容量規劃的核心問題是失溫時資料層能承受的回源放大係數。命中率從 95% 掉到 80% 意味資料層流量 4x、能否承受決定快取退化會不會升級為事故。預先設計 graceful degradation 路徑跟 rewarm 節奏、能避免快取失溫變成連鎖退化。詳見 <a href="/blog/backend/02-cache-redis/cache-migration-stampede-rollback/" data-link-title="2.9 Cache Migration 與 Stampede Rollback（實作示範）" data-link-desc="以商品詳情與價格快取示範 cache migration 如何交付 evidence package、release gate 與 incident decision log。">2.9 cache stampede rollback</a>。</p>
<h2 id="產業情境電商與零售">產業情境：電商與零售</h2>
<p>電商的容量規劃受峰值倍率與峰值持續時間的雙重約束。為年度一次的峰值預留全年容量成本過高，但峰值容量不足會直接損失營收 — 容量不足在電商是可量化的商業損失（每分鐘宕機對應可估算的 GMV 損失），技術事故與營收衝擊直接掛鉤。</p>
<p>峰值容量策略有三種模式，各自的成本與風險形狀不同。全年預留是最安全但成本最高的做法，適合峰值與日常倍率差距小（&lt; 3x）的服務。彈性擴容依賴 auto-scaling 在峰值到來時及時反應，但擴容延遲（分鐘級）加上依賴層的 warm-up 時間可能讓尖峰初期無法承接。峰值前臨時擴容需要提前 provision 並用 game day 驗證擴容路徑，是中等成本但需要較高工程投入的選項。多數大型電商混用三者：核心路徑全年預留、彈性層 auto-scale、輔助服務臨時擴容。</p>
<p>降級策略在電商有明確的不可降級邊界。推薦引擎、搜尋排序、個人化功能可以在壓力下退回簡化版或靜態結果，但結帳路徑（購物車 → 付款 → 訂單確認）不能降級 — 結帳流程中斷等於訂單流失，使用者不會等系統恢復後重新結帳。降級策略的設計需要把服務按「可降級 / 不可降級」分層，壓力下優先保護不可降級路徑的資源配額。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>06.2 load testing：capacity 輸入來自 workload model</li>
<li>06.9 reliability metrics：容量與成本要有量測口徑</li>
<li>06.13 perf regression gate：效能退化通常伴隨成本上升</li>
</ul>
<h2 id="判讀訊號">判讀訊號</h2>
<ul>
<li>autoscaling max 設無限大、或長期未觸碰</li>
<li>容量規劃只看 CPU、忽略 connection pool / queue / 第三方 quota</li>
<li>peak 流量 forecast 是直線外推、未考慮 promo / seasonal / 行銷事件</li>
<li>成本告警觸發後才回頭討論容量</li>
<li>降級邏輯被當成常態容量緩衝、而非例外保護</li>
</ul>
<h2 id="交接路由">交接路由</h2>
<ul>
<li>04 觀測：saturation metric、cost dashboard</li>
<li>05 部署：HPA / autoscaling policy</li>
<li>06.6 SLO：容量不足導致 SLO 風險</li>
<li>04.15 cost attribution：observability 成本作為總體成本一部分</li>
</ul>
]]></content:encoded></item><item><title>Gremlin</title><link>https://tarrragon.github.io/blog/backend/06-reliability/vendors/gremlin/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/vendors/gremlin/</guid><description>&lt;p>Gremlin 是商業 chaos engineering SaaS、承擔三個責任：跨平台 chaos（VM / container / K8s / cloud 都有 agent）、GameDay 設計 + 報告功能、enterprise-grade audit + blast radius guardrail。設計取捨偏向「商業支援 + 跨平台 + 企業安全 + Halt button 緊急中止」、適合非純 K8s 環境 + 需要商業 SLA 的團隊。Founder 來自 Netflix Chaos team。&lt;/p>
&lt;h2 id="本章目標">本章目標&lt;/h2>
&lt;p>讀完本章後、你應該能：&lt;/p>
&lt;ol>
&lt;li>部署 Gremlin agent 到 VM / container / K8s&lt;/li>
&lt;li>設計 attack（resource / state / network）+ blast radius&lt;/li>
&lt;li>跑 Scenario / GameDay + 報告交付&lt;/li>
&lt;li>用 Halt button 緊急中止&lt;/li>
&lt;li>評估 Gremlin vs Chaos Mesh / LitmusChaos 的選用&lt;/li>
&lt;/ol>
&lt;h2 id="最短路徑5-分鐘把-gremlin-跑起來">最短路徑：5 分鐘把 Gremlin 跑起來&lt;/h2>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 1. 註冊 + 取得 team API key&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: gremlin install or container agent&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 2. 第一個 attack&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: gremlin attack-container --target ... --type cpu&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 3. Dashboard 看 attack timeline&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: app.gremlin.com&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="日常操作與決策形狀">日常操作與決策形狀&lt;/h2>
&lt;h3 id="attack-types">Attack types&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>Resource：CPU / memory / disk / IO&lt;/li>
&lt;li>State：shutdown / process kill / time travel&lt;/li>
&lt;li>Network：blackhole / DNS / latency / packet loss&lt;/li>
&lt;li>Application：custom error inject&lt;/li>
&lt;/ul>
&lt;h3 id="blast-radius--magnitude">Blast radius + magnitude&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>Target selection（host / container / K8s pod）&lt;/li>
&lt;li>Magnitude（影響度、CPU %、latency ms）&lt;/li>
&lt;li>Duration（短到分鐘 / 長到小時）&lt;/li>
&lt;li>Halt button：emergency stop&lt;/li>
&lt;/ul>
&lt;h3 id="scenario--gameday-設計">Scenario / GameDay 設計&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>Multi-step attack scenario&lt;/li>
&lt;li>GameDay 跨 team 演練設計&lt;/li>
&lt;li>Report 自動產生&lt;/li>
&lt;/ul>
&lt;h2 id="進階主題按需閱讀">進階主題（按需閱讀）&lt;/h2>
&lt;h3 id="cross-platform-agent">Cross-platform agent&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>VM agent（Linux / Windows）&lt;/li>
&lt;li>Container agent（Docker / Kubernetes DaemonSet）&lt;/li>
&lt;li>Cloud agent（AWS / GCP / Azure）&lt;/li>
&lt;li>Agent-less mode（限制較多）&lt;/li>
&lt;/ul>
&lt;h3 id="enterprise-audit--rbac">Enterprise audit + RBAC&lt;/h3>
&lt;p>子議題：&lt;/p></description><content:encoded><![CDATA[<p>Gremlin 是商業 chaos engineering SaaS、承擔三個責任：跨平台 chaos（VM / container / K8s / cloud 都有 agent）、GameDay 設計 + 報告功能、enterprise-grade audit + blast radius guardrail。設計取捨偏向「商業支援 + 跨平台 + 企業安全 + Halt button 緊急中止」、適合非純 K8s 環境 + 需要商業 SLA 的團隊。Founder 來自 Netflix Chaos team。</p>
<h2 id="本章目標">本章目標</h2>
<p>讀完本章後、你應該能：</p>
<ol>
<li>部署 Gremlin agent 到 VM / container / K8s</li>
<li>設計 attack（resource / state / network）+ blast radius</li>
<li>跑 Scenario / GameDay + 報告交付</li>
<li>用 Halt button 緊急中止</li>
<li>評估 Gremlin vs Chaos Mesh / LitmusChaos 的選用</li>
</ol>
<h2 id="最短路徑5-分鐘把-gremlin-跑起來">最短路徑：5 分鐘把 Gremlin 跑起來</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 1. 註冊 + 取得 team API key</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># TODO: gremlin install or container agent</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 2. 第一個 attack</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># TODO: gremlin attack-container --target ... --type cpu</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># 3. Dashboard 看 attack timeline</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c1"># TODO: app.gremlin.com</span></span></span></code></pre></div><h2 id="日常操作與決策形狀">日常操作與決策形狀</h2>
<h3 id="attack-types">Attack types</h3>
<p>子議題：</p>
<ul>
<li>Resource：CPU / memory / disk / IO</li>
<li>State：shutdown / process kill / time travel</li>
<li>Network：blackhole / DNS / latency / packet loss</li>
<li>Application：custom error inject</li>
</ul>
<h3 id="blast-radius--magnitude">Blast radius + magnitude</h3>
<p>子議題：</p>
<ul>
<li>Target selection（host / container / K8s pod）</li>
<li>Magnitude（影響度、CPU %、latency ms）</li>
<li>Duration（短到分鐘 / 長到小時）</li>
<li>Halt button：emergency stop</li>
</ul>
<h3 id="scenario--gameday-設計">Scenario / GameDay 設計</h3>
<p>子議題：</p>
<ul>
<li>Multi-step attack scenario</li>
<li>GameDay 跨 team 演練設計</li>
<li>Report 自動產生</li>
</ul>
<h2 id="進階主題按需閱讀">進階主題（按需閱讀）</h2>
<h3 id="cross-platform-agent">Cross-platform agent</h3>
<p>子議題：</p>
<ul>
<li>VM agent（Linux / Windows）</li>
<li>Container agent（Docker / Kubernetes DaemonSet）</li>
<li>Cloud agent（AWS / GCP / Azure）</li>
<li>Agent-less mode（限制較多）</li>
</ul>
<h3 id="enterprise-audit--rbac">Enterprise audit + RBAC</h3>
<p>子議題：</p>
<ul>
<li>Team / Project / Role 設計</li>
<li>Attack approval workflow</li>
<li>Audit log</li>
<li>SSO / SAML</li>
</ul>
<h3 id="跟-oss-chaos-對比">跟 OSS chaos 對比</h3>
<p>子議題：</p>
<ul>
<li>Gremlin：商業 / 跨平台 / GameDay / 報告</li>
<li>OSS（Chaos Mesh / Litmus）：成本低 / K8s-only / 自管</li>
<li>選型判讀：企業合規 + 跨平台 → Gremlin；K8s-only + 預算敏感 → OSS</li>
</ul>
<h3 id="halt-button">Halt button</h3>
<p>子議題：</p>
<ul>
<li>緊急 stop 所有 active attack</li>
<li>對應 <a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 Experiment Safety Boundary</a></li>
<li>跟 incident response 連動</li>
</ul>
<h3 id="application-level-fault">Application-level fault</h3>
<p>子議題：</p>
<ul>
<li>Gremlin ALFI（Application-Level Fault Injection）</li>
<li>SDK integration</li>
<li>Custom exception inject</li>
</ul>
<h2 id="排錯快速判讀">排錯快速判讀</h2>
<h3 id="agent-連不上-gremlin">Agent 連不上 Gremlin</h3>
<p>操作原則：API key / network 不通、proxy 配置錯。</p>
<h3 id="attack-沒生效">Attack 沒生效</h3>
<p>操作原則：target selection 沒匹配 / agent 沒安裝。</p>
<h3 id="halt-不及時">Halt 不及時</h3>
<p>操作原則：halt button 全 active attack 立即停、但已造成影響不會回滾。</p>
<h3 id="blast-radius-過大">Blast radius 過大</h3>
<p>操作原則：magnitude / duration 設過大、影響超預期。修法：staging 先測 / 分階段放大。</p>
<h2 id="何時改走其他服務">何時改走其他服務</h2>
<table>
  <thead>
      <tr>
          <th>需求形狀</th>
          <th>改走</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>K8s OSS</td>
          <td><a href="/blog/backend/06-reliability/vendors/chaos-mesh/" data-link-title="Chaos Mesh" data-link-desc="Kubernetes-native chaos engineering（CNCF incubating）">Chaos Mesh</a> / <a href="/blog/backend/06-reliability/vendors/litmuschaos/" data-link-title="LitmusChaos" data-link-desc="Kubernetes chaos engineering 平台（CNCF graduated）">LitmusChaos</a></td>
      </tr>
      <tr>
          <td>Integration test 模擬</td>
          <td><a href="/blog/backend/06-reliability/vendors/toxiproxy/" data-link-title="Toxiproxy" data-link-desc="TCP-level fault injection proxy（Shopify 開源）">Toxiproxy</a></td>
      </tr>
      <tr>
          <td>AWS-only</td>
          <td>AWS Fault Injection Service</td>
      </tr>
      <tr>
          <td>Azure-only</td>
          <td>Azure Chaos Studio</td>
      </tr>
      <tr>
          <td>預算極敏感</td>
          <td>OSS chaos 工具</td>
      </tr>
  </tbody>
</table>
<h2 id="不在本頁內的主題">不在本頁內的主題</h2>
<ul>
<li>Gremlin pricing</li>
<li>各 attack parameter detail</li>
<li>Agent internal</li>
</ul>
<h2 id="案例回寫">案例回寫</h2>
<table>
  <thead>
      <tr>
          <th>案例方向</th>
          <th>對應主題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/netflix/steady-state-chaos-and-fit/" data-link-title="Netflix：Steady State、Chaos 與 FIT 的驗證路徑" data-link-desc="把故障注入從工具操作升級成可驗證流程：先定義穩態，再設計注入與回復條件。">Netflix：Steady State、Chaos 與 FIT</a></td>
          <td>chaos 文化的對照組、商業 vs 自建工具的選擇</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/netflix/chaos-monkey-business-hours-guardrails/" data-link-title="Netflix：Business-Hours Chaos 與 Guardrails" data-link-desc="Chaos Monkey 為何刻意在 business hours 執行：把即時應變能力納入驗證，並用 guardrails 限制實驗風險。">Netflix：Business-Hours Guardrails</a></td>
          <td>attack scope / halt 條件對應時段與 blast radius 控制</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/" data-link-title="Stripe：Idempotency 與零停機遷移的交易安全設計" data-link-desc="把 API 重試與資料遷移放在同一套安全模型，維持支付交易的一致結果。">Stripe：Idempotency 與零停機遷移</a></td>
          <td>Game Day 設計 + 商業 chaos SaaS 的演練節奏</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/shopify/bfcm-capacity-and-game-day/" data-link-title="Shopify：BFCM 容量治理與 Game Day 驗證節奏" data-link-desc="把季節性流量峰值轉成年度可靠性流程，透過容量模型、演練與隔離策略提前吸收風險。">Shopify：BFCM 容量治理與 Game Day</a></td>
          <td>峰值前 Game Day 演練的攻擊類型清單</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/spotify/platform-engineering-and-reliability-contracts/" data-link-title="Spotify：平台工程與可靠性契約" data-link-desc="用平台契約統一服務團隊的可靠性最低標準，降低跨團隊變更造成的隱性風險。">Spotify：平台工程與可靠性契約</a></td>
          <td>squad-based 採用 chaos 的商業工具落地</td>
      </tr>
  </tbody>
</table>
<p><strong>待補 Gremlin customer case</strong>：Stripe / Shopify / Slack 直接公開的 Gremlin GameDay engineering blog（目前以 cases/ 內的可靠性脈絡引用為主）。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>上游概念：<a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 Experiment Safety Boundary</a></li>
<li>平行 vendor：<a href="/blog/backend/06-reliability/vendors/chaos-mesh/" data-link-title="Chaos Mesh" data-link-desc="Kubernetes-native chaos engineering（CNCF incubating）">Chaos Mesh</a>、<a href="/blog/backend/06-reliability/vendors/toxiproxy/" data-link-title="Toxiproxy" data-link-desc="TCP-level fault injection proxy（Shopify 開源）">Toxiproxy</a></li>
<li>下游能力：<a href="/blog/backend/08-incident-response/" data-link-title="模組八：事故處理與復盤" data-link-desc="用 IR 領域詞彙建問題節點、以服務級案例庫累積事故脈絡，先建概念與案例庫再進實作交接">8 incident response</a></li>
</ul>
]]></content:encoded></item><item><title>6.10 Contract Testing 與 Schema 演進</title><link>https://tarrragon.github.io/blog/backend/06-reliability/contract-testing/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/contract-testing/</guid><description>&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>Contract testing 在服務邊界上驗證 producer 與 consumer 的相容性，把跨團隊協作的隱性期待變成可執行的&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/contract/" data-link-title="Boundary Contract" data-link-desc="說明跨邊界約定如何維持相容與可驗證">契約&lt;/a>。&lt;/p>
&lt;p>這一頁處理的是服務邊界上的信任問題。當服務彼此頻繁演進，契約測試是避免變更互相踩踏的最小保護層。契約對準的是真實 consumer 的期待，而不是抽象的 spec 文件。&lt;/p>
&lt;h2 id="核心判讀">核心判讀&lt;/h2>
&lt;p>好的 contract testing 會明確劃出兼容視窗，並把驗證放進 CI 或 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">release gate&lt;/a>。&lt;/p>
&lt;p>判讀時看三件事：&lt;/p>
&lt;ul>
&lt;li>契約是否對準真實 consumer，而非假想 client&lt;/li>
&lt;li>schema evolution 是否有明確 compatibility window&lt;/li>
&lt;li>失敗是否能回到責任邊界，而非只看到測試紅燈&lt;/li>
&lt;/ul>
&lt;h2 id="consumer-driven-vs-provider-driven">Consumer-driven vs Provider-driven&lt;/h2>
&lt;p>契約驗證有兩個驅動方向，適用場景不同。&lt;/p>
&lt;p>&lt;strong>Consumer-driven&lt;/strong>：consumer 先定義對 producer 回應的期望（欄位、型別、值域），producer 驗證是否能滿足。這種做法讓驗證對準真實消費需求 — consumer 只關心它用到的欄位，producer 可以自由演進不被使用的部分。缺點是 consumer 數量多時，契約管理成本上升：每個 consumer 維護自己的契約檔，producer 需要跑所有 consumer 契約才能確認相容。&lt;/p>
&lt;p>&lt;strong>Provider-driven&lt;/strong>：producer 定義 API spec（OpenAPI / gRPC schema），consumer 驗證自己能否適配。producer 主導 schema 演進節奏，consumer 接收變更通知並更新。這種做法適合公開 API 或 consumer 數量大且不可控的服務。缺點是可能漏掉 consumer 依賴的隱性行為 — spec 上合規但語意變了，consumer 仍會失敗。&lt;/p>
&lt;p>判斷依據：consumer 少且已知（內部微服務）→ consumer-driven；consumer 多或不可控（公開 API / 平台整合）→ provider-driven。兩者可混用：核心 consumer 用 consumer-driven 保護關鍵路徑，其他 consumer 靠 provider spec 覆蓋。&lt;/p>
&lt;h2 id="契約驗證的三個層次">契約驗證的三個層次&lt;/h2>
&lt;p>契約驗證按深度分三層，每一層攔截不同類型的破壞。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>層次&lt;/th>
 &lt;th>驗證內容&lt;/th>
 &lt;th>常見工具&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Schema 結構&lt;/td>
 &lt;td>欄位是否存在、型別是否一致&lt;/td>
 &lt;td>JSON Schema validation / protobuf 編譯&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>語意相容&lt;/td>
 &lt;td>值域、enum 範圍、nullable 語意是否對齊&lt;/td>
 &lt;td>Pact interaction / custom assertion&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>向後相容性&lt;/td>
 &lt;td>新版輸出能否被舊版 consumer 解析&lt;/td>
 &lt;td>Avro compatibility check / Buf&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Schema 結構&lt;/strong>是最基礎的防線。欄位缺失或型別錯誤會直接導致 runtime 解析失敗。這一層成本低、回饋快，適合放在 CI fast path。&lt;/p>
&lt;p>&lt;strong>語意相容&lt;/strong>攔截的是「schema 通過但行為不同」的問題。例如某個欄位從 nullable 改成 required，或 enum 新增一個值但 consumer 的 switch 沒有 default branch。這類問題在結構層驗證不出來，需要 consumer 定義語意期望（Pact interaction 的 matcher / assertion）。&lt;/p>
&lt;p>&lt;strong>向後相容性&lt;/strong>是跨版本共存的保障。Avro 和 Protobuf 有內建 compatibility mode（backward / forward / full）；JSON Schema 需要外部工具（如 json-schema-diff）做版本比較。向後相容性驗證的成本最高，但能攔截最嚴重的破壞 — 一旦 event 寫入 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/broker/" data-link-title="Broker" data-link-desc="說明 broker 在訊息傳遞系統中負責保存、路由與交付訊息">broker&lt;/a>，舊版 consumer 就必須能解析它。&lt;/p>
&lt;h2 id="schema-演進規則">Schema 演進規則&lt;/h2>
&lt;p>Schema 演進按協議類型有不同的安全邊界。&lt;/p>
&lt;h3 id="api-schemaopenapi--grpc">API schema（OpenAPI / gRPC）&lt;/h3>
&lt;p>API schema 的演進判讀：新增可選欄位通常安全；移除欄位、重新命名欄位、或把可選改成必填是 breaking change；型別變更（如 int32 → int64）視 consumer 的容忍度而定。gRPC 的 field number 機制讓欄位新增與移除的相容性比 JSON 更明確 — 未知 field number 被忽略，已知 field number 被刪除會觸發 default value，兩者都有可預測行為。&lt;/p></description><content:encoded><![CDATA[<h2 id="概念定位">概念定位</h2>
<p>Contract testing 在服務邊界上驗證 producer 與 consumer 的相容性，把跨團隊協作的隱性期待變成可執行的<a href="/blog/backend/knowledge-cards/contract/" data-link-title="Boundary Contract" data-link-desc="說明跨邊界約定如何維持相容與可驗證">契約</a>。</p>
<p>這一頁處理的是服務邊界上的信任問題。當服務彼此頻繁演進，契約測試是避免變更互相踩踏的最小保護層。契約對準的是真實 consumer 的期待，而不是抽象的 spec 文件。</p>
<h2 id="核心判讀">核心判讀</h2>
<p>好的 contract testing 會明確劃出兼容視窗，並把驗證放進 CI 或 <a href="/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">release gate</a>。</p>
<p>判讀時看三件事：</p>
<ul>
<li>契約是否對準真實 consumer，而非假想 client</li>
<li>schema evolution 是否有明確 compatibility window</li>
<li>失敗是否能回到責任邊界，而非只看到測試紅燈</li>
</ul>
<h2 id="consumer-driven-vs-provider-driven">Consumer-driven vs Provider-driven</h2>
<p>契約驗證有兩個驅動方向，適用場景不同。</p>
<p><strong>Consumer-driven</strong>：consumer 先定義對 producer 回應的期望（欄位、型別、值域），producer 驗證是否能滿足。這種做法讓驗證對準真實消費需求 — consumer 只關心它用到的欄位，producer 可以自由演進不被使用的部分。缺點是 consumer 數量多時，契約管理成本上升：每個 consumer 維護自己的契約檔，producer 需要跑所有 consumer 契約才能確認相容。</p>
<p><strong>Provider-driven</strong>：producer 定義 API spec（OpenAPI / gRPC schema），consumer 驗證自己能否適配。producer 主導 schema 演進節奏，consumer 接收變更通知並更新。這種做法適合公開 API 或 consumer 數量大且不可控的服務。缺點是可能漏掉 consumer 依賴的隱性行為 — spec 上合規但語意變了，consumer 仍會失敗。</p>
<p>判斷依據：consumer 少且已知（內部微服務）→ consumer-driven；consumer 多或不可控（公開 API / 平台整合）→ provider-driven。兩者可混用：核心 consumer 用 consumer-driven 保護關鍵路徑，其他 consumer 靠 provider spec 覆蓋。</p>
<h2 id="契約驗證的三個層次">契約驗證的三個層次</h2>
<p>契約驗證按深度分三層，每一層攔截不同類型的破壞。</p>
<table>
  <thead>
      <tr>
          <th>層次</th>
          <th>驗證內容</th>
          <th>常見工具</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Schema 結構</td>
          <td>欄位是否存在、型別是否一致</td>
          <td>JSON Schema validation / protobuf 編譯</td>
      </tr>
      <tr>
          <td>語意相容</td>
          <td>值域、enum 範圍、nullable 語意是否對齊</td>
          <td>Pact interaction / custom assertion</td>
      </tr>
      <tr>
          <td>向後相容性</td>
          <td>新版輸出能否被舊版 consumer 解析</td>
          <td>Avro compatibility check / Buf</td>
      </tr>
  </tbody>
</table>
<p><strong>Schema 結構</strong>是最基礎的防線。欄位缺失或型別錯誤會直接導致 runtime 解析失敗。這一層成本低、回饋快，適合放在 CI fast path。</p>
<p><strong>語意相容</strong>攔截的是「schema 通過但行為不同」的問題。例如某個欄位從 nullable 改成 required，或 enum 新增一個值但 consumer 的 switch 沒有 default branch。這類問題在結構層驗證不出來，需要 consumer 定義語意期望（Pact interaction 的 matcher / assertion）。</p>
<p><strong>向後相容性</strong>是跨版本共存的保障。Avro 和 Protobuf 有內建 compatibility mode（backward / forward / full）；JSON Schema 需要外部工具（如 json-schema-diff）做版本比較。向後相容性驗證的成本最高，但能攔截最嚴重的破壞 — 一旦 event 寫入 <a href="/blog/backend/knowledge-cards/broker/" data-link-title="Broker" data-link-desc="說明 broker 在訊息傳遞系統中負責保存、路由與交付訊息">broker</a>，舊版 consumer 就必須能解析它。</p>
<h2 id="schema-演進規則">Schema 演進規則</h2>
<p>Schema 演進按協議類型有不同的安全邊界。</p>
<h3 id="api-schemaopenapi--grpc">API schema（OpenAPI / gRPC）</h3>
<p>API schema 的演進判讀：新增可選欄位通常安全；移除欄位、重新命名欄位、或把可選改成必填是 breaking change；型別變更（如 int32 → int64）視 consumer 的容忍度而定。gRPC 的 field number 機制讓欄位新增與移除的相容性比 JSON 更明確 — 未知 field number 被忽略，已知 field number 被刪除會觸發 default value，兩者都有可預測行為。</p>
<h3 id="event-schemaavro--protobuf--json-schema">Event schema（Avro / Protobuf / JSON Schema）</h3>
<p>Event schema 的相容性要求比 API 更嚴格。API 的 breaking change 可以靠 versioning（<code>/v2/</code>）隔離，event 一旦寫入 broker 就跟所有版本的 consumer 共存。backward compatibility（新 schema 能讀舊資料）是最低要求；forward compatibility（舊 schema 能讀新資料）讓 consumer 可以延遲升級。</p>
<p>Schema registry（Confluent Schema Registry / AWS Glue Schema Registry）提供集中式的相容性 gate：producer 註冊新版 schema 前，registry 自動比對相容性規則，拒絕 breaking change。這個 gate 比 CI 更早攔截，因為它在 schema 發布時就生效。</p>
<p>DB schema 演進的契約驗證銜接到 <a href="/blog/backend/06-reliability/migration-safety/" data-link-title="6.11 Migration Safety 與 DB Rollout" data-link-desc="把 schema migration 從一次性事件變成可逆、可漸進的 rollout 流程">6.11 migration safety</a> — expand/contract pattern 讓新舊版本共存，本質上跟 event schema 的 backward compatibility 是同一個問題。</p>
<h2 id="ci-整合">CI 整合</h2>
<p>Contract test 在 CI 的位置跟 unit test 不同 — 需要跨服務的契約同步。</p>
<p><strong>Fast path</strong>：producer 的 schema 變更觸發 consumer 的 contract test。實作上需要 CI 能跨 repo 觸發（webhook / pipeline trigger），或用 contract broker（如 Pact Broker）做非同步驗證。fast path 只跑受影響 consumer 的契約，保持回饋速度。</p>
<p><strong>Slow path</strong>：完整 contract matrix 驗證 — 所有 consumer × producer 組合。這個矩陣在 merge gate 或 scheduled path 跑，覆蓋 fast path 漏掉的間接影響。矩陣規模隨服務數增長，需要 selective matrix（只跑有變更的 producer 相關 consumer）控制成本。</p>
<p><strong>失敗處理</strong>：contract test 失敗時的責任分派是關鍵流程。失敗可能來自 producer 的 breaking change，也可能來自 consumer 的 expectation 過期。Pact 的 can-i-deploy 機制提供自動化判斷：比對 producer 當前版本與 consumer 上次驗證通過的版本，定位責任方。</p>
<h2 id="案例對照">案例對照</h2>
<ul>
<li><a href="/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/" data-link-title="Stripe：Idempotency 與零停機遷移的交易安全設計" data-link-desc="把 API 重試與資料遷移放在同一套安全模型，維持支付交易的一致結果。">Stripe</a>：外部整合的 API 需要嚴格的 backward compatibility — 交易 API 的 breaking change 會直接影響商戶收入，schema 演進靠 expand/contract 逐步過渡。</li>
<li><a href="/blog/backend/06-reliability/cases/shopify/bfcm-capacity-and-game-day/" data-link-title="Shopify：BFCM 容量治理與 Game Day 驗證節奏" data-link-desc="把季節性流量峰值轉成年度可靠性流程，透過容量模型、演練與隔離策略提前吸收風險。">Shopify</a>：跨服務 deploy 順序錯誤是高峰期常見事故源 — contract test 攔截 schema 不相容，讓 deploy 順序有驗證依據。</li>
<li><a href="/blog/backend/08-incident-response/cases/github/" data-link-title="GitHub" data-link-desc="GitHub 重大事故時間線與架構脈絡">GitHub</a>：API 與 webhook 的契約覆蓋面廣，契約失配會直接影響整合生態。</li>
</ul>
<h2 id="判讀訊號">判讀訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀條件</th>
          <th>行動建議</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>跨服務 deploy 順序錯誤導致 production 故障</td>
          <td>contract test 應在 CI 攔截相容性問題，deploy 順序才有驗證依據</td>
          <td>補 contract test 到 CI fast path</td>
      </tr>
      <tr>
          <td>API 文件跟實作漂移、新接入服務出意外</td>
          <td>provider-driven spec 需要自動化 diff 偵測，手動更新會漂移</td>
          <td>接 OpenAPI diff 工具到 CI、spec 變更自動 PR</td>
      </tr>
      <tr>
          <td>event schema 變更後下游 consumer 解析失敗</td>
          <td>schema registry 的 compatibility gate 應在 publish 前攔截</td>
          <td>啟用 schema registry 的 compatibility check</td>
      </tr>
      <tr>
          <td>breaking change 靠 release note 標註</td>
          <td>標註是通知、contract test 是攔截，兩者責任不同</td>
          <td>加 CI contract gate 攔截 breaking change</td>
      </tr>
      <tr>
          <td>contract 違規只在 staging 才發現</td>
          <td>contract test 應在 CI fast path 跑，staging 發現代表 CI 沒覆蓋</td>
          <td>把 contract test 從 staging 提前到 CI push 觸發</td>
      </tr>
  </tbody>
</table>
<h2 id="交接路由">交接路由</h2>
<ul>
<li><a href="/blog/backend/06-reliability/ci-pipeline/" data-link-title="6.1 CI pipeline" data-link-desc="CI pipeline 的分層策略、artifact 管理、flaky 治理與 release gate 輸入">6.1 CI pipeline</a>：contract test 作為 fast path 的跨服務驗證</li>
<li><a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate</a>：contract 通過作為放行條件</li>
<li><a href="/blog/backend/06-reliability/migration-safety/" data-link-title="6.11 Migration Safety 與 DB Rollout" data-link-desc="把 schema migration 從一次性事件變成可逆、可漸進的 rollout 流程">6.11 migration safety</a>：DB schema 演進的契約驗證</li>
<li><a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14 dependency budget</a>：依賴契約穩定性</li>
<li><a href="/blog/backend/06-reliability/environment-parity/" data-link-title="6.15 Environment Parity 與漂移控制" data-link-desc="把 staging / preprod / prod 之間的差異視為一級風險，按漂移來源分類偵測與治理">6.15 environment parity</a>：契約覆蓋的環境邊界</li>
<li><a href="/blog/backend/06-reliability/test-data-management/" data-link-title="6.16 Test Data Management" data-link-desc="把 fixture / seed / production-like data 作為跨模組共用 artifact，治理資料層次、遮罩策略與可重現性">6.16 test data</a>：fixture shape 契約</li>
<li><a href="/blog/backend/06-reliability/feature-flag-governance/" data-link-title="6.17 Feature Flag Governance" data-link-desc="把 feature flag 從上線開關升級為有角色分類、lifecycle 管理與 debt 治理的 runtime artifact">6.17 feature flag</a>：flag 不同分支的契約覆蓋</li>
<li><a href="/blog/backend/05-deployment-platform/" data-link-title="模組五：部署平台與網路入口" data-link-desc="整理 Kubernetes、systemd、load balancer、container 與服務生命週期合約">05 部署</a>：跨服務 deploy 順序協調</li>
</ul>
]]></content:encoded></item><item><title>Toxiproxy</title><link>https://tarrragon.github.io/blog/backend/06-reliability/vendors/toxiproxy/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/vendors/toxiproxy/</guid><description>&lt;p>Toxiproxy 是 Shopify 開源的 TCP-level fault injection proxy、承擔三個責任：TCP 層 fault inject（latency / bandwidth / partition / slow_close）、integration test 中可程式化故障注入（reproducible）、client SDK 多語言（Go / Ruby / Python / JS）。設計取捨偏向「CI-friendly + reproducible + 細粒度 TCP control」、不適合 production chaos、適合 integration test 跟 dependency failure 模擬。&lt;/p>
&lt;h2 id="本章目標">本章目標&lt;/h2>
&lt;p>讀完本章後、你應該能：&lt;/p>
&lt;ol>
&lt;li>跑起 Toxiproxy server + 設 listener / upstream proxy&lt;/li>
&lt;li>用 client SDK 注入 latency / partition / bandwidth toxic&lt;/li>
&lt;li>整合 Toxiproxy 到 integration test（before/after test hook）&lt;/li>
&lt;li>用 Docker Compose 整合&lt;/li>
&lt;li>評估 Toxiproxy vs Chaos Mesh NetworkChaos 的選用&lt;/li>
&lt;/ol>
&lt;h2 id="最短路徑5-分鐘把-toxiproxy-跑起來">最短路徑：5 分鐘把 Toxiproxy 跑起來&lt;/h2>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 1. 啟動 server&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: docker run -d -p 8474:8474 -p 26379:26379 ghcr.io/shopify/toxiproxy&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 2. 建 proxy（Redis 為例）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: curl -X POST localhost:8474/proxies -d &amp;#39;{&amp;#34;name&amp;#34;:&amp;#34;redis&amp;#34;,&amp;#34;listen&amp;#34;:&amp;#34;0.0.0.0:26379&amp;#34;,&amp;#34;upstream&amp;#34;:&amp;#34;redis:6379&amp;#34;}&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 3. 注入 toxic&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: curl -X POST localhost:8474/proxies/redis/toxics -d &amp;#39;{&amp;#34;type&amp;#34;:&amp;#34;latency&amp;#34;,&amp;#34;attributes&amp;#34;:{&amp;#34;latency&amp;#34;:1000}}&amp;#39;&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="日常操作與決策形狀">日常操作與決策形狀&lt;/h2>
&lt;h3 id="toxic-types">Toxic types&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>latency：增加延遲&lt;/li>
&lt;li>bandwidth：限制頻寬&lt;/li>
&lt;li>slow_close：connection close 慢&lt;/li>
&lt;li>timeout：connection timeout&lt;/li>
&lt;li>slicer：把 TCP packet 切片&lt;/li>
&lt;li>limit_data：limit 傳輸量&lt;/li>
&lt;/ul>
&lt;h3 id="api--client-sdk">API + Client SDK&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>HTTP API（8474 default）&lt;/li>
&lt;li>Client SDK：Go / Ruby / Python / JS&lt;/li>
&lt;li>Programmatic toxic enable/disable&lt;/li>
&lt;/ul>
&lt;h3 id="integration-test-pattern">Integration test pattern&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>before each test 設 toxic&lt;/li>
&lt;li>after each test cleanup&lt;/li>
&lt;li>Test isolation：每 test reset proxy state&lt;/li>
&lt;/ul>
&lt;h2 id="進階主題按需閱讀">進階主題（按需閱讀）&lt;/h2>
&lt;h3 id="docker-compose-整合">Docker Compose 整合&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>service depends_on toxiproxy&lt;/li>
&lt;li>應用透過 toxiproxy connect 真正 DB / cache&lt;/li>
&lt;li>environment variable 切換 toxiproxy vs direct&lt;/li>
&lt;/ul>
&lt;h3 id="reproducible-chaos">Reproducible chaos&lt;/h3>
&lt;p>子議題：&lt;/p></description><content:encoded><![CDATA[<p>Toxiproxy 是 Shopify 開源的 TCP-level fault injection proxy、承擔三個責任：TCP 層 fault inject（latency / bandwidth / partition / slow_close）、integration test 中可程式化故障注入（reproducible）、client SDK 多語言（Go / Ruby / Python / JS）。設計取捨偏向「CI-friendly + reproducible + 細粒度 TCP control」、不適合 production chaos、適合 integration test 跟 dependency failure 模擬。</p>
<h2 id="本章目標">本章目標</h2>
<p>讀完本章後、你應該能：</p>
<ol>
<li>跑起 Toxiproxy server + 設 listener / upstream proxy</li>
<li>用 client SDK 注入 latency / partition / bandwidth toxic</li>
<li>整合 Toxiproxy 到 integration test（before/after test hook）</li>
<li>用 Docker Compose 整合</li>
<li>評估 Toxiproxy vs Chaos Mesh NetworkChaos 的選用</li>
</ol>
<h2 id="最短路徑5-分鐘把-toxiproxy-跑起來">最短路徑：5 分鐘把 Toxiproxy 跑起來</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 1. 啟動 server</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># TODO: docker run -d -p 8474:8474 -p 26379:26379 ghcr.io/shopify/toxiproxy</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 2. 建 proxy（Redis 為例）</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># TODO: curl -X POST localhost:8474/proxies -d &#39;{&#34;name&#34;:&#34;redis&#34;,&#34;listen&#34;:&#34;0.0.0.0:26379&#34;,&#34;upstream&#34;:&#34;redis:6379&#34;}&#39;</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># 3. 注入 toxic</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c1"># TODO: curl -X POST localhost:8474/proxies/redis/toxics -d &#39;{&#34;type&#34;:&#34;latency&#34;,&#34;attributes&#34;:{&#34;latency&#34;:1000}}&#39;</span></span></span></code></pre></div><h2 id="日常操作與決策形狀">日常操作與決策形狀</h2>
<h3 id="toxic-types">Toxic types</h3>
<p>子議題：</p>
<ul>
<li>latency：增加延遲</li>
<li>bandwidth：限制頻寬</li>
<li>slow_close：connection close 慢</li>
<li>timeout：connection timeout</li>
<li>slicer：把 TCP packet 切片</li>
<li>limit_data：limit 傳輸量</li>
</ul>
<h3 id="api--client-sdk">API + Client SDK</h3>
<p>子議題：</p>
<ul>
<li>HTTP API（8474 default）</li>
<li>Client SDK：Go / Ruby / Python / JS</li>
<li>Programmatic toxic enable/disable</li>
</ul>
<h3 id="integration-test-pattern">Integration test pattern</h3>
<p>子議題：</p>
<ul>
<li>before each test 設 toxic</li>
<li>after each test cleanup</li>
<li>Test isolation：每 test reset proxy state</li>
</ul>
<h2 id="進階主題按需閱讀">進階主題（按需閱讀）</h2>
<h3 id="docker-compose-整合">Docker Compose 整合</h3>
<p>子議題：</p>
<ul>
<li>service depends_on toxiproxy</li>
<li>應用透過 toxiproxy connect 真正 DB / cache</li>
<li>environment variable 切換 toxiproxy vs direct</li>
</ul>
<h3 id="reproducible-chaos">Reproducible chaos</h3>
<p>子議題：</p>
<ul>
<li>Toxic seed（reproducible random）</li>
<li>Toxic stream（upstream / downstream）</li>
<li>對應 test reproducibility</li>
</ul>
<h3 id="跟-chaos-mesh-networkchaos-對比">跟 Chaos Mesh NetworkChaos 對比</h3>
<p>子議題：</p>
<ul>
<li>Toxiproxy：CI / integration test、TCP 層</li>
<li>Chaos Mesh：production、K8s pod 層</li>
<li>選擇判讀：testing CI → Toxiproxy；K8s staging chaos → Chaos Mesh</li>
</ul>
<h3 id="跟-client-retry--circuit-breaker-配合">跟 client retry / circuit breaker 配合</h3>
<p>子議題：</p>
<ul>
<li>驗證 client 對 dependency failure 的應對</li>
<li>Retry budget / backoff 測試</li>
<li>Circuit breaker trigger 測試</li>
<li>對應 <a href="/blog/backend/knowledge-cards/retry-budget/" data-link-title="Retry Budget" data-link-desc="說明重試次數如何受整體容量與錯誤預算限制">knowledge cards retry-budget</a></li>
</ul>
<h2 id="排錯快速判讀">排錯快速判讀</h2>
<h3 id="proxy-連不上">Proxy 連不上</h3>
<p>操作原則：先 <code>curl :8474/proxies</code> 看 proxy state、再看 network。</p>
<h3 id="toxic-沒生效">Toxic 沒生效</h3>
<p>操作原則：toxic enabled 但 attribute 設錯。判讀：API GET toxics 看當前狀態。</p>
<h3 id="test-state-pollute">Test state pollute</h3>
<p>操作原則：test 間沒 reset proxy、state 殘留。修法：每 test 開頭 reset。</p>
<h3 id="performance-overhead">Performance overhead</h3>
<p>操作原則：Toxiproxy 本身有 latency overhead（μs 級）、不適合 production sensitivity。</p>
<h2 id="何時改走其他服務">何時改走其他服務</h2>
<table>
  <thead>
      <tr>
          <th>需求形狀</th>
          <th>改走</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>K8s production chaos</td>
          <td><a href="/blog/backend/06-reliability/vendors/chaos-mesh/" data-link-title="Chaos Mesh" data-link-desc="Kubernetes-native chaos engineering（CNCF incubating）">Chaos Mesh</a> NetworkChaos</td>
      </tr>
      <tr>
          <td>商業跨平台</td>
          <td><a href="/blog/backend/06-reliability/vendors/gremlin/" data-link-title="Gremlin" data-link-desc="商業 chaos engineering 平台、跨平台與 GameDay">Gremlin</a></td>
      </tr>
      <tr>
          <td>Application-level error</td>
          <td>Mock / stub library</td>
      </tr>
      <tr>
          <td>AWS-native</td>
          <td>AWS Fault Injection Service</td>
      </tr>
  </tbody>
</table>
<h2 id="不在本頁內的主題">不在本頁內的主題</h2>
<ul>
<li>Toxic 內部實作</li>
<li>各語言 SDK 完整 API</li>
<li>TCP protocol 細節</li>
</ul>
<h2 id="案例回寫">案例回寫</h2>
<p><strong>Shopify 自家</strong>：Toxiproxy 是 Shopify 開源、Shopify reliability cases 多有引用。</p>
<table>
  <thead>
      <tr>
          <th>案例方向</th>
          <th>對應主題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/shopify/bfcm-capacity-and-game-day/" data-link-title="Shopify：BFCM 容量治理與 Game Day 驗證節奏" data-link-desc="把季節性流量峰值轉成年度可靠性流程，透過容量模型、演練與隔離策略提前吸收風險。">Shopify：BFCM 容量治理與 Game Day</a></td>
          <td>resiliency matrix + TCP-level fault injection 的原生使用脈絡</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/" data-link-title="Stripe：Idempotency 與零停機遷移的交易安全設計" data-link-desc="把 API 重試與資料遷移放在同一套安全模型，維持支付交易的一致結果。">Stripe：Idempotency 與零停機遷移</a></td>
          <td>integration test 模擬 dependency 失敗、驗證 retry 與 idempotency</td>
      </tr>
  </tbody>
</table>
<p><strong>Case 庫稀薄</strong>：Toxiproxy 主要 case 集中在 Shopify 自家、其他 adopter 案例待補。</p>
<ul>
<li><strong>待補 Toxiproxy adopter case</strong>：其他公司用 Toxiproxy 做 dependency failure 測試</li>
<li><strong>候選 case</strong>：Pinterest（cache failure mode integration test）、Spotify（squad 自管 integration chaos）— 若未來收錄需先在 cases/ 補正文</li>
</ul>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>上游概念：<a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 Experiment Safety Boundary</a></li>
<li>平行 vendor：<a href="/blog/backend/06-reliability/vendors/chaos-mesh/" data-link-title="Chaos Mesh" data-link-desc="Kubernetes-native chaos engineering（CNCF incubating）">Chaos Mesh</a>、<a href="/blog/backend/06-reliability/vendors/gremlin/" data-link-title="Gremlin" data-link-desc="商業 chaos engineering 平台、跨平台與 GameDay">Gremlin</a></li>
<li>下游能力：<a href="/blog/backend/knowledge-cards/retry-budget/" data-link-title="Retry Budget" data-link-desc="說明重試次數如何受整體容量與錯誤預算限制">knowledge cards retry-budget</a></li>
</ul>
]]></content:encoded></item><item><title>Google：Error Budget 政策如何決定發布節奏</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/google/error-budget-policy-and-release-gating/</link><pubDate>Thu, 07 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/google/error-budget-policy-and-release-gating/</guid><description>&lt;p>Error budget policy 的核心責任是把「可靠性目標」轉成「發布節奏控制」。團隊不需要在每次風險升高時重新爭論要不要繼續推版，而是用同一套 SLO 消耗判準決定放行、限流或凍結。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>高變更頻率服務最常見的失效是小幅回歸連續累積，單點故障反而少見。每次回歸都不夠大，不會立刻觸發全停；但連續幾週後，使用者體感持續惡化，團隊才發現可靠性債已經超標。&lt;/p>
&lt;p>這種情境需要的是「連續消耗判讀」，不是單次事故判讀。error budget policy 就是把連續消耗變成可操作的放行規則。&lt;/p>
&lt;h2 id="決策機制">決策機制&lt;/h2>
&lt;p>政策設計先做三個對齊，再做門檻定義。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>對齊項目&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>產出&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>使用者行為對齊&lt;/td>
 &lt;td>哪些 journey 直接反映服務價值&lt;/td>
 &lt;td>SLI 範圍&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>可靠性承諾對齊&lt;/td>
 &lt;td>什麼水準算服務仍可接受&lt;/td>
 &lt;td>SLO 目標&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>交付節奏對齊&lt;/td>
 &lt;td>可靠性消耗到哪裡要改變發布策略&lt;/td>
 &lt;td>Budget gate&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>有了這三個對齊後，release gate 可以從「主觀風險判斷」轉成「政策驅動」：&lt;/p>
&lt;ol>
&lt;li>budget 健康：正常發版。&lt;/li>
&lt;li>budget 快速消耗：啟用變更限速、提高驗證門檻。&lt;/li>
&lt;li>budget 透支：凍結非必要變更，先修復與回補訊號。&lt;/li>
&lt;/ol>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;p>政策有效與否要靠訊號判讀，不靠會議共識。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>burn rate&lt;/td>
 &lt;td>是否進入短期高消耗區&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>release failure ratio&lt;/td>
 &lt;td>發版後回歸是否集中&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>alert noise&lt;/td>
 &lt;td>告警是否支持 gate 判讀&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/04-observability/sli-slo-signal/" data-link-title="4.6 SLI 量測與 SLO 訊號設計" data-link-desc="把可靠性目標的訊號從 metric 端設計好、餵給 6.6 SLO 政策">4.6&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>recovery latency&lt;/td>
 &lt;td>凍結後修復是否收斂&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/containment-recovery-strategy/" data-link-title="8.3 止血、降級與回復策略" data-link-desc="把短期止血與正式回復拆成可執行步驟">8.3&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="常見陷阱">常見陷阱&lt;/h2>
&lt;p>把 error budget 當 KPI 會讓政策失真。這個機制的責任是「保護可靠性與交付節奏的平衡」，不是讓團隊追求某個固定分數。當 KPI 化開始主導行為，常見結果是 SLI 縮小、告警延後或例外條件過度擴張，最終反而降低判讀可信度。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>要把這個案例落到制度層，先回到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6&lt;/a> 定義政策欄位，再到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8&lt;/a> 實作 gate。若你發現訊號不足，先補 &lt;a href="https://tarrragon.github.io/blog/backend/04-observability/observability-readiness-review/" data-link-title="4.16 Observability Readiness Review" data-link-desc="在服務上線、重大變更與演練前檢查 log / metric / trace / alert 是否可支援事故判讀">4.16&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/04-observability/observability-evidence-package/" data-link-title="4.20 Observability Evidence Package" data-link-desc="把 log、metric、trace、audit 與資料品質限制包成可交接證據">4.20&lt;/a>。&lt;/p></description><content:encoded><![CDATA[<p>Error budget policy 的核心責任是把「可靠性目標」轉成「發布節奏控制」。團隊不需要在每次風險升高時重新爭論要不要繼續推版，而是用同一套 SLO 消耗判準決定放行、限流或凍結。</p>
<h2 id="問題場景">問題場景</h2>
<p>高變更頻率服務最常見的失效是小幅回歸連續累積，單點故障反而少見。每次回歸都不夠大，不會立刻觸發全停；但連續幾週後，使用者體感持續惡化，團隊才發現可靠性債已經超標。</p>
<p>這種情境需要的是「連續消耗判讀」，不是單次事故判讀。error budget policy 就是把連續消耗變成可操作的放行規則。</p>
<h2 id="決策機制">決策機制</h2>
<p>政策設計先做三個對齊，再做門檻定義。</p>
<table>
  <thead>
      <tr>
          <th>對齊項目</th>
          <th>核心問題</th>
          <th>產出</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>使用者行為對齊</td>
          <td>哪些 journey 直接反映服務價值</td>
          <td>SLI 範圍</td>
      </tr>
      <tr>
          <td>可靠性承諾對齊</td>
          <td>什麼水準算服務仍可接受</td>
          <td>SLO 目標</td>
      </tr>
      <tr>
          <td>交付節奏對齊</td>
          <td>可靠性消耗到哪裡要改變發布策略</td>
          <td>Budget gate</td>
      </tr>
  </tbody>
</table>
<p>有了這三個對齊後，release gate 可以從「主觀風險判斷」轉成「政策驅動」：</p>
<ol>
<li>budget 健康：正常發版。</li>
<li>budget 快速消耗：啟用變更限速、提高驗證門檻。</li>
<li>budget 透支：凍結非必要變更，先修復與回補訊號。</li>
</ol>
<h2 id="可觀測訊號">可觀測訊號</h2>
<p>政策有效與否要靠訊號判讀，不靠會議共識。</p>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>burn rate</td>
          <td>是否進入短期高消耗區</td>
          <td><a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6</a></td>
      </tr>
      <tr>
          <td>release failure ratio</td>
          <td>發版後回歸是否集中</td>
          <td><a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8</a></td>
      </tr>
      <tr>
          <td>alert noise</td>
          <td>告警是否支持 gate 判讀</td>
          <td><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</a></td>
      </tr>
      <tr>
          <td>recovery latency</td>
          <td>凍結後修復是否收斂</td>
          <td><a href="/blog/backend/08-incident-response/containment-recovery-strategy/" data-link-title="8.3 止血、降級與回復策略" data-link-desc="把短期止血與正式回復拆成可執行步驟">8.3</a></td>
      </tr>
  </tbody>
</table>
<h2 id="常見陷阱">常見陷阱</h2>
<p>把 error budget 當 KPI 會讓政策失真。這個機制的責任是「保護可靠性與交付節奏的平衡」，不是讓團隊追求某個固定分數。當 KPI 化開始主導行為，常見結果是 SLI 縮小、告警延後或例外條件過度擴張，最終反而降低判讀可信度。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>要把這個案例落到制度層，先回到 <a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6</a> 定義政策欄位，再到 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8</a> 實作 gate。若你發現訊號不足，先補 <a href="/blog/backend/04-observability/observability-readiness-review/" data-link-title="4.16 Observability Readiness Review" data-link-desc="在服務上線、重大變更與演練前檢查 log / metric / trace / alert 是否可支援事故判讀">4.16</a> 與 <a href="/blog/backend/04-observability/observability-evidence-package/" data-link-title="4.20 Observability Evidence Package" data-link-desc="把 log、metric、trace、audit 與資料品質限制包成可交接證據">4.20</a>。</p>
]]></content:encoded></item><item><title>6.11 Migration Safety 與 DB Rollout</title><link>https://tarrragon.github.io/blog/backend/06-reliability/migration-safety/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/migration-safety/</guid><description>&lt;h2 id="大綱">大綱&lt;/h2>
&lt;ul>
&lt;li>migration 的核心約束：schema 變更必須跟程式碼版本相容&lt;/li>
&lt;li>expand / contract 模式：先擴展（雙寫 / 雙讀）、再收斂（移除舊欄位）&lt;/li>
&lt;li>雙寫驗證：&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/shadow-read/" data-link-title="Shadow Read" data-link-desc="說明正式讀取仍走舊路徑時如何暗中讀新路徑比對結果">shadow read&lt;/a>、checksum 比對、流量採樣&lt;/li>
&lt;li>線上 DDL 工具：pt-online-schema-change / gh-ost / Vitess online schema change&lt;/li>
&lt;li>大表 migration 策略：批次、節流、避開 peak&lt;/li>
&lt;li>rollback 路徑設計：每階段必須可逆&lt;/li>
&lt;li>跟 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/contract-testing/" data-link-title="6.10 Contract Testing 與 Schema 演進" data-link-desc="把跨服務 / API / event schema 的隱性期待變成可驗證契約，控制演進相容性">6.10 contract testing&lt;/a> 的整合：schema 契約驗證&lt;/li>
&lt;li>跟 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate&lt;/a> 的整合：migration 可逆性作為 gate 條件&lt;/li>
&lt;li>反模式：schema change 跟 code deploy 同 PR、rollback 變不可能；大表 ALTER 直接打、production 鎖表；新欄位 NOT NULL 無 default&lt;/li>
&lt;/ul>
&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/schema-migration/" data-link-title="Schema Migration" data-link-desc="說明資料庫結構如何隨應用程式版本安全演進">Schema migration&lt;/a> 是把 schema migration 從一次性事件變成可逆、可漸進的 rollout 流程，責任是避免資料結構變更直接把 production 推向不可回復狀態。&lt;/p>
&lt;p>這一頁關心的是結構變更的節奏。當 code 與 schema 必須一起演進，安全做法是保留回退與相容窗口，一次到位的思路會壓縮容錯空間。&lt;/p>
&lt;h2 id="核心判讀">核心判讀&lt;/h2>
&lt;p>判讀 migration 時，先看每一步是否可逆，再看它是否能在 peak 外執行。&lt;/p>
&lt;p>重點訊號包括：&lt;/p>
&lt;ul>
&lt;li>expand / contract 是否真的分開&lt;/li>
&lt;li>rollback 路徑是否先於 production 變更設計&lt;/li>
&lt;li>大表操作是否有節流與 dry-run&lt;/li>
&lt;li>雙寫 / shadow read 是否有一致性驗證&lt;/li>
&lt;/ul>
&lt;h2 id="案例對照">案例對照&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/pinterest/" data-link-title="Pinterest" data-link-desc="Pinterest Capacity Planning 與儲存架構可靠性">Pinterest&lt;/a>：資料結構與產品演進常同步變化。&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/cases/github/" data-link-title="GitHub" data-link-desc="GitHub 重大事故時間線與架構脈絡">GitHub&lt;/a>：大規模平台 migration 容易把結構風險放大。&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/stripe/" data-link-title="Stripe" data-link-desc="Stripe Deploy Strategy / Game Day / Idempotency 實踐">Stripe&lt;/a>：金流系統對 migration rollback 與一致性要求特別高。&lt;/li>
&lt;/ul>
&lt;h2 id="交易類-migration-的特殊性">交易類 migration 的特殊性&lt;/h2>
&lt;p>交易類 migration 同時承擔可用性跟正確性兩條軸。一般 schema migration 失敗的代價是停機、交易類失敗的代價額外包含結果不一致（重複扣款、訂單漏建、reconciliation 缺口）。守住兩條軸需要 idempotency + 漸進遷移 + 可回退發布 + 交易路徑可追溯四件事配合。&lt;/p>
&lt;p>對應 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/" data-link-title="Stripe：Idempotency 與零停機遷移的交易安全設計" data-link-desc="把 API 重試與資料遷移放在同一套安全模型，維持支付交易的一致結果。">S1 Stripe Idempotency 與零停機遷移&lt;/a>：揭露四個機制對應上述四件事 — idempotency key（同一交易重送如何得到同一結果）、expand/contract migration（資料變更如何與新舊版本共存）、canary + rollback gate（發版異常如何快速收斂）、transaction-path observability（交易路徑是否可追溯）。&lt;/p>
&lt;p>交易類 migration 的關鍵 observables：&lt;/p>
&lt;ul>
&lt;li>duplicate request collapse ratio：重試是否被正確合併&lt;/li>
&lt;li>migration phase error drift：遷移各階段錯誤是否收斂&lt;/li>
&lt;li>canary transaction anomaly：小流量交易是否出現偏差&lt;/li>
&lt;li>payment trace consistency：trace 是否完整覆蓋交易關鍵欄位&lt;/li>
&lt;/ul>
&lt;p>把這四個機制視為「交易類 migration 的安全 baseline」、跟 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/idempotency-replay/" data-link-title="6.12 Idempotency 與 Replay 驗證" data-link-desc="把重試、重播與冪等性從口頭約定變成可驗證屬性">6.12 idempotency-replay&lt;/a> 共用 idempotency key 設計、跟 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/release-gate/#%e4%ba%a4%e6%98%93%e9%a1%9e%e8%ae%8a%e6%9b%b4%e7%9a%84-gate-%e8%a8%ad%e8%a8%88" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate 交易類變更段&lt;/a> 共用 canary 條件。&lt;/p></description><content:encoded><![CDATA[<h2 id="大綱">大綱</h2>
<ul>
<li>migration 的核心約束：schema 變更必須跟程式碼版本相容</li>
<li>expand / contract 模式：先擴展（雙寫 / 雙讀）、再收斂（移除舊欄位）</li>
<li>雙寫驗證：<a href="/blog/backend/knowledge-cards/shadow-read/" data-link-title="Shadow Read" data-link-desc="說明正式讀取仍走舊路徑時如何暗中讀新路徑比對結果">shadow read</a>、checksum 比對、流量採樣</li>
<li>線上 DDL 工具：pt-online-schema-change / gh-ost / Vitess online schema change</li>
<li>大表 migration 策略：批次、節流、避開 peak</li>
<li>rollback 路徑設計：每階段必須可逆</li>
<li>跟 <a href="/blog/backend/06-reliability/contract-testing/" data-link-title="6.10 Contract Testing 與 Schema 演進" data-link-desc="把跨服務 / API / event schema 的隱性期待變成可驗證契約，控制演進相容性">6.10 contract testing</a> 的整合：schema 契約驗證</li>
<li>跟 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate</a> 的整合：migration 可逆性作為 gate 條件</li>
<li>反模式：schema change 跟 code deploy 同 PR、rollback 變不可能；大表 ALTER 直接打、production 鎖表；新欄位 NOT NULL 無 default</li>
</ul>
<h2 id="概念定位">概念定位</h2>
<p><a href="/blog/backend/knowledge-cards/schema-migration/" data-link-title="Schema Migration" data-link-desc="說明資料庫結構如何隨應用程式版本安全演進">Schema migration</a> 是把 schema migration 從一次性事件變成可逆、可漸進的 rollout 流程，責任是避免資料結構變更直接把 production 推向不可回復狀態。</p>
<p>這一頁關心的是結構變更的節奏。當 code 與 schema 必須一起演進，安全做法是保留回退與相容窗口，一次到位的思路會壓縮容錯空間。</p>
<h2 id="核心判讀">核心判讀</h2>
<p>判讀 migration 時，先看每一步是否可逆，再看它是否能在 peak 外執行。</p>
<p>重點訊號包括：</p>
<ul>
<li>expand / contract 是否真的分開</li>
<li>rollback 路徑是否先於 production 變更設計</li>
<li>大表操作是否有節流與 dry-run</li>
<li>雙寫 / shadow read 是否有一致性驗證</li>
</ul>
<h2 id="案例對照">案例對照</h2>
<ul>
<li><a href="/blog/backend/06-reliability/cases/pinterest/" data-link-title="Pinterest" data-link-desc="Pinterest Capacity Planning 與儲存架構可靠性">Pinterest</a>：資料結構與產品演進常同步變化。</li>
<li><a href="/blog/backend/08-incident-response/cases/github/" data-link-title="GitHub" data-link-desc="GitHub 重大事故時間線與架構脈絡">GitHub</a>：大規模平台 migration 容易把結構風險放大。</li>
<li><a href="/blog/backend/06-reliability/cases/stripe/" data-link-title="Stripe" data-link-desc="Stripe Deploy Strategy / Game Day / Idempotency 實踐">Stripe</a>：金流系統對 migration rollback 與一致性要求特別高。</li>
</ul>
<h2 id="交易類-migration-的特殊性">交易類 migration 的特殊性</h2>
<p>交易類 migration 同時承擔可用性跟正確性兩條軸。一般 schema migration 失敗的代價是停機、交易類失敗的代價額外包含結果不一致（重複扣款、訂單漏建、reconciliation 缺口）。守住兩條軸需要 idempotency + 漸進遷移 + 可回退發布 + 交易路徑可追溯四件事配合。</p>
<p>對應 <a href="/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/" data-link-title="Stripe：Idempotency 與零停機遷移的交易安全設計" data-link-desc="把 API 重試與資料遷移放在同一套安全模型，維持支付交易的一致結果。">S1 Stripe Idempotency 與零停機遷移</a>：揭露四個機制對應上述四件事 — idempotency key（同一交易重送如何得到同一結果）、expand/contract migration（資料變更如何與新舊版本共存）、canary + rollback gate（發版異常如何快速收斂）、transaction-path observability（交易路徑是否可追溯）。</p>
<p>交易類 migration 的關鍵 observables：</p>
<ul>
<li>duplicate request collapse ratio：重試是否被正確合併</li>
<li>migration phase error drift：遷移各階段錯誤是否收斂</li>
<li>canary transaction anomaly：小流量交易是否出現偏差</li>
<li>payment trace consistency：trace 是否完整覆蓋交易關鍵欄位</li>
</ul>
<p>把這四個機制視為「交易類 migration 的安全 baseline」、跟 <a href="/blog/backend/06-reliability/idempotency-replay/" data-link-title="6.12 Idempotency 與 Replay 驗證" data-link-desc="把重試、重播與冪等性從口頭約定變成可驗證屬性">6.12 idempotency-replay</a> 共用 idempotency key 設計、跟 <a href="/blog/backend/06-reliability/release-gate/#%e4%ba%a4%e6%98%93%e9%a1%9e%e8%ae%8a%e6%9b%b4%e7%9a%84-gate-%e8%a8%ad%e8%a8%88" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate 交易類變更段</a> 共用 canary 條件。</p>
<p>交易類 migration 的反模式是把 migration 當「資料庫任務」獨立執行、跟 release gate 分離。正確做法是把 migration 跟 release 綁定治理、用同一套 evidence 跟 rollback 條件判讀。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>01.6 <a href="/blog/backend/01-database/database-migration-playbook/" data-link-title="1.6 資料庫轉換實作：雙寫、回填、切流與回滾" data-link-desc="同 DB 內 schema 演進與資料變更的可分段驗證流程、跟 1.12 cross-DB migration 分工">資料庫轉換實作</a>：雙寫、回填、切流與回滾</li>
<li>01.7 <a href="/blog/backend/01-database/schema-migration-rollout-evidence/" data-link-title="1.7 Schema Migration Rollout 證據（Schema Migration Rollout Evidence）實作示範" data-link-desc="以訂單付款狀態欄位演進示範 schema migration 如何產出 evidence、release gate 與 incident decision log。">Schema Migration Rollout 證據</a>：把 migration plan 落成 <a href="/blog/backend/knowledge-cards/validation-query/" data-link-title="Validation Query" data-link-desc="說明遷移、回填與修復期間如何用查詢證明資料語意是否一致">validation query</a>、evidence package、release gate 與 decision log</li>
<li>06.8 release gate：把可逆性放進放行條件</li>
<li>06.10 contract testing：先驗 schema 相容性</li>
<li>08.5 <a href="/blog/backend/knowledge-cards/post-incident-review/" data-link-title="Post-Incident Review" data-link-desc="說明事故後如何完成復盤、學習與改進閉環">post-incident review</a>：migration 類事故通常需要結構化復盤</li>
</ul>
<h2 id="判讀訊號">判讀訊號</h2>
<ul>
<li>migration 失敗只能 forward-fix、無 rollback 路徑</li>
<li>大表 ALTER 在 peak 時段執行造成鎖表</li>
<li>程式碼跟 schema 必須同步部署、deploy 失敗風險高</li>
<li>雙寫期間無一致性驗證、cutover 後才發現資料漂移</li>
<li>migration 工具無 dry-run、production 才知道執行時間</li>
</ul>
<h2 id="交接路由">交接路由</h2>
<ul>
<li>01.6 <a href="/blog/backend/01-database/database-migration-playbook/" data-link-title="1.6 資料庫轉換實作：雙寫、回填、切流與回滾" data-link-desc="同 DB 內 schema 演進與資料變更的可分段驗證流程、跟 1.12 cross-DB migration 分工">資料庫轉換實作</a>：執行層流程</li>
<li>01.7 <a href="/blog/backend/01-database/schema-migration-rollout-evidence/" data-link-title="1.7 Schema Migration Rollout 證據（Schema Migration Rollout Evidence）實作示範" data-link-desc="以訂單付款狀態欄位演進示範 schema migration 如何產出 evidence、release gate 與 incident decision log。">Schema Migration Rollout 證據</a>：production rollout evidence 與 gate 欄位</li>
<li>0.C4 <a href="/blog/backend/00-service-selection/cases/post-scale-migration-language-tool-architecture/" data-link-title="營運後技術轉換：語言、工具與架構何時該換" data-link-desc="服務營運一段時間後，如何判讀何時該轉語言、工具或架構，並用案例說明轉換動機。">營運後技術轉換</a>：決策層判讀</li>
<li>06.7 DR / rollback：migration rollback 演練</li>
<li>06.8 release gate：可逆性檢查</li>
<li>06.10 contract testing：schema 契約驗證</li>
<li>08.5 <a href="/blog/backend/knowledge-cards/post-incident-review/" data-link-title="Post-Incident Review" data-link-desc="說明事故後如何完成復盤、學習與改進閉環">post-incident review</a>：migration 引發的事故型態</li>
</ul>
]]></content:encoded></item><item><title>LinkedIn</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/linkedin/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/linkedin/</guid><description>&lt;p>LinkedIn 是大規模社交平台、capacity planning 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/on-call/" data-link-title="On-Call" data-link-desc="說明值班制度如何承接告警、事故分級與升級流程">on-call&lt;/a> 結構的工程文章公開度高、是「中型公司如何規模化 SRE」的教學標竿。&lt;/p>
&lt;h2 id="規劃重點">規劃重點&lt;/h2>
&lt;ul>
&lt;li>Capacity Planning：跨 region / 跨服務的容量預測方法&lt;/li>
&lt;li>On-call 結構：primary / secondary / SME escalation&lt;/li>
&lt;li>Operability culture：把可運維性納入服務設計門檻&lt;/li>
&lt;li>Internal tooling：LinkedIn engineering blog 公開的內部工具設計&lt;/li>
&lt;/ul>
&lt;h2 id="預計收錄實踐">預計收錄實踐&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>議題&lt;/th>
 &lt;th>教學重點&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Capacity Planning&lt;/td>
 &lt;td>預測模型、headroom、growth rate&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>On-call Tiers&lt;/td>
 &lt;td>多層 escalation 設計&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Site Reliability Eng&lt;/td>
 &lt;td>LinkedIn SRE 組織演化&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Internal Chaos / Drills&lt;/td>
 &lt;td>Project Waterbear 等內部演練&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="案例定位">案例定位&lt;/h2>
&lt;p>LinkedIn 這個案例在講的是中大型平台如何把容量規劃、自動化壓測與 metrics 收集做成可運營的系統。讀者先抓 capacity planning、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/on-call/" data-link-title="On-Call" data-link-desc="說明值班制度如何承接告警、事故分級與升級流程">on-call&lt;/a> tiers 與 self-service metrics 的關係，再看它們怎麼把 operability 變成團隊責任。&lt;/p>
&lt;h2 id="判讀重點">判讀重點&lt;/h2>
&lt;p>當 replication latency 上升時，先看 headroom 是否足夠，再看壓測與自動化是否真的覆蓋了常見瓶頸。當 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/on-call/" data-link-title="On-Call" data-link-desc="說明值班制度如何承接告警、事故分級與升級流程">on-call&lt;/a> 需要多層升級時，重點是每一層是否知道何時接手、何時回退，階層形式本身是次要的。&lt;/p>
&lt;h2 id="可操作判準">可操作判準&lt;/h2>
&lt;ul>
&lt;li>能否把容量預測連到實際 growth rate&lt;/li>
&lt;li>能否讓 load testing 自動化到可重用&lt;/li>
&lt;li>能否把 metrics collection 做成 self-service&lt;/li>
&lt;li>能否清楚劃分 primary、secondary 與 SME escalation&lt;/li>
&lt;/ul>
&lt;h2 id="與其他案例的關係">與其他案例的關係&lt;/h2>
&lt;p>LinkedIn 的焦點是把 operability 變成日常流程，這和 Shopify 的峰值準備、Microsoft 的治理模式、Spotify 的平台化做法都很接近。差別在於 LinkedIn 更強調內部工具與 metrics pipeline，適合拿來當「中型平台如何長大」的範本。&lt;/p>
&lt;h2 id="代表樣本">代表樣本&lt;/h2>
&lt;ul>
&lt;li>automated load testing 把壓測變成日常流程，而不是臨時活動。&lt;/li>
&lt;li>self-service metrics 讓團隊不用等平台工程師才能看見關鍵訊號。&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/on-call/" data-link-title="On-Call" data-link-desc="說明值班制度如何承接告警、事故分級與升級流程">on-call&lt;/a> tiers 讓升級與接手邏輯有固定路徑。&lt;/li>
&lt;li>capacity planning 讓 replication latency 與 headroom 直接相連。&lt;/li>
&lt;li>site reliability engineering 讓中型平台開始形成自己的可靠性職能。&lt;/li>
&lt;li>internal tooling 讓 operability 變成平台化能力而不是個人技巧。&lt;/li>
&lt;li>project waterbear 類演練讓內部故障情境能被規律化測試。&lt;/li>
&lt;li>primary / secondary / SME escalation 讓責任與知識分工更清楚。&lt;/li>
&lt;/ul>
&lt;h2 id="章節列表">章節列表&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>章節&lt;/th>
 &lt;th>主題&lt;/th>
 &lt;th>核心責任&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/linkedin/capacity-headroom-and-oncall-tiering/" data-link-title="LinkedIn：Capacity Headroom 與 On-call 分層" data-link-desc="把容量預測與值班分層綁在一起，降低高峰時段的升級混亂與恢復延遲。">L1&lt;/a>&lt;/td>
 &lt;td>Capacity 與 On-call 分層&lt;/td>
 &lt;td>把容量邊界與值班交接綁成同一套治理節奏&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/linkedin/automated-load-testing-and-capacity-forecasting/" data-link-title="LinkedIn：Automated Load Testing 與 Capacity Forecasting" data-link-desc="持續壓測驅動容量預測：用自動化回饋取代一次性壓測的容量規劃。">L2&lt;/a>&lt;/td>
 &lt;td>Automated Load Testing 與 Forecasting&lt;/td>
 &lt;td>用持續壓測驅動容量預測，取代一次性壓測的容量規劃&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://engineering.linkedin.com/20/welcome-linkedin-engineering-blog">Welcome to the LinkedIn Engineering Blog&lt;/a>：LinkedIn Engineering Blog 的入口。&lt;/li>
&lt;li>&lt;a href="https://engineering.linkedin.com/performance/taming-database-replication-latency-capacity-planning">Taming Database Replication Latency by Capacity Planning&lt;/a>：容量規劃與 replication latency 的經典案例。&lt;/li>
&lt;li>&lt;a href="https://engineering.linkedin.com/content/engineering/en-us/blog/2019/eliminating-toil-with-fully-automated-load-testing">Eliminating toil with fully automated load testing&lt;/a>：自動化壓測與 operability 的實踐。&lt;/li>
&lt;li>&lt;a href="https://engineering.linkedin.com/metrics/scaling-collection-self-service-metrics">Scaling the collection of self-service metrics&lt;/a>：metrics pipeline 與可運維性基礎。&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>LinkedIn 是大規模社交平台、capacity planning 與 <a href="/blog/backend/knowledge-cards/on-call/" data-link-title="On-Call" data-link-desc="說明值班制度如何承接告警、事故分級與升級流程">on-call</a> 結構的工程文章公開度高、是「中型公司如何規模化 SRE」的教學標竿。</p>
<h2 id="規劃重點">規劃重點</h2>
<ul>
<li>Capacity Planning：跨 region / 跨服務的容量預測方法</li>
<li>On-call 結構：primary / secondary / SME escalation</li>
<li>Operability culture：把可運維性納入服務設計門檻</li>
<li>Internal tooling：LinkedIn engineering blog 公開的內部工具設計</li>
</ul>
<h2 id="預計收錄實踐">預計收錄實踐</h2>
<table>
  <thead>
      <tr>
          <th>議題</th>
          <th>教學重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Capacity Planning</td>
          <td>預測模型、headroom、growth rate</td>
      </tr>
      <tr>
          <td>On-call Tiers</td>
          <td>多層 escalation 設計</td>
      </tr>
      <tr>
          <td>Site Reliability Eng</td>
          <td>LinkedIn SRE 組織演化</td>
      </tr>
      <tr>
          <td>Internal Chaos / Drills</td>
          <td>Project Waterbear 等內部演練</td>
      </tr>
  </tbody>
</table>
<h2 id="案例定位">案例定位</h2>
<p>LinkedIn 這個案例在講的是中大型平台如何把容量規劃、自動化壓測與 metrics 收集做成可運營的系統。讀者先抓 capacity planning、<a href="/blog/backend/knowledge-cards/on-call/" data-link-title="On-Call" data-link-desc="說明值班制度如何承接告警、事故分級與升級流程">on-call</a> tiers 與 self-service metrics 的關係，再看它們怎麼把 operability 變成團隊責任。</p>
<h2 id="判讀重點">判讀重點</h2>
<p>當 replication latency 上升時，先看 headroom 是否足夠，再看壓測與自動化是否真的覆蓋了常見瓶頸。當 <a href="/blog/backend/knowledge-cards/on-call/" data-link-title="On-Call" data-link-desc="說明值班制度如何承接告警、事故分級與升級流程">on-call</a> 需要多層升級時，重點是每一層是否知道何時接手、何時回退，階層形式本身是次要的。</p>
<h2 id="可操作判準">可操作判準</h2>
<ul>
<li>能否把容量預測連到實際 growth rate</li>
<li>能否讓 load testing 自動化到可重用</li>
<li>能否把 metrics collection 做成 self-service</li>
<li>能否清楚劃分 primary、secondary 與 SME escalation</li>
</ul>
<h2 id="與其他案例的關係">與其他案例的關係</h2>
<p>LinkedIn 的焦點是把 operability 變成日常流程，這和 Shopify 的峰值準備、Microsoft 的治理模式、Spotify 的平台化做法都很接近。差別在於 LinkedIn 更強調內部工具與 metrics pipeline，適合拿來當「中型平台如何長大」的範本。</p>
<h2 id="代表樣本">代表樣本</h2>
<ul>
<li>automated load testing 把壓測變成日常流程，而不是臨時活動。</li>
<li>self-service metrics 讓團隊不用等平台工程師才能看見關鍵訊號。</li>
<li><a href="/blog/backend/knowledge-cards/on-call/" data-link-title="On-Call" data-link-desc="說明值班制度如何承接告警、事故分級與升級流程">on-call</a> tiers 讓升級與接手邏輯有固定路徑。</li>
<li>capacity planning 讓 replication latency 與 headroom 直接相連。</li>
<li>site reliability engineering 讓中型平台開始形成自己的可靠性職能。</li>
<li>internal tooling 讓 operability 變成平台化能力而不是個人技巧。</li>
<li>project waterbear 類演練讓內部故障情境能被規律化測試。</li>
<li>primary / secondary / SME escalation 讓責任與知識分工更清楚。</li>
</ul>
<h2 id="章節列表">章節列表</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/linkedin/capacity-headroom-and-oncall-tiering/" data-link-title="LinkedIn：Capacity Headroom 與 On-call 分層" data-link-desc="把容量預測與值班分層綁在一起，降低高峰時段的升級混亂與恢復延遲。">L1</a></td>
          <td>Capacity 與 On-call 分層</td>
          <td>把容量邊界與值班交接綁成同一套治理節奏</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/linkedin/automated-load-testing-and-capacity-forecasting/" data-link-title="LinkedIn：Automated Load Testing 與 Capacity Forecasting" data-link-desc="持續壓測驅動容量預測：用自動化回饋取代一次性壓測的容量規劃。">L2</a></td>
          <td>Automated Load Testing 與 Forecasting</td>
          <td>用持續壓測驅動容量預測，取代一次性壓測的容量規劃</td>
      </tr>
  </tbody>
</table>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://engineering.linkedin.com/20/welcome-linkedin-engineering-blog">Welcome to the LinkedIn Engineering Blog</a>：LinkedIn Engineering Blog 的入口。</li>
<li><a href="https://engineering.linkedin.com/performance/taming-database-replication-latency-capacity-planning">Taming Database Replication Latency by Capacity Planning</a>：容量規劃與 replication latency 的經典案例。</li>
<li><a href="https://engineering.linkedin.com/content/engineering/en-us/blog/2019/eliminating-toil-with-fully-automated-load-testing">Eliminating toil with fully automated load testing</a>：自動化壓測與 operability 的實踐。</li>
<li><a href="https://engineering.linkedin.com/metrics/scaling-collection-self-service-metrics">Scaling the collection of self-service metrics</a>：metrics pipeline 與可運維性基礎。</li>
</ul>
]]></content:encoded></item><item><title>Nobl9</title><link>https://tarrragon.github.io/blog/backend/06-reliability/vendors/nobl9/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/vendors/nobl9/</guid><description>&lt;p>Nobl9 是商業 SLO 平台、承擔三個責任：跨 data source SLO 統一治理（Datadog / Prometheus / New Relic / CloudWatch / Splunk 等）、error budget + burn rate alerting、organizational SLO governance（service catalog / project / role）。設計取捨偏向「multi-source + governance + OpenSLO standard」、創辦人來自 Google SRE、推動 OpenSLO 標準。&lt;/p>
&lt;h2 id="本章目標">本章目標&lt;/h2>
&lt;p>讀完本章後、你應該能：&lt;/p>
&lt;ol>
&lt;li>在 Nobl9 定義 SLO（SLI / target / time window）&lt;/li>
&lt;li>配置 error budget + burn rate alert（multi-window）&lt;/li>
&lt;li>設計 composite SLO（跨服務組合）&lt;/li>
&lt;li>用 OpenSLO YAML 管 SLO as code&lt;/li>
&lt;li>評估 Nobl9 vs Sloth / Pyrra / vendor 內建 SLO&lt;/li>
&lt;/ol>
&lt;h2 id="最短路徑5-分鐘把-nobl9-跑起來">最短路徑：5 分鐘把 Nobl9 跑起來&lt;/h2>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 1. 註冊 Nobl9 + connect data source&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: app.nobl9.com、connect Datadog / Prometheus&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 2. 寫 SLO YAML（OpenSLO）&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: SLO spec with service / indicator / objective&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 3. sloctl apply&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: sloctl apply -f slo.yaml&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="日常操作與決策形狀">日常操作與決策形狀&lt;/h2>
&lt;h3 id="slo-定義">SLO 定義&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>SLI（Service Level Indicator）：metric to measure&lt;/li>
&lt;li>Objective：target percentage&lt;/li>
&lt;li>Time window：rolling / calendar&lt;/li>
&lt;li>對應 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">knowledge cards burn-rate&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="error-budget">Error budget&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>Budget = (1 - SLO target) × time window&lt;/li>
&lt;li>Consumed budget / remaining budget&lt;/li>
&lt;li>跟 release gate 對應（budget 用完 → freeze deploy）&lt;/li>
&lt;/ul>
&lt;h3 id="burn-rate-alert">Burn rate alert&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>Multi-window multi-burn-rate alert&lt;/li>
&lt;li>Fast burn alert（短期 high rate）+ slow burn alert（長期 low rate）&lt;/li>
&lt;li>對應 Google SRE burn rate alerting&lt;/li>
&lt;/ul>
&lt;h2 id="進階主題按需閱讀">進階主題（按需閱讀）&lt;/h2>
&lt;h3 id="composite-slo">Composite SLO&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>跨多 service 組合成單一 SLO&lt;/li>
&lt;li>適合：user journey SLO（不只單一 service）&lt;/li>
&lt;/ul>
&lt;h3 id="openslo-標準">OpenSLO 標準&lt;/h3>
&lt;p>子議題：&lt;/p></description><content:encoded><![CDATA[<p>Nobl9 是商業 SLO 平台、承擔三個責任：跨 data source SLO 統一治理（Datadog / Prometheus / New Relic / CloudWatch / Splunk 等）、error budget + burn rate alerting、organizational SLO governance（service catalog / project / role）。設計取捨偏向「multi-source + governance + OpenSLO standard」、創辦人來自 Google SRE、推動 OpenSLO 標準。</p>
<h2 id="本章目標">本章目標</h2>
<p>讀完本章後、你應該能：</p>
<ol>
<li>在 Nobl9 定義 SLO（SLI / target / time window）</li>
<li>配置 error budget + burn rate alert（multi-window）</li>
<li>設計 composite SLO（跨服務組合）</li>
<li>用 OpenSLO YAML 管 SLO as code</li>
<li>評估 Nobl9 vs Sloth / Pyrra / vendor 內建 SLO</li>
</ol>
<h2 id="最短路徑5-分鐘把-nobl9-跑起來">最短路徑：5 分鐘把 Nobl9 跑起來</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 1. 註冊 Nobl9 + connect data source</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># TODO: app.nobl9.com、connect Datadog / Prometheus</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 2. 寫 SLO YAML（OpenSLO）</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># TODO: SLO spec with service / indicator / objective</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># 3. sloctl apply</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c1"># TODO: sloctl apply -f slo.yaml</span></span></span></code></pre></div><h2 id="日常操作與決策形狀">日常操作與決策形狀</h2>
<h3 id="slo-定義">SLO 定義</h3>
<p>子議題：</p>
<ul>
<li>SLI（Service Level Indicator）：metric to measure</li>
<li>Objective：target percentage</li>
<li>Time window：rolling / calendar</li>
<li>對應 <a href="/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">knowledge cards burn-rate</a></li>
</ul>
<h3 id="error-budget">Error budget</h3>
<p>子議題：</p>
<ul>
<li>Budget = (1 - SLO target) × time window</li>
<li>Consumed budget / remaining budget</li>
<li>跟 release gate 對應（budget 用完 → freeze deploy）</li>
</ul>
<h3 id="burn-rate-alert">Burn rate alert</h3>
<p>子議題：</p>
<ul>
<li>Multi-window multi-burn-rate alert</li>
<li>Fast burn alert（短期 high rate）+ slow burn alert（長期 low rate）</li>
<li>對應 Google SRE burn rate alerting</li>
</ul>
<h2 id="進階主題按需閱讀">進階主題（按需閱讀）</h2>
<h3 id="composite-slo">Composite SLO</h3>
<p>子議題：</p>
<ul>
<li>跨多 service 組合成單一 SLO</li>
<li>適合：user journey SLO（不只單一 service）</li>
</ul>
<h3 id="openslo-標準">OpenSLO 標準</h3>
<p>子議題：</p>
<ul>
<li>Vendor-neutral SLO spec</li>
<li>YAML 配置</li>
<li>跟 Nobl9 主導</li>
<li>對應 vendor lock-in 取捨</li>
</ul>
<h3 id="data-source-整合">Data source 整合</h3>
<p>子議題：</p>
<ul>
<li>Datadog / Prometheus / New Relic / CloudWatch / Splunk / AppDynamics / Honeycomb / Lightstep</li>
<li>多 source SLO 統一 view</li>
<li>對應 <a href="/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">04 observability</a> 模組</li>
</ul>
<h3 id="alert-routing">Alert routing</h3>
<p>子議題：</p>
<ul>
<li>跟 PagerDuty / Opsgenie / Slack 整合</li>
<li>跟 <a href="/blog/backend/08-incident-response/" data-link-title="模組八：事故處理與復盤" data-link-desc="用 IR 領域詞彙建問題節點、以服務級案例庫累積事故脈絡，先建概念與案例庫再進實作交接">08 incident response</a> 對應</li>
</ul>
<h3 id="service-catalog--governance">Service catalog + governance</h3>
<p>子議題：</p>
<ul>
<li>Project / Service / SLO 階層</li>
<li>Role-based access</li>
<li>Audit log</li>
</ul>
<h3 id="slo-as-code">SLO as code</h3>
<p>子議題：</p>
<ul>
<li>sloctl CLI</li>
<li>YAML version control</li>
<li>CI integration</li>
</ul>
<h2 id="排錯快速判讀">排錯快速判讀</h2>
<h3 id="slo-calculation-不準">SLO calculation 不準</h3>
<p>操作原則：SLI query 不對 / data source 延遲。判讀：raw metric vs SLO calculation 比對。</p>
<h3 id="alert-noise">Alert noise</h3>
<p>操作原則：burn rate window 設過短 / threshold 過嚴。</p>
<h3 id="data-source-disconnect">Data source disconnect</h3>
<p>操作原則：API key / network / quota。</p>
<h3 id="composite-slo-行為不符預期">Composite SLO 行為不符預期</h3>
<p>操作原則：composite 算法（AND / OR / custom）不對。</p>
<h2 id="何時改走其他服務">何時改走其他服務</h2>
<table>
  <thead>
      <tr>
          <th>需求形狀</th>
          <th>改走</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>OSS / 預算敏感</td>
          <td><a href="/blog/backend/06-reliability/vendors/sloth/" data-link-title="Sloth" data-link-desc="OSS SLO generator for Prometheus">Sloth</a> / Pyrra</td>
      </tr>
      <tr>
          <td>單一 vendor 環境</td>
          <td>Datadog SLO / Honeycomb SLO / Grafana SLO</td>
      </tr>
      <tr>
          <td>K8s-native CRD</td>
          <td>Pyrra（K8s Operator）</td>
      </tr>
      <tr>
          <td>純 Prometheus</td>
          <td>Sloth（Prometheus generator）</td>
      </tr>
      <tr>
          <td>Enterprise + multi-cloud</td>
          <td>Nobl9（本頁）</td>
      </tr>
  </tbody>
</table>
<h2 id="不在本頁內的主題">不在本頁內的主題</h2>
<ul>
<li>OpenSLO 完整 spec</li>
<li>Nobl9 pricing</li>
<li>sloctl 完整 CLI reference</li>
</ul>
<h2 id="案例回寫">案例回寫</h2>
<table>
  <thead>
      <tr>
          <th>案例方向</th>
          <th>對應主題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/google/error-budget-policy-and-release-gating/" data-link-title="Google：Error Budget 政策如何決定發布節奏" data-link-desc="把 SLO 消耗量轉成 release gate，讓可靠性與交付速度共用同一套決策語言。">Google：Error Budget 與 Release Gating</a></td>
          <td>SLI / SLO / error budget 原典、多源聚合 SLO 平台的對齊對象</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/honeycomb/burn-rate-driven-reliability-operations/" data-link-title="Honeycomb：以 Burn Rate 驅動的可靠性操作" data-link-desc="把 SLO burn rate 直接連到值班決策與改善優先序，降低高噪音告警造成的判讀失真。">Honeycomb：Burn Rate 驅動可靠性</a></td>
          <td>burn rate alert 對應 SLO 平台的 alert policy</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/microsoft/change-management-and-reliability-governance/" data-link-title="Microsoft：變更治理與可靠性門檻" data-link-desc="透過分層變更管理與發布閘門，降低大型 SaaS 平台的系統性回歸風險。">Microsoft：變更治理與可靠性門檻</a></td>
          <td>企業合規 + SLO 治理的對應路徑</td>
      </tr>
  </tbody>
</table>
<p><strong>待補 Nobl9 customer case</strong>：企業 SLO 治理採用案例、OpenSLO adopter。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>上游概念：<a href="/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">knowledge cards burn-rate</a></li>
<li>平行 vendor：<a href="/blog/backend/06-reliability/vendors/sloth/" data-link-title="Sloth" data-link-desc="OSS SLO generator for Prometheus">Sloth</a></li>
<li>下游能力：<a href="/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">04 observability</a>、<a href="/blog/backend/08-incident-response/" data-link-title="模組八：事故處理與復盤" data-link-desc="用 IR 領域詞彙建問題節點、以服務級案例庫累積事故脈絡，先建概念與案例庫再進實作交接">08 incident response</a></li>
</ul>
]]></content:encoded></item><item><title>Jenkins → GitHub Actions：Pipeline 5 段 lifecycle 的對位 + 翻譯</title><link>https://tarrragon.github.io/blog/backend/06-reliability/vendors/github-actions/migrate-from-jenkins/</link><pubDate>Tue, 19 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/vendors/github-actions/migrate-from-jenkins/</guid><description>&lt;blockquote>
&lt;p>本文是跨 vendor migration playbook、cross-link &lt;a href="https://www.jenkins.io/">Jenkins&lt;/a> 跟 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/vendors/github-actions/" data-link-title="GitHub Actions" data-link-desc="GitHub 原生 CI/CD、PR check、deploy gate">GitHub Actions&lt;/a>。跑 &lt;a href="https://tarrragon.github.io/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">migration-playbook-methodology 6 維 audit&lt;/a> 後對映 &lt;em>Schema = High（Groovy DSL ↔ YAML workflow）→ Type A phased translation&lt;/em>。&lt;/p>&lt;/blockquote>
&lt;h2 id="pipeline-5-段-lifecycle-的對位--翻譯">Pipeline 5 段 lifecycle 的對位 + 翻譯&lt;/h2>
&lt;p>本文按 &lt;em>pipeline lifecycle 5 段&lt;/em> 組織內容（variant E）— 不是「為什麼遷」driver 開頭，是 &lt;em>Jenkins vs GHA 對 5 段各自的處理&lt;/em>：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Lifecycle 段&lt;/th>
 &lt;th>Jenkins 機制&lt;/th>
 &lt;th>GHA 機制&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>1. Source / SCM&lt;/td>
 &lt;td>SCM polling / webhook trigger&lt;/td>
 &lt;td>&lt;code>on: [push, pull_request]&lt;/code> event&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>2. Build / Package&lt;/td>
 &lt;td>&lt;code>stage('Build') { sh 'mvn package' }&lt;/code>&lt;/td>
 &lt;td>&lt;code>jobs.build.steps[].run: mvn package&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>3. Test / 並行 matrix&lt;/td>
 &lt;td>&lt;code>parallel { ... }&lt;/code> + agents&lt;/td>
 &lt;td>&lt;code>jobs.test.strategy.matrix: ...&lt;/code>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>4. Security scan&lt;/td>
 &lt;td>Plugin（Snyk / SonarQube / Aqua）&lt;/td>
 &lt;td>Action（snyk/actions / sonarsource-actions）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>5. Deploy / promote&lt;/td>
 &lt;td>Deploy plugin + approval gate&lt;/td>
 &lt;td>&lt;code>environment: production&lt;/code> + reviewer approval&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>跑 &lt;a href="https://tarrragon.github.io/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">6 維 diff dimension audit&lt;/a>：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>維度&lt;/th>
 &lt;th>評估&lt;/th>
 &lt;th>等級&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Schema / API&lt;/td>
 &lt;td>Groovy DSL ↔ YAML、syntax 完全不同&lt;/td>
 &lt;td>&lt;strong>High&lt;/strong>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Operational model&lt;/td>
 &lt;td>Self-hosted Jenkins → GHA SaaS / self-hosted runners&lt;/td>
 &lt;td>Medium&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Paradigm&lt;/td>
 &lt;td>Imperative pipeline → declarative workflow + events&lt;/td>
 &lt;td>Medium&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Components&lt;/td>
 &lt;td>Jenkins + plugins → GHA + actions marketplace&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Application change&lt;/td>
 &lt;td>Build script 多數不改、CI integration 端要改&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Data topology&lt;/td>
 &lt;td>同單一 build state&lt;/td>
 &lt;td>Low&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Schema = High（其他 Medium-Low）→ &lt;strong>Type A phased translation&lt;/strong> 為主、加 paradigm + operational 獨立段。&lt;/p></description><content:encoded><![CDATA[<blockquote>
<p>本文是跨 vendor migration playbook、cross-link <a href="https://www.jenkins.io/">Jenkins</a> 跟 <a href="/blog/backend/06-reliability/vendors/github-actions/" data-link-title="GitHub Actions" data-link-desc="GitHub 原生 CI/CD、PR check、deploy gate">GitHub Actions</a>。跑 <a href="/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">migration-playbook-methodology 6 維 audit</a> 後對映 <em>Schema = High（Groovy DSL ↔ YAML workflow）→ Type A phased translation</em>。</p></blockquote>
<h2 id="pipeline-5-段-lifecycle-的對位--翻譯">Pipeline 5 段 lifecycle 的對位 + 翻譯</h2>
<p>本文按 <em>pipeline lifecycle 5 段</em> 組織內容（variant E）— 不是「為什麼遷」driver 開頭，是 <em>Jenkins vs GHA 對 5 段各自的處理</em>：</p>
<table>
  <thead>
      <tr>
          <th>Lifecycle 段</th>
          <th>Jenkins 機制</th>
          <th>GHA 機制</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>1. Source / SCM</td>
          <td>SCM polling / webhook trigger</td>
          <td><code>on: [push, pull_request]</code> event</td>
      </tr>
      <tr>
          <td>2. Build / Package</td>
          <td><code>stage('Build') { sh 'mvn package' }</code></td>
          <td><code>jobs.build.steps[].run: mvn package</code></td>
      </tr>
      <tr>
          <td>3. Test / 並行 matrix</td>
          <td><code>parallel { ... }</code> + agents</td>
          <td><code>jobs.test.strategy.matrix: ...</code></td>
      </tr>
      <tr>
          <td>4. Security scan</td>
          <td>Plugin（Snyk / SonarQube / Aqua）</td>
          <td>Action（snyk/actions / sonarsource-actions）</td>
      </tr>
      <tr>
          <td>5. Deploy / promote</td>
          <td>Deploy plugin + approval gate</td>
          <td><code>environment: production</code> + reviewer approval</td>
      </tr>
  </tbody>
</table>
<p>跑 <a href="/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">6 維 diff dimension audit</a>：</p>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>評估</th>
          <th>等級</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Schema / API</td>
          <td>Groovy DSL ↔ YAML、syntax 完全不同</td>
          <td><strong>High</strong></td>
      </tr>
      <tr>
          <td>Operational model</td>
          <td>Self-hosted Jenkins → GHA SaaS / self-hosted runners</td>
          <td>Medium</td>
      </tr>
      <tr>
          <td>Paradigm</td>
          <td>Imperative pipeline → declarative workflow + events</td>
          <td>Medium</td>
      </tr>
      <tr>
          <td>Components</td>
          <td>Jenkins + plugins → GHA + actions marketplace</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Application change</td>
          <td>Build script 多數不改、CI integration 端要改</td>
          <td>Low</td>
      </tr>
      <tr>
          <td>Data topology</td>
          <td>同單一 build state</td>
          <td>Low</td>
      </tr>
  </tbody>
</table>
<p>Schema = High（其他 Medium-Low）→ <strong>Type A phased translation</strong> 為主、加 paradigm + operational 獨立段。</p>
<h2 id="為什麼遷cost--vendor--cloud-native-三條-driver">為什麼遷：cost / vendor / cloud-native 三條 driver</h2>
<ul>
<li><strong>Cost</strong>：Jenkins self-hosted 是「免費 software + 高 ops cost」、GHA 按 minute 計費對中小團隊更便宜</li>
<li><strong>Vendor consolidation</strong>：repository 已在 GitHub、整合進 GHA 省一個外部系統</li>
<li><strong>Cloud-native</strong>：GHA matrix build + reusable workflow 對 cloud-native deploy（K8s / serverless）有 first-class action</li>
</ul>
<h2 id="phase-0audit--classify">Phase 0：Audit + classify</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1"># Jenkins workspace 盤點</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">find . -name <span class="s2">&#34;Jenkinsfile&#34;</span> -o -name <span class="s2">&#34;*.groovy&#34;</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1"># 列所有 pipeline file</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="c1"># 統計 plugin 使用</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="c1"># Jenkinsfile 內 import / @Library / sh &#34;tool plugin...&#34;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">grep -rE <span class="s2">&#34;@Library|import|tools\s*\{&#34;</span> Jenkinsfile*
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="c1"># 每 pipeline 評估 complexity</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1"># - Simple linear pipeline: 1-3 stage、無 shared library</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"># - Medium: parallel stage + 2-5 shared library</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="c1"># - Complex: 條件分支 + 動態 stage + 10+ plugin / 5+ shared library</span></span></span></code></pre></div><p>Audit output：</p>
<ul>
<li>列「100 個 pipeline、35 simple / 50 medium / 15 complex」</li>
<li>每 complexity level 估翻譯時間（simple 0.5 day / medium 2 day / complex 5-10 day）</li>
<li>Plugin 依賴清單對應 GHA action 替代品</li>
</ul>
<h2 id="phase-1schema-對位groovy-dsl--yaml">Phase 1：Schema 對位（Groovy DSL ↔ YAML）</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-groovy" data-lang="groovy"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// Jenkins Declarative Pipeline
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1"></span><span class="n">pipeline</span> <span class="o">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">  <span class="n">agent</span> <span class="o">{</span> <span class="n">label</span> <span class="s1">&#39;docker-build&#39;</span> <span class="o">}</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">  <span class="n">stages</span> <span class="o">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="n">stage</span><span class="o">(</span><span class="s1">&#39;Test&#39;</span><span class="o">)</span> <span class="o">{</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">      <span class="n">parallel</span> <span class="o">{</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        <span class="n">stage</span><span class="o">(</span><span class="s1">&#39;Unit&#39;</span><span class="o">)</span> <span class="o">{</span> <span class="n">steps</span> <span class="o">{</span> <span class="n">sh</span> <span class="s1">&#39;mvn test&#39;</span> <span class="o">}</span> <span class="o">}</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">        <span class="n">stage</span><span class="o">(</span><span class="s1">&#39;Integration&#39;</span><span class="o">)</span> <span class="o">{</span> <span class="n">steps</span> <span class="o">{</span> <span class="n">sh</span> <span class="s1">&#39;mvn verify&#39;</span> <span class="o">}</span> <span class="o">}</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">      <span class="o">}</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="o">}</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">  <span class="o">}</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">  <span class="n">post</span> <span class="o">{</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="n">failure</span> <span class="o">{</span> <span class="n">mail</span> <span class="nl">to:</span> <span class="s1">&#39;devops@&#39;</span><span class="o">,</span> <span class="nl">subject:</span> <span class="s1">&#39;Build failed&#39;</span> <span class="o">}</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">  <span class="o">}</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="o">}</span></span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c"># GHA Workflow 對等</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">CI</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w"></span><span class="nt">on</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">push]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w"></span><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">  </span><span class="nt">test</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">self-hosted, docker-build]</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="nt">strategy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">      </span><span class="nt">matrix</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">        </span><span class="nt">suite</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">unit, integration]</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v4</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Run ${{ matrix.suite }}</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="sd">          case &#34;${{ matrix.suite }}&#34; in
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="sd">            unit) mvn test ;;
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="sd">            integration) mvn verify ;;
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="sd">          esac</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">  </span><span class="nt">notify-failure</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">    </span><span class="nt">needs</span><span class="p">:</span><span class="w"> </span><span class="l">test</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">    </span><span class="nt">if</span><span class="p">:</span><span class="w"> </span><span class="l">failure()</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">      </span>- <span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">dawidd6/action-send-mail@v3</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">          </span><span class="nt">to</span><span class="p">:</span><span class="w"> </span><span class="l">devops@</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">          </span><span class="nt">subject</span><span class="p">:</span><span class="w"> </span><span class="l">Build failed</span></span></span></code></pre></div><p>對位差異：</p>
<ul>
<li><code>parallel { ... }</code> → <code>strategy.matrix</code>（粒度不同、matrix 是「同 step 不同參數」、parallel 是「不同 step」）</li>
<li><code>post.failure</code> → 獨立 job + <code>if: failure()</code></li>
<li><code>@Library</code> shared library → reusable workflow（<code>uses: ./.github/workflows/reusable.yml</code>）</li>
<li>Jenkins <code>tools { jdk 'java17' }</code> → setup-java action（手動配 toolchain）</li>
</ul>
<h2 id="phase-2translation-pipeline3-tier-hybrid">Phase 2：Translation pipeline（3-tier hybrid）</h2>
<p>對應 <a href="/blog/backend/07-security-data-protection/vendors/splunk/migrate-to-elastic-security/" data-link-title="Splunk → Elastic Security Detection Rule Migration：6 段 phased playbook 跟 5 大踩雷" data-link-desc="從 Splunk Enterprise Security 遷到 Elastic Security 的 detection rule translation playbook：SPL ↔ KQL/ES|QL schema 對位、AI-assisted translation pipeline、parallel run 比對、cutover routing、5 個 production 踩雷（macro 沒對應 / time zone 差異 / summary index 不對位 / alert dedup key 衝突 / 過早 decommission）、capacity / cost 對照">Splunk → Elastic translation</a> 同 3-tier：</p>
<ul>
<li><strong>Tier 1</strong>：community tool（jenkins-to-actions converter、cover 簡單 pipeline 30-50%）</li>
<li><strong>Tier 2</strong>：LLM-assisted（Claude / GPT 翻 medium complexity、人工 verify）</li>
<li><strong>Tier 3</strong>：manual（shared library 改 reusable workflow / conditional 動態 stage 重寫）</li>
</ul>
<h2 id="phase-3parallel-run雙-ci-跑-4-8-週">Phase 3：Parallel run（雙 CI 跑 4-8 週）</h2>





<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">Repository ──┬─→ Jenkins webhook ──→ Jenkinsfile pipeline
</span></span><span class="line"><span class="ln">2</span><span class="cl">             └─→ GitHub Action ────→ .github/workflows/ci.yml
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl">Compare:
</span></span><span class="line"><span class="ln">5</span><span class="cl">- 同 commit 兩端結果一致
</span></span><span class="line"><span class="ln">6</span><span class="cl">- Latency / cost / artifact location 對齊</span></span></code></pre></div><p>Diff dashboard 列「test pass rate / build time / failure mode」三 metric、跑到 95%+ 一致才進 cutover。</p>
<h2 id="phase-4cutover--cleanup">Phase 4：Cutover + cleanup</h2>
<ul>
<li>Disable Jenkins webhook</li>
<li>GHA 成 primary CI</li>
<li>Jenkins 留 standby 2 週 fallback</li>
<li>Decommission Jenkins controller + agents</li>
</ul>
<h2 id="production-故障演練">Production 故障演練</h2>
<h3 id="case-1shared-library-equivalencereusable-workflow-表達不足">Case 1：Shared library equivalence、reusable workflow 表達不足</h3>
<p><strong>徵兆</strong>：複雜 Jenkins shared library（含 Groovy class / closure / 動態變數）翻成 reusable workflow 後失準、某些動態邏輯無法表達。</p>
<p><strong>根因</strong>：Jenkins Groovy 是 imperative + 完整 programming language；GHA reusable workflow 是 declarative YAML、limited expressiveness。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>複雜邏輯外包到 script</strong>：reusable workflow 只當 <em>orchestrator</em>、複雜邏輯放 <code>.github/scripts/*.sh</code> 或 <code>actions/javascript-action</code></li>
<li><strong>自定 composite action</strong>：multi-step logic 包進 composite action、reuse 程度比 reusable workflow 高</li>
<li><strong>退役過度設計的 shared library</strong>：trans 過程暴露 90% library code 其實只用 10%</li>
</ol>
<h3 id="case-2ephemeral-workspacebuild-cache-失敗">Case 2：Ephemeral workspace、build cache 失敗</h3>
<p><strong>徵兆</strong>：cutover 後 build time 從 5 分鐘漲到 20 分鐘；Maven / Gradle / node_modules / Docker layer 每次都重抓。</p>
<p><strong>根因</strong>：Jenkins agent workspace persistent、build cache 跨 build 保留；GHA ephemeral runner 每次新 VM、cache 預設沒帶。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong><code>actions/cache@v4</code></strong>：cache key 用 <code>hashFiles('**/pom.xml')</code> 等 lock file、cross-build 復用</li>
<li><strong>Self-hosted runner with cache</strong>：critical pipeline 跑 self-hosted runner、persistent volume</li>
<li><strong>Docker layer cache</strong>：用 <code>docker/build-push-action</code> 配 BuildKit cache、不 rebuild full image</li>
</ol>
<h3 id="case-3plugin-不對等ci-feature-退化">Case 3：Plugin 不對等、CI feature 退化</h3>
<p><strong>徵兆</strong>：Jenkins 用 50+ plugin、GHA action marketplace 找不到對應；team 對 SonarQube quality gate / Jira integration / custom report 等失去 first-class 支援。</p>
<p><strong>根因</strong>：Jenkins plugin ecosystem 20+ 年累積、GHA marketplace 5 年；某些 niche plugin 在 GHA 沒對等 action。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>API-based integration</strong>：用 <code>curl</code> 對 vendor API 直接 call、不依賴 plugin / action</li>
<li><strong>自寫 action</strong>：critical feature 自寫 composite / JavaScript action、publish 到 marketplace</li>
<li><strong>退役舊 plugin</strong>：trans 期間 audit plugin 真實使用、80% 可退役</li>
</ol>
<h3 id="case-4self-hosted-runner-setup--scaling">Case 4：Self-hosted runner setup + scaling</h3>
<p><strong>徵兆</strong>：production workload 需要 GPU / large memory runner；GHA hosted runner spec 不夠、想用 self-hosted runner、發現 scaling / security / monitoring 比 Jenkins agent 複雜。</p>
<p><strong>根因</strong>：GHA self-hosted runner 是 ephemeral、scaling 需要 <em>runner controller</em>（actions-runner-controller on K8s）；跟 Jenkins agent / Kubernetes plugin 對應但 setup 不同。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>actions-runner-controller (ARC)</strong>：K8s-native runner scaling、跟 Jenkins K8s plugin 對應</li>
<li><strong>Runner labels</strong>：用 label 路由 job（<code>runs-on: [self-hosted, gpu, linux]</code>）</li>
<li><strong>Security</strong>：ephemeral runner 用 short-lived token、不跨 job persist secret</li>
</ol>
<h3 id="case-5matrix-build-vs-parallel-stage-表達差">Case 5：Matrix build vs parallel stage 表達差</h3>
<p><strong>徵兆</strong>：Jenkins 有 <em>動態 parallel</em>（runtime 決定要跑哪些 stage、按 input 變動）；GHA matrix 是 <em>static at workflow load time</em>、表達不到。</p>
<p><strong>根因</strong>：GHA matrix 是 declarative、workflow parse 時 expand；runtime 動態決定 stage 需要用 <code>if:</code> condition + 多 job。</p>
<p><strong>修法</strong>：</p>
<ol>
<li><strong>動態 matrix</strong>：用 <code>jobs.set-matrix</code> 先跑一個 job 算 matrix、輸出 JSON、後續 job <code>strategy.matrix: ${{ needs.set-matrix.outputs.matrix }}</code></li>
<li><strong>conditional job</strong>：每個 dynamic stage 寫獨立 job + <code>if:</code> 控制觸發</li>
<li><strong>重設計</strong>：90% 動態邏輯其實可改 static matrix + condition、純 runtime 動態通常是 over-engineering</li>
</ol>
<h2 id="capacity--cost">Capacity / cost</h2>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>Self-managed Jenkins</th>
          <th>GitHub Actions</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Compute cost</td>
          <td>EC2 + agent licenses</td>
          <td>per-minute billing（free tier + over-cap）</td>
      </tr>
      <tr>
          <td>Operational FTE</td>
          <td>0.5-1.5 FTE</td>
          <td>0.1-0.3 FTE</td>
      </tr>
      <tr>
          <td>Plugin / action ecosystem</td>
          <td>20+ 年成熟</td>
          <td>5 年快速成長</td>
      </tr>
      <tr>
          <td>Cold start</td>
          <td>Agent ready &lt; 1 min</td>
          <td>Hosted runner 30-60s spin-up</td>
      </tr>
      <tr>
          <td>Self-hosted scaling</td>
          <td>Jenkins K8s plugin</td>
          <td>ARC（actions-runner-controller）</td>
      </tr>
      <tr>
          <td>Security</td>
          <td>Self-managed VPC + secret</td>
          <td>OIDC + repository secret + environment</td>
      </tr>
      <tr>
          <td>Migration cost</td>
          <td>-</td>
          <td>1-3 FTE × 1-3 個月</td>
      </tr>
  </tbody>
</table>
<p><strong>判讀</strong>：100+ pipeline organization 切 GHA 通常 6-12 月 ROI 持平、之後省 ops cost；&lt; 30 pipeline 早就該切。</p>
<h2 id="整合--下一步">整合 / 下一步</h2>
<h3 id="跟-gitlab-ci-對位">跟 <a href="https://docs.gitlab.com/ee/ci/">GitLab CI</a> 對位</h3>
<p>GitLab CI YAML 語法跟 GHA 接近、shared library 對應 <code>include:</code>、self-hosted runner 對等；Jenkins → GitLab CI migration 流程跟本文鏡像對稱、3-tier translation pipeline 通用。</p>
<h3 id="跟-circle-ci-對位">跟 <a href="/blog/backend/06-reliability/vendors/circleci/" data-link-title="CircleCI" data-link-desc="CI/CD 平台、強 cache 與 parallelism">Circle CI</a> 對位</h3>
<p>CircleCI orb 對等 GHA composite action；跨 SaaS CI 切換比 Jenkins → GHA 簡單（都 YAML-based）。</p>
<h3 id="反向-migrationgha--jenkins">反向 migration（GHA → Jenkins）</h3>
<p>少數 enterprise（金融 / 政府）合規要求 self-hosted CI / on-prem；GHA → Jenkins 鏡像對稱、注意 Jenkins shared library 表達力更強、reusable workflow 內 dynamic 邏輯可不必拆。</p>
<h3 id="下一步議題">下一步議題</h3>
<ul>
<li><strong>Reusable workflow + composite action 混用</strong>：reusable workflow 適合 <em>跨 repo orchestration</em>、composite action 適合 <em>單 repo logic encapsulation</em></li>
<li><strong>OIDC + cloud deploy</strong>：用 OIDC token 取代 long-lived cloud credential、是 GHA migration 順便升級的機會</li>
<li><strong>Cost optimization</strong>：minute-based billing 對 high-volume CI 需要 monitoring + budget alert</li>
</ul>
<h2 id="相關連結">相關連結</h2>
<ul>
<li>Target vendor：<a href="/blog/backend/06-reliability/vendors/github-actions/" data-link-title="GitHub Actions" data-link-desc="GitHub 原生 CI/CD、PR check、deploy gate">GitHub Actions</a></li>
<li>平行 vendor：<a href="/blog/backend/06-reliability/vendors/circleci/" data-link-title="CircleCI" data-link-desc="CI/CD 平台、強 cache 與 parallelism">CircleCI</a></li>
<li>平行 migration playbook（Type A）：<a href="/blog/backend/07-security-data-protection/vendors/splunk/migrate-to-elastic-security/" data-link-title="Splunk → Elastic Security Detection Rule Migration：6 段 phased playbook 跟 5 大踩雷" data-link-desc="從 Splunk Enterprise Security 遷到 Elastic Security 的 detection rule translation playbook：SPL ↔ KQL/ES|QL schema 對位、AI-assisted translation pipeline、parallel run 比對、cutover routing、5 個 production 踩雷（macro 沒對應 / time zone 差異 / summary index 不對位 / alert dedup key 衝突 / 過早 decommission）、capacity / cost 對照">Splunk → Elastic Security</a> / <a href="/blog/backend/01-database/vendors/mysql/migrate-to-postgresql/" data-link-title="MySQL → PostgreSQL：從 SQL dialect diff 跑出來的 Type A 6-phase migration" data-link-desc="MySQL → PostgreSQL 是 Type A 高 schema 差 migration 的標準形態 — SQL dialect / collation / case sensitivity / replication 模型差異主導；用 pgloader / AWS DMS / 自管 dual-write 三條 path、5 個 production 踩雷（auto_increment vs SERIAL / charset 跟 collation / case sensitivity / index syntax / triggers）">MySQL → PostgreSQL</a></li>
<li>Methodology：<a href="/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">Migration playbook methodology</a></li>
</ul>
]]></content:encoded></item><item><title>Google：Postmortem Action Item Closure 治理</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/google/postmortem-action-item-closure-governance/</link><pubDate>Thu, 07 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/google/postmortem-action-item-closure-governance/</guid><description>&lt;p>Postmortem 的核心責任是把事故轉成會被完成的工程改進，解釋事故只是第一步。Google 的做法重點在 action item closure：每個改進項都要有 owner、完成條件、追蹤節奏與逾期處理規則。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>很多團隊 postmortem 寫得完整，但事故仍反覆發生。根因通常是 action item 沒有被制度化追蹤，分析能力本身不是瓶頸。當改進工作和日常 feature 競爭同一批資源時，沒有 closure 機制的 action item 很容易被延後到失效。&lt;/p>
&lt;h2 id="治理機制">治理機制&lt;/h2>
&lt;p>可靠的 closure 機制要先把 action item 分級，再對應不同完成標準。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>分級&lt;/th>
 &lt;th>風險型態&lt;/th>
 &lt;th>最低完成標準&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>P0&lt;/td>
 &lt;td>重複事故高機率再發生&lt;/td>
 &lt;td>需在下個 release 週期前完成並驗證&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>P1&lt;/td>
 &lt;td>會放大事故影響面&lt;/td>
 &lt;td>要有落地日期與 gate 條件&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>P2&lt;/td>
 &lt;td>提升診斷或操作效率&lt;/td>
 &lt;td>可排入 backlog，但要保留追蹤節點&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>分級之後要做三件事：&lt;/p>
&lt;ol>
&lt;li>為每個 action item 指派單一 owner。&lt;/li>
&lt;li>寫出可驗證完成條件（不是「優化」「強化」這類抽象字）。&lt;/li>
&lt;li>把 closure 狀態納入固定 review cadence。&lt;/li>
&lt;/ol>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>overdue action-item ratio&lt;/td>
 &lt;td>是否長期積壓高風險改進&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/post-incident-review/" data-link-title="8.5 復盤與改進追蹤" data-link-desc="把 RCA 與 action items 轉成可驗證閉環">8.5&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>repeated-incident similarity&lt;/td>
 &lt;td>同型事故是否仍反覆發生&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/repeated-incident-toil/" data-link-title="8.13 Repeated Incident 與 Toil 治理" data-link-desc="把同型事故反覆發生與重複手動修復作為工程化治理對象">8.13&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>gate bypass count&lt;/td>
 &lt;td>是否在高風險情況下跳過治理閘門&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>verification evidence coverage&lt;/td>
 &lt;td>完成項是否附驗證證據&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="常見陷阱">常見陷阱&lt;/h2>
&lt;p>最常見陷阱是把 action item 當作「會後待辦」而不是 release policy 的一部分。這會讓高風險改進沒有實際約束力。正確做法是把 P0/P1 項目直接綁到 release gate，未完成時不得放行關聯變更。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>先在 &lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/incident-decision-log/" data-link-title="8.19 Incident Decision Log" data-link-desc="把事中假設、決策、證據、回退條件與責任人留下可復盤紀錄">8.19 Incident Decision Log&lt;/a> 保留 action item 的決策脈絡，再到 &lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/incident-evidence-write-back/" data-link-title="8.22 Incident Evidence Write-back" data-link-desc="把事故證據、決策與復盤結論回寫到 observability、reliability 與 runbook">8.22 Incident Evidence Write-back&lt;/a> 回寫觀測與驗證項目。若要把 closure 變成制度，回到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 Reliability Debt Backlog&lt;/a> 進行排序治理。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://sre.google/sre-book/table-of-contents/">Google SRE Book&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://sre.google/workbook/table-of-contents/">Google SRE Workbook&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Postmortem 的核心責任是把事故轉成會被完成的工程改進，解釋事故只是第一步。Google 的做法重點在 action item closure：每個改進項都要有 owner、完成條件、追蹤節奏與逾期處理規則。</p>
<h2 id="問題場景">問題場景</h2>
<p>很多團隊 postmortem 寫得完整，但事故仍反覆發生。根因通常是 action item 沒有被制度化追蹤，分析能力本身不是瓶頸。當改進工作和日常 feature 競爭同一批資源時，沒有 closure 機制的 action item 很容易被延後到失效。</p>
<h2 id="治理機制">治理機制</h2>
<p>可靠的 closure 機制要先把 action item 分級，再對應不同完成標準。</p>
<table>
  <thead>
      <tr>
          <th>分級</th>
          <th>風險型態</th>
          <th>最低完成標準</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>P0</td>
          <td>重複事故高機率再發生</td>
          <td>需在下個 release 週期前完成並驗證</td>
      </tr>
      <tr>
          <td>P1</td>
          <td>會放大事故影響面</td>
          <td>要有落地日期與 gate 條件</td>
      </tr>
      <tr>
          <td>P2</td>
          <td>提升診斷或操作效率</td>
          <td>可排入 backlog，但要保留追蹤節點</td>
      </tr>
  </tbody>
</table>
<p>分級之後要做三件事：</p>
<ol>
<li>為每個 action item 指派單一 owner。</li>
<li>寫出可驗證完成條件（不是「優化」「強化」這類抽象字）。</li>
<li>把 closure 狀態納入固定 review cadence。</li>
</ol>
<h2 id="可觀測訊號">可觀測訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>overdue action-item ratio</td>
          <td>是否長期積壓高風險改進</td>
          <td><a href="/blog/backend/08-incident-response/post-incident-review/" data-link-title="8.5 復盤與改進追蹤" data-link-desc="把 RCA 與 action items 轉成可驗證閉環">8.5</a></td>
      </tr>
      <tr>
          <td>repeated-incident similarity</td>
          <td>同型事故是否仍反覆發生</td>
          <td><a href="/blog/backend/08-incident-response/repeated-incident-toil/" data-link-title="8.13 Repeated Incident 與 Toil 治理" data-link-desc="把同型事故反覆發生與重複手動修復作為工程化治理對象">8.13</a></td>
      </tr>
      <tr>
          <td>gate bypass count</td>
          <td>是否在高風險情況下跳過治理閘門</td>
          <td><a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8</a></td>
      </tr>
      <tr>
          <td>verification evidence coverage</td>
          <td>完成項是否附驗證證據</td>
          <td><a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23</a></td>
      </tr>
  </tbody>
</table>
<h2 id="常見陷阱">常見陷阱</h2>
<p>最常見陷阱是把 action item 當作「會後待辦」而不是 release policy 的一部分。這會讓高風險改進沒有實際約束力。正確做法是把 P0/P1 項目直接綁到 release gate，未完成時不得放行關聯變更。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>先在 <a href="/blog/backend/08-incident-response/incident-decision-log/" data-link-title="8.19 Incident Decision Log" data-link-desc="把事中假設、決策、證據、回退條件與責任人留下可復盤紀錄">8.19 Incident Decision Log</a> 保留 action item 的決策脈絡，再到 <a href="/blog/backend/08-incident-response/incident-evidence-write-back/" data-link-title="8.22 Incident Evidence Write-back" data-link-desc="把事故證據、決策與復盤結論回寫到 observability、reliability 與 runbook">8.22 Incident Evidence Write-back</a> 回寫觀測與驗證項目。若要把 closure 變成制度，回到 <a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 Reliability Debt Backlog</a> 進行排序治理。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://sre.google/sre-book/table-of-contents/">Google SRE Book</a></li>
<li><a href="https://sre.google/workbook/table-of-contents/">Google SRE Workbook</a></li>
</ul>
]]></content:encoded></item><item><title>0.12 觀測、可靠性與事故服務選型</title><link>https://tarrragon.github.io/blog/backend/00-service-selection/operations-control-service-selection/</link><pubDate>Sat, 02 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/00-service-selection/operations-control-service-selection/</guid><description>&lt;p>觀測、可靠性與事故服務選型的核心責任是把操作風險拆成「看得見、驗得過、接得住」三層能力。&lt;a href="https://tarrragon.github.io/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">可觀測性平台&lt;/a>處理訊號是否足以支援判讀，&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/" data-link-title="模組六：可靠性驗證流程" data-link-desc="用 SRE 領域詞彙建問題節點、以服務級案例庫累積驗證脈絡，先建概念與案例庫再進實作交接">可靠性驗證流程&lt;/a>處理失敗是否能被安全預演，&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/" data-link-title="模組八：事故處理與復盤" data-link-desc="用 IR 領域詞彙建問題節點、以服務級案例庫累積事故脈絡，先建概念與案例庫再進實作交接">事故處理與復盤&lt;/a>處理事故是否能被接住、分工與回寫。&lt;/p>
&lt;p>這三類服務常被一起採購或一起導入，但它們回答不同問題。觀測平台回答「現在發生什麼」，可靠性工具回答「失敗前能否先驗證」，事故平台回答「事情發生後誰做什麼」。選型時先分清能力層，再比較 vendor、SaaS、OSS 或自建方案，能降低工具堆疊與流程空轉的風險。&lt;/p>
&lt;h2 id="選型錨點">選型錨點&lt;/h2>
&lt;p>選型錨點是先問服務要降低哪一種操作不確定性。當團隊只知道系統「好像怪怪的」，優先補訊號；當團隊知道風險但缺少安全驗證路徑，優先補可靠性驗證；當團隊知道事故已發生但協作混亂，優先補事故流程。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>能力層&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>對應模組&lt;/th>
 &lt;th>常見服務類型&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>訊號層&lt;/td>
 &lt;td>發生什麼、影響哪裡&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">可觀測性平台&lt;/a>&lt;/td>
 &lt;td>telemetry、APM、log、dashboard&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>驗證層&lt;/td>
 &lt;td>風險能否提前預演&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/" data-link-title="模組六：可靠性驗證流程" data-link-desc="用 SRE 領域詞彙建問題節點、以服務級案例庫累積驗證脈絡，先建概念與案例庫再進實作交接">可靠性驗證流程&lt;/a>&lt;/td>
 &lt;td>CI、load test、chaos、SLO&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>響應層&lt;/td>
 &lt;td>誰接手、如何收斂&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/" data-link-title="模組八：事故處理與復盤" data-link-desc="用 IR 領域詞彙建問題節點、以服務級案例庫累積事故脈絡，先建概念與案例庫再進實作交接">事故處理與復盤&lt;/a>&lt;/td>
 &lt;td>on-call、IR、status、postmortem&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>閉環層&lt;/td>
 &lt;td>教訓如何回寫&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/observability-reliability-incident-loop/" data-link-title="8.11 Observability / Reliability / Incident Response 閉環" data-link-desc="把 04 / 06 / 08 三個模組的雙向反饋串成可判讀循環，定義閉環健康度判讀訊號">觀測、驗證與事故閉環&lt;/a>&lt;/td>
 &lt;td>workflow、action tracking&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>訊號層的責任是讓系統行為可被查詢與判讀。這一層的選型重點是資料模型、查詢能力、關聯能力、保留成本與告警品質；產品名稱排在後面，因為 log、metric、trace 與 error event 是否能互相串接，才是事故時真正影響判讀速度的條件。&lt;/p>
&lt;p>驗證層的責任是讓風險在事故前被安全暴露。這一層的選型重點是測試是否接近真實 workload、故障注入是否有停止條件、SLO 是否能被量測、release gate 是否能阻止高風險變更；工具越強，越需要 blast radius 與權限邊界。&lt;/p>
&lt;p>響應層的責任是讓事故進入可交接流程。這一層的選型重點是 paging、升級、角色分工、狀態更新、decision log、stakeholder mapping 與 post-incident action tracking；工具的價值來自流程一致性，通知訊息數量只是輔助訊號。&lt;/p>
&lt;p>閉環層的責任是把事故與演練教訓回寫到系統設計。這一層可能由 incident platform、ticket system、runbook repository 或內部 workflow 承擔；判準是 action item 是否能被排序、驗證、關閉，並回到訊號治理、可靠性演練或事故流程。&lt;/p>
&lt;h2 id="判讀順序">判讀順序&lt;/h2>
&lt;p>操作服務選型的穩定順序是「症狀 → 缺口 → 能力 → 工具」。症狀描述使用者痛點或工程痛點，缺口描述目前缺少的判讀或流程，能力描述需要補的系統責任，工具才是最後的落地選項。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>症狀&lt;/th>
 &lt;th>主要缺口&lt;/th>
 &lt;th>優先能力&lt;/th>
 &lt;th>下一步路由&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>客訴比告警早&lt;/td>
 &lt;td>訊號覆蓋不足&lt;/td>
 &lt;td>symptom-based alert&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/04-observability/dashboard-alert/" data-link-title="4.4 dashboard 與 alert 設計" data-link-desc="讓 dashboard 與 alert 對應 runbook 與容量趨勢">dashboard 與 alert&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>事故時 trace 接不上 queue&lt;/td>
 &lt;td>關聯線索斷裂&lt;/td>
 &lt;td>context propagation&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/04-observability/tracing-context/" data-link-title="4.3 tracing 與 context link" data-link-desc="整理 trace id、span 與跨服務 context propagation">tracing 與 context link&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>發版後才發現容量曲線崩壞&lt;/td>
 &lt;td>失敗前驗證不足&lt;/td>
 &lt;td>load / perf gate&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">load test&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>chaos 實驗影響超出預期&lt;/td>
 &lt;td>實驗安全邊界不足&lt;/td>
 &lt;td>experiment guardrail&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">experiment safety boundary&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>多人同時修事故但決策互相覆蓋&lt;/td>
 &lt;td>指揮與紀錄不足&lt;/td>
 &lt;td>command / decision log&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/incident-decision-log/" data-link-title="8.19 Incident Decision Log" data-link-desc="把事中假設、決策、證據、回退條件與責任人留下可復盤紀錄">incident decision log&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>對外狀態更新慢於內部復原&lt;/td>
 &lt;td>stakeholder 節奏不足&lt;/td>
 &lt;td>status / comms&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/stakeholder-communication/" data-link-title="8.10 Stakeholder 通訊與外部狀態頁" data-link-desc="把 impact scope、status page、補償政策串成節奏">stakeholder comms&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>客訴比告警早代表系統的外部痛點先於內部訊號出現。這種情境應先補服務健康指標、使用者可感知訊號與 alert runbook，再討論要用哪個監控平台；否則平台上線後仍可能只收集到工程師方便看的資料。&lt;/p></description><content:encoded><![CDATA[<p>觀測、可靠性與事故服務選型的核心責任是把操作風險拆成「看得見、驗得過、接得住」三層能力。<a href="/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">可觀測性平台</a>處理訊號是否足以支援判讀，<a href="/blog/backend/06-reliability/" data-link-title="模組六：可靠性驗證流程" data-link-desc="用 SRE 領域詞彙建問題節點、以服務級案例庫累積驗證脈絡，先建概念與案例庫再進實作交接">可靠性驗證流程</a>處理失敗是否能被安全預演，<a href="/blog/backend/08-incident-response/" data-link-title="模組八：事故處理與復盤" data-link-desc="用 IR 領域詞彙建問題節點、以服務級案例庫累積事故脈絡，先建概念與案例庫再進實作交接">事故處理與復盤</a>處理事故是否能被接住、分工與回寫。</p>
<p>這三類服務常被一起採購或一起導入，但它們回答不同問題。觀測平台回答「現在發生什麼」，可靠性工具回答「失敗前能否先驗證」，事故平台回答「事情發生後誰做什麼」。選型時先分清能力層，再比較 vendor、SaaS、OSS 或自建方案，能降低工具堆疊與流程空轉的風險。</p>
<h2 id="選型錨點">選型錨點</h2>
<p>選型錨點是先問服務要降低哪一種操作不確定性。當團隊只知道系統「好像怪怪的」，優先補訊號；當團隊知道風險但缺少安全驗證路徑，優先補可靠性驗證；當團隊知道事故已發生但協作混亂，優先補事故流程。</p>
<table>
  <thead>
      <tr>
          <th>能力層</th>
          <th>核心問題</th>
          <th>對應模組</th>
          <th>常見服務類型</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>訊號層</td>
          <td>發生什麼、影響哪裡</td>
          <td><a href="/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">可觀測性平台</a></td>
          <td>telemetry、APM、log、dashboard</td>
      </tr>
      <tr>
          <td>驗證層</td>
          <td>風險能否提前預演</td>
          <td><a href="/blog/backend/06-reliability/" data-link-title="模組六：可靠性驗證流程" data-link-desc="用 SRE 領域詞彙建問題節點、以服務級案例庫累積驗證脈絡，先建概念與案例庫再進實作交接">可靠性驗證流程</a></td>
          <td>CI、load test、chaos、SLO</td>
      </tr>
      <tr>
          <td>響應層</td>
          <td>誰接手、如何收斂</td>
          <td><a href="/blog/backend/08-incident-response/" data-link-title="模組八：事故處理與復盤" data-link-desc="用 IR 領域詞彙建問題節點、以服務級案例庫累積事故脈絡，先建概念與案例庫再進實作交接">事故處理與復盤</a></td>
          <td>on-call、IR、status、postmortem</td>
      </tr>
      <tr>
          <td>閉環層</td>
          <td>教訓如何回寫</td>
          <td><a href="/blog/backend/08-incident-response/observability-reliability-incident-loop/" data-link-title="8.11 Observability / Reliability / Incident Response 閉環" data-link-desc="把 04 / 06 / 08 三個模組的雙向反饋串成可判讀循環，定義閉環健康度判讀訊號">觀測、驗證與事故閉環</a></td>
          <td>workflow、action tracking</td>
      </tr>
  </tbody>
</table>
<p>訊號層的責任是讓系統行為可被查詢與判讀。這一層的選型重點是資料模型、查詢能力、關聯能力、保留成本與告警品質；產品名稱排在後面，因為 log、metric、trace 與 error event 是否能互相串接，才是事故時真正影響判讀速度的條件。</p>
<p>驗證層的責任是讓風險在事故前被安全暴露。這一層的選型重點是測試是否接近真實 workload、故障注入是否有停止條件、SLO 是否能被量測、release gate 是否能阻止高風險變更；工具越強，越需要 blast radius 與權限邊界。</p>
<p>響應層的責任是讓事故進入可交接流程。這一層的選型重點是 paging、升級、角色分工、狀態更新、decision log、stakeholder mapping 與 post-incident action tracking；工具的價值來自流程一致性，通知訊息數量只是輔助訊號。</p>
<p>閉環層的責任是把事故與演練教訓回寫到系統設計。這一層可能由 incident platform、ticket system、runbook repository 或內部 workflow 承擔；判準是 action item 是否能被排序、驗證、關閉，並回到訊號治理、可靠性演練或事故流程。</p>
<h2 id="判讀順序">判讀順序</h2>
<p>操作服務選型的穩定順序是「症狀 → 缺口 → 能力 → 工具」。症狀描述使用者痛點或工程痛點，缺口描述目前缺少的判讀或流程，能力描述需要補的系統責任，工具才是最後的落地選項。</p>
<table>
  <thead>
      <tr>
          <th>症狀</th>
          <th>主要缺口</th>
          <th>優先能力</th>
          <th>下一步路由</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>客訴比告警早</td>
          <td>訊號覆蓋不足</td>
          <td>symptom-based alert</td>
          <td><a href="/blog/backend/04-observability/dashboard-alert/" data-link-title="4.4 dashboard 與 alert 設計" data-link-desc="讓 dashboard 與 alert 對應 runbook 與容量趨勢">dashboard 與 alert</a></td>
      </tr>
      <tr>
          <td>事故時 trace 接不上 queue</td>
          <td>關聯線索斷裂</td>
          <td>context propagation</td>
          <td><a href="/blog/backend/04-observability/tracing-context/" data-link-title="4.3 tracing 與 context link" data-link-desc="整理 trace id、span 與跨服務 context propagation">tracing 與 context link</a></td>
      </tr>
      <tr>
          <td>發版後才發現容量曲線崩壞</td>
          <td>失敗前驗證不足</td>
          <td>load / perf gate</td>
          <td><a href="/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">load test</a></td>
      </tr>
      <tr>
          <td>chaos 實驗影響超出預期</td>
          <td>實驗安全邊界不足</td>
          <td>experiment guardrail</td>
          <td><a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">experiment safety boundary</a></td>
      </tr>
      <tr>
          <td>多人同時修事故但決策互相覆蓋</td>
          <td>指揮與紀錄不足</td>
          <td>command / decision log</td>
          <td><a href="/blog/backend/08-incident-response/incident-decision-log/" data-link-title="8.19 Incident Decision Log" data-link-desc="把事中假設、決策、證據、回退條件與責任人留下可復盤紀錄">incident decision log</a></td>
      </tr>
      <tr>
          <td>對外狀態更新慢於內部復原</td>
          <td>stakeholder 節奏不足</td>
          <td>status / comms</td>
          <td><a href="/blog/backend/08-incident-response/stakeholder-communication/" data-link-title="8.10 Stakeholder 通訊與外部狀態頁" data-link-desc="把 impact scope、status page、補償政策串成節奏">stakeholder comms</a></td>
      </tr>
  </tbody>
</table>
<p>客訴比告警早代表系統的外部痛點先於內部訊號出現。這種情境應先補服務健康指標、使用者可感知訊號與 alert runbook，再討論要用哪個監控平台；否則平台上線後仍可能只收集到工程師方便看的資料。</p>
<p>trace 接不上 queue 代表跨邊界關聯失效。這種情境應先檢查 trace context、correlation id、message metadata 與 sampling 策略，再選擇 OpenTelemetry backend、APM SaaS 或 log search 方案。</p>
<p>發版後才發現容量曲線崩壞代表驗證層缺少 gate。這種情境應先建立 workload model、baseline、回歸門檻與 release gate，再選 load test 工具或 performance dashboard。</p>
<p>chaos 實驗影響超出預期代表驗證工具先於安全邊界。這種情境應先定義 steady state、blast radius、停止條件與授權範圍，再決定使用 chaos mesh、fault proxy 或商業 chaos 平台。</p>
<p>多人同時修事故但決策互相覆蓋代表響應層缺少 command model。這種情境應先定義 incident commander、scribe、owner、decision log 與 handoff，再導入 IR 平台或 chat workflow。</p>
<p>對外狀態更新慢於內部復原代表 stakeholder 節奏不足。這種情境應先定義影響評估、更新頻率、外部狀態頁與客戶溝通責任，再選 status page 或 customer comms 工具。</p>
<h2 id="服務組合策略">服務組合策略</h2>
<p>服務組合策略的核心原則是先選最小閉環，再擴展平台覆蓋。完整閉環至少包含一個可判讀訊號、一個可驗證門檻、一個可接手流程與一個可回寫的 action tracking；缺任一層時，工具組合就會變成單點能力。</p>
<table>
  <thead>
      <tr>
          <th>組合型態</th>
          <th>適合情境</th>
          <th>主要風險</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>雲端原生整合</td>
          <td>團隊集中在單一 cloud provider</td>
          <td>跨雲、跨 SaaS 與高階查詢受限</td>
      </tr>
      <tr>
          <td>OSS 可組裝平台</td>
          <td>團隊有平台工程能力</td>
          <td>維護、升級、容量與成本治理重</td>
      </tr>
      <tr>
          <td>All-in-one SaaS</td>
          <td>團隊需要快速覆蓋與低維運</td>
          <td>成本、資料鎖定與自訂邊界受限</td>
      </tr>
      <tr>
          <td>混合式最小閉環</td>
          <td>既有工具已分散</td>
          <td>整合責任與 ownership 容易模糊</td>
      </tr>
  </tbody>
</table>
<p>雲端原生整合適合雲端邊界清楚的團隊。它能快速取得 infrastructure 訊號、IAM 整合與預設 dashboard，但跨外部 SaaS、跨語言 trace 或高基數探索時，需要提前確認資料出口與查詢能力。</p>
<p>OSS 可組裝平台適合有平台團隊維護 ingestion、storage、query 與 dashboard 的組織。它能降低 vendor lock-in 並保留彈性，但容量規劃、升級、安全修補、保留策略與 on-call 都會變成內部成本。</p>
<p>All-in-one SaaS 適合需要快速建立可觀測、告警與事故協作的團隊。它能把 log、metric、trace、APM、paging 或 workflow 整合在單一產品，但成本模型、資料保留、客製化限制與資料治理要在導入前確認。</p>
<p>混合式最小閉環適合已經有多套工具的團隊。它的重點是定義哪個系統是 alert source、哪個系統是 incident source of truth、哪個系統負責 action item closure；整合邊界比新增工具更重要。</p>
<h2 id="導入順序">導入順序</h2>
<p>導入順序的責任是降低一次導入多套工具的失敗風險。觀測、驗證與事故服務應依照事故風險與團隊成熟度逐層補齊，功能清單只適合放在能力判準之後。</p>
<ol>
<li>先補最小訊號：定義 SLI、error rate、latency、dependency failure、queue lag 與 customer-facing symptom。</li>
<li>再補最小告警與 runbook：讓 alert 指向可執行動作，避免只把噪音送到 on-call。</li>
<li>接著補驗證門檻：把 load、contract、migration、chaos 或 SLO 變成 release 前後的 gate。</li>
<li>然後補事故協作：定義 paging、severity、角色、decision log、status update 與 post-incident review。</li>
<li>最後補閉環治理：把偵測缺口、演練缺口與 action item 回寫到觀測、驗證與事故流程。</li>
</ol>
<p>這個順序讓工具投資跟風險暴露同步。若團隊在沒有基本訊號時先導入 incident workflow，事故流程會缺少證據；若在沒有實驗安全邊界時先導入 chaos 工具，驗證本身會變成風險來源；若在沒有 action tracking 時只做 postmortem，復盤會停在文字紀錄。</p>
<h2 id="交接路由">交接路由</h2>
<p>交接路由的責任是把服務選型判斷送到正確模組。選型章只決定「需要哪一類能力」，後續模組負責欄位、流程、工具與實作細節。</p>
<ul>
<li>需要判斷訊號是否足以支援診斷時，進入 <a href="/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">可觀測性平台</a>。</li>
<li>需要判斷失敗是否能被安全驗證時，進入 <a href="/blog/backend/06-reliability/" data-link-title="模組六：可靠性驗證流程" data-link-desc="用 SRE 領域詞彙建問題節點、以服務級案例庫累積驗證脈絡，先建概念與案例庫再進實作交接">可靠性驗證流程</a>。</li>
<li>需要判斷事故是否能被接住與回寫時，進入 <a href="/blog/backend/08-incident-response/" data-link-title="模組八：事故處理與復盤" data-link-desc="用 IR 領域詞彙建問題節點、以服務級案例庫累積事故脈絡，先建概念與案例庫再進實作交接">事故處理與復盤</a>。</li>
<li>需要比較具體 vendor 時，先讀各模組的 vendors index，再回到本章確認工具是否補到正確能力層。</li>
</ul>
<h2 id="完成判準">完成判準</h2>
<p>本章完成的判準是能把工具需求翻成能力需求。當團隊能說清楚「我們缺的是訊號、驗證、響應還是閉環」，選型討論才適合進入 vendor 比較。</p>
<p>檢查時可以問四個問題：</p>
<ol>
<li>現在的痛點是看不見、驗不過、接不住，還是回寫斷掉？</li>
<li>這個工具補的是哪一層能力，會產生哪些新操作成本？</li>
<li>導入後誰負責維護資料品質、流程品質與 action closure？</li>
<li>如果三個月後事故型態改變，哪個 tripwire 會提醒團隊重新評估？</li>
</ol>
]]></content:encoded></item><item><title>6.12 Idempotency 與 Replay 驗證</title><link>https://tarrragon.github.io/blog/backend/06-reliability/idempotency-replay/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/idempotency-replay/</guid><description>&lt;h2 id="大綱">大綱&lt;/h2>
&lt;ul>
&lt;li>為何 idempotency 是分散式系統一級屬性：retry / failover / replay 的前提&lt;/li>
&lt;li>idempotency key 的設計：來源、生命週期、儲存&lt;/li>
&lt;li>exactly-once 是幻象、at-least-once + idempotent 才實際&lt;/li>
&lt;li>replay 驗證：從 log / event store 重播能否得到相同最終狀態&lt;/li>
&lt;li>跟 &lt;a href="https://tarrragon.github.io/blog/backend/03-message-queue/" data-link-title="模組三：訊息佇列與事件傳遞" data-link-desc="整理 durable queue、broker、retry、outbox 與 idempotency 的後端實務">03 message-queue&lt;/a> 的關係：consumer idempotency 是延伸專題&lt;/li>
&lt;li>payment / order / messaging 的 idempotency 模式差異&lt;/li>
&lt;li>跟 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/chaos-testing/" data-link-title="6.4 chaos testing" data-link-desc="把故障注入從工具操作升級成可驗證流程：先定義穩態，再按依賴類型設計注入、控制 blast radius 與收集證據">6.4 chaos&lt;/a> 的整合：注入重複訊息驗證冪等&lt;/li>
&lt;li>反模式：idempotency 只靠 DB unique constraint、無 key 設計；retry 後副作用重複；replay 路徑從未驗證&lt;/li>
&lt;/ul>
&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/idempotency/" data-link-title="Idempotency" data-link-desc="說明同一操作執行多次時如何保持結果一致">Idempotency&lt;/a> 與 replay 驗證是把重試、重播與副作用控制變成可驗證屬性，責任是讓 at-least-once 與 failover 不會把系統推向重複執行。&lt;/p>
&lt;p>這一頁處理的是分散式系統的重複輸入問題。只要有 retry、補償或訊息重送，冪等性就是正確性前提，把它當優化項會低估風險。&lt;/p>
&lt;h2 id="核心判讀">核心判讀&lt;/h2>
&lt;p>判讀 idempotency 時，先看 key 的生命週期，再看 replay 是否能落在同一狀態。&lt;/p>
&lt;p>重點訊號包括：&lt;/p>
&lt;ul>
&lt;li>idempotency key 是否由 server 可控、可追蹤&lt;/li>
&lt;li>replay 路徑是否與 production 對齊&lt;/li>
&lt;li>late retry 是否會被誤視為新請求&lt;/li>
&lt;li>重複副作用是否能靠狀態機吸收&lt;/li>
&lt;/ul>
&lt;h2 id="案例對照">案例對照&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/stripe/" data-link-title="Stripe" data-link-desc="Stripe Deploy Strategy / Game Day / Idempotency 實踐">Stripe&lt;/a>：交易流程需要嚴格控制重複請求。&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/cases/github/" data-link-title="GitHub" data-link-desc="GitHub 重大事故時間線與架構脈絡">GitHub&lt;/a>：webhook / event replay 經常直接暴露冪等缺口。&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/cases/slack/" data-link-title="Slack" data-link-desc="Slack 通訊服務事故與外部狀態頁設計">Slack&lt;/a>：訊息與通知類流程特別依賴重複輸入控制。&lt;/li>
&lt;/ul>
&lt;h2 id="支付類-idempotency-的設計約束">支付類 Idempotency 的設計約束&lt;/h2>
&lt;p>支付類 idempotency 的核心約束是「key 邊界跟業務操作邊界一致」 — 同一筆支付的所有 retry 必須共用 key、跨支付 key 必須不同、key 不可被偽造、且要保留足夠重放證據。失敗代價（重複扣款、重複建單）讓這四個約束從 best practice 變成正確性前提。&lt;/p>
&lt;p>對應 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/" data-link-title="Stripe：Idempotency 與零停機遷移的交易安全設計" data-link-desc="把 API 重試與資料遷移放在同一套安全模型，維持支付交易的一致結果。">S1 Stripe Idempotency 與零停機遷移&lt;/a> 揭露的 idempotency key 跟 transaction-path observability 兩個機制（S1 case 直接列出）；以下實作層判讀條件屬通用工程知識展開、case 本身只給「key 跟業務邊界一致」這一條方向。&lt;/p>
&lt;p>實作層的判讀條件：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Key 邊界跟業務一致&lt;/strong>：同一筆支付的 retry 共用 idempotency key、跨支付 key 不同。Key 來源 / TTL / fallback 設計屬實作細節、跟 6.12 SSoT 描述的 server 端 key 設計呼應&lt;/li>
&lt;li>&lt;strong>保留足夠證據供重放&lt;/strong>：transaction-path observability 要覆蓋交易關鍵欄位、讓 reconciliation 跟稽核可重放判讀&lt;/li>
&lt;/ul>
&lt;p>跟 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/migration-safety/#%e4%ba%a4%e6%98%93%e9%a1%9e-migration-%e7%9a%84%e7%89%b9%e6%ae%8a%e6%80%a7" data-link-title="6.11 Migration Safety 與 DB Rollout" data-link-desc="把 schema migration 從一次性事件變成可逆、可漸進的 rollout 流程">6.11 migration-safety 交易類段&lt;/a> 共用 transaction-path observability、避免 migration 期間 idempotency 判讀失效。支付 reconciliation 跟交易語義詳見 01 資料庫模組（具體章節依 reconciliation / transaction 主題、目前待 01 模組對應頁建立）。&lt;/p></description><content:encoded><![CDATA[<h2 id="大綱">大綱</h2>
<ul>
<li>為何 idempotency 是分散式系統一級屬性：retry / failover / replay 的前提</li>
<li>idempotency key 的設計：來源、生命週期、儲存</li>
<li>exactly-once 是幻象、at-least-once + idempotent 才實際</li>
<li>replay 驗證：從 log / event store 重播能否得到相同最終狀態</li>
<li>跟 <a href="/blog/backend/03-message-queue/" data-link-title="模組三：訊息佇列與事件傳遞" data-link-desc="整理 durable queue、broker、retry、outbox 與 idempotency 的後端實務">03 message-queue</a> 的關係：consumer idempotency 是延伸專題</li>
<li>payment / order / messaging 的 idempotency 模式差異</li>
<li>跟 <a href="/blog/backend/06-reliability/chaos-testing/" data-link-title="6.4 chaos testing" data-link-desc="把故障注入從工具操作升級成可驗證流程：先定義穩態，再按依賴類型設計注入、控制 blast radius 與收集證據">6.4 chaos</a> 的整合：注入重複訊息驗證冪等</li>
<li>反模式：idempotency 只靠 DB unique constraint、無 key 設計；retry 後副作用重複；replay 路徑從未驗證</li>
</ul>
<h2 id="概念定位">概念定位</h2>
<p><a href="/blog/backend/knowledge-cards/idempotency/" data-link-title="Idempotency" data-link-desc="說明同一操作執行多次時如何保持結果一致">Idempotency</a> 與 replay 驗證是把重試、重播與副作用控制變成可驗證屬性，責任是讓 at-least-once 與 failover 不會把系統推向重複執行。</p>
<p>這一頁處理的是分散式系統的重複輸入問題。只要有 retry、補償或訊息重送，冪等性就是正確性前提，把它當優化項會低估風險。</p>
<h2 id="核心判讀">核心判讀</h2>
<p>判讀 idempotency 時，先看 key 的生命週期，再看 replay 是否能落在同一狀態。</p>
<p>重點訊號包括：</p>
<ul>
<li>idempotency key 是否由 server 可控、可追蹤</li>
<li>replay 路徑是否與 production 對齊</li>
<li>late retry 是否會被誤視為新請求</li>
<li>重複副作用是否能靠狀態機吸收</li>
</ul>
<h2 id="案例對照">案例對照</h2>
<ul>
<li><a href="/blog/backend/06-reliability/cases/stripe/" data-link-title="Stripe" data-link-desc="Stripe Deploy Strategy / Game Day / Idempotency 實踐">Stripe</a>：交易流程需要嚴格控制重複請求。</li>
<li><a href="/blog/backend/08-incident-response/cases/github/" data-link-title="GitHub" data-link-desc="GitHub 重大事故時間線與架構脈絡">GitHub</a>：webhook / event replay 經常直接暴露冪等缺口。</li>
<li><a href="/blog/backend/08-incident-response/cases/slack/" data-link-title="Slack" data-link-desc="Slack 通訊服務事故與外部狀態頁設計">Slack</a>：訊息與通知類流程特別依賴重複輸入控制。</li>
</ul>
<h2 id="支付類-idempotency-的設計約束">支付類 Idempotency 的設計約束</h2>
<p>支付類 idempotency 的核心約束是「key 邊界跟業務操作邊界一致」 — 同一筆支付的所有 retry 必須共用 key、跨支付 key 必須不同、key 不可被偽造、且要保留足夠重放證據。失敗代價（重複扣款、重複建單）讓這四個約束從 best practice 變成正確性前提。</p>
<p>對應 <a href="/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/" data-link-title="Stripe：Idempotency 與零停機遷移的交易安全設計" data-link-desc="把 API 重試與資料遷移放在同一套安全模型，維持支付交易的一致結果。">S1 Stripe Idempotency 與零停機遷移</a> 揭露的 idempotency key 跟 transaction-path observability 兩個機制（S1 case 直接列出）；以下實作層判讀條件屬通用工程知識展開、case 本身只給「key 跟業務邊界一致」這一條方向。</p>
<p>實作層的判讀條件：</p>
<ul>
<li><strong>Key 邊界跟業務一致</strong>：同一筆支付的 retry 共用 idempotency key、跨支付 key 不同。Key 來源 / TTL / fallback 設計屬實作細節、跟 6.12 SSoT 描述的 server 端 key 設計呼應</li>
<li><strong>保留足夠證據供重放</strong>：transaction-path observability 要覆蓋交易關鍵欄位、讓 reconciliation 跟稽核可重放判讀</li>
</ul>
<p>跟 <a href="/blog/backend/06-reliability/migration-safety/#%e4%ba%a4%e6%98%93%e9%a1%9e-migration-%e7%9a%84%e7%89%b9%e6%ae%8a%e6%80%a7" data-link-title="6.11 Migration Safety 與 DB Rollout" data-link-desc="把 schema migration 從一次性事件變成可逆、可漸進的 rollout 流程">6.11 migration-safety 交易類段</a> 共用 transaction-path observability、避免 migration 期間 idempotency 判讀失效。支付 reconciliation 跟交易語義詳見 01 資料庫模組（具體章節依 reconciliation / transaction 主題、目前待 01 模組對應頁建立）。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>03 message-queue：consumer 端冪等設計</li>
<li>06.4 chaos：注入重複訊息驗證</li>
<li>06.7 DR：replay 作為回復手段的前提</li>
</ul>
<h2 id="判讀訊號">判讀訊號</h2>
<ul>
<li>用戶被重複扣款 / 重複建立資源、靠人工對帳發現</li>
<li>retry policy 開啟後事故變嚴重、不敢開 retry</li>
<li>replay 從 event store 跑一次、結果跟 production 不同</li>
<li>idempotency key 從 client 端帶上來、無 server 端 fallback</li>
<li>key TTL 過短、晚到的 retry 變成新請求</li>
</ul>
<h2 id="交接路由">交接路由</h2>
<ul>
<li>03 message-queue：consumer idempotency 實作</li>
<li>06.4 chaos：注入重複訊息 / 故障 retry 場景</li>
<li>06.7 DR：replay 作為回復手段的前提</li>
<li>07 資安：idempotency key 不可被預測 / 偽造</li>
</ul>
]]></content:encoded></item><item><title>Honeycomb</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/honeycomb/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/honeycomb/</guid><description>&lt;p>Honeycomb 是 observability platform、由創辦人之一 Charity Majors 推動的 observability-driven SRE 是領域 thought leadership 來源。教學重點在「以 observability 為主軸的 SRE 工程文化」。&lt;/p>
&lt;h2 id="規劃重點">規劃重點&lt;/h2>
&lt;ul>
&lt;li>High-cardinality observability：相對 metrics-first 的觀測哲學&lt;/li>
&lt;li>Service Level Objective 實作：SLO budget、burn rate alert&lt;/li>
&lt;li>Test in production：feature flag + observability 的 production testing&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/on-call/" data-link-title="On-Call" data-link-desc="說明值班制度如何承接告警、事故分級與升級流程">on-call&lt;/a> 文化：Charity Majors 的 SRE / on-call 觀點&lt;/li>
&lt;/ul>
&lt;h2 id="預計收錄實踐">預計收錄實踐&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>議題&lt;/th>
 &lt;th>教學重點&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Observability Engineering&lt;/td>
 &lt;td>high-cardinality 與 unknown-unknowns&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>SLO Burn Rate Alert&lt;/td>
 &lt;td>error budget 速率告警設計&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Test in Production&lt;/td>
 &lt;td>feature flag + observability 的安全推進&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Production Excellence&lt;/td>
 &lt;td>Honeycomb 推動的 SRE 文化框架&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="案例定位">案例定位&lt;/h2>
&lt;p>Honeycomb 這個案例在講的是 observability 如何變成工程決策，而不是只剩看板與指標。讀者先抓 high-cardinality、burn rate 與 test in production 這三個原語，再把它們看成觀測能力如何支撐 SRE 文化。&lt;/p>
&lt;h2 id="判讀重點">判讀重點&lt;/h2>
&lt;p>當訊號維度開始膨脹時，重點是先判斷資料還能不能回答問題，增加更多圖表解決不了維度膨脹。當 SLO 進入 burn 速率區間時，觀測系統要能直接幫團隊看見風險，而不是等事故發生後才補證據。&lt;/p>
&lt;h2 id="可操作判準">可操作判準&lt;/h2>
&lt;ul>
&lt;li>能否辨認 high cardinality 何時讓查詢與告警失真&lt;/li>
&lt;li>能否把 SLO burn rate 轉成當下可行動的訊號&lt;/li>
&lt;li>能否在 production testing 中保住 blast radius&lt;/li>
&lt;li>能否把 observability 當成工程責任，而不是 ops 專屬工作&lt;/li>
&lt;/ul>
&lt;h2 id="與其他案例的關係">與其他案例的關係&lt;/h2>
&lt;p>Honeycomb 把觀測責任直接拉到每個工程團隊，這和 Google 的 SLO 制度、Datadog 的自我觀測、Slack 的狀態揭露形成一組互補視角。當讀者先懂這頁，就比較容易看懂為什麼高 cardinality 與 burn rate 是決策前提，當成報表細節會低估它們的影響。&lt;/p>
&lt;h2 id="代表樣本">代表樣本&lt;/h2>
&lt;ul>
&lt;li>high cardinality 讓問題能按 tenant、feature、path 切開，而不是只看總平均。&lt;/li>
&lt;li>burn rate alert 直接把 SLO 消耗速度變成行動訊號。&lt;/li>
&lt;li>test in production 讓觀測訊號在真實流量下被驗證。&lt;/li>
&lt;li>observability engineering 把看板轉成工程決策入口。&lt;/li>
&lt;li>unknown-unknowns 讓觀測系統要先能回答「不知道要查什麼」的問題。&lt;/li>
&lt;li>production excellence 讓 observability 成為每個工程師的日常責任。&lt;/li>
&lt;li>query latency 會反過來告訴你資料建模是否已經失真。&lt;/li>
&lt;li>feature flag 配合觀測訊號，讓 production testing 可以安全推進。&lt;/li>
&lt;/ul>
&lt;h2 id="章節列表">章節列表&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>章節&lt;/th>
 &lt;th>主題&lt;/th>
 &lt;th>核心責任&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/honeycomb/burn-rate-driven-reliability-operations/" data-link-title="Honeycomb：以 Burn Rate 驅動的可靠性操作" data-link-desc="把 SLO burn rate 直接連到值班決策與改善優先序，降低高噪音告警造成的判讀失真。">HC1&lt;/a>&lt;/td>
 &lt;td>Burn Rate 驅動可靠性&lt;/td>
 &lt;td>把 SLO 消耗速度轉成值班與改善優先序&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/honeycomb/production-excellence-and-test-in-production/" data-link-title="Honeycomb：Production Excellence 與 Test in Production" data-link-desc="用 high-cardinality observability 把 production 變成安全的驗證環境：feature flag、progressive rollout 與即時回饋的配合。">HC2&lt;/a>&lt;/td>
 &lt;td>Production Excellence 與 Test in Prod&lt;/td>
 &lt;td>用 observability 把 production 變成安全的驗證環境&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://www.honeycomb.io/resources/getting-started/what-is-observability-engineering">What Is Observability Engineering?&lt;/a>：Honeycomb 對 observability engineering 的核心定義。&lt;/li>
&lt;li>&lt;a href="https://docs.honeycomb.io/get-started/basics/observability/concepts/high-cardinality">High Cardinality&lt;/a>：高 cardinality / dimensionality 的官方說明。&lt;/li>
&lt;li>&lt;a href="https://docs.honeycomb.io/reference/honeycomb-ui/slos/slo-detail-view/">SLO Detail View&lt;/a>：burn rate 與 budget burndown 的產品視角。&lt;/li>
&lt;li>&lt;a href="https://www.honeycomb.io/blog/observability-every-engineers-job-not-just-ops-problem">Observability: It&amp;rsquo;s Every Engineer’s Job, Not Just Ops’ Problem&lt;/a>：觀測責任不只在 ops 的實踐論述。&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Honeycomb 是 observability platform、由創辦人之一 Charity Majors 推動的 observability-driven SRE 是領域 thought leadership 來源。教學重點在「以 observability 為主軸的 SRE 工程文化」。</p>
<h2 id="規劃重點">規劃重點</h2>
<ul>
<li>High-cardinality observability：相對 metrics-first 的觀測哲學</li>
<li>Service Level Objective 實作：SLO budget、burn rate alert</li>
<li>Test in production：feature flag + observability 的 production testing</li>
<li><a href="/blog/backend/knowledge-cards/on-call/" data-link-title="On-Call" data-link-desc="說明值班制度如何承接告警、事故分級與升級流程">on-call</a> 文化：Charity Majors 的 SRE / on-call 觀點</li>
</ul>
<h2 id="預計收錄實踐">預計收錄實踐</h2>
<table>
  <thead>
      <tr>
          <th>議題</th>
          <th>教學重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Observability Engineering</td>
          <td>high-cardinality 與 unknown-unknowns</td>
      </tr>
      <tr>
          <td>SLO Burn Rate Alert</td>
          <td>error budget 速率告警設計</td>
      </tr>
      <tr>
          <td>Test in Production</td>
          <td>feature flag + observability 的安全推進</td>
      </tr>
      <tr>
          <td>Production Excellence</td>
          <td>Honeycomb 推動的 SRE 文化框架</td>
      </tr>
  </tbody>
</table>
<h2 id="案例定位">案例定位</h2>
<p>Honeycomb 這個案例在講的是 observability 如何變成工程決策，而不是只剩看板與指標。讀者先抓 high-cardinality、burn rate 與 test in production 這三個原語，再把它們看成觀測能力如何支撐 SRE 文化。</p>
<h2 id="判讀重點">判讀重點</h2>
<p>當訊號維度開始膨脹時，重點是先判斷資料還能不能回答問題，增加更多圖表解決不了維度膨脹。當 SLO 進入 burn 速率區間時，觀測系統要能直接幫團隊看見風險，而不是等事故發生後才補證據。</p>
<h2 id="可操作判準">可操作判準</h2>
<ul>
<li>能否辨認 high cardinality 何時讓查詢與告警失真</li>
<li>能否把 SLO burn rate 轉成當下可行動的訊號</li>
<li>能否在 production testing 中保住 blast radius</li>
<li>能否把 observability 當成工程責任，而不是 ops 專屬工作</li>
</ul>
<h2 id="與其他案例的關係">與其他案例的關係</h2>
<p>Honeycomb 把觀測責任直接拉到每個工程團隊，這和 Google 的 SLO 制度、Datadog 的自我觀測、Slack 的狀態揭露形成一組互補視角。當讀者先懂這頁，就比較容易看懂為什麼高 cardinality 與 burn rate 是決策前提，當成報表細節會低估它們的影響。</p>
<h2 id="代表樣本">代表樣本</h2>
<ul>
<li>high cardinality 讓問題能按 tenant、feature、path 切開，而不是只看總平均。</li>
<li>burn rate alert 直接把 SLO 消耗速度變成行動訊號。</li>
<li>test in production 讓觀測訊號在真實流量下被驗證。</li>
<li>observability engineering 把看板轉成工程決策入口。</li>
<li>unknown-unknowns 讓觀測系統要先能回答「不知道要查什麼」的問題。</li>
<li>production excellence 讓 observability 成為每個工程師的日常責任。</li>
<li>query latency 會反過來告訴你資料建模是否已經失真。</li>
<li>feature flag 配合觀測訊號，讓 production testing 可以安全推進。</li>
</ul>
<h2 id="章節列表">章節列表</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/honeycomb/burn-rate-driven-reliability-operations/" data-link-title="Honeycomb：以 Burn Rate 驅動的可靠性操作" data-link-desc="把 SLO burn rate 直接連到值班決策與改善優先序，降低高噪音告警造成的判讀失真。">HC1</a></td>
          <td>Burn Rate 驅動可靠性</td>
          <td>把 SLO 消耗速度轉成值班與改善優先序</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/honeycomb/production-excellence-and-test-in-production/" data-link-title="Honeycomb：Production Excellence 與 Test in Production" data-link-desc="用 high-cardinality observability 把 production 變成安全的驗證環境：feature flag、progressive rollout 與即時回饋的配合。">HC2</a></td>
          <td>Production Excellence 與 Test in Prod</td>
          <td>用 observability 把 production 變成安全的驗證環境</td>
      </tr>
  </tbody>
</table>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://www.honeycomb.io/resources/getting-started/what-is-observability-engineering">What Is Observability Engineering?</a>：Honeycomb 對 observability engineering 的核心定義。</li>
<li><a href="https://docs.honeycomb.io/get-started/basics/observability/concepts/high-cardinality">High Cardinality</a>：高 cardinality / dimensionality 的官方說明。</li>
<li><a href="https://docs.honeycomb.io/reference/honeycomb-ui/slos/slo-detail-view/">SLO Detail View</a>：burn rate 與 budget burndown 的產品視角。</li>
<li><a href="https://www.honeycomb.io/blog/observability-every-engineers-job-not-just-ops-problem">Observability: It&rsquo;s Every Engineer’s Job, Not Just Ops’ Problem</a>：觀測責任不只在 ops 的實踐論述。</li>
</ul>
]]></content:encoded></item><item><title>Sloth</title><link>https://tarrragon.github.io/blog/backend/06-reliability/vendors/sloth/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/vendors/sloth/</guid><description>&lt;p>Sloth 是 OSS Prometheus SLO generator、承擔三個責任：輸入簡單 YAML 定義 SLO、輸出 Prometheus recording rules + alerting rules（multi-window multi-burn-rate）、降低 SLO 維護成本。設計取捨偏向「Prometheus-only + OSS + GitOps-friendly」、適合 Prometheus-based 環境的純 OSS SLO 流程、跟 Nobl9 的 SaaS / multi-source 是不同定位。&lt;/p>
&lt;h2 id="本章目標">本章目標&lt;/h2>
&lt;p>讀完本章後、你應該能：&lt;/p>
&lt;ol>
&lt;li>寫 Sloth SLO YAML&lt;/li>
&lt;li>產生 Prometheus recording / alerting rules&lt;/li>
&lt;li>設計 multi-window multi-burn-rate alert&lt;/li>
&lt;li>用 K8s Operator mode 自動同步&lt;/li>
&lt;li>評估從 Sloth 升級到 Nobl9 / OpenSLO 路徑&lt;/li>
&lt;/ol>
&lt;h2 id="最短路徑5-分鐘把-sloth-跑起來">最短路徑：5 分鐘把 Sloth 跑起來&lt;/h2>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c1"># 1. 安裝&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: brew install slok/sloth/sloth / docker run&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="c1"># 2. 寫 SLO spec YAML&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: version: prometheus/v1, service, slos: [{name, objective, sli}]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl">&lt;span class="c1"># 3. Generate rules&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: sloth generate -i slo.yaml &amp;gt; rules.yaml&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">9&lt;/span>&lt;span class="cl">&lt;span class="c1"># TODO: 把 rules.yaml 載入 Prometheus&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="日常操作與決策形狀">日常操作與決策形狀&lt;/h2>
&lt;h3 id="slo-yaml-結構">SLO YAML 結構&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>version + service&lt;/li>
&lt;li>slos[]：name / objective / SLI（events / raw）&lt;/li>
&lt;li>Alerting（page / ticket）&lt;/li>
&lt;/ul>
&lt;h3 id="multi-window-multi-burn-rate-alert">Multi-window multi-burn-rate alert&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>Sloth 預設產生 Google SRE recommended alert（4 windows）&lt;/li>
&lt;li>Fast burn / slow burn&lt;/li>
&lt;li>對應 page（urgent）vs ticket（non-urgent）&lt;/li>
&lt;/ul>
&lt;h3 id="generate-rules-workflow">Generate rules workflow&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>CLI generate&lt;/li>
&lt;li>Output: recording rules + alert rules&lt;/li>
&lt;li>放進 Prometheus rule_files 載入&lt;/li>
&lt;/ul>
&lt;h2 id="進階主題按需閱讀">進階主題（按需閱讀）&lt;/h2>
&lt;h3 id="kubernetes-operator-mode">Kubernetes Operator mode&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>Sloth K8s Operator&lt;/li>
&lt;li>PrometheusServiceLevel CRD&lt;/li>
&lt;li>自動 reconcile + 同步 Prometheus rules&lt;/li>
&lt;li>對應 &lt;a href="https://tarrragon.github.io/blog/backend/05-deployment-platform/vendors/kubernetes/" data-link-title="Kubernetes" data-link-desc="Container orchestration 主流、GKE / EKS / AKS / 自管">Kubernetes vendor 頁&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="slo-types">SLO types&lt;/h3>
&lt;p>子議題：&lt;/p>
&lt;ul>
&lt;li>Events-based SLI（好 events / 總 events）&lt;/li>
&lt;li>Raw query SLI（自訂 PromQL）&lt;/li>
&lt;li>對應 PromQL 撰寫&lt;/li>
&lt;/ul>
&lt;h3 id="ci--gitops">CI / GitOps&lt;/h3>
&lt;p>子議題：&lt;/p></description><content:encoded><![CDATA[<p>Sloth 是 OSS Prometheus SLO generator、承擔三個責任：輸入簡單 YAML 定義 SLO、輸出 Prometheus recording rules + alerting rules（multi-window multi-burn-rate）、降低 SLO 維護成本。設計取捨偏向「Prometheus-only + OSS + GitOps-friendly」、適合 Prometheus-based 環境的純 OSS SLO 流程、跟 Nobl9 的 SaaS / multi-source 是不同定位。</p>
<h2 id="本章目標">本章目標</h2>
<p>讀完本章後、你應該能：</p>
<ol>
<li>寫 Sloth SLO YAML</li>
<li>產生 Prometheus recording / alerting rules</li>
<li>設計 multi-window multi-burn-rate alert</li>
<li>用 K8s Operator mode 自動同步</li>
<li>評估從 Sloth 升級到 Nobl9 / OpenSLO 路徑</li>
</ol>
<h2 id="最短路徑5-分鐘把-sloth-跑起來">最短路徑：5 分鐘把 Sloth 跑起來</h2>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 1. 安裝</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1"># TODO: brew install slok/sloth/sloth / docker run</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 2. 寫 SLO spec YAML</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="c1"># TODO: version: prometheus/v1, service, slos: [{name, objective, sli}]</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">
</span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="c1"># 3. Generate rules</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="c1"># TODO: sloth generate -i slo.yaml &gt; rules.yaml</span>
</span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="c1"># TODO: 把 rules.yaml 載入 Prometheus</span></span></span></code></pre></div><h2 id="日常操作與決策形狀">日常操作與決策形狀</h2>
<h3 id="slo-yaml-結構">SLO YAML 結構</h3>
<p>子議題：</p>
<ul>
<li>version + service</li>
<li>slos[]：name / objective / SLI（events / raw）</li>
<li>Alerting（page / ticket）</li>
</ul>
<h3 id="multi-window-multi-burn-rate-alert">Multi-window multi-burn-rate alert</h3>
<p>子議題：</p>
<ul>
<li>Sloth 預設產生 Google SRE recommended alert（4 windows）</li>
<li>Fast burn / slow burn</li>
<li>對應 page（urgent）vs ticket（non-urgent）</li>
</ul>
<h3 id="generate-rules-workflow">Generate rules workflow</h3>
<p>子議題：</p>
<ul>
<li>CLI generate</li>
<li>Output: recording rules + alert rules</li>
<li>放進 Prometheus rule_files 載入</li>
</ul>
<h2 id="進階主題按需閱讀">進階主題（按需閱讀）</h2>
<h3 id="kubernetes-operator-mode">Kubernetes Operator mode</h3>
<p>子議題：</p>
<ul>
<li>Sloth K8s Operator</li>
<li>PrometheusServiceLevel CRD</li>
<li>自動 reconcile + 同步 Prometheus rules</li>
<li>對應 <a href="/blog/backend/05-deployment-platform/vendors/kubernetes/" data-link-title="Kubernetes" data-link-desc="Container orchestration 主流、GKE / EKS / AKS / 自管">Kubernetes vendor 頁</a></li>
</ul>
<h3 id="slo-types">SLO types</h3>
<p>子議題：</p>
<ul>
<li>Events-based SLI（好 events / 總 events）</li>
<li>Raw query SLI（自訂 PromQL）</li>
<li>對應 PromQL 撰寫</li>
</ul>
<h3 id="ci--gitops">CI / GitOps</h3>
<p>子議題：</p>
<ul>
<li>Sloth 在 CI 跑 generate</li>
<li>Git commit rules.yaml</li>
<li>Prometheus pull rules.yaml</li>
</ul>
<h3 id="vs-pyrra">vs Pyrra</h3>
<p>子議題：</p>
<ul>
<li>Sloth：CLI + Operator、產生 rules</li>
<li>Pyrra：K8s-native CRD、UI 內建</li>
<li>選擇判讀：簡單 / CI-first → Sloth；K8s-native + UI → Pyrra</li>
</ul>
<h3 id="vs-nobl9">vs Nobl9</h3>
<p>子議題：</p>
<ul>
<li>Sloth：OSS / Prometheus-only / 無 SaaS</li>
<li>Nobl9：商業 SaaS / 多 source / governance</li>
<li>升級路徑：OpenSLO YAML 部分相容</li>
</ul>
<h3 id="alert-tuning">Alert tuning</h3>
<p>子議題：</p>
<ul>
<li>Burn rate threshold 調整（依 service criticality）</li>
<li>Inhibition（alert 之間互相壓制）</li>
<li>對應 Alertmanager routing</li>
</ul>
<h2 id="排錯快速判讀">排錯快速判讀</h2>
<h3 id="generate-fail">Generate fail</h3>
<p>操作原則：YAML 格式錯 / SLI query 語法錯。判讀：sloth validate。</p>
<h3 id="alert-noise">Alert noise</h3>
<p>操作原則：burn rate threshold 過嚴。</p>
<h3 id="recording-rule-太多">Recording rule 太多</h3>
<p>操作原則：每 SLO 產生 N recording rules、cardinality 累積快。判讀：Prometheus series count。</p>
<h3 id="operator-reconcile-失敗">Operator reconcile 失敗</h3>
<p>操作原則：CRD permission / Prometheus rule API 連不上。</p>
<h2 id="何時改走其他服務">何時改走其他服務</h2>
<table>
  <thead>
      <tr>
          <th>需求形狀</th>
          <th>改走</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Multi-source</td>
          <td><a href="/blog/backend/06-reliability/vendors/nobl9/" data-link-title="Nobl9" data-link-desc="SLO platform、跨 data source、企業 SLO 治理">Nobl9</a></td>
      </tr>
      <tr>
          <td>K8s-native CRD + UI</td>
          <td>Pyrra</td>
      </tr>
      <tr>
          <td>Vendor 內建 SLO</td>
          <td>Datadog / Grafana / Honeycomb SLO</td>
      </tr>
      <tr>
          <td>純 SaaS</td>
          <td><a href="/blog/backend/06-reliability/vendors/nobl9/" data-link-title="Nobl9" data-link-desc="SLO platform、跨 data source、企業 SLO 治理">Nobl9</a></td>
      </tr>
      <tr>
          <td>完整 OpenSLO</td>
          <td>OpenSLO + 對應 generator</td>
      </tr>
  </tbody>
</table>
<h2 id="不在本頁內的主題">不在本頁內的主題</h2>
<ul>
<li>PromQL 語法基礎</li>
<li>Prometheus alerting rule 內部</li>
<li>Sloth 完整 CLI option</li>
</ul>
<h2 id="案例回寫">案例回寫</h2>
<table>
  <thead>
      <tr>
          <th>案例方向</th>
          <th>對應主題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/google/error-budget-policy-and-release-gating/" data-link-title="Google：Error Budget 政策如何決定發布節奏" data-link-desc="把 SLO 消耗量轉成 release gate，讓可靠性與交付速度共用同一套決策語言。">Google：Error Budget 與 Release Gating</a></td>
          <td>SLI / SLO 原典、用來生成 Prometheus rule 的對齊對象</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/honeycomb/burn-rate-driven-reliability-operations/" data-link-title="Honeycomb：以 Burn Rate 驅動的可靠性操作" data-link-desc="把 SLO burn rate 直接連到值班決策與改善優先序，降低高噪音告警造成的判讀失真。">Honeycomb：Burn Rate 驅動可靠性</a></td>
          <td>multi-window multi-burn-rate alert 的 PromQL 落地語意</td>
      </tr>
  </tbody>
</table>
<p><strong>Case 庫稀薄</strong>：本 cases/ 目錄目前沒有以 Sloth 為主軸的案例。</p>
<ul>
<li><strong>待補 Sloth customer case</strong>：Prometheus 重度團隊採用、Kubernetes Operator 落地案例</li>
<li><strong>候選 case</strong>：Spotify（Backstage + Prometheus 結合 SLO metadata）、LinkedIn（self-service metrics + SLO rule generation）— 若未來收錄需先在 cases/ 補正文</li>
</ul>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>上游概念：<a href="/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">knowledge cards burn-rate</a></li>
<li>平行 vendor：<a href="/blog/backend/06-reliability/vendors/nobl9/" data-link-title="Nobl9" data-link-desc="SLO platform、跨 data source、企業 SLO 治理">Nobl9</a>、<a href="/blog/backend/04-observability/vendors/prometheus/" data-link-title="Prometheus" data-link-desc="Pull-based metrics 主流 OSS、PromQL 與 alerting">Prometheus</a></li>
<li>下游能力：<a href="/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">04 observability</a>、<a href="/blog/backend/04-observability/vendors/prometheus/" data-link-title="Prometheus" data-link-desc="Pull-based metrics 主流 OSS、PromQL 與 alerting">Alertmanager</a></li>
</ul>
]]></content:encoded></item><item><title>0.13 操作控制 vertical slice 實作入口</title><link>https://tarrragon.github.io/blog/backend/00-service-selection/operations-control-vertical-slice/</link><pubDate>Thu, 07 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/00-service-selection/operations-control-vertical-slice/</guid><description>&lt;p>操作控制 vertical slice 的核心責任是把「看得見、驗得過、接得住、回寫得動」落到同一個服務流程。這一章把 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/evidence-package/" data-link-title="Evidence Package" data-link-desc="說明觀測、驗證與事故流程如何把證據包成可交接、可回放的 artifact">evidence package&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/steady-state/" data-link-title="Steady State" data-link-desc="說明可靠性實驗與事故恢復如何定義系統應維持的可接受狀態">steady state&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/incident-decision-log/" data-link-title="Incident Decision Log" data-link-desc="說明事故期間如何保留決策、證據、owner 與回退條件">incident decision log&lt;/a> 與 action item closure 串成第一個可實作切片。&lt;/p>
&lt;h2 id="大綱">大綱&lt;/h2>
&lt;ul>
&lt;li>實作目標：選一個核心 user journey，建立最小操作控制閉環&lt;/li>
&lt;li>輸入：服務入口、核心依賴、SLO / SLI、告警、驗證場景、事故流程&lt;/li>
&lt;li>產出：evidence package、verification evidence handoff、incident decision log、write-back item&lt;/li>
&lt;li>邊界：先做 artifact 與路由，工具與語言實作留給 04 / 06 / 08 與語言教材&lt;/li>
&lt;li>驗收：能從一次異常走完 triage、verification、decision、write-back&lt;/li>
&lt;/ul>
&lt;h2 id="實作目標">實作目標&lt;/h2>
&lt;p>Vertical slice 的目標是先做一條可回放的操作控制路徑。選一個核心 user journey，例如 checkout、message delivery、document publish、login 或 invoice generation，讓這條路徑同時具備觀測證據、驗證門檻、事故決策與回寫機制。&lt;/p>
&lt;p>這一輪的交付是 artifact 與流程責任。工具可以是現有 log search、dashboard、ticket、runbook repository 與 chat；重點是資料欄位與流程責任先成立，後續才判斷是否需要 Prometheus、OpenTelemetry backend、PagerDuty、incident.io 或 chaos tooling。&lt;/p>
&lt;h2 id="選擇服務切片">選擇服務切片&lt;/h2>
&lt;p>服務切片的選擇責任是找到最能暴露 04 / 06 / 08 交接問題的路徑。第一條 slice 應該具備使用者影響、依賴邊界、可量測訊號與可驗證失敗模式。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>候選切片&lt;/th>
 &lt;th>適合原因&lt;/th>
 &lt;th>常見失敗模式&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Checkout&lt;/td>
 &lt;td>直接連到收入與客戶痛點&lt;/td>
 &lt;td>payment timeout、inventory lag&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Message delivery&lt;/td>
 &lt;td>同時包含同步入口與非同步處理&lt;/td>
 &lt;td>queue lag、redelivery loop&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Login&lt;/td>
 &lt;td>影響所有後續功能&lt;/td>
 &lt;td>identity provider outage&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Document publish&lt;/td>
 &lt;td>涵蓋寫入、背景工作與通知&lt;/td>
 &lt;td>stale read、worker backlog&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Invoice&lt;/td>
 &lt;td>牽涉正確性與客戶信任&lt;/td>
 &lt;td>duplicate charge、missing file&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Checkout 適合第一輪，因為它同時暴露 latency、dependency failure、customer impact 與 rollback decision。若團隊沒有交易路徑，可以選 message delivery 或 login；判準是這條路徑一旦失效，on-call 需要在 15 分鐘內做出明確決策。&lt;/p>
&lt;p>Message delivery 適合用來驗證 async observability。它能暴露 request id、correlation id、queue lag、DLQ、retry policy 與 replay runbook 的交接品質。&lt;/p>
&lt;p>Login 適合用來驗證外部依賴事故。它能暴露 identity provider、fallback、status page、security split 與 customer communication 的邊界。&lt;/p>
&lt;h2 id="artifact-契約">Artifact 契約&lt;/h2>
&lt;p>Artifact 契約的責任是讓每個環節都有可交接輸出。這些 artifact 可以先用 Markdown、ticket 欄位或 incident template 表達，等流程跑通後再導入工具自動化。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Artifact&lt;/th>
 &lt;th>最小欄位&lt;/th>
 &lt;th>來源章節&lt;/th>
 &lt;th>下游使用&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Observability evidence package&lt;/td>
 &lt;td>source、time range、query link、owner、data quality、confidence、known gap&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/04-observability/observability-evidence-package/" data-link-title="4.20 Observability Evidence Package" data-link-desc="把 log、metric、trace、audit 與資料品質限制包成可交接證據">4.20&lt;/a>&lt;/td>
 &lt;td>triage、release gate、PIR&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Verification evidence handoff&lt;/td>
 &lt;td>hypothesis、scope、steady state、workload / fault、result、decision、owner&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23&lt;/a>&lt;/td>
 &lt;td>release gate、runbook、drill&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Incident decision log&lt;/td>
 &lt;td>timestamp、decision、context、evidence、owner、expected effect、rollback condition&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/incident-decision-log/" data-link-title="8.19 Incident Decision Log" data-link-desc="把事中假設、決策、證據、回退條件與責任人留下可復盤紀錄">8.19&lt;/a>&lt;/td>
 &lt;td>handoff、stakeholder update、PIR&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Incident evidence write-back&lt;/td>
 &lt;td>finding、evidence、target artifact、owner、closure signal、review date&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/incident-evidence-write-back/" data-link-title="8.22 Incident Evidence Write-back" data-link-desc="把事故證據、決策與復盤結論回寫到 observability、reliability 與 runbook">8.22&lt;/a>&lt;/td>
 &lt;td>dashboard、experiment、runbook&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Observability evidence package 是第一個 artifact。它保存查詢、時間窗、資料品質與 owner，讓後面的驗證與事故流程使用同一組事實。&lt;/p></description><content:encoded><![CDATA[<p>操作控制 vertical slice 的核心責任是把「看得見、驗得過、接得住、回寫得動」落到同一個服務流程。這一章把 <a href="/blog/backend/knowledge-cards/evidence-package/" data-link-title="Evidence Package" data-link-desc="說明觀測、驗證與事故流程如何把證據包成可交接、可回放的 artifact">evidence package</a>、<a href="/blog/backend/knowledge-cards/steady-state/" data-link-title="Steady State" data-link-desc="說明可靠性實驗與事故恢復如何定義系統應維持的可接受狀態">steady state</a>、<a href="/blog/backend/knowledge-cards/incident-decision-log/" data-link-title="Incident Decision Log" data-link-desc="說明事故期間如何保留決策、證據、owner 與回退條件">incident decision log</a> 與 action item closure 串成第一個可實作切片。</p>
<h2 id="大綱">大綱</h2>
<ul>
<li>實作目標：選一個核心 user journey，建立最小操作控制閉環</li>
<li>輸入：服務入口、核心依賴、SLO / SLI、告警、驗證場景、事故流程</li>
<li>產出：evidence package、verification evidence handoff、incident decision log、write-back item</li>
<li>邊界：先做 artifact 與路由，工具與語言實作留給 04 / 06 / 08 與語言教材</li>
<li>驗收：能從一次異常走完 triage、verification、decision、write-back</li>
</ul>
<h2 id="實作目標">實作目標</h2>
<p>Vertical slice 的目標是先做一條可回放的操作控制路徑。選一個核心 user journey，例如 checkout、message delivery、document publish、login 或 invoice generation，讓這條路徑同時具備觀測證據、驗證門檻、事故決策與回寫機制。</p>
<p>這一輪的交付是 artifact 與流程責任。工具可以是現有 log search、dashboard、ticket、runbook repository 與 chat；重點是資料欄位與流程責任先成立，後續才判斷是否需要 Prometheus、OpenTelemetry backend、PagerDuty、incident.io 或 chaos tooling。</p>
<h2 id="選擇服務切片">選擇服務切片</h2>
<p>服務切片的選擇責任是找到最能暴露 04 / 06 / 08 交接問題的路徑。第一條 slice 應該具備使用者影響、依賴邊界、可量測訊號與可驗證失敗模式。</p>
<table>
  <thead>
      <tr>
          <th>候選切片</th>
          <th>適合原因</th>
          <th>常見失敗模式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Checkout</td>
          <td>直接連到收入與客戶痛點</td>
          <td>payment timeout、inventory lag</td>
      </tr>
      <tr>
          <td>Message delivery</td>
          <td>同時包含同步入口與非同步處理</td>
          <td>queue lag、redelivery loop</td>
      </tr>
      <tr>
          <td>Login</td>
          <td>影響所有後續功能</td>
          <td>identity provider outage</td>
      </tr>
      <tr>
          <td>Document publish</td>
          <td>涵蓋寫入、背景工作與通知</td>
          <td>stale read、worker backlog</td>
      </tr>
      <tr>
          <td>Invoice</td>
          <td>牽涉正確性與客戶信任</td>
          <td>duplicate charge、missing file</td>
      </tr>
  </tbody>
</table>
<p>Checkout 適合第一輪，因為它同時暴露 latency、dependency failure、customer impact 與 rollback decision。若團隊沒有交易路徑，可以選 message delivery 或 login；判準是這條路徑一旦失效，on-call 需要在 15 分鐘內做出明確決策。</p>
<p>Message delivery 適合用來驗證 async observability。它能暴露 request id、correlation id、queue lag、DLQ、retry policy 與 replay runbook 的交接品質。</p>
<p>Login 適合用來驗證外部依賴事故。它能暴露 identity provider、fallback、status page、security split 與 customer communication 的邊界。</p>
<h2 id="artifact-契約">Artifact 契約</h2>
<p>Artifact 契約的責任是讓每個環節都有可交接輸出。這些 artifact 可以先用 Markdown、ticket 欄位或 incident template 表達，等流程跑通後再導入工具自動化。</p>
<table>
  <thead>
      <tr>
          <th>Artifact</th>
          <th>最小欄位</th>
          <th>來源章節</th>
          <th>下游使用</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Observability evidence package</td>
          <td>source、time range、query link、owner、data quality、confidence、known gap</td>
          <td><a href="/blog/backend/04-observability/observability-evidence-package/" data-link-title="4.20 Observability Evidence Package" data-link-desc="把 log、metric、trace、audit 與資料品質限制包成可交接證據">4.20</a></td>
          <td>triage、release gate、PIR</td>
      </tr>
      <tr>
          <td>Verification evidence handoff</td>
          <td>hypothesis、scope、steady state、workload / fault、result、decision、owner</td>
          <td><a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23</a></td>
          <td>release gate、runbook、drill</td>
      </tr>
      <tr>
          <td>Incident decision log</td>
          <td>timestamp、decision、context、evidence、owner、expected effect、rollback condition</td>
          <td><a href="/blog/backend/08-incident-response/incident-decision-log/" data-link-title="8.19 Incident Decision Log" data-link-desc="把事中假設、決策、證據、回退條件與責任人留下可復盤紀錄">8.19</a></td>
          <td>handoff、stakeholder update、PIR</td>
      </tr>
      <tr>
          <td>Incident evidence write-back</td>
          <td>finding、evidence、target artifact、owner、closure signal、review date</td>
          <td><a href="/blog/backend/08-incident-response/incident-evidence-write-back/" data-link-title="8.22 Incident Evidence Write-back" data-link-desc="把事故證據、決策與復盤結論回寫到 observability、reliability 與 runbook">8.22</a></td>
          <td>dashboard、experiment、runbook</td>
      </tr>
  </tbody>
</table>
<p>Observability evidence package 是第一個 artifact。它保存查詢、時間窗、資料品質與 owner，讓後面的驗證與事故流程使用同一組事實。</p>
<p>Verification evidence handoff 是第二個 artifact。它把一次 load test、chaos drill、DR rehearsal 或 readiness review 的結果轉成 release gate 與 incident drill 可用的證據。</p>
<p>Incident decision log 是第三個 artifact。它把事中決策、證據、預期效果與回退條件保存下來，讓交班與復盤可以直接引用。</p>
<p>Incident evidence write-back 是第四個 artifact。它把事故學習轉成 dashboard、alert、SLO、experiment、runbook 或 automation boundary 的修改項。</p>
<h2 id="實作步驟">實作步驟</h2>
<p>實作步驟的責任是讓 slice 能被單次演練走完。每一步都產生一個可檢查輸出，避免流程只停在口頭共識。</p>
<ol>
<li>選定服務切片與核心 user journey。</li>
<li>定義 steady state：success rate、latency、queue lag、data correctness、customer impact。</li>
<li>補 observability evidence package：dashboard、query、trace、log、audit、data quality。</li>
<li>補 verification evidence handoff：load、chaos、DR 或 rollback rehearsal 的 hypothesis 與 result。</li>
<li>建 incident intake template：source、confidence、impact scope、evidence link、severity candidate。</li>
<li>建 incident decision log template：decision、owner、expected effect、rollback condition。</li>
<li>建 write-back template：finding、target artifact、closure signal、review date。</li>
<li>跑一次 tabletop 或 game day，確認 artifact 能被實際填寫。</li>
<li>把缺口回寫到 04 readiness、06 experiment 或 08 runbook。</li>
</ol>
<p>第一步要避免選太大的系統。選「checkout」比選「整個支付平台」更好，因為 slice 需要在一輪演練中跑完。</p>
<p>第二步要先定義穩態。沒有 steady state，load test、chaos 與 incident recovery 都會缺少共同終點。</p>
<p>第三步要保留 data quality 限制。若 trace sampling、log drop 或 metric ingest delay 會影響判讀，限制要跟 evidence 一起交接。</p>
<p>第四步要把驗證結果變成下游可用語言。Pass、conditional、fail 都要附上 scope、hypothesis 與下一步路由。</p>
<p>第五到第七步要先用輕量 template。template 跑通後，再把欄位搬進 incident tool、ticket system 或 runbook platform。</p>
<p>第八步要實際演練。tabletop 可以先驗證欄位與角色，game day 再驗證工具與訊號。</p>
<h2 id="最小-template">最小 template</h2>
<p>最小 template 的責任是讓第一輪不用等待工具導入。以下欄位可以直接放進 Markdown、ticket、incident doc 或 runbook。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nt">service_slice</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">  </span><span class="nt">journey</span><span class="p">:</span><span class="w"> </span><span class="l">checkout</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">  </span><span class="nt">owner</span><span class="p">:</span><span class="w"> </span><span class="l">payments-team</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">  </span><span class="nt">steady_state</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="nt">success_rate</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;&gt;= 99.9% over 30m&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="nt">latency</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;p95 &lt;= 800ms&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="nt">queue_lag</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;&lt;= 5m&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="nt">customer_impact</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;failed checkout count &lt;= threshold&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w"></span><span class="nt">evidence_package</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">  </span><span class="nt">source</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;dashboard / log query / trace / audit&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">  </span><span class="nt">time_range</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;incident window plus baseline&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">  </span><span class="nt">query_link</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;stable query URL or saved query name&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">  </span><span class="nt">owner</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;service or platform owner&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">  </span><span class="nt">data_quality</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;sampling, freshness, missing fields&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">  </span><span class="nt">confidence</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;confirmed / suspected / weak&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">  </span><span class="nt">known_gap</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;missing signal or schema drift&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w"></span><span class="nt">verification_handoff</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">  </span><span class="nt">hypothesis</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;payment provider timeout triggers fallback within 2m&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">  </span><span class="nt">scope</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;staging or 10% production traffic&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">  </span><span class="nt">workload_or_fault</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;timeout injection against provider adapter&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">  </span><span class="nt">result</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;pass / conditional / fail&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">  </span><span class="nt">decision</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;release / block / follow-up / runbook update&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">  </span><span class="nt">owner</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;closure owner&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w"></span><span class="nt">incident_decision</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">  </span><span class="nt">timestamp</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;2026-05-07T10:15:00Z&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">  </span><span class="nt">decision</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;enable checkout fallback&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">  </span><span class="nt">context</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;provider timeout and rising failed checkout&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">  </span><span class="nt">evidence</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;evidence_package link&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">  </span><span class="nt">owner</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;incident commander or service owner&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">  </span><span class="nt">expected_effect</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;failed checkout drops within 10m&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="w">  </span><span class="nt">rollback_condition</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;fallback stale data exceeds threshold&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="w"></span><span class="nt">write_back</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="w">  </span><span class="nt">finding</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;provider timeout alert lacks tenant dimension&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="w">  </span><span class="nt">target_artifact</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;dashboard / alert / experiment / runbook&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">39</span><span class="cl"><span class="w">  </span><span class="nt">closure_signal</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;game day triggers tenant-scoped alert within 5m&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">40</span><span class="cl"><span class="w">  </span><span class="nt">review_date</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;next readiness review&#34;</span></span></span></code></pre></div><p>這份 template 的價值是把四個 artifact 放在同一份文件中。第一輪可以手動填寫，第二輪再拆到不同工具。</p>
<h2 id="驗收門檻">驗收門檻</h2>
<p>驗收門檻的責任是判斷 slice 是否已經能支援實際事故。完成狀態要由團隊能否沿著 artifact 做出同一組判斷來確認。</p>
<table>
  <thead>
      <tr>
          <th>驗收項目</th>
          <th>通過訊號</th>
          <th>回寫位置</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Triage</td>
          <td>on-call 能用 evidence 判斷是否啟動事故</td>
          <td>8.18 intake</td>
      </tr>
      <tr>
          <td>Verification</td>
          <td>release owner 能讀 handoff 做放行判斷</td>
          <td>6.8 release gate</td>
      </tr>
      <tr>
          <td>Decision</td>
          <td>IC 能用 decision log 交班與回退</td>
          <td>8.19 decision log</td>
      </tr>
      <tr>
          <td>Communication</td>
          <td>stakeholder update 能引用同一組 impact</td>
          <td>8.10 comms</td>
      </tr>
      <tr>
          <td>Write-back</td>
          <td>PIR action item 有 target 與 closure</td>
          <td>8.22 write-back</td>
      </tr>
  </tbody>
</table>
<p>Triage 通過代表 evidence 能支援事故啟動。若 on-call 還需要臨場重新找資料，回到 4.16 readiness 與 4.20 evidence package。</p>
<p>Verification 通過代表驗證結果能支援 release 決策。若 release owner 只看到 pass / fail，回到 6.23 handoff 補 hypothesis、scope 與 data quality。</p>
<p>Decision 通過代表事故現場有共同記憶。若交班後需要重問背景，回到 8.19 decision log 補 context、evidence 與 rollback condition。</p>
<p>Write-back 通過代表事故學習有落點。若 action item 只有「補監控」或「更新文件」，回到 8.22 write-back 補 target artifact 與 closure signal。</p>
<h2 id="tripwire">Tripwire</h2>
<p>Tripwire 的責任是提醒團隊何時回到概念層補缺口。Vertical slice 的目的在於快速暴露 routing chain 哪裡斷掉，再用最小修正補上 artifact 與 owner。</p>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀</th>
          <th>下一步</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>evidence 找不到 owner</td>
          <td>觀測 operating model 缺口</td>
          <td>回到 4.18 owner 與 review cadence</td>
      </tr>
      <tr>
          <td>pass / fail 缺少決策力</td>
          <td>verification handoff 缺口</td>
          <td>回到 6.23 補 scope、hypothesis、decision</td>
      </tr>
      <tr>
          <td>IC 交班缺少共同記憶</td>
          <td>decision log 缺口</td>
          <td>回到 8.19 補最近決策、未完成動作與 rollback 條件</td>
      </tr>
      <tr>
          <td>PIR action 缺少關閉力</td>
          <td>write-back 缺口</td>
          <td>回到 8.22 補 closure signal 與 review date</td>
      </tr>
      <tr>
          <td>template 填寫成本過高</td>
          <td>欄位過多或工具摩擦</td>
          <td>刪到最小欄位，再跑一次 tabletop</td>
      </tr>
  </tbody>
</table>
<p>這些 tripwire 出現時，先修 artifact 與流程，再考慮導入新工具。工具能降低填寫成本，但欄位責任與 owner 需要先清楚。</p>
<h2 id="交接路由">交接路由</h2>
<ul>
<li><a href="/blog/backend/00-service-selection/operations-control-service-selection/" data-link-title="0.12 觀測、可靠性與事故服務選型" data-link-desc="從訊號、驗證與響應三層能力判斷操作控制服務的選型順序">0.12 operations control service selection</a>：判斷目前缺的是訊號、驗證、響應還是閉環。</li>
<li><a href="/blog/backend/04-observability/observability-evidence-package/" data-link-title="4.20 Observability Evidence Package" data-link-desc="把 log、metric、trace、audit 與資料品質限制包成可交接證據">4.20 observability evidence package</a>：建立可交接觀測證據。</li>
<li><a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22 steady state definition</a>：定義實驗與事故共用成功條件。</li>
<li><a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23 verification evidence handoff</a>：把驗證結果交給 release 與 incident。</li>
<li><a href="/blog/backend/08-incident-response/incident-decision-log/" data-link-title="8.19 Incident Decision Log" data-link-desc="把事中假設、決策、證據、回退條件與責任人留下可復盤紀錄">8.19 incident decision log</a>：保存事中決策與回退條件。</li>
<li><a href="/blog/backend/08-incident-response/incident-evidence-write-back/" data-link-title="8.22 Incident Evidence Write-back" data-link-desc="把事故證據、決策與復盤結論回寫到 observability、reliability 與 runbook">8.22 incident evidence write-back</a>：把事故學習回寫成可關閉改善。</li>
</ul>
]]></content:encoded></item><item><title>Google：Toil Budget 與 Automation 投資政策</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/google/toil-budget-and-automation-investment-policy/</link><pubDate>Thu, 07 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/google/toil-budget-and-automation-investment-policy/</guid><description>&lt;p>Toil budget 的核心責任是把重複手動工作變成可治理成本。Google SRE 的關鍵做法是先量化 toil，再把超額部分強制導向自動化投資，而不是持續靠人力吸收。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>許多團隊的可靠性工作會被 incident handling 與手動修復吃掉。短期看似把事情解決，長期會造成兩個後果：一是 on-call 壓力升高，二是系統問題持續累積。沒有 toil budget 時，團隊很難判斷何時該停止加功能、先補工程基礎。&lt;/p>
&lt;h2 id="決策機制">決策機制&lt;/h2>
&lt;p>Toil budget 是把工時結果接到 release 與 backlog 決策的機制，單純統計工時只完成一半。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>機制&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>實際輸出&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Toil 分類&lt;/td>
 &lt;td>哪些工作屬於可自動化 toil&lt;/td>
 &lt;td>toil taxonomy&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>時間配比&lt;/td>
 &lt;td>toil 比例是否超過可承受區&lt;/td>
 &lt;td>budget 門檻（例如 50%）&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>超標處理&lt;/td>
 &lt;td>超標後怎麼調整優先序&lt;/td>
 &lt;td>凍結部分 feature、轉投自動化&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>改善驗證&lt;/td>
 &lt;td>自動化是否真的回收工時&lt;/td>
 &lt;td>closure 指標與 evidence&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>toil ratio&lt;/td>
 &lt;td>是否長期超出預算&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>incident manual-step count&lt;/td>
 &lt;td>事故處理是否過度依賴人工&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/runbook-lifecycle/" data-link-title="8.16 Runbook Lifecycle 管理" data-link-desc="把 runbook 從一次性文件變成有版本、有演練、會過期的 artifact">8.16&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>automation closure rate&lt;/td>
 &lt;td>改善項是否真的落地&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/incident-evidence-write-back/" data-link-title="8.22 Incident Evidence Write-back" data-link-desc="把事故證據、決策與復盤結論回寫到 observability、reliability 與 runbook">8.22&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>on-call overload signal&lt;/td>
 &lt;td>值班負荷是否持續上升&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/drills-and-oncall-readiness/" data-link-title="8.6 演練與值班能力建設" data-link-desc="用演練與值班訓練提升事故反應品質">8.6&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="常見陷阱">常見陷阱&lt;/h2>
&lt;p>最常見錯誤是把 toil 視為「正常運維工作」，結果讓超標狀態常態化。另一個錯誤是只記錄工時，不把結果接到 release gate 與優先序調整。這兩種做法都會讓可靠性債繼續滾大。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>把 toil budget 落地時，先在 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 Reliability Debt Backlog&lt;/a> 建立分類與排序，再把超標條件接到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate&lt;/a>。事後改善要回寫 &lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/incident-evidence-write-back/" data-link-title="8.22 Incident Evidence Write-back" data-link-desc="把事故證據、決策與復盤結論回寫到 observability、reliability 與 runbook">8.22 Incident Evidence Write-back&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://sre.google/sre-book/table-of-contents/">Google SRE Book&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://sre.google/workbook/table-of-contents/">Google SRE Workbook&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Toil budget 的核心責任是把重複手動工作變成可治理成本。Google SRE 的關鍵做法是先量化 toil，再把超額部分強制導向自動化投資，而不是持續靠人力吸收。</p>
<h2 id="問題場景">問題場景</h2>
<p>許多團隊的可靠性工作會被 incident handling 與手動修復吃掉。短期看似把事情解決，長期會造成兩個後果：一是 on-call 壓力升高，二是系統問題持續累積。沒有 toil budget 時，團隊很難判斷何時該停止加功能、先補工程基礎。</p>
<h2 id="決策機制">決策機制</h2>
<p>Toil budget 是把工時結果接到 release 與 backlog 決策的機制，單純統計工時只完成一半。</p>
<table>
  <thead>
      <tr>
          <th>機制</th>
          <th>核心問題</th>
          <th>實際輸出</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Toil 分類</td>
          <td>哪些工作屬於可自動化 toil</td>
          <td>toil taxonomy</td>
      </tr>
      <tr>
          <td>時間配比</td>
          <td>toil 比例是否超過可承受區</td>
          <td>budget 門檻（例如 50%）</td>
      </tr>
      <tr>
          <td>超標處理</td>
          <td>超標後怎麼調整優先序</td>
          <td>凍結部分 feature、轉投自動化</td>
      </tr>
      <tr>
          <td>改善驗證</td>
          <td>自動化是否真的回收工時</td>
          <td>closure 指標與 evidence</td>
      </tr>
  </tbody>
</table>
<h2 id="可觀測訊號">可觀測訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>toil ratio</td>
          <td>是否長期超出預算</td>
          <td><a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21</a></td>
      </tr>
      <tr>
          <td>incident manual-step count</td>
          <td>事故處理是否過度依賴人工</td>
          <td><a href="/blog/backend/08-incident-response/runbook-lifecycle/" data-link-title="8.16 Runbook Lifecycle 管理" data-link-desc="把 runbook 從一次性文件變成有版本、有演練、會過期的 artifact">8.16</a></td>
      </tr>
      <tr>
          <td>automation closure rate</td>
          <td>改善項是否真的落地</td>
          <td><a href="/blog/backend/08-incident-response/incident-evidence-write-back/" data-link-title="8.22 Incident Evidence Write-back" data-link-desc="把事故證據、決策與復盤結論回寫到 observability、reliability 與 runbook">8.22</a></td>
      </tr>
      <tr>
          <td>on-call overload signal</td>
          <td>值班負荷是否持續上升</td>
          <td><a href="/blog/backend/08-incident-response/drills-and-oncall-readiness/" data-link-title="8.6 演練與值班能力建設" data-link-desc="用演練與值班訓練提升事故反應品質">8.6</a></td>
      </tr>
  </tbody>
</table>
<h2 id="常見陷阱">常見陷阱</h2>
<p>最常見錯誤是把 toil 視為「正常運維工作」，結果讓超標狀態常態化。另一個錯誤是只記錄工時，不把結果接到 release gate 與優先序調整。這兩種做法都會讓可靠性債繼續滾大。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>把 toil budget 落地時，先在 <a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 Reliability Debt Backlog</a> 建立分類與排序，再把超標條件接到 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate</a>。事後改善要回寫 <a href="/blog/backend/08-incident-response/incident-evidence-write-back/" data-link-title="8.22 Incident Evidence Write-back" data-link-desc="把事故證據、決策與復盤結論回寫到 observability、reliability 與 runbook">8.22 Incident Evidence Write-back</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://sre.google/sre-book/table-of-contents/">Google SRE Book</a></li>
<li><a href="https://sre.google/workbook/table-of-contents/">Google SRE Workbook</a></li>
</ul>
]]></content:encoded></item><item><title>6.13 Performance Regression Gate</title><link>https://tarrragon.github.io/blog/backend/06-reliability/performance-regression-gate/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/performance-regression-gate/</guid><description>&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>Performance regression gate 守住系統的效能餘裕 — 避免看似功能正確的變更悄悄拖垮延遲、吞吐或成本。&lt;/p>
&lt;p>這一頁關心的是變更有沒有偷走系統的效能餘裕。沒有 gate，效能退化常常要等使用者感受到才會被看見。跟 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2 load test&lt;/a> 的分工是：6.2 訂定 baseline 與 saturation point，6.13 確保每次變更不會讓 baseline 被偷走。&lt;/p>
&lt;h2 id="核心判讀">核心判讀&lt;/h2>
&lt;p>效能 gate 的健康度取決於 baseline 是否穩定、regression 偵測是否足夠敏感。&lt;/p>
&lt;p>重點訊號包括：&lt;/p>
&lt;ul>
&lt;li>baseline 是否來自 production-like workload&lt;/li>
&lt;li>regression 是否能分辨 noise 與真實退化&lt;/li>
&lt;li>perf budget 是否跟 release gate 綁定&lt;/li>
&lt;li>當退化出現時，是否能快速定位到 code path 或依賴&lt;/li>
&lt;/ul>
&lt;h2 id="baseline-設定">Baseline 設定&lt;/h2>
&lt;p>Baseline 的責任是提供可比較的效能基準。沒有穩定 baseline，gate 判讀就無法區分「系統真的變慢了」跟「環境噪音」。&lt;/p>
&lt;p>Baseline 有三種來源，各自的可信度與維護成本不同。&lt;/p>
&lt;p>&lt;strong>Production percentile&lt;/strong>：從 production 的 latency / &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/throughput/" data-link-title="Throughput" data-link-desc="整理系統單位時間內可處理的工作量">throughput&lt;/a> 分佈取 p50 / p95 / p99 作為基準。優點是最接近真實使用者體驗；限制是 production 流量本身有時段波動，需要選定穩定時段的統計窗口。適合作為最終判準，但不適合作為 CI 內的即時 gate（CI 環境跟 production 差異太大）。&lt;/p>
&lt;p>&lt;strong>CI benchmark history&lt;/strong>：在同一 CI 環境、同一 workload 下累積歷史趨勢。優點是環境一致，regression 可歸因到 code 變更；限制是 CI 環境本身可能有波動（runner 硬體、鄰居效應），需要 variance 控制。適合作為每次 merge 的即時 gate。&lt;/p>
&lt;p>&lt;strong>Load test 結果&lt;/strong>：&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2 load test&lt;/a> 產出的 saturation point 與 latency inflection。優點是覆蓋高負載場景；限制是執行成本高、不適合每次 push 跑。適合作為 scheduled path 的 baseline 校準來源。&lt;/p>
&lt;p>Baseline 更新頻率跟系統變更頻率對齊。高頻變更服務（每日多次 deploy）需要 rolling baseline（取最近 N 次 CI 結果的中位數）；低頻變更服務可以用固定 baseline 搭配季度校準。&lt;/p>
&lt;p>Baseline 品質的判準是自身 variance。若 baseline 的 p99 波動超過 5-10%，任何小於這個幅度的 regression 都落在噪音區間內，gate 無法可靠判讀。此時應先控制 variance（見下段），再設定 regression 門檻。&lt;/p>
&lt;h2 id="regression-判讀方法">Regression 判讀方法&lt;/h2>
&lt;p>Regression 判讀有三種方法，選擇取決於 CI 環境的穩定性與測試時間預算。&lt;/p>
&lt;h3 id="絕對門檻">絕對門檻&lt;/h3>
&lt;p>設定 p99 latency 上限（例如 200ms）或 throughput 下限（例如 1000 RPS），超過就 fail。&lt;/p>
&lt;p>這種方法實作最簡單，適合有明確 SLA 的服務。限制是容易誤報（環境噪音造成的瞬間飆高）或漏報（慢速退化每次只惡化 2-3ms，始終低於門檻，累積半年後才被注意到）。適合作為安全網而非主要判讀手段。&lt;/p>
&lt;h3 id="相對退化">相對退化&lt;/h3>
&lt;p>跟前一版 baseline 比較，退化超過 Y%（例如 latency 增加 &amp;gt; 10%）就 fail。&lt;/p>
&lt;p>這種方法能抓到漸進退化，因為每一次小幅惡化都會觸發。前提是 baseline 穩定 — 若 baseline 自身波動 8%，設定 10% 門檻幾乎沒有判讀空間。適合 variance 已被控制到 3-5% 以內的 CI 環境。&lt;/p></description><content:encoded><![CDATA[<h2 id="概念定位">概念定位</h2>
<p>Performance regression gate 守住系統的效能餘裕 — 避免看似功能正確的變更悄悄拖垮延遲、吞吐或成本。</p>
<p>這一頁關心的是變更有沒有偷走系統的效能餘裕。沒有 gate，效能退化常常要等使用者感受到才會被看見。跟 <a href="/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2 load test</a> 的分工是：6.2 訂定 baseline 與 saturation point，6.13 確保每次變更不會讓 baseline 被偷走。</p>
<h2 id="核心判讀">核心判讀</h2>
<p>效能 gate 的健康度取決於 baseline 是否穩定、regression 偵測是否足夠敏感。</p>
<p>重點訊號包括：</p>
<ul>
<li>baseline 是否來自 production-like workload</li>
<li>regression 是否能分辨 noise 與真實退化</li>
<li>perf budget 是否跟 release gate 綁定</li>
<li>當退化出現時，是否能快速定位到 code path 或依賴</li>
</ul>
<h2 id="baseline-設定">Baseline 設定</h2>
<p>Baseline 的責任是提供可比較的效能基準。沒有穩定 baseline，gate 判讀就無法區分「系統真的變慢了」跟「環境噪音」。</p>
<p>Baseline 有三種來源，各自的可信度與維護成本不同。</p>
<p><strong>Production percentile</strong>：從 production 的 latency / <a href="/blog/backend/knowledge-cards/throughput/" data-link-title="Throughput" data-link-desc="整理系統單位時間內可處理的工作量">throughput</a> 分佈取 p50 / p95 / p99 作為基準。優點是最接近真實使用者體驗；限制是 production 流量本身有時段波動，需要選定穩定時段的統計窗口。適合作為最終判準，但不適合作為 CI 內的即時 gate（CI 環境跟 production 差異太大）。</p>
<p><strong>CI benchmark history</strong>：在同一 CI 環境、同一 workload 下累積歷史趨勢。優點是環境一致，regression 可歸因到 code 變更；限制是 CI 環境本身可能有波動（runner 硬體、鄰居效應），需要 variance 控制。適合作為每次 merge 的即時 gate。</p>
<p><strong>Load test 結果</strong>：<a href="/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2 load test</a> 產出的 saturation point 與 latency inflection。優點是覆蓋高負載場景；限制是執行成本高、不適合每次 push 跑。適合作為 scheduled path 的 baseline 校準來源。</p>
<p>Baseline 更新頻率跟系統變更頻率對齊。高頻變更服務（每日多次 deploy）需要 rolling baseline（取最近 N 次 CI 結果的中位數）；低頻變更服務可以用固定 baseline 搭配季度校準。</p>
<p>Baseline 品質的判準是自身 variance。若 baseline 的 p99 波動超過 5-10%，任何小於這個幅度的 regression 都落在噪音區間內，gate 無法可靠判讀。此時應先控制 variance（見下段），再設定 regression 門檻。</p>
<h2 id="regression-判讀方法">Regression 判讀方法</h2>
<p>Regression 判讀有三種方法，選擇取決於 CI 環境的穩定性與測試時間預算。</p>
<h3 id="絕對門檻">絕對門檻</h3>
<p>設定 p99 latency 上限（例如 200ms）或 throughput 下限（例如 1000 RPS），超過就 fail。</p>
<p>這種方法實作最簡單，適合有明確 SLA 的服務。限制是容易誤報（環境噪音造成的瞬間飆高）或漏報（慢速退化每次只惡化 2-3ms，始終低於門檻，累積半年後才被注意到）。適合作為安全網而非主要判讀手段。</p>
<h3 id="相對退化">相對退化</h3>
<p>跟前一版 baseline 比較，退化超過 Y%（例如 latency 增加 &gt; 10%）就 fail。</p>
<p>這種方法能抓到漸進退化，因為每一次小幅惡化都會觸發。前提是 baseline 穩定 — 若 baseline 自身波動 8%，設定 10% 門檻幾乎沒有判讀空間。適合 variance 已被控制到 3-5% 以內的 CI 環境。</p>
<h3 id="統計顯著性">統計顯著性</h3>
<p>用統計檢定（t-test、Mann-Whitney U）判斷兩組測量的分佈是否有顯著差異。</p>
<p>這種方法最準確，能在高 variance 環境中篩掉噪音。限制是需要足夠樣本量 — CI 短時間測試可能只跑 10-20 次 iteration，樣本不足時統計功效低，真實退化也可能被判為不顯著。適合測試時間預算充裕的 scheduled path。</p>
<p>三種方法可以組合：fast path 用絕對門檻做安全網，slow path 用相對退化做主要判讀，scheduled path 用統計檢定做精確校準。</p>
<h2 id="variance-控制">Variance 控制</h2>
<p>CI 環境的噪音是 perf gate 最大的干擾源。噪音讓真實退化被遮蓋，也讓正常變更被誤報，兩者都會侵蝕團隊對 gate 的信任。</p>
<p>主要噪音來源與對應控制方式：</p>
<table>
  <thead>
      <tr>
          <th>噪音來源</th>
          <th>機制</th>
          <th>控制方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Shared runner 鄰居效應</td>
          <td>其他 job 搶 CPU / memory / I/O</td>
          <td>Dedicated runner 或 ephemeral instance</td>
      </tr>
      <tr>
          <td>Cold start</td>
          <td>JIT warmup、cache miss、connection 建立</td>
          <td>Warmup iteration（丟棄前 N 次結果）</td>
      </tr>
      <tr>
          <td>GC pause</td>
          <td>記憶體壓力觸發 stop-the-world GC</td>
          <td>固定 heap size、GC log 同步收集</td>
      </tr>
      <tr>
          <td>Network jitter</td>
          <td>跨服務通訊的延遲波動</td>
          <td>Local dependency（mock / sidecar）</td>
      </tr>
      <tr>
          <td>Hardware 差異</td>
          <td>不同世代 runner 的 CPU 效能不同</td>
          <td>Pinned hardware config / instance type</td>
      </tr>
  </tbody>
</table>
<p>Variance 控制的投資報酬是讓 regression 門檻可以設得更敏感。當 variance 從 15% 降到 3%，gate 就能攔住 5% 的退化；否則只能設 20% 門檻，等於放過大量漸進退化。</p>
<p>連到 <a href="/blog/backend/06-reliability/ci-pipeline/" data-link-title="6.1 CI pipeline" data-link-desc="CI pipeline 的分層策略、artifact 管理、flaky 治理與 release gate 輸入">6.1 CI pipeline</a> 的 environment 隔離段 — perf gate 需要的 runner 隔離等級通常高於一般功能測試。</p>
<h2 id="micro-benchmark-vs-end-to-end-perf-test">Micro benchmark vs End-to-end perf test</h2>
<p>兩種測試粒度服務不同的判讀需求，分工而非替代。</p>
<p><strong>Micro benchmark</strong> 對準單一函式、code path 或演算法。variance 小（不涉及 I/O、network、GC 壓力低）、回饋快（秒級）、定位精準（退化直接指向特定函式）。限制是覆蓋不到跨服務退化、serialization 成本或 middleware 堆疊的效能影響。適合跑在 CI fast path（每次 push）。</p>
<p><strong>End-to-end perf test</strong> 覆蓋真實請求路徑，從 API gateway 到 database 到 response。能抓到跨層退化（middleware 累積、serialization 成本、connection pool 競爭），但 variance 大、定位困難（退化可能來自任何一層）。適合跑在 CI slow path（merge gate）或 scheduled path。</p>
<p>分工原則：micro benchmark 負責守住 code-level baseline，end-to-end perf test 負責守住 service-level baseline。兩者都 fail 時，micro benchmark 的結果通常能直接定位 regression 來源；只有 end-to-end fail 時，需要搭配 profiling diff 做進一步歸因。</p>
<h2 id="退化定位與行動">退化定位與行動</h2>
<p>Gate 攔住 regression 後，下一步是定位來源並決定行動。</p>
<p><strong>Profiling diff</strong>：比較兩版的 flame graph 或 CPU profile，找出新增的 hot path。連到 <a href="/blog/backend/04-observability/continuous-profiling/" data-link-title="4.9 Continuous Profiling" data-link-desc="把 CPU / memory / lock profile 從一次性除錯升級為持續訊號">4.9 continuous profiling</a> — 若 production 已有 continuous profiling，可以直接比較 canary 與 stable 版本的 profile 差異，定位精度高於 CI 環境的 benchmark。</p>
<p><strong>Commit bisect</strong>：在 CI benchmark history 中二分搜尋 regression 引入點。當多個 commit 合併後才觸發 gate fail，bisect 能縮小到具體 commit。前提是 CI benchmark 有逐 commit 的歷史紀錄。</p>
<p>定位後的行動有三種：</p>
<ul>
<li><strong>修復</strong>：regression 來源明確、修復成本可接受。這是預設行動。</li>
<li><strong>接受</strong>：regression 是預期的 trade-off（例如安全性改善帶來的加密成本）。此時更新 baseline，並在 <a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23 evidence handoff</a> 記錄接受理由。</li>
<li><strong>延後</strong>：regression 來源複雜、修復需要大幅重構。記錄到 <a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 reliability debt backlog</a> 並設定修復期限。延後的風險是多次延後累積成使用者可感知的退化。</li>
</ul>
<h2 id="產業情境串流與媒體服務">產業情境：串流與媒體服務</h2>
<p>串流服務的效能 regression 量測維度跟一般 web service 不同。API latency 只是其中一層，媒體交付品質才是使用者直接感受的指標。</p>
<p>串流特有的 regression 指標包含 video start time（TTFB to first frame）、rebuffering rate（播放中斷頻率）、bitrate switches per session（畫質跳動次數）與 ABR algorithm response time（adaptive bitrate 的反應速度）。這些指標需要專門的量測管線，CI 環境的 mock player 很難完全模擬真實觀看行為，canary 階段的 real user monitoring 是更可靠的 regression 偵測來源。</p>
<p>Transcoding pipeline 的 regression 需要三維判讀。新 codec 或 encoder 版本可能改善壓縮率但增加 encoding latency，CI gate 需要同時量化 encoding speed、output quality 與 cost — 只看其中一個維度會漏掉 trade-off。例如 AV1 encoder 比 H.264 壓縮率更好，但 encoding 時間可能增加數倍，若 gate 只看 latency 就會擋住合理的品質升級。</p>
<p>CDN cache hit rate 是隱性的 regression 指標。code 變更如果改變了 cache key 策略或 content fingerprint，CDN cache hit rate 會下降，回源流量上升，間接造成 origin latency 惡化與成本跳升。這類 regression 在 staging 壓測中看不到（staging 沒有 CDN 快取層），需要 canary 階段的 CDN 層監控才能偵測。</p>
<h2 id="案例對照">案例對照</h2>
<ul>
<li><a href="/blog/backend/06-reliability/cases/google/error-budget-policy-and-release-gating/" data-link-title="Google：Error Budget 政策如何決定發布節奏" data-link-desc="把 SLO 消耗量轉成 release gate，讓可靠性與交付速度共用同一套決策語言。">Google G1</a>：效能退化會加速 error budget 消耗。當 latency regression 導致 SLO breach 頻率上升，perf gate 的門檻應與 error budget 政策連動 — budget 健康時接受較寬鬆的門檻，budget 緊繃時收緊。</li>
<li><a href="/blog/backend/06-reliability/cases/linkedin/capacity-headroom-and-oncall-tiering/" data-link-title="LinkedIn：Capacity Headroom 與 On-call 分層" data-link-desc="把容量預測與值班分層綁在一起，降低高峰時段的升級混亂與恢復延遲。">LinkedIn L1</a>：效能退化直接壓縮 capacity headroom。當 p99 latency 上升 20%，等效 headroom 下降，可能觸發 on-call 層級升級。perf gate 的門檻應考慮 headroom ratio 的安全邊界。</li>
<li><a href="/blog/backend/06-reliability/cases/shopify/bfcm-capacity-and-game-day/" data-link-title="Shopify：BFCM 容量治理與 Game Day 驗證節奏" data-link-desc="把季節性流量峰值轉成年度可靠性流程，透過容量模型、演練與隔離策略提前吸收風險。">Shopify H1</a>：高峰前的效能退化風險比平時更高。BFCM 前收緊 perf gate 門檻，避免峰值期間 latency regression 與流量尖峰疊加。</li>
<li><a href="/blog/backend/06-reliability/cases/linkedin/automated-load-testing-and-capacity-forecasting/" data-link-title="LinkedIn：Automated Load Testing 與 Capacity Forecasting" data-link-desc="持續壓測驅動容量預測：用自動化回饋取代一次性壓測的容量規劃。">LinkedIn L2</a>：持續壓測作為 regression 偵測的輸入來源 — 自動化壓測的 saturation point 趨勢可以補充 CI benchmark 看不到的系統級退化。</li>
</ul>
<h2 id="判讀訊號">判讀訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀條件</th>
          <th>行動建議</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>連續多版微小退化、累積後才被發現</td>
          <td>相對退化門檻未設或太寬鬆，改用 rolling baseline + 相對退化判讀</td>
          <td>設 rolling baseline + 5-10% 相對退化 threshold</td>
      </tr>
      <tr>
          <td>大版本升級 latency 漲、定位困難</td>
          <td>缺少逐 commit benchmark history，補 commit bisect 機制</td>
          <td>每個 commit 跑 micro benchmark、保留歷史</td>
      </tr>
      <tr>
          <td>Benchmark variance &gt; 退化幅度</td>
          <td>CI 環境噪音未控制，先降 variance 再設門檻</td>
          <td>改用 dedicated runner + warmup iteration</td>
      </tr>
      <tr>
          <td>Canary 只看 error rate、不看 latency</td>
          <td>perf gate 與 canary 判讀脫鉤，把 latency percentile 加入 canary</td>
          <td>補 p95/p99 latency 到 canary 判讀指標</td>
      </tr>
      <tr>
          <td>第三方依賴效能變化未納入 baseline</td>
          <td>baseline 只看本服務、漏掉依賴，補 end-to-end perf test 覆蓋</td>
          <td>加 end-to-end perf test 到 slow path</td>
      </tr>
      <tr>
          <td>Gate 頻繁誤報、團隊開始忽略</td>
          <td>門檻未對齊 variance，或測試環境不穩定，先修 variance 再調門檻</td>
          <td>先量測 variance、再設 threshold = baseline + 2σ</td>
      </tr>
  </tbody>
</table>
<h2 id="交接路由">交接路由</h2>
<ul>
<li><a href="/blog/backend/04-observability/continuous-profiling/" data-link-title="4.9 Continuous Profiling" data-link-desc="把 CPU / memory / lock profile 從一次性除錯升級為持續訊號">4.9 continuous profiling</a>：退化定位到 callstack</li>
<li><a href="/blog/backend/05-deployment-platform/" data-link-title="模組五：部署平台與網路入口" data-link-desc="整理 Kubernetes、systemd、load balancer、container 與服務生命週期合約">5 部署平台</a>：canary 階段的 perf gate</li>
<li><a href="/blog/backend/06-reliability/ci-pipeline/" data-link-title="6.1 CI pipeline" data-link-desc="CI pipeline 的分層策略、artifact 管理、flaky 治理與 release gate 輸入">6.1 CI pipeline</a>：perf test 在 CI 分層中的位置與 runner 隔離</li>
<li><a href="/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2 load test</a>：baseline 來源與 saturation point</li>
<li><a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate</a>：退化觸發 freeze</li>
<li><a href="/blog/backend/06-reliability/feature-flag-governance/" data-link-title="6.17 Feature Flag Governance" data-link-desc="把 feature flag 從上線開關升級為有角色分類、lifecycle 管理與 debt 治理的 runtime artifact">6.17 feature flag</a>：flag 切換後的效能驗證</li>
<li><a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 reliability debt</a>：延後修復的 regression 進入 debt backlog</li>
<li><a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23 evidence handoff</a>：接受 regression 時的理由留存</li>
</ul>
]]></content:encoded></item><item><title>Microsoft / Azure SRE</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/microsoft/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/microsoft/</guid><description>&lt;p>Microsoft Azure 的 SRE 文章與 Resilience patterns 文件是大型雲端供應商的可靠性工程公開素材。教學重點在「企業導向 cloud 的可靠性 patterns 與 governance」。&lt;/p>
&lt;h2 id="規劃重點">規劃重點&lt;/h2>
&lt;ul>
&lt;li>Azure Well-Architected Framework：Reliability pillar 的設計指導&lt;/li>
&lt;li>Resilience patterns：retry、circuit breaker、bulkhead 的官方範例&lt;/li>
&lt;li>Site Reliability Engineering at Microsoft：內部 SRE 組織與實踐&lt;/li>
&lt;li>Compliance-driven reliability：企業客戶要求下的可靠性 SLA&lt;/li>
&lt;/ul>
&lt;h2 id="預計收錄實踐">預計收錄實踐&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>議題&lt;/th>
 &lt;th>教學重點&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Well-Architected Framework&lt;/td>
 &lt;td>Reliability pillar 結構與審查流程&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Resilience Design Patterns&lt;/td>
 &lt;td>retry / breaker / bulkhead 等實作範例&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Azure SRE Engineering&lt;/td>
 &lt;td>Microsoft 內部 SRE 演化&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Chaos Studio&lt;/td>
 &lt;td>Azure 平台原生 chaos 工具&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="案例定位">案例定位&lt;/h2>
&lt;p>Microsoft 這個案例在講的是企業雲端如何把可靠性寫進架構規範與設計模式。讀者先抓 reliability pillar、self-healing 與 design patterns 的分工，再把它們視為治理語言，而不是單純的文件清單。&lt;/p>
&lt;h2 id="判讀重點">判讀重點&lt;/h2>
&lt;p>當服務要面對企業客戶的 SLA 要求時，先看設計模式能否對應 failure mode，再看治理流程是否能把 pattern 真的落到架構審查。當團隊需要做 retry 或 bulkhead 時，重點是能不能選到正確的位置與層級。&lt;/p>
&lt;h2 id="可操作判準">可操作判準&lt;/h2>
&lt;ul>
&lt;li>能否從 failure mode 反推適合的 reliability pattern&lt;/li>
&lt;li>能否把 self-healing 寫成可驗證的設計要求&lt;/li>
&lt;li>能否把架構審查和 SLA 約束對齊&lt;/li>
&lt;li>能否把 Azure SRE 實踐轉成團隊可用的治理語言&lt;/li>
&lt;/ul>
&lt;h2 id="與其他案例的關係">與其他案例的關係&lt;/h2>
&lt;p>Microsoft 這頁和 Stripe、Google 的差異在於它更偏治理與設計審查，而不是單一事故。讀者若先懂這頁，再看 Azure AD 和 M365，就能把 identity 失效與企業雲端的 reliability pattern 串成同一條理解路徑。&lt;/p>
&lt;h2 id="代表樣本">代表樣本&lt;/h2>
&lt;ul>
&lt;li>self-healing 把故障轉成可恢復的設計要求，而不是單靠人工補救。&lt;/li>
&lt;li>reliability pillar 讓團隊在架構審查時就對齊失效模式與補救方式。&lt;/li>
&lt;li>retry / circuit breaker / bulkhead 提供可重複使用的設計模式。&lt;/li>
&lt;li>compliance-driven reliability 把 SLA 約束寫進雲端治理。&lt;/li>
&lt;li>chaos studio 讓雲端平台本身提供測試失效的工具。&lt;/li>
&lt;li>Well-Architected Framework 讓可靠性審查變成標準流程。&lt;/li>
&lt;li>health check / retry policy 讓應用層能和平台層恢復節奏對齊。&lt;/li>
&lt;li>governance 語言把企業 SLA 與技術決策連起來。&lt;/li>
&lt;/ul>
&lt;h2 id="章節列表">章節列表&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>章節&lt;/th>
 &lt;th>主題&lt;/th>
 &lt;th>核心責任&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/microsoft/change-management-and-reliability-governance/" data-link-title="Microsoft：變更治理與可靠性門檻" data-link-desc="透過分層變更管理與發布閘門，降低大型 SaaS 平台的系統性回歸風險。">MS1&lt;/a>&lt;/td>
 &lt;td>變更治理與可靠性門檻&lt;/td>
 &lt;td>以風險分層與 release gate 降低系統性回歸&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/microsoft/safe-deployment-practices-and-resilience-patterns/" data-link-title="Microsoft：Safe Deployment Practices 與 Resilience Patterns" data-link-desc="大型 SaaS 用 ring-based deployment 控制變更擴散，用標準化 resilience patterns 讓依賴失效時的降級行為可預測。">MS2&lt;/a>&lt;/td>
 &lt;td>Safe Deployment Practices 與 Resilience Patterns&lt;/td>
 &lt;td>ring-based deployment 與標準化韌性設計模式的制度化&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://learn.microsoft.com/en-us/azure/architecture/">Azure Architecture Center&lt;/a>：Azure 架構中心總入口。&lt;/li>
&lt;li>&lt;a href="https://learn.microsoft.com/en-us/azure/well-architected/resiliency/overview">Reliability quick links&lt;/a>：Azure Well-Architected Reliability 入口。&lt;/li>
&lt;li>&lt;a href="https://learn.microsoft.com/en-us/azure/architecture/guide/design-principles/self-healing">Design for self-healing&lt;/a>：self-healing 與 failover 的官方設計原則。&lt;/li>
&lt;li>&lt;a href="https://learn.microsoft.com/en-gb/azure/well-architected/reliability/design-patterns">Architecture design patterns that support reliability&lt;/a>：可靠性設計模式總覽。&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Microsoft Azure 的 SRE 文章與 Resilience patterns 文件是大型雲端供應商的可靠性工程公開素材。教學重點在「企業導向 cloud 的可靠性 patterns 與 governance」。</p>
<h2 id="規劃重點">規劃重點</h2>
<ul>
<li>Azure Well-Architected Framework：Reliability pillar 的設計指導</li>
<li>Resilience patterns：retry、circuit breaker、bulkhead 的官方範例</li>
<li>Site Reliability Engineering at Microsoft：內部 SRE 組織與實踐</li>
<li>Compliance-driven reliability：企業客戶要求下的可靠性 SLA</li>
</ul>
<h2 id="預計收錄實踐">預計收錄實踐</h2>
<table>
  <thead>
      <tr>
          <th>議題</th>
          <th>教學重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Well-Architected Framework</td>
          <td>Reliability pillar 結構與審查流程</td>
      </tr>
      <tr>
          <td>Resilience Design Patterns</td>
          <td>retry / breaker / bulkhead 等實作範例</td>
      </tr>
      <tr>
          <td>Azure SRE Engineering</td>
          <td>Microsoft 內部 SRE 演化</td>
      </tr>
      <tr>
          <td>Chaos Studio</td>
          <td>Azure 平台原生 chaos 工具</td>
      </tr>
  </tbody>
</table>
<h2 id="案例定位">案例定位</h2>
<p>Microsoft 這個案例在講的是企業雲端如何把可靠性寫進架構規範與設計模式。讀者先抓 reliability pillar、self-healing 與 design patterns 的分工，再把它們視為治理語言，而不是單純的文件清單。</p>
<h2 id="判讀重點">判讀重點</h2>
<p>當服務要面對企業客戶的 SLA 要求時，先看設計模式能否對應 failure mode，再看治理流程是否能把 pattern 真的落到架構審查。當團隊需要做 retry 或 bulkhead 時，重點是能不能選到正確的位置與層級。</p>
<h2 id="可操作判準">可操作判準</h2>
<ul>
<li>能否從 failure mode 反推適合的 reliability pattern</li>
<li>能否把 self-healing 寫成可驗證的設計要求</li>
<li>能否把架構審查和 SLA 約束對齊</li>
<li>能否把 Azure SRE 實踐轉成團隊可用的治理語言</li>
</ul>
<h2 id="與其他案例的關係">與其他案例的關係</h2>
<p>Microsoft 這頁和 Stripe、Google 的差異在於它更偏治理與設計審查，而不是單一事故。讀者若先懂這頁，再看 Azure AD 和 M365，就能把 identity 失效與企業雲端的 reliability pattern 串成同一條理解路徑。</p>
<h2 id="代表樣本">代表樣本</h2>
<ul>
<li>self-healing 把故障轉成可恢復的設計要求，而不是單靠人工補救。</li>
<li>reliability pillar 讓團隊在架構審查時就對齊失效模式與補救方式。</li>
<li>retry / circuit breaker / bulkhead 提供可重複使用的設計模式。</li>
<li>compliance-driven reliability 把 SLA 約束寫進雲端治理。</li>
<li>chaos studio 讓雲端平台本身提供測試失效的工具。</li>
<li>Well-Architected Framework 讓可靠性審查變成標準流程。</li>
<li>health check / retry policy 讓應用層能和平台層恢復節奏對齊。</li>
<li>governance 語言把企業 SLA 與技術決策連起來。</li>
</ul>
<h2 id="章節列表">章節列表</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/microsoft/change-management-and-reliability-governance/" data-link-title="Microsoft：變更治理與可靠性門檻" data-link-desc="透過分層變更管理與發布閘門，降低大型 SaaS 平台的系統性回歸風險。">MS1</a></td>
          <td>變更治理與可靠性門檻</td>
          <td>以風險分層與 release gate 降低系統性回歸</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/microsoft/safe-deployment-practices-and-resilience-patterns/" data-link-title="Microsoft：Safe Deployment Practices 與 Resilience Patterns" data-link-desc="大型 SaaS 用 ring-based deployment 控制變更擴散，用標準化 resilience patterns 讓依賴失效時的降級行為可預測。">MS2</a></td>
          <td>Safe Deployment Practices 與 Resilience Patterns</td>
          <td>ring-based deployment 與標準化韌性設計模式的制度化</td>
      </tr>
  </tbody>
</table>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://learn.microsoft.com/en-us/azure/architecture/">Azure Architecture Center</a>：Azure 架構中心總入口。</li>
<li><a href="https://learn.microsoft.com/en-us/azure/well-architected/resiliency/overview">Reliability quick links</a>：Azure Well-Architected Reliability 入口。</li>
<li><a href="https://learn.microsoft.com/en-us/azure/architecture/guide/design-principles/self-healing">Design for self-healing</a>：self-healing 與 failover 的官方設計原則。</li>
<li><a href="https://learn.microsoft.com/en-gb/azure/well-architected/reliability/design-patterns">Architecture design patterns that support reliability</a>：可靠性設計模式總覽。</li>
</ul>
]]></content:encoded></item><item><title>6.14 Dependency Reliability Budget</title><link>https://tarrragon.github.io/blog/backend/06-reliability/dependency-reliability-budget/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/dependency-reliability-budget/</guid><description>&lt;h2 id="大綱">大綱&lt;/h2>
&lt;ul>
&lt;li>為何依賴需要 budget：自家服務的 SLO 是依賴 SLO 的乘積&lt;/li>
&lt;li>依賴類別：內部服務、第三方 API、SaaS、基礎設施（DB / cache / queue）&lt;/li>
&lt;li>依賴 SLA 對照：vendor 公布的 SLA 跟 observed reliability 的差距&lt;/li>
&lt;li>budget 計算：依賴 99.9% × 自家 99.9% = 99.8% 上限&lt;/li>
&lt;li>降級設計：依賴失效時的 fallback / cache / 隊列緩衝&lt;/li>
&lt;li>circuit breaker 與 budget 的關聯&lt;/li>
&lt;li>跟 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6 SLO&lt;/a> 的整合：依賴 budget 是 SLO 算式的一部分&lt;/li>
&lt;li>跟 &lt;a href="https://tarrragon.github.io/blog/backend/04-observability/service-topology/" data-link-title="4.13 Service Topology 與 Dependency Map" data-link-desc="把跨服務依賴從文件變成自動發現的觀測訊號">4.13 topology&lt;/a> 的整合：依賴拓撲提供 budget 評估資料&lt;/li>
&lt;li>反模式：SLO 訂目標時忽略依賴可靠性；vendor SLA 抄進合約但無監測；依賴掛了才發現有依賴&lt;/li>
&lt;/ul>
&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>Dependency reliability budget 是把外部服務與跨團隊依賴的可靠性納入設計約束，責任是避免把自己系統的目標建立在不可控前提上。&lt;/p>
&lt;p>這一頁處理的是依賴一旦變差，自己服務還能保住多少功能。當依賴不是自己能修的時候，budget 就是把不確定性明文化。&lt;/p>
&lt;h2 id="核心判讀">核心判讀&lt;/h2>
&lt;p>判讀依賴風險時，不只看 SLA，而是看依賴失效後的降級能力與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/blast-radius/" data-link-title="Blast Radius" data-link-desc="說明事故影響面如何估算與隔離">blast radius&lt;/a>。&lt;/p>
&lt;p>重點訊號包括：&lt;/p>
&lt;ul>
&lt;li>依賴是否有明確 failure domain&lt;/li>
&lt;li>是否有 graceful degradation 或 fallback&lt;/li>
&lt;li>budget 是否會隨依賴變更而更新&lt;/li>
&lt;li>外部 outage 是否能快速路由到替代策略&lt;/li>
&lt;/ul>
&lt;h2 id="案例對照">案例對照&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/cases/aws-s3/" data-link-title="AWS S3" data-link-desc="AWS S3 重大事故時間線與架構脈絡">AWS S3&lt;/a>：基礎儲存依賴的邊界一旦縮小，整體可靠性就會被放大影響。&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/cases/cloudflare/" data-link-title="Cloudflare" data-link-desc="Cloudflare 全球 edge 事故時間線與架構脈絡">Cloudflare&lt;/a>：edge / control-plane 依賴需要有明確降級路徑。&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/cases/azure-ad/" data-link-title="Azure AD / Entra ID" data-link-desc="Microsoft Identity 控制面失效與 cascading 影響">Azure AD&lt;/a>：身份依賴失效時，影響通常跨產品、跨流程。&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/amazon/static-stability-and-constant-work/" data-link-title="Amazon：Static Stability 與 Constant Work Pattern" data-link-desc="控制面失效時資料面如何維持服務：用快取、預計算與固定工作量避免恢復放大。">Amazon A2&lt;/a>：static stability 讓資料面在控制面失效時仍能服務，constant work 避免恢復放大。控制面是依賴 budget 中風險最高的項目。&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/shopify/pod-architecture-and-resiliency-matrix/" data-link-title="Shopify：Pod Architecture 與 Resiliency Matrix" data-link-desc="多租戶隔離與系統化失敗模式盤點：pod 邊界控制擴散、resiliency matrix 驅動演練。">Shopify H2&lt;/a>：pod 隔離把依賴 budget 從全域帳本拆成 per-pod 結構，resiliency matrix 把依賴缺口可視化。&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/meta/bgp-control-plane-recovery-ordering/" data-link-title="Meta：BGP 事故與控制面恢復順序" data-link-desc="當回復工具依賴已故障的系統：2021-10 事故揭露控制面恢復順序與 out-of-band 存取的設計約束。">Meta M2&lt;/a>：回復工具依賴被回復的系統（BGP / DNS / 遠端存取），揭露控制面的隱性循環依賴。&lt;/li>
&lt;/ul>
&lt;h2 id="失效局部化cell-邊界跟-shuffle-sharding">失效局部化：cell 邊界跟 shuffle sharding&lt;/h2>
&lt;p>失效局部化是把單一依賴退化限制在最小可影響範圍的能力。把「依賴 budget」從統一全域帳本拆成 per-cell 可用度結構、是這層治理的核心責任。失效局部化要解四個子問題：擴散邊界、熱點重疊、控制面解耦、失敗模式工作量恆定。&lt;/p>
&lt;p>對應 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/amazon/shuffle-sharding-and-cell-boundary/" data-link-title="Amazon：Shuffle Sharding 與 Cell 邊界的失效局部化" data-link-desc="用 cell 與 shuffle sharding 將多租戶故障限制在局部，讓恢復策略可分批執行。">A1 Amazon Shuffle Sharding 與 Cell 邊界&lt;/a>：揭露四個機制對應上述四個子問題 — cell 邊界（擴散邊界）、shuffle sharding（熱點重疊）、static stability（控制面解耦）、constant work（失敗模式工作量恆定）。這四個機制把恢復策略從「全域搶救」轉為「分批收斂」。Cell 邊界是 6.14 SSoT；實驗時 blast radius 的邊界控制由 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 experiment-safety-boundary&lt;/a> 處理、兩者邊界互補（前者是常態架構、後者是實驗範圍控制）。&lt;/p></description><content:encoded><![CDATA[<h2 id="大綱">大綱</h2>
<ul>
<li>為何依賴需要 budget：自家服務的 SLO 是依賴 SLO 的乘積</li>
<li>依賴類別：內部服務、第三方 API、SaaS、基礎設施（DB / cache / queue）</li>
<li>依賴 SLA 對照：vendor 公布的 SLA 跟 observed reliability 的差距</li>
<li>budget 計算：依賴 99.9% × 自家 99.9% = 99.8% 上限</li>
<li>降級設計：依賴失效時的 fallback / cache / 隊列緩衝</li>
<li>circuit breaker 與 budget 的關聯</li>
<li>跟 <a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6 SLO</a> 的整合：依賴 budget 是 SLO 算式的一部分</li>
<li>跟 <a href="/blog/backend/04-observability/service-topology/" data-link-title="4.13 Service Topology 與 Dependency Map" data-link-desc="把跨服務依賴從文件變成自動發現的觀測訊號">4.13 topology</a> 的整合：依賴拓撲提供 budget 評估資料</li>
<li>反模式：SLO 訂目標時忽略依賴可靠性；vendor SLA 抄進合約但無監測；依賴掛了才發現有依賴</li>
</ul>
<h2 id="概念定位">概念定位</h2>
<p>Dependency reliability budget 是把外部服務與跨團隊依賴的可靠性納入設計約束，責任是避免把自己系統的目標建立在不可控前提上。</p>
<p>這一頁處理的是依賴一旦變差，自己服務還能保住多少功能。當依賴不是自己能修的時候，budget 就是把不確定性明文化。</p>
<h2 id="核心判讀">核心判讀</h2>
<p>判讀依賴風險時，不只看 SLA，而是看依賴失效後的降級能力與 <a href="/blog/backend/knowledge-cards/blast-radius/" data-link-title="Blast Radius" data-link-desc="說明事故影響面如何估算與隔離">blast radius</a>。</p>
<p>重點訊號包括：</p>
<ul>
<li>依賴是否有明確 failure domain</li>
<li>是否有 graceful degradation 或 fallback</li>
<li>budget 是否會隨依賴變更而更新</li>
<li>外部 outage 是否能快速路由到替代策略</li>
</ul>
<h2 id="案例對照">案例對照</h2>
<ul>
<li><a href="/blog/backend/08-incident-response/cases/aws-s3/" data-link-title="AWS S3" data-link-desc="AWS S3 重大事故時間線與架構脈絡">AWS S3</a>：基礎儲存依賴的邊界一旦縮小，整體可靠性就會被放大影響。</li>
<li><a href="/blog/backend/08-incident-response/cases/cloudflare/" data-link-title="Cloudflare" data-link-desc="Cloudflare 全球 edge 事故時間線與架構脈絡">Cloudflare</a>：edge / control-plane 依賴需要有明確降級路徑。</li>
<li><a href="/blog/backend/08-incident-response/cases/azure-ad/" data-link-title="Azure AD / Entra ID" data-link-desc="Microsoft Identity 控制面失效與 cascading 影響">Azure AD</a>：身份依賴失效時，影響通常跨產品、跨流程。</li>
<li><a href="/blog/backend/06-reliability/cases/amazon/static-stability-and-constant-work/" data-link-title="Amazon：Static Stability 與 Constant Work Pattern" data-link-desc="控制面失效時資料面如何維持服務：用快取、預計算與固定工作量避免恢復放大。">Amazon A2</a>：static stability 讓資料面在控制面失效時仍能服務，constant work 避免恢復放大。控制面是依賴 budget 中風險最高的項目。</li>
<li><a href="/blog/backend/06-reliability/cases/shopify/pod-architecture-and-resiliency-matrix/" data-link-title="Shopify：Pod Architecture 與 Resiliency Matrix" data-link-desc="多租戶隔離與系統化失敗模式盤點：pod 邊界控制擴散、resiliency matrix 驅動演練。">Shopify H2</a>：pod 隔離把依賴 budget 從全域帳本拆成 per-pod 結構，resiliency matrix 把依賴缺口可視化。</li>
<li><a href="/blog/backend/06-reliability/cases/meta/bgp-control-plane-recovery-ordering/" data-link-title="Meta：BGP 事故與控制面恢復順序" data-link-desc="當回復工具依賴已故障的系統：2021-10 事故揭露控制面恢復順序與 out-of-band 存取的設計約束。">Meta M2</a>：回復工具依賴被回復的系統（BGP / DNS / 遠端存取），揭露控制面的隱性循環依賴。</li>
</ul>
<h2 id="失效局部化cell-邊界跟-shuffle-sharding">失效局部化：cell 邊界跟 shuffle sharding</h2>
<p>失效局部化是把單一依賴退化限制在最小可影響範圍的能力。把「依賴 budget」從統一全域帳本拆成 per-cell 可用度結構、是這層治理的核心責任。失效局部化要解四個子問題：擴散邊界、熱點重疊、控制面解耦、失敗模式工作量恆定。</p>
<p>對應 <a href="/blog/backend/06-reliability/cases/amazon/shuffle-sharding-and-cell-boundary/" data-link-title="Amazon：Shuffle Sharding 與 Cell 邊界的失效局部化" data-link-desc="用 cell 與 shuffle sharding 將多租戶故障限制在局部，讓恢復策略可分批執行。">A1 Amazon Shuffle Sharding 與 Cell 邊界</a>：揭露四個機制對應上述四個子問題 — cell 邊界（擴散邊界）、shuffle sharding（熱點重疊）、static stability（控制面解耦）、constant work（失敗模式工作量恆定）。這四個機制把恢復策略從「全域搶救」轉為「分批收斂」。Cell 邊界是 6.14 SSoT；實驗時 blast radius 的邊界控制由 <a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 experiment-safety-boundary</a> 處理、兩者邊界互補（前者是常態架構、後者是實驗範圍控制）。</p>
<p>把 cell 邊界跟 shuffle sharding 視為依賴 budget 的前置結構：先限制擴散邊界、再談恢復策略。budget 算式裡的「依賴失效」應該對應到「最大可影響 cell」、不是「整個服務全停」。</p>
<h2 id="跨區故障跟回復順序">跨區故障跟回復順序</h2>
<p>跨區故障的核心責任是把「單區極限失效」跟「跨區連鎖退化」拆成兩個治理面。fault domain 限制單區擴散、ordered failover 控制回復節奏、dependency isolation 切斷共享路徑放大風險、三者構成跨區治理 contract。大規模平台的關鍵風險來自跨區相依引發的連鎖退化 — 單點失效只是觸發點、真正的擴散面在共享相依路徑。</p>
<p>對應 <a href="/blog/backend/06-reliability/cases/meta/region-failover-and-reliability-boundaries/" data-link-title="Meta：Region Failover 與可靠性邊界" data-link-desc="把跨區故障視為邊界治理問題，透過分區隔離與回復順序控制失效擴散。">M1 Meta Region Failover 邊界治理</a>：揭露三個機制 — region fault domain（影響面最多到哪裡）、ordered failover（先恢復哪條路徑）、dependency isolation（共享相依如何降風險）。</p>
<p>回復順序的核心是分批恢復、不同時恢復所有路徑。同時恢復多條路徑可能在剛恢復的依賴上引發回源放大或連鎖過載、把原本可控的回復變成第二次故障。實際的做法跟 ordered failover 對齊：依事故 timeline 跟團隊既定 runbook 安排回復批次、每批驗證 baseline 穩定後再進下一批。具體的批次設計跟 ordered failover 證據交給 <a href="/blog/backend/08-incident-response/containment-recovery-strategy/" data-link-title="8.3 止血、降級與回復策略" data-link-desc="把短期止血與正式回復拆成可執行步驟">8.3 containment-recovery-strategy</a>。</p>
<h2 id="跨團隊-reliability-契約">跨團隊 reliability 契約</h2>
<p>跨團隊 reliability 契約的核心責任是讓「依賴 budget」變成「契約欄位」：每個被依賴的服務承諾哪些 SLI、提供哪些降級路徑、failure mode 是什麼。團隊自治程度高的組織需要共同契約把跨服務的可靠性最低標準對齊、避免風險在整合時集中爆發。</p>
<p>對應 <a href="/blog/backend/06-reliability/cases/spotify/platform-engineering-and-reliability-contracts/" data-link-title="Spotify：平台工程與可靠性契約" data-link-desc="用平台契約統一服務團隊的可靠性最低標準，降低跨團隊變更造成的隱性風險。">SP1 Spotify 平台工程與可靠性契約</a>：揭露三個機制 — reliability contract（每個服務最低要提供什麼）、platform self-service（標準如何降低導入成本）、cross-team evidence（證據如何跨團隊共享）。SP1 case 主場景是內部跨團隊契約、不是 vendor 軸；vendor SLA 治理請見前段「依賴類別」跟 <a href="/blog/backend/04-observability/observability-operating-model/" data-link-title="4.18 Observability Operating Model" data-link-desc="定義 platform / service team / on-call 對訊號、dashboard、alert 與成本的 ownership">04.18 operating model</a> 的 ownership 邊界。</p>
<p>契約讓內部依賴 budget 可以基於 observed reliability（被依賴服務實際的 SLI 觀測值）、補強只靠 vendor SLA 的不足 — 後者通常是上界、不反映實際失效特性。</p>
<h2 id="產業情境saas-與-b2b-服務的依賴約束">產業情境：SaaS 與 B2B 服務的依賴約束</h2>
<p>SaaS 服務的可靠性直接綁定客戶合約，依賴 budget 的分配需要按最嚴格的 SLA 需求設計。enterprise 客戶要求 99.99%、self-serve 客戶接受 99.9% — 共享依賴的 budget 必須對齊最高 SLA，否則高階客戶的承諾無法兌現。</p>
<p>多租戶共享依賴的 budget 分配是 SaaS 特有的治理問題。所有租戶共用同一組 DB / cache / queue，但高 SLA 客戶對依賴可靠性的要求更嚴格。實務做法是把高 SLA 客戶路由到獨立依賴池（dedicated instance / priority queue），或在共享依賴上做租戶級隔離（connection pool per tenant / rate limit per tenant）。隔離策略跟 <a href="/blog/backend/06-reliability/cases/amazon/shuffle-sharding-and-cell-boundary/" data-link-title="Amazon：Shuffle Sharding 與 Cell 邊界的失效局部化" data-link-desc="用 cell 與 shuffle sharding 將多租戶故障限制在局部，讓恢復策略可分批執行。">Amazon A1 的 shuffle sharding</a> 同源 — 差異在 SaaS 的隔離單位是租戶合約等級而非 cell。</p>
<p>第三方依賴的 SLA 傳遞是另一個 SaaS 常見壓力。SaaS 產品常依賴其他 SaaS（payment provider / email service / auth provider），這些依賴的 SLA 是自身 SLA 的理論上限。若 payment provider 只承諾 99.9%，自身對客戶承諾 99.99% 的結帳成功率就需要 fallback 設計（如多 provider 切換、本地排隊 + 延遲處理）。budget 計算時要把第三方依賴的 observed reliability 納入，而非照抄 vendor SLA。</p>
<p>跟 <a href="/blog/backend/06-reliability/cases/spotify/platform-engineering-and-reliability-contracts/" data-link-title="Spotify：平台工程與可靠性契約" data-link-desc="用平台契約統一服務團隊的可靠性最低標準，降低跨團隊變更造成的隱性風險。">Spotify SP1 平台工程與可靠性契約</a> 的關聯：分散團隊共用可靠性基線的契約模型，在 SaaS 組織中同時服務內部團隊對齊與外部客戶 SLA 承諾兩個面向。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>6.6 SLO / <a href="/blog/backend/knowledge-cards/error-budget/" data-link-title="Error Budget" data-link-desc="說明 SLO 允許的失敗額度如何影響發版與可靠性投入">error budget</a>：把依賴可靠性納入目標計算</li>
<li>6.8 release gate：把依賴健康度變成放行條件</li>
<li>08.15 vendor 事故：第三方事故的事中處理</li>
</ul>
<h2 id="判讀訊號">判讀訊號</h2>
<ul>
<li>自家服務 SLO 高於依賴 SLA 的乘積、目標不可達</li>
<li>第三方 API 退化時無 observed metric、靠用戶投訴發現</li>
<li>vendor SLA credit 從未請領、無流程</li>
<li>新依賴接入無 reliability review</li>
<li>關鍵路徑上有「不知道掛了會怎樣」的依賴</li>
</ul>
<h2 id="交接路由">交接路由</h2>
<ul>
<li>04.13 topology：依賴自動發現</li>
<li>06.6 SLO：依賴 budget 納入 SLO 算式</li>
<li>06.10 contract testing：依賴契約穩定性</li>
<li>08.15 vendor 事故：依賴方掛掉的決策模型</li>
</ul>
]]></content:encoded></item><item><title>端到端資料完整性</title><link>https://tarrragon.github.io/blog/monitoring/04-collector/data-integrity/</link><pubDate>Wed, 24 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/monitoring/04-collector/data-integrity/</guid><description>&lt;p>監控資料從事件產生到寫入 storage，經過 SDK buffer、HTTP transport、collector pipeline、storage backend 四個環節。每個環節都有丟失事件的可能 — 記憶體 buffer 溢出、網路超時、背壓丟棄、磁碟寫入失敗。端到端資料完整性的目標是讓每個損失點都是有意識的設計取捨，而非靜默丟失。&lt;/p>
&lt;p>監控資料和交易資料的根本差異在這裡：交易資料的損失會直接造成商業損害（少了一筆訂單），監控資料的損失影響的是可觀測性的覆蓋率（少了幾筆 event 不影響趨勢判斷，但漏了 error 可能讓 bug 晚幾天被發現）。這個差異決定了完整性設計的方向 — 追求的是「損失可控且可觀測」，而非「零損失」。合規稽核 log、billing event 和安全事件不適用這個假設 — 它們的損失有法規或商業後果，需要 at-least-once delivery 和獨立的持久化保證，通常用 transaction log 而非監控管線處理。&lt;/p>
&lt;h2 id="資料損失地圖">資料損失地圖&lt;/h2>
&lt;p>一筆事件從產生到持久化，依序經過四個環節。每個環節的損失類型、發生條件和影響範圍各不同。&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">事件產生 → [SDK buffer] → HTTP POST → [Collector pipeline] → [Storage]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl"> ① ② ③ ④ ⑤&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="環節一事件產生階段">環節一：事件產生階段&lt;/h3>
&lt;p>事件在 SDK 的 &lt;code>monitor.event()&lt;/code> / &lt;code>monitor.error()&lt;/code> 被呼叫時產生，進入記憶體 buffer。這個階段的損失來自取樣和 SDK 初始化時序。&lt;/p>
&lt;p>&lt;strong>靜態取樣&lt;/strong>：SDK config 中設定的取樣率（例如 metric 類 0.1 = 每 10 筆只收 1 筆）是設計內的損失。取樣後的事件量直接影響後續所有環節的負載。取樣率的設定依據見&lt;a href="https://tarrragon.github.io/blog/monitoring/03-sdk-design/sensor-lifecycle-management/" data-link-title="感測器生命週期管理" data-link-desc="產品生命週期的五個階段各啟用什麼感測器 — feature flag 整合、取樣率動態調整、感測器開關的可觀察性">感測器生命週期管理&lt;/a>。&lt;/p>
&lt;p>&lt;strong>SDK 未初始化&lt;/strong>：app 啟動後到 &lt;code>monitor.init()&lt;/code> 完成之間的事件會被丟棄。如果 init 排在其他初始化邏輯之後，啟動階段的 crash 可能漏捕。商業 SDK（Sentry、Crashlytics）用 native crash handler 在 SDK 層之外攔截這類 crash，自架方案通常接受這個損失。&lt;/p>
&lt;h3 id="環節二sdk-buffer-階段">環節二：SDK buffer 階段&lt;/h3>
&lt;p>事件進入記憶體 buffer 後，等待 flush 觸發。Buffer 溢出和 app 強制終止是這段路徑上的兩個風險。&lt;/p>
&lt;p>&lt;strong>FIFO 丟棄&lt;/strong>：記憶體 buffer 有容量上限（典型值 200-500 筆）。離線時間過長或事件產生速率過高時，buffer 滿了會丟棄最舊的事件。丟棄策略見&lt;a href="https://tarrragon.github.io/blog/monitoring/03-sdk-design/offline-buffer/" data-link-title="離線 buffer 與重試" data-link-desc="網路不可用時的事件保存策略 — FIFO 丟棄、本地 persistence、恢復後補發的取捨">離線 buffer 與重試&lt;/a>，優先級丟棄見 &lt;a href="https://tarrragon.github.io/blog/monitoring/04-collector/ingestion-scaling/" data-link-title="Ingestion Scaling" data-link-desc="四層防線應對 ingestion 端的流量擴展 — SDK 取樣、Collector 背壓、水平擴展、Queue 解耦">Ingestion Scaling 第一層&lt;/a>。&lt;/p>
&lt;p>&lt;strong>App 強制終止&lt;/strong>：iOS 的 &lt;code>kill&lt;/code>、Android 的 process death、Python 的 &lt;code>SIGKILL&lt;/code> — 記憶體 buffer 中未 flush 的事件全部遺失。&lt;a href="https://tarrragon.github.io/blog/monitoring/03-sdk-design/batch-flush/" data-link-title="攢批送出策略" data-link-desc="flush interval / buffer size / flush on close 三個控制點決定事件何時離開 SDK — 平衡即時性和網路效率">攢批送出策略&lt;/a>的 close flush 嘗試在 app 正常退出時送出剩餘事件，但強制終止時連 close callback 都不會執行。&lt;/p>
&lt;p>&lt;strong>動態取樣&lt;/strong>：收到 collector 的 HTTP 429（Too Many Requests，表示 collector 過載）後，SDK 自動降低取樣率（從 1.0 降到 0.5 → 0.1）。這是對 collector 過載的回饋反應 — 損失的事件量隨背壓程度增加。和靜態取樣的差異是動態取樣在正常情況下不生效，只在過載時啟用。&lt;/p>
&lt;h3 id="環節三transport-階段">環節三：Transport 階段&lt;/h3>
&lt;p>SDK flush 時透過 HTTP POST 送出 batch。網路故障和重試耗盡構成 transport 層的主要損失。&lt;/p></description><content:encoded><![CDATA[<p>監控資料從事件產生到寫入 storage，經過 SDK buffer、HTTP transport、collector pipeline、storage backend 四個環節。每個環節都有丟失事件的可能 — 記憶體 buffer 溢出、網路超時、背壓丟棄、磁碟寫入失敗。端到端資料完整性的目標是讓每個損失點都是有意識的設計取捨，而非靜默丟失。</p>
<p>監控資料和交易資料的根本差異在這裡：交易資料的損失會直接造成商業損害（少了一筆訂單），監控資料的損失影響的是可觀測性的覆蓋率（少了幾筆 event 不影響趨勢判斷，但漏了 error 可能讓 bug 晚幾天被發現）。這個差異決定了完整性設計的方向 — 追求的是「損失可控且可觀測」，而非「零損失」。合規稽核 log、billing event 和安全事件不適用這個假設 — 它們的損失有法規或商業後果，需要 at-least-once delivery 和獨立的持久化保證，通常用 transaction log 而非監控管線處理。</p>
<h2 id="資料損失地圖">資料損失地圖</h2>
<p>一筆事件從產生到持久化，依序經過四個環節。每個環節的損失類型、發生條件和影響範圍各不同。</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">事件產生 → [SDK buffer] → HTTP POST → [Collector pipeline] → [Storage]
</span></span><span class="line"><span class="ln">2</span><span class="cl">     ①          ②            ③              ④                   ⑤</span></span></code></pre></div><h3 id="環節一事件產生階段">環節一：事件產生階段</h3>
<p>事件在 SDK 的 <code>monitor.event()</code> / <code>monitor.error()</code> 被呼叫時產生，進入記憶體 buffer。這個階段的損失來自取樣和 SDK 初始化時序。</p>
<p><strong>靜態取樣</strong>：SDK config 中設定的取樣率（例如 metric 類 0.1 = 每 10 筆只收 1 筆）是設計內的損失。取樣後的事件量直接影響後續所有環節的負載。取樣率的設定依據見<a href="/blog/monitoring/03-sdk-design/sensor-lifecycle-management/" data-link-title="感測器生命週期管理" data-link-desc="產品生命週期的五個階段各啟用什麼感測器 — feature flag 整合、取樣率動態調整、感測器開關的可觀察性">感測器生命週期管理</a>。</p>
<p><strong>SDK 未初始化</strong>：app 啟動後到 <code>monitor.init()</code> 完成之間的事件會被丟棄。如果 init 排在其他初始化邏輯之後，啟動階段的 crash 可能漏捕。商業 SDK（Sentry、Crashlytics）用 native crash handler 在 SDK 層之外攔截這類 crash，自架方案通常接受這個損失。</p>
<h3 id="環節二sdk-buffer-階段">環節二：SDK buffer 階段</h3>
<p>事件進入記憶體 buffer 後，等待 flush 觸發。Buffer 溢出和 app 強制終止是這段路徑上的兩個風險。</p>
<p><strong>FIFO 丟棄</strong>：記憶體 buffer 有容量上限（典型值 200-500 筆）。離線時間過長或事件產生速率過高時，buffer 滿了會丟棄最舊的事件。丟棄策略見<a href="/blog/monitoring/03-sdk-design/offline-buffer/" data-link-title="離線 buffer 與重試" data-link-desc="網路不可用時的事件保存策略 — FIFO 丟棄、本地 persistence、恢復後補發的取捨">離線 buffer 與重試</a>，優先級丟棄見 <a href="/blog/monitoring/04-collector/ingestion-scaling/" data-link-title="Ingestion Scaling" data-link-desc="四層防線應對 ingestion 端的流量擴展 — SDK 取樣、Collector 背壓、水平擴展、Queue 解耦">Ingestion Scaling 第一層</a>。</p>
<p><strong>App 強制終止</strong>：iOS 的 <code>kill</code>、Android 的 process death、Python 的 <code>SIGKILL</code> — 記憶體 buffer 中未 flush 的事件全部遺失。<a href="/blog/monitoring/03-sdk-design/batch-flush/" data-link-title="攢批送出策略" data-link-desc="flush interval / buffer size / flush on close 三個控制點決定事件何時離開 SDK — 平衡即時性和網路效率">攢批送出策略</a>的 close flush 嘗試在 app 正常退出時送出剩餘事件，但強制終止時連 close callback 都不會執行。</p>
<p><strong>動態取樣</strong>：收到 collector 的 HTTP 429（Too Many Requests，表示 collector 過載）後，SDK 自動降低取樣率（從 1.0 降到 0.5 → 0.1）。這是對 collector 過載的回饋反應 — 損失的事件量隨背壓程度增加。和靜態取樣的差異是動態取樣在正常情況下不生效，只在過載時啟用。</p>
<h3 id="環節三transport-階段">環節三：Transport 階段</h3>
<p>SDK flush 時透過 HTTP POST 送出 batch。網路故障和重試耗盡構成 transport 層的主要損失。</p>
<p><strong>HTTP 超時 / 連線失敗</strong>：collector 不可達時，batch 保留在 SDK buffer 等待下次 flush 重試。重試次數有上限（3 次），超過後丟棄 batch 並記錄 <code>sdk.flush.dropped</code> metric。重試策略見<a href="/blog/monitoring/03-sdk-design/batch-flush/" data-link-title="攢批送出策略" data-link-desc="flush interval / buffer size / flush on close 三個控制點決定事件何時離開 SDK — 平衡即時性和網路效率">攢批送出策略</a>。</p>
<p><strong>離線補發擁塞</strong>：離線恢復後，SDK 一次補發大量累積事件。如果補發速率過高（一批 500 筆 × 多個 SDK 同時恢復），collector 可能觸發背壓回 429，SDK 又進入動態降採樣 — 補發本身造成新的損失。<a href="/blog/monitoring/03-sdk-design/offline-buffer/" data-link-title="離線 buffer 與重試" data-link-desc="網路不可用時的事件保存策略 — FIFO 丟棄、本地 persistence、恢復後補發的取捨">離線 buffer 與重試</a>的分批補發（每批 50-100 筆、間隔 1-2 秒）用來避免這個問題。</p>
<h3 id="環節四collector-pipeline-階段">環節四：Collector pipeline 階段</h3>
<p>Collector 收到 HTTP request 後，事件進入處理鏈路。背壓、驗證拒絕和 pipeline 內部的 buffer 溢出都可能在這裡造成損失。</p>
<p><strong>Channel 背壓</strong>：Collector 內部用一個專屬的寫入 goroutine 搭配 Go channel 做序列化寫入（<a href="/blog/monitoring/04-collector/architecture/" data-link-title="Collector 架構" data-link-desc="HTTP endpoint → JSON Schema 驗證 → 儲存 → 查詢 → rule engine 的五段式處理鏈路">Collector 架構</a>的並發寫入策略段），channel 有固定容量。Channel 滿時 HTTP handler 回 429，事件被拒絕。SDK 收到 429 後保留事件在 buffer 等待重試，但如果 SDK buffer 也快滿，部分事件會被 FIFO 丟棄。這裡的損失是 SDK 層和 collector 層的連鎖反應 — collector 的背壓壓力最終由 SDK 的 buffer 承擔。</p>
<p><strong>Schema validation reject</strong>：事件格式不符合 JSON Schema 的事件被拒絕（400 或 207 中的 rejected 部分）。這是品質閘門而非容量限制 — 被拒絕的事件無論重試多少次都不會通過，SDK 應該清除這些事件並記錄 warning。問題在 SDK 端的事件建構邏輯（程式碼 bug），需要修 SDK 而非重試。</p>
<p><strong>429 後事件已回 202 但未寫入</strong>：collector 回了 202（已接受）但事件還在 channel buffer 中未寫入 storage 時，如果 collector crash 或被 SIGKILL，channel 中的事件遺失。這是「已承諾但未持久化」的窗口。<a href="/blog/monitoring/04-collector/container-deployment/" data-link-title="Container 部署設計" data-link-desc="Docker 部署 collector 的設計 — SQLite 在 overlay filesystem 的 I/O 考量、volume mount、graceful shutdown、資源限制">Container 部署設計</a>的 graceful shutdown 序列嘗試在 shutdown 時 flush pending writes，但非 graceful shutdown（OOMKill、硬體故障）無法保護。</p>
<h3 id="環節五storage-階段">環節五：Storage 階段</h3>
<p>事件從 channel 寫入 storage backend。寫入失敗和資料管理操作（downsample / purge）構成最後一段損失。</p>
<p><strong>SQLite <code>database is locked</code></strong>：busy timeout 到期後寫入失敗。Single-writer pattern 降低發生機率但不能完全消除 — downsample / purge job 執行期間持有 write lock，如果 job 跑太久（數秒以上），ingestion 的寫入可能逾時。</p>
<p><strong>磁碟空間不足</strong>：SQLite 寫入需要磁碟空間（WAL 檔案 + 主資料庫 + 臨時檔案）。磁碟滿時寫入失敗，事件遺失。保留策略的 purge job 負責控制磁碟使用量，但如果 purge 頻率低於寫入增長速率，磁碟可能在兩次 purge 之間被填滿。</p>
<p><strong>Downsample / purge 的設計內損失</strong>：保留策略到期的原始事件被刪除（purge），只保留聚合摘要（hourly_summary / daily_summary）。這是設計內的損失 — 原始事件的 stack trace、完整 JSON data 在 purge 後不可回復，只剩下計數。保留策略見<a href="/blog/monitoring/04-collector/scaling-evolution/" data-link-title="規模演進" data-link-desc="可插拔 Storage Backend 架構 — SQLite 預設、PostgreSQL 觸發切換、時間序列 DB 長期演進">規模演進</a>的分層保留段。</p>
<h2 id="設計內損失-vs-異常損失">設計內損失 vs 異常損失</h2>
<p>上述損失點可以分成兩類，處理方式根本不同。</p>
<table>
  <thead>
      <tr>
          <th>類型</th>
          <th>損失點</th>
          <th>特徵</th>
          <th>處理方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>設計內</td>
          <td>靜態取樣、動態取樣、FIFO 丟棄、purge</td>
          <td>有意識的取捨、可預測的量</td>
          <td>在 config 中設定、用指標監控</td>
      </tr>
      <tr>
          <td>異常</td>
          <td>crash 丟 buffer、disk full、WAL 損壞</td>
          <td>非預期的故障、不可預測</td>
          <td>用告警偵測、用恢復機制應對</td>
      </tr>
      <tr>
          <td>品質閘門</td>
          <td>schema reject</td>
          <td>SDK 端 bug 導致、重試無效</td>
          <td>修 SDK 程式碼、不在 collector</td>
      </tr>
  </tbody>
</table>
<p>設計內損失的目標是讓損失量可控 — 取樣率設 0.1 代表預期丟 90%，FIFO buffer 容量 200 代表離線超過 20 分鐘（每分鐘 10 筆）後開始丟棄。這些數字是 config 參數，可以根據業務需求調整。</p>
<p>異常損失的目標是儘早偵測 — collector crash 後 channel 中有多少筆未寫入？磁碟使用率到多少該告警？下方的完整性指標段專門處理偵測異常損失的方法。</p>
<p>品質閘門的處理在 SDK 端而非 collector 端 — schema validation reject 的事件無論重試多少次都不會通過，問題在事件建構邏輯。具體的 reject 行為和回應格式見<a href="#%e7%92%b0%e7%af%80%e5%9b%9bcollector-pipeline-%e9%9a%8e%e6%ae%b5">環節四的 Schema validation reject 段</a>。</p>
<h2 id="監控損失本身的方法">監控損失本身的方法</h2>
<p>監控系統的完整性需要「監控自己的監控」— 用獨立的指標追蹤每個環節的進出量，損失量 = 進量 - 出量。</p>
<h3 id="sdk-端指標">SDK 端指標</h3>
<p>SDK 內部維護計數器，每次 flush 成功後一起送出（作為 metric 類事件）：</p>
<table>
  <thead>
      <tr>
          <th>指標</th>
          <th>含義</th>
          <th>計算方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>sdk.events.produced</code></td>
          <td>事件產生總數（取樣前）</td>
          <td>每次 <code>monitor.event()</code> 調用 +1</td>
      </tr>
      <tr>
          <td><code>sdk.events.sampled</code></td>
          <td>取樣後保留的事件數</td>
          <td>通過取樣邏輯的事件 +1</td>
      </tr>
      <tr>
          <td><code>sdk.events.sent</code></td>
          <td>成功送出的事件數（收到 200/207 的 accepted）</td>
          <td>flush 成功後按 accepted 累加</td>
      </tr>
      <tr>
          <td><code>sdk.events.dropped</code></td>
          <td>被 FIFO 丟棄或重試耗盡的事件數</td>
          <td>每次丟棄 +1</td>
      </tr>
      <tr>
          <td><code>sdk.flush.failures</code></td>
          <td>flush 失敗次數（429 / 5xx / timeout）</td>
          <td>每次 flush 失敗 +1</td>
      </tr>
      <tr>
          <td><code>sdk.sampling.rate</code></td>
          <td>當前動態取樣率</td>
          <td>收到 429 後更新</td>
      </tr>
  </tbody>
</table>
<p><code>produced - sampled</code> = 取樣損失（設計內）。<code>sampled - sent - dropped</code> 如果不為零，代表有事件卡在 buffer 中尚未送出或未被計入任何分類。</p>
<h3 id="collector-端指標">Collector 端指標</h3>
<p>Collector 在 <code>/metrics</code> endpoint（或 health endpoint 的擴展欄位）暴露處理計數器：</p>
<table>
  <thead>
      <tr>
          <th>指標</th>
          <th>含義</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>collector.events.received</code></td>
          <td>收到的事件總數（HTTP handler 層計數）</td>
      </tr>
      <tr>
          <td><code>collector.events.rejected</code></td>
          <td>schema validation 拒絕的事件數</td>
      </tr>
      <tr>
          <td><code>collector.events.stored</code></td>
          <td>成功寫入 storage 的事件數</td>
      </tr>
      <tr>
          <td><code>collector.events.backpressure</code></td>
          <td>因 channel 滿回 429 的事件數</td>
      </tr>
      <tr>
          <td><code>collector.channel.depth</code></td>
          <td>當前 channel 中待寫入的事件數</td>
      </tr>
      <tr>
          <td><code>collector.storage.errors</code></td>
          <td>storage 寫入失敗的次數</td>
      </tr>
  </tbody>
</table>
<p><code>received - rejected - stored - backpressure</code> 如果不為零，代表有事件在 pipeline 中遺失（channel buffer 中的事件在 crash 時丟失就會造成這個差距）。</p>
<h3 id="端到端比對">端到端比對</h3>
<p>SDK 的 <code>sent</code> 和 collector 的 <code>received</code> 之間的差距是 transport 層的損失 — 網路丟包、中間件攔截（reverse proxy 的 body size limit）或 collector 重啟期間的連線失敗。</p>
<p>這個比對在自用場景下用手動 spot check 就夠（SDK log 的 sent count vs collector dashboard 的 received count）。小型以上規模需要自動化：一個定期 job 比對兩邊的計數器，差距超過閾值時告警。</p>
<h3 id="損失率的可接受範圍">損失率的可接受範圍</h3>
<table>
  <thead>
      <tr>
          <th>規模</th>
          <th>event 類損失率</th>
          <th>error 類損失率</th>
          <th>監控粒度</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>自用</td>
          <td>&lt; 10%</td>
          <td>&lt; 1%</td>
          <td>手動 spot check</td>
      </tr>
      <tr>
          <td>小型團隊</td>
          <td>&lt; 5%</td>
          <td>&lt; 0.5%</td>
          <td>每日自動比對</td>
      </tr>
      <tr>
          <td>中型以上</td>
          <td>&lt; 1%</td>
          <td>&lt; 0.1%</td>
          <td>即時 dashboard + 告警</td>
      </tr>
  </tbody>
</table>
<p>閾值的推導邏輯：event 類的損失影響統計精度 — 取樣率 0.9 加上 transport 和 collector 層的少量損失，自用場景合計 &lt; 10% 是合理的上限；funnel 分析用取樣校正（除以取樣率）仍然有效。Error 類的損失直接影響 bug 發現速度 — 容忍度比 event 低一個數量級。中型以上規模的 &lt; 1% / &lt; 0.1% 接近商業方案（Sentry / Datadog）的 SLA 水準。</p>
<p><a href="/blog/monitoring/04-collector/ingestion-scaling/" data-link-title="Ingestion Scaling" data-link-desc="四層防線應對 ingestion 端的流量擴展 — SDK 取樣、Collector 背壓、水平擴展、Queue 解耦">Ingestion Scaling</a> 的 error 快通道設計就是基於這個優先級差異。</p>
<h2 id="被自己的-sdk-ddos">被自己的 SDK DDoS</h2>
<p>「SDK 產生的流量壓垮自己的 collector」是自架監控系統最常見的可靠性事故。來源是自家 SDK 的異常行為或正常行為在特定條件下的放大效應 — 內部流量失控，而非外部攻擊。外部偽造流量的防護見 <a href="/blog/monitoring/07-security-privacy/client-sdk-authentication/" data-link-title="Client-side SDK 認證的根本限制" data-link-desc="嵌在 client 端的 credential 必然可被提取 — 認清 architecture 天花板後的多層緩解策略，從 origin 驗證到 device attestation">Client-side SDK 認證</a>。</p>
<p>本段按觸發場景分類（SDK bug / 部署推送 / 使用者暴增），和 <a href="/blog/monitoring/04-collector/ingestion-scaling/" data-link-title="Ingestion Scaling" data-link-desc="四層防線應對 ingestion 端的流量擴展 — SDK 取樣、Collector 背壓、水平擴展、Queue 解耦">Ingestion Scaling</a> 的四層防線（SDK 端 / collector 單機 / 水平擴展 / queue 解耦）是不同切面。四層防線按防護位置劃分、說明機制怎麼做；本段按場景劃分、說明什麼時候哪些機制會被觸發。</p>
<h3 id="sdk-bug事件風暴">SDK bug：事件風暴</h3>
<p>SDK 程式碼 bug 導致事件無限迴圈 — 常見於事件處理器內再次觸發事件（error handler 中呼叫 <code>monitor.event()</code> 又觸發 error），或 UI 事件綁定錯誤導致每個 frame 產生一筆事件（60 fps = 每秒 60 筆）。</p>
<p><strong>損失路徑</strong>：事件風暴首先填滿 SDK buffer → 觸發高頻 flush → collector 收到大量 request → channel 滿觸發 429 → SDK 動態降採樣。如果 SDK 的動態降採樣邏輯本身也有 bug（降到 0.1 後不再降），collector 仍然會持續承壓。</p>
<p><strong>防護層級</strong>：</p>
<p>SDK 端 — 事件產生速率上限。SDK 內部維護每秒事件計數器，超過閾值（例如 100 events/sec）後的事件直接丟棄，不進 buffer。這個上限獨立於取樣和背壓機制，是防止 SDK 自身 bug 的最後一道防線。</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">// SDK 端的 rate limiter（偽碼，各語言實作不同）
</span></span><span class="line"><span class="ln">2</span><span class="cl">count = atomicIncrement(eventCounter)
</span></span><span class="line"><span class="ln">3</span><span class="cl">if count &gt; maxEventsPerSecond:
</span></span><span class="line"><span class="ln">4</span><span class="cl">    atomicIncrement(droppedCounter)
</span></span><span class="line"><span class="ln">5</span><span class="cl">    return  // 不進 buffer</span></span></code></pre></div><p>Collector 端 — per-key rate limit。每個 API key（或 source.app）的請求速率獨立限制。一個失控的 SDK 被限速時，其他 SDK 的事件不受影響。這和 <a href="/blog/monitoring/04-collector/ingestion-scaling/" data-link-title="Ingestion Scaling" data-link-desc="四層防線應對 ingestion 端的流量擴展 — SDK 取樣、Collector 背壓、水平擴展、Queue 解耦">Ingestion Scaling</a> 的 per-SDK rate limiting 是同一個機制。</p>
<p>Collector 端 — circuit breaker。如果某個 API key 的 429 回應次數在短時間內超過閾值，collector 暫時拒絕該 key 的所有請求（回 503），不再逐筆檢查 rate limit。冷卻期過後自動恢復。這降低了 rate limit 本身的 CPU 開銷 — 高頻 429 回應也有成本。閾值需高於正常 burst 的 per-key 429 頻率 — 如果正常 flush 在 burst 時每分鐘最多觸發 N 次 429，circuit breaker 閾值設為 5N-10N 避免誤觸。具體數字（例如 50 次/分鐘、5 分鐘冷卻）依部署規模調整。</p>
<h3 id="部署推送補發風暴">部署推送：補發風暴</h3>
<p>100 台機器同時重啟（rolling deploy），每台機器的 SDK 在啟動時：</p>
<ol>
<li>讀取本地 persistence 中的離線事件</li>
<li>初始化後立即 flush 離線事件 + 新的 lifecycle 事件</li>
</ol>
<p>100 個 SDK 在幾秒內同時發起離線補發 + 正常 flush，collector 瞬間承受 100 倍的正常流量。</p>
<p><strong>防護方式</strong>：init jitter — SDK 初始化後不立即 flush，而是等待一個隨機延遲（0 到 flush_interval 之間的均勻分佈）。100 個 SDK 的首次 flush 分散在 0-30 秒內，流量從一個尖峰變成斜坡。</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="ln">1</span><span class="cl"><span class="kn">import</span> <span class="nn">random</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">initial_delay</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">uniform</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">flush_interval_seconds</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="c1"># 第一次 flush 延遲 initial_delay 秒，後續按正常 interval</span></span></span></code></pre></div><p>離線補發也加 jitter — 每批補發之間的間隔從固定的 1 秒改為 1-3 秒的隨機值。100 個 SDK 的補發批次在時間軸上交錯，避免所有 SDK 以相同節奏同時送出。</p>
<h3 id="使用者行為高峰同時在線暴增">使用者行為高峰：同時在線暴增</h3>
<p>行銷活動、媒體報導、季節性高峰 — 同時在線使用者從 100 人暴增到 10,000 人。每個使用者的 SDK 正常運作，但總量超出 collector 的處理能力。</p>
<p>這個場景和 SDK bug 的差異：每個 SDK 的行為完全正常，問題在總量。Per-key rate limit 不會觸發（每個 SDK 的速率在正常範圍），需要的是全域流量控制。</p>
<p><strong>防護方式</strong>：Collector 端的全域 channel 背壓（<a href="/blog/monitoring/04-collector/ingestion-scaling/" data-link-title="Ingestion Scaling" data-link-desc="四層防線應對 ingestion 端的流量擴展 — SDK 取樣、Collector 背壓、水平擴展、Queue 解耦">Ingestion Scaling 第二層</a>）是第一道防線 — channel 滿時所有 SDK 收到 429，各自動態降採樣。如果動態降採樣後流量仍然過大，水平擴展（多 collector + load balancer）或 queue 解耦是解法。</p>
<p>行銷活動的可預測性是優勢 — 活動日期已知，可以提前擴展 collector 容量（加機器或調高 channel 容量）。突發的媒體報導則依賴動態降採樣和背壓的自動調節。</p>
<h3 id="三種場景的防護對照">三種場景的防護對照</h3>
<table>
  <thead>
      <tr>
          <th>場景</th>
          <th>流量特徵</th>
          <th>首要防護</th>
          <th>次要防護</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>SDK bug</td>
          <td>單 SDK 異常高頻</td>
          <td>SDK 端 rate limit + per-key limit</td>
          <td>Circuit breaker</td>
      </tr>
      <tr>
          <td>部署推送</td>
          <td>多 SDK 同時突發</td>
          <td>Init jitter + 補發 jitter</td>
          <td>Channel 背壓</td>
      </tr>
      <tr>
          <td>使用者暴增</td>
          <td>全域持續高量</td>
          <td>動態降採樣 + channel 背壓</td>
          <td>水平擴展 / queue 解耦</td>
      </tr>
  </tbody>
</table>
<h2 id="資料恢復-vs-接受損失">資料恢復 vs 接受損失</h2>
<p>每個損失點都可以投入工程努力降低損失量。問題是恢復的工程成本是否值得 — 監控資料不是交易紀錄，恢復的價值取決於損失的事件類型和數量。</p>
<h3 id="值得恢復的場景">值得恢復的場景</h3>
<p><strong>Error 事件</strong>：每筆 error 都可能對應一個需要修的 bug。Error 的損失代表 bug 可能更晚被發現、在更多使用者身上發生後才被注意到。值得投入本地 persistence、優先級丟棄（error 最後丟）、error 快通道等機制降低損失。</p>
<p><strong>Lifecycle 事件</strong>：session 邊界（session.begin / session.end）是 cohort 分析和 session replay 的基礎。丟失 session 邊界會讓整個 session 的事件無法正確歸屬。Lifecycle 事件量低（每 session 幾筆），保留成本小、損失影響大。</p>
<h3 id="接受損失的場景">接受損失的場景</h3>
<p><strong>高頻 metric 事件</strong>：render.frame_time 每秒 60 筆，丟幾筆對趨勢分析的影響在統計誤差範圍內。聚合前移（SDK 端每 5 秒送一筆 summary）比逐筆保留更有效率。</p>
<p><strong>行為 event 事件</strong>：button.click、page.view 在取樣後丟幾筆，funnel 的轉換率計算用取樣校正（除以取樣率）仍然有效。單筆行為事件的 debug 價值低 — 知道某使用者點了某按鈕通常不影響決策。</p>
<p><strong>超過保留期的原始事件</strong>：purge 後只剩聚合摘要。如果分析需求發現需要更長的原始事件保留期，調整 retention config，不要嘗試從聚合摘要「恢復」原始事件 — 那是不可能的。</p>
<h3 id="恢復成本的判斷">恢復成本的判斷</h3>
<p>本地 persistence（SDK 端把 buffer 寫到檔案系統）的實作成本和收益：</p>
<table>
  <thead>
      <tr>
          <th>因素</th>
          <th>記憶體 FIFO（簡單）</th>
          <th>本地 persistence（完整）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>實作成本</td>
          <td>array + 容量檢查</td>
          <td>檔案讀寫 + 並發安全 + 容量管理 + 去重</td>
      </tr>
      <tr>
          <td>保護範圍</td>
          <td>短暫離線（buffer 容量內）</td>
          <td>長時間離線（本地儲存容量內）</td>
      </tr>
      <tr>
          <td>不保護</td>
          <td>app 強制終止</td>
          <td>app 強制終止（寫入中的事件仍然遺失）</td>
      </tr>
      <tr>
          <td>適用場景</td>
          <td>自用工具、SDK 初期版本</td>
          <td>行動 app、離線場景頻繁的使用環境</td>
      </tr>
  </tbody>
</table>
<p>MVP 階段用記憶體 FIFO。本地 persistence 作為第二階段功能，在離線損失率超出可接受範圍時投入。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>SDK 端的離線保護 → <a href="/blog/monitoring/03-sdk-design/offline-buffer/" data-link-title="離線 buffer 與重試" data-link-desc="網路不可用時的事件保存策略 — FIFO 丟棄、本地 persistence、恢復後補發的取捨">離線 buffer 與重試</a></li>
<li>Collector 端的流量防護 → <a href="/blog/monitoring/04-collector/ingestion-scaling/" data-link-title="Ingestion Scaling" data-link-desc="四層防線應對 ingestion 端的流量擴展 — SDK 取樣、Collector 背壓、水平擴展、Queue 解耦">Ingestion Scaling</a></li>
<li>Collector 的處理鏈路 → <a href="/blog/monitoring/04-collector/architecture/" data-link-title="Collector 架構" data-link-desc="HTTP endpoint → JSON Schema 驗證 → 儲存 → 查詢 → rule engine 的五段式處理鏈路">Collector 架構</a></li>
<li>Container 環境的 graceful shutdown → <a href="/blog/monitoring/04-collector/container-deployment/" data-link-title="Container 部署設計" data-link-desc="Docker 部署 collector 的設計 — SQLite 在 overlay filesystem 的 I/O 考量、volume mount、graceful shutdown、資源限制">Container 部署設計</a></li>
<li>保留策略和降採樣 → <a href="/blog/monitoring/04-collector/scaling-evolution/" data-link-title="規模演進" data-link-desc="可插拔 Storage Backend 架構 — SQLite 預設、PostgreSQL 觸發切換、時間序列 DB 長期演進">規模演進</a></li>
<li>SDK 認證和偽造流量防護 → <a href="/blog/monitoring/07-security-privacy/client-sdk-authentication/" data-link-title="Client-side SDK 認證的根本限制" data-link-desc="嵌在 client 端的 credential 必然可被提取 — 認清 architecture 天花板後的多層緩解策略，從 origin 驗證到 device attestation">Client-side SDK 認證</a></li>
</ul>
]]></content:encoded></item><item><title>6.15 Environment Parity 與漂移控制</title><link>https://tarrragon.github.io/blog/backend/06-reliability/environment-parity/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/environment-parity/</guid><description>&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>Staging 通過但 production 上線失敗 — 這類事故的根因常常是環境差異。Environment parity 把 staging 與 production 的差異視為一級風險，要求會影響行為的差異被識別與管理。&lt;/p>
&lt;p>三個環境完全相同既不可能也不必要，但未被追蹤的差異會讓測試結論與真實服務脫鉤。&lt;/p>
&lt;h2 id="核心判讀">核心判讀&lt;/h2>
&lt;p>Parity 漂移最先暴露的訊號是差異是否可見，接著決定差異是否會改變驗證結果。&lt;/p>
&lt;p>判讀時看四件事：&lt;/p>
&lt;ul>
&lt;li>config drift 是否有清單與責任人&lt;/li>
&lt;li>data shape 是否接近 production&lt;/li>
&lt;li>infra parity 是否涵蓋 network、storage、identity&lt;/li>
&lt;li>release 前是否知道哪些差異會影響判讀&lt;/li>
&lt;/ul>
&lt;h2 id="漂移來源分類">漂移來源分類&lt;/h2>
&lt;p>Parity 漂移按來源分類，不同來源的風險特徵與偵測手段不同。&lt;/p>
&lt;h3 id="config-drift">Config drift&lt;/h3>
&lt;p>環境變數、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/timeout/" data-link-title="Timeout" data-link-desc="說明等待外部操作的時間上限如何保護資源與使用者體驗">timeout&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/connection-pool/" data-link-title="Connection Pool" data-link-desc="說明連線池如何限制下游資源並影響服務容量">connection pool&lt;/a> size、retry config、feature flag 在 staging 與 prod 不同步。這是最常見的漂移來源，因為 config 變更頻率高且通常不走完整 review 流程。&lt;/p>
&lt;p>典型暴露時機：staging 測試通過，但 prod 上線後 timeout 觸發或 pool 耗盡，根因是 staging 的 timeout 設定比 prod 寬鬆。偵測手段：定期 config snapshot diff，標註差異項目與 owner。&lt;/p>
&lt;h3 id="scale-drift">Scale drift&lt;/h3>
&lt;p>Staging 用單機或少量 replica，prod 用多區多 replica。query plan 在小資料集走 index scan、在大資料集走 table scan；&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/connection-pool/" data-link-title="Connection Pool" data-link-desc="說明連線池如何限制下游資源並影響服務容量">connection pool&lt;/a> 在低併發下不飽和、在高併發下排隊；load balancer 在少 replica 時的路由行為跟多 replica 時不同。&lt;/p>
&lt;p>典型暴露時機：壓測在 staging 通過，但 prod 出現 connection pool 耗盡或 load balancer 的 least-connection 策略在高 replica 數下行為不同。偵測手段：對照 staging 與 prod 的 replica count、resource quota、auto-scaling 設定。&lt;/p>
&lt;h3 id="data-drift">Data drift&lt;/h3>
&lt;p>Staging 資料量遠小於 prod，資料分佈也不同。index scan vs table scan 的切換點跟資料量直接相關；cache hit ratio 跟 key 分佈與資料量相關；pagination 行為在千筆與百萬筆資料下差異顯著。&lt;/p>
&lt;p>典型暴露時機：staging 的查詢 &amp;lt; 50ms，prod 同一查詢 &amp;gt; 2s，根因是 staging 資料量不足以觸發 full table scan。偵測手段：比較 staging 與 prod 的資料表 row count 與 key 分佈統計。&lt;/p>
&lt;h3 id="dependency-drift">Dependency drift&lt;/h3>
&lt;p>Staging 跟 prod 使用不同版本的 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/database/" data-link-title="Database" data-link-desc="說明 database 在後端系統中如何承擔正式狀態、查詢與一致性責任">database&lt;/a> engine、cache、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/broker/" data-link-title="Broker" data-link-desc="說明 broker 在訊息傳遞系統中負責保存、路由與交付訊息">broker&lt;/a> 或 cloud service。版本差異的行為差異通常在 edge case 才暴露：不同版本的 SQL dialect、cache eviction policy、message ordering guarantee 可能不同。&lt;/p>
&lt;p>典型暴露時機：DB engine 小版本升級改變了 query optimizer 行為，staging 早已升級但 prod 延遲升級，兩邊 query plan 不同。偵測手段：維護 dependency version matrix，每次版本變更時檢查跨環境一致性。&lt;/p></description><content:encoded><![CDATA[<h2 id="概念定位">概念定位</h2>
<p>Staging 通過但 production 上線失敗 — 這類事故的根因常常是環境差異。Environment parity 把 staging 與 production 的差異視為一級風險，要求會影響行為的差異被識別與管理。</p>
<p>三個環境完全相同既不可能也不必要，但未被追蹤的差異會讓測試結論與真實服務脫鉤。</p>
<h2 id="核心判讀">核心判讀</h2>
<p>Parity 漂移最先暴露的訊號是差異是否可見，接著決定差異是否會改變驗證結果。</p>
<p>判讀時看四件事：</p>
<ul>
<li>config drift 是否有清單與責任人</li>
<li>data shape 是否接近 production</li>
<li>infra parity 是否涵蓋 network、storage、identity</li>
<li>release 前是否知道哪些差異會影響判讀</li>
</ul>
<h2 id="漂移來源分類">漂移來源分類</h2>
<p>Parity 漂移按來源分類，不同來源的風險特徵與偵測手段不同。</p>
<h3 id="config-drift">Config drift</h3>
<p>環境變數、<a href="/blog/backend/knowledge-cards/timeout/" data-link-title="Timeout" data-link-desc="說明等待外部操作的時間上限如何保護資源與使用者體驗">timeout</a>、<a href="/blog/backend/knowledge-cards/connection-pool/" data-link-title="Connection Pool" data-link-desc="說明連線池如何限制下游資源並影響服務容量">connection pool</a> size、retry config、feature flag 在 staging 與 prod 不同步。這是最常見的漂移來源，因為 config 變更頻率高且通常不走完整 review 流程。</p>
<p>典型暴露時機：staging 測試通過，但 prod 上線後 timeout 觸發或 pool 耗盡，根因是 staging 的 timeout 設定比 prod 寬鬆。偵測手段：定期 config snapshot diff，標註差異項目與 owner。</p>
<h3 id="scale-drift">Scale drift</h3>
<p>Staging 用單機或少量 replica，prod 用多區多 replica。query plan 在小資料集走 index scan、在大資料集走 table scan；<a href="/blog/backend/knowledge-cards/connection-pool/" data-link-title="Connection Pool" data-link-desc="說明連線池如何限制下游資源並影響服務容量">connection pool</a> 在低併發下不飽和、在高併發下排隊；load balancer 在少 replica 時的路由行為跟多 replica 時不同。</p>
<p>典型暴露時機：壓測在 staging 通過，但 prod 出現 connection pool 耗盡或 load balancer 的 least-connection 策略在高 replica 數下行為不同。偵測手段：對照 staging 與 prod 的 replica count、resource quota、auto-scaling 設定。</p>
<h3 id="data-drift">Data drift</h3>
<p>Staging 資料量遠小於 prod，資料分佈也不同。index scan vs table scan 的切換點跟資料量直接相關；cache hit ratio 跟 key 分佈與資料量相關；pagination 行為在千筆與百萬筆資料下差異顯著。</p>
<p>典型暴露時機：staging 的查詢 &lt; 50ms，prod 同一查詢 &gt; 2s，根因是 staging 資料量不足以觸發 full table scan。偵測手段：比較 staging 與 prod 的資料表 row count 與 key 分佈統計。</p>
<h3 id="dependency-drift">Dependency drift</h3>
<p>Staging 跟 prod 使用不同版本的 <a href="/blog/backend/knowledge-cards/database/" data-link-title="Database" data-link-desc="說明 database 在後端系統中如何承擔正式狀態、查詢與一致性責任">database</a> engine、cache、<a href="/blog/backend/knowledge-cards/broker/" data-link-title="Broker" data-link-desc="說明 broker 在訊息傳遞系統中負責保存、路由與交付訊息">broker</a> 或 cloud service。版本差異的行為差異通常在 edge case 才暴露：不同版本的 SQL dialect、cache eviction policy、message ordering guarantee 可能不同。</p>
<p>典型暴露時機：DB engine 小版本升級改變了 query optimizer 行為，staging 早已升級但 prod 延遲升級，兩邊 query plan 不同。偵測手段：維護 dependency version matrix，每次版本變更時檢查跨環境一致性。</p>
<h3 id="infra-drift">Infra drift</h3>
<p>Network topology、DNS 解析路徑、TLS 配置、identity provider 設定在不同環境不同。跨服務通訊路徑的差異最難偵測，因為這些差異通常在正常流量下不可見，只在跨區切換、failover 或 mTLS 驗證時才暴露。</p>
<p>典型暴露時機：staging 用同區呼叫、prod 跨區呼叫，latency 差異導致 timeout 觸發條件不同。偵測手段：infra-as-code diff 與定期 topology audit。</p>
<h2 id="漂移偵測機制">漂移偵測機制</h2>
<p>偵測環境漂移需要多種手段組合，單一手段無法覆蓋所有漂移來源。</p>
<h3 id="automated-config-diff">Automated config diff</h3>
<p>定期比較 staging 與 prod 的 config snapshot，輸出差異清單並標註 owner。diff 結果按風險等級分類：會影響行為的差異（timeout、pool size、retry policy）標為高風險；只影響標籤或描述的差異標為低風險。高風險差異在 release review 時必須被討論。</p>
<h3 id="contract--parity-test">Contract + parity test</h3>
<p><a href="/blog/backend/06-reliability/contract-testing/" data-link-title="6.10 Contract Testing 與 Schema 演進" data-link-desc="把跨服務 / API / event schema 的隱性期待變成可驗證契約，控制演進相容性">Contract test</a> 驗證 API 邊界（schema、欄位、狀態碼）在不同環境的一致性。Parity test 更進一步，驗證同一請求在 staging 與 prod 的行為結果是否相同。兩者互補：contract test 抓結構差異，parity test 抓行為差異。</p>
<h3 id="shadow-traffic">Shadow traffic</h3>
<p>用 prod 流量的副本打 staging，比較回應差異。shadow traffic 能偵測 data drift 和 dependency drift，因為它用真實請求觸發真實查詢路徑。限制是寫入操作需要隔離處理（shadow write 不能影響 prod 資料），且 staging 需要有足夠容量承接 prod 流量副本。跟 <a href="/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2 load testing</a> 的 synthetic traffic 限制互補 — synthetic traffic 偵測不到的環境差異，shadow traffic 通常能暴露。</p>
<h3 id="canary-作為中間層">Canary 作為中間層</h3>
<p>Canary 環境處於 staging 與 prod 之間，用少量真實流量驗證變更。parity 差異在 canary 階段暴露的成本遠低於在 prod 全量暴露。canary 的偵測價值在於它跑在 prod infra 上但只承接部分流量，能暴露 scale drift 和 infra drift。</p>
<p>canary 的限制是覆蓋時間：流量比例低時，low-frequency 的 edge case 可能在 canary 期間不出現。canary 時間越長覆蓋率越高，但拉長 canary 會延遲交付。這個 trade-off 要對齊變更風險等級 — 高風險變更拉長 canary，低風險變更可以縮短。</p>
<h2 id="production-like-data-策略">Production-like data 策略</h2>
<p>Staging 需要接近 prod 的資料才能讓驗證結果可信。三種策略各有 trade-off。</p>
<table>
  <thead>
      <tr>
          <th>策略</th>
          <th>真實度</th>
          <th>隱私風險</th>
          <th>維護成本</th>
          <th>適用場景</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Production sample（脫敏）</td>
          <td>高</td>
          <td>中</td>
          <td>高</td>
          <td>query plan 敏感、資料分佈關鍵</td>
      </tr>
      <tr>
          <td>Synthetic generation</td>
          <td>中</td>
          <td>低</td>
          <td>中</td>
          <td>功能驗證為主、分佈次要</td>
      </tr>
      <tr>
          <td>Schema-only + seed</td>
          <td>低</td>
          <td>低</td>
          <td>低</td>
          <td>早期開發、schema 驗證</td>
      </tr>
  </tbody>
</table>
<p>Production sample 從 prod 抽樣後做 PII masking，資料分佈最接近真實，但需要遮罩管線且每次 schema 變更後要重新抽樣。Synthetic generation 用程式生成接近 prod 分佈的假資料，安全性高但分佈模型需要維護，偏移累積後資料特徵會跟 prod 脫鉤。Schema-only + seed 只複製 schema、用 seed 填少量資料，速度最快但跟 prod 差距最大，query plan 幾乎無法對齊。</p>
<p>選擇策略的判斷條件：如果系統的風險集中在 query performance 或 data-dependent 行為，production sample 是必要的；如果風險集中在功能正確性，synthetic generation 足夠；如果還在早期開發階段，schema-only + seed 可以先用，但上線前要升級。詳見 <a href="/blog/backend/06-reliability/test-data-management/" data-link-title="6.16 Test Data Management" data-link-desc="把 fixture / seed / production-like data 作為跨模組共用 artifact，治理資料層次、遮罩策略與可重現性">6.16 test data management</a>。</p>
<h2 id="parity-治理流程">Parity 治理流程</h2>
<p>環境漂移是持續的，一次對齊不代表之後不會漂移。治理流程的責任是讓漂移保持可見且可決策。</p>
<p><strong>維護環境差異清單</strong>：記錄所有已知的環境差異，每項標注 owner、風險等級與存在理由。有些差異是刻意的（staging 用較小 instance 節省成本），有些是遺忘的（某次 prod hotfix 沒同步到 staging）。區分刻意與遺忘的差異，才能知道哪些差異需要修復、哪些需要在判讀時考慮。</p>
<p><strong>Release 前 review 差異清單</strong>：每次 release 前把差異清單跟變更內容交叉比對。如果本次變更涉及 connection pool 設定，但 staging 的 pool size 跟 prod 不同，這個差異就會影響驗證結論，必須在放行時被標記。連到 <a href="/blog/backend/06-reliability/reliability-readiness-review/" data-link-title="6.19 Reliability Readiness Review" data-link-desc="把上線前、重大變更前與高風險操作前的可靠性準備度變成可檢查門檻">6.19 reliability readiness review</a> 的 pre-release checklist。</p>
<p><strong>Infra 變更同步</strong>：新增 infra 變更時，同步更新 staging 或在差異清單中標記新增風險。infra-as-code 讓同步變得可自動化，但仍需要 review 確認 staging 的資源配額是否需要調整。</p>
<h2 id="案例對照">案例對照</h2>
<ul>
<li><a href="/blog/backend/08-incident-response/cases/heroku/" data-link-title="Heroku" data-link-desc="Heroku PaaS 事故與 router 層架構脈絡">Heroku</a>：平台抽象越高，環境行為差異越不可見，漂移偵測需要更主動的手段。</li>
<li><a href="/blog/backend/08-incident-response/cases/gcp/" data-link-title="Google Cloud Platform" data-link-desc="GCP 重大事故時間線與架構脈絡">GCP</a>：區域、網路與權限設定差異會直接影響驗證結論，infra drift 在跨區場景最先暴露。</li>
<li><a href="/blog/backend/08-incident-response/cases/github/" data-link-title="GitHub" data-link-desc="GitHub 重大事故時間線與架構脈絡">GitHub</a>：大規模部署時，環境差異通常先變成事故放大器，漂移控制是降低放大倍數的前置工作。</li>
</ul>
<h2 id="判讀訊號">判讀訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀條件</th>
          <th>行動建議</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>staging 通過、prod 上線失敗，根因是 config / scale 差異</td>
          <td>parity 差異未被 release review 識別</td>
          <td>把失敗根因加入環境差異清單 + release checklist</td>
      </tr>
      <tr>
          <td>staging 跟 prod 用不同 DB engine 版本 / cache 配置</td>
          <td>dependency drift 未被 version matrix 追蹤</td>
          <td>建 dependency version matrix、定期 diff</td>
      </tr>
      <tr>
          <td>shadow traffic 從未啟用、staging 流量靠手動測試</td>
          <td>data drift 和 dependency drift 沒有持續偵測機制</td>
          <td>啟用 shadow traffic 或 canary 驗證</td>
      </tr>
      <tr>
          <td>prod-only bug 反覆出現、staging 無法重現</td>
          <td>環境差異是 bug 的根因，差異清單可能遺漏關鍵項目</td>
          <td>回查差異清單、補漏項 + owner</td>
      </tr>
      <tr>
          <td>環境差異無 owner、漂移無 review</td>
          <td>parity 治理流程不存在或已停止運作</td>
          <td>指定 parity owner、加入 release review 流程</td>
      </tr>
  </tbody>
</table>
<h2 id="交接路由">交接路由</h2>
<ul>
<li><a href="/blog/backend/05-deployment-platform/" data-link-title="模組五：部署平台與網路入口" data-link-desc="整理 Kubernetes、systemd、load balancer、container 與服務生命週期合約">05 部署平台</a>：環境拓撲一致性與 canary 機制</li>
<li><a href="/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2 load testing</a>：staging 壓測結果的可信度受 parity 影響</li>
<li><a href="/blog/backend/06-reliability/contract-testing/" data-link-title="6.10 Contract Testing 與 Schema 演進" data-link-desc="把跨服務 / API / event schema 的隱性期待變成可驗證契約，控制演進相容性">6.10 contract testing</a>：契約覆蓋環境邊界</li>
<li><a href="/blog/backend/06-reliability/test-data-management/" data-link-title="6.16 Test Data Management" data-link-desc="把 fixture / seed / production-like data 作為跨模組共用 artifact，治理資料層次、遮罩策略與可重現性">6.16 test data management</a>：production-like data 來源與策略</li>
<li><a href="/blog/backend/06-reliability/reliability-readiness-review/" data-link-title="6.19 Reliability Readiness Review" data-link-desc="把上線前、重大變更前與高風險操作前的可靠性準備度變成可檢查門檻">6.19 reliability readiness review</a>：release 前的 parity review</li>
<li><a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 experiment safety boundary</a>：staging vs production 測試的安全邊界</li>
<li><a href="/blog/backend/08-incident-response/post-incident-review/" data-link-title="8.5 復盤與改進追蹤" data-link-desc="把 RCA 與 action items 轉成可驗證閉環">8.5 post-incident review</a>：parity 漂移作為事故根因類別</li>
</ul>
]]></content:encoded></item><item><title>6.16 Test Data Management</title><link>https://tarrragon.github.io/blog/backend/06-reliability/test-data-management/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/test-data-management/</guid><description>&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>測試常常失敗在資料而非邏輯 — fixture 過期、seed 跟 schema 漂移、staging 資料分佈跟 production 差太遠。Test data management 把 fixture、seed 與 production-like data 當成共用資產來治理，讓測試建立在可控且可重播的資料基礎上。&lt;/p>
&lt;h2 id="核心判讀">核心判讀&lt;/h2>
&lt;p>Test data 的健康度先看資料是否足夠代表真實情境，再看資料是否能安全重建與清理。&lt;/p>
&lt;p>關鍵判準：&lt;/p>
&lt;ul>
&lt;li>fixture 是否覆蓋關鍵情境，而不是只有 happy path&lt;/li>
&lt;li>seed 是否可版本化與重播&lt;/li>
&lt;li>production-like data 是否完成去識別化與權限隔離&lt;/li>
&lt;li>data lifecycle 是否和 CI / migration / contract testing 互相對齊&lt;/li>
&lt;/ul>
&lt;h2 id="資料層次">資料層次&lt;/h2>
&lt;p>測試資料按用途分四層，每層的責任、治理成本與真實度不同。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>層次&lt;/th>
 &lt;th>生命週期&lt;/th>
 &lt;th>真實度&lt;/th>
 &lt;th>治理成本&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Unit fixture&lt;/td>
 &lt;td>跟 test case 綁定&lt;/td>
 &lt;td>低&lt;/td>
 &lt;td>低&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Integration seed&lt;/td>
 &lt;td>跟 test suite 綁定&lt;/td>
 &lt;td>中&lt;/td>
 &lt;td>中&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Staging dataset&lt;/td>
 &lt;td>長期存在於環境中&lt;/td>
 &lt;td>中高&lt;/td>
 &lt;td>高&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Production sample&lt;/td>
 &lt;td>定期從 prod 抽樣&lt;/td>
 &lt;td>高&lt;/td>
 &lt;td>最高&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Unit fixture&lt;/strong> 是硬編碼或 factory-generated 的資料，不碰外部系統。fixture 的責任是提供可控的輸入與預期輸出，讓 unit test 驗證邏輯正確性。fixture 覆蓋 happy path 與 edge case，但不反映 production 資料分佈 — 這是設計取捨，因為分佈驗證的責任在更高層次。&lt;/p>
&lt;p>&lt;strong>Integration seed&lt;/strong> 寫進真實 DB / &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/broker/" data-link-title="Broker" data-link-desc="說明 broker 在訊息傳遞系統中負責保存、路由與交付訊息">broker&lt;/a> / cache，生命週期跟 test suite 綁定（setup 建立、teardown 清理）。seed 需要版本化，跟 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/schema-migration/" data-link-title="Schema Migration" data-link-desc="說明資料庫結構如何隨應用程式版本安全演進">schema migration&lt;/a> 對齊 — 見下方「可重現性與版本化」段。seed 品質的判準是：它是否能讓 integration test 驗證跨服務邊界的行為，而不是只驗證資料是否存在。&lt;/p>
&lt;p>&lt;strong>Staging dataset&lt;/strong> 長期存在於 staging 環境，模擬 production 規模與分佈。這一層的挑戰是漂移：production 的資料結構、量體與分佈持續變化，staging dataset 需要定期更新才能維持代表性。更新頻率跟 schema 變更頻率對齊 — 每次重大 schema 變更後，staging dataset 應同步重建。&lt;/p>
&lt;p>&lt;strong>Production sample（脫敏）&lt;/strong> 從 production 抽樣加 PII masking，是真實度最高的選項。它的價值在於保留真實資料的分佈、關聯與邊界條件 — 這些是 synthetic data 很難完整模擬的。代價是隱私風險與合規成本，需要遮罩管線、存取控制與定期稽核。連到 &lt;a href="https://tarrragon.github.io/blog/backend/07-security-data-protection/" data-link-title="模組七：資安與資料保護" data-link-desc="以問題驅動方式擴充資安知識網：先定義服務環節問題，再以案例作為觸發式參考">07 資料保護&lt;/a>。&lt;/p>
&lt;h2 id="遮罩與合成策略">遮罩與合成策略&lt;/h2>
&lt;p>當測試需要接近 production 的資料，PII 處理策略決定了安全性與真實度的平衡。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>策略&lt;/th>
 &lt;th>原理&lt;/th>
 &lt;th>適用場景&lt;/th>
 &lt;th>限制&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Tokenization&lt;/td>
 &lt;td>PII 替換成無意義 token、保留格式&lt;/td>
 &lt;td>需要 referential integrity&lt;/td>
 &lt;td>token mapping 本身需要安全儲存&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Format-preserving encryption&lt;/td>
 &lt;td>保留原始格式但值不可逆&lt;/td>
 &lt;td>需要格式驗證（信用卡位數）&lt;/td>
 &lt;td>加密強度受格式限制&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Synthetic generation&lt;/td>
 &lt;td>用規則或統計模型生成假資料&lt;/td>
 &lt;td>無 PII 風險、合規最簡單&lt;/td>
 &lt;td>資料分佈可能偏移&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Tokenization 適合需要跨表關聯的場景：同一個 user ID 在 order、payment、session 表中需要一致替換，referential integrity 才不會被破壞。format-preserving encryption 適合需要通過格式驗證的場景（信用卡號通過 Luhn check）。synthetic generation 最安全，但資料分佈偏移會讓某些測試結論失真 — &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/pinterest/cache-reliability-and-capacity-surprises/" data-link-title="Pinterest：快取可靠性與容量驚奇治理" data-link-desc="針對快取層失效與流量突增，建立容量緩衝、退化路徑與重建節奏。">Pinterest 的快取可靠性案例&lt;/a>說明資料分佈差異會改變 cache 命中率，進而改變瓶頸位置。&lt;/p></description><content:encoded><![CDATA[<h2 id="概念定位">概念定位</h2>
<p>測試常常失敗在資料而非邏輯 — fixture 過期、seed 跟 schema 漂移、staging 資料分佈跟 production 差太遠。Test data management 把 fixture、seed 與 production-like data 當成共用資產來治理，讓測試建立在可控且可重播的資料基礎上。</p>
<h2 id="核心判讀">核心判讀</h2>
<p>Test data 的健康度先看資料是否足夠代表真實情境，再看資料是否能安全重建與清理。</p>
<p>關鍵判準：</p>
<ul>
<li>fixture 是否覆蓋關鍵情境，而不是只有 happy path</li>
<li>seed 是否可版本化與重播</li>
<li>production-like data 是否完成去識別化與權限隔離</li>
<li>data lifecycle 是否和 CI / migration / contract testing 互相對齊</li>
</ul>
<h2 id="資料層次">資料層次</h2>
<p>測試資料按用途分四層，每層的責任、治理成本與真實度不同。</p>
<table>
  <thead>
      <tr>
          <th>層次</th>
          <th>生命週期</th>
          <th>真實度</th>
          <th>治理成本</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Unit fixture</td>
          <td>跟 test case 綁定</td>
          <td>低</td>
          <td>低</td>
      </tr>
      <tr>
          <td>Integration seed</td>
          <td>跟 test suite 綁定</td>
          <td>中</td>
          <td>中</td>
      </tr>
      <tr>
          <td>Staging dataset</td>
          <td>長期存在於環境中</td>
          <td>中高</td>
          <td>高</td>
      </tr>
      <tr>
          <td>Production sample</td>
          <td>定期從 prod 抽樣</td>
          <td>高</td>
          <td>最高</td>
      </tr>
  </tbody>
</table>
<p><strong>Unit fixture</strong> 是硬編碼或 factory-generated 的資料，不碰外部系統。fixture 的責任是提供可控的輸入與預期輸出，讓 unit test 驗證邏輯正確性。fixture 覆蓋 happy path 與 edge case，但不反映 production 資料分佈 — 這是設計取捨，因為分佈驗證的責任在更高層次。</p>
<p><strong>Integration seed</strong> 寫進真實 DB / <a href="/blog/backend/knowledge-cards/broker/" data-link-title="Broker" data-link-desc="說明 broker 在訊息傳遞系統中負責保存、路由與交付訊息">broker</a> / cache，生命週期跟 test suite 綁定（setup 建立、teardown 清理）。seed 需要版本化，跟 <a href="/blog/backend/knowledge-cards/schema-migration/" data-link-title="Schema Migration" data-link-desc="說明資料庫結構如何隨應用程式版本安全演進">schema migration</a> 對齊 — 見下方「可重現性與版本化」段。seed 品質的判準是：它是否能讓 integration test 驗證跨服務邊界的行為，而不是只驗證資料是否存在。</p>
<p><strong>Staging dataset</strong> 長期存在於 staging 環境，模擬 production 規模與分佈。這一層的挑戰是漂移：production 的資料結構、量體與分佈持續變化，staging dataset 需要定期更新才能維持代表性。更新頻率跟 schema 變更頻率對齊 — 每次重大 schema 變更後，staging dataset 應同步重建。</p>
<p><strong>Production sample（脫敏）</strong> 從 production 抽樣加 PII masking，是真實度最高的選項。它的價值在於保留真實資料的分佈、關聯與邊界條件 — 這些是 synthetic data 很難完整模擬的。代價是隱私風險與合規成本，需要遮罩管線、存取控制與定期稽核。連到 <a href="/blog/backend/07-security-data-protection/" data-link-title="模組七：資安與資料保護" data-link-desc="以問題驅動方式擴充資安知識網：先定義服務環節問題，再以案例作為觸發式參考">07 資料保護</a>。</p>
<h2 id="遮罩與合成策略">遮罩與合成策略</h2>
<p>當測試需要接近 production 的資料，PII 處理策略決定了安全性與真實度的平衡。</p>
<table>
  <thead>
      <tr>
          <th>策略</th>
          <th>原理</th>
          <th>適用場景</th>
          <th>限制</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Tokenization</td>
          <td>PII 替換成無意義 token、保留格式</td>
          <td>需要 referential integrity</td>
          <td>token mapping 本身需要安全儲存</td>
      </tr>
      <tr>
          <td>Format-preserving encryption</td>
          <td>保留原始格式但值不可逆</td>
          <td>需要格式驗證（信用卡位數）</td>
          <td>加密強度受格式限制</td>
      </tr>
      <tr>
          <td>Synthetic generation</td>
          <td>用規則或統計模型生成假資料</td>
          <td>無 PII 風險、合規最簡單</td>
          <td>資料分佈可能偏移</td>
      </tr>
  </tbody>
</table>
<p>Tokenization 適合需要跨表關聯的場景：同一個 user ID 在 order、payment、session 表中需要一致替換，referential integrity 才不會被破壞。format-preserving encryption 適合需要通過格式驗證的場景（信用卡號通過 Luhn check）。synthetic generation 最安全，但資料分佈偏移會讓某些測試結論失真 — <a href="/blog/backend/06-reliability/cases/pinterest/cache-reliability-and-capacity-surprises/" data-link-title="Pinterest：快取可靠性與容量驚奇治理" data-link-desc="針對快取層失效與流量突增，建立容量緩衝、退化路徑與重建節奏。">Pinterest 的快取可靠性案例</a>說明資料分佈差異會改變 cache 命中率，進而改變瓶頸位置。</p>
<p>三者的選擇取決於測試需要的真實度與隱私風險。多數團隊會混合使用：unit fixture 用 synthetic、integration seed 用 tokenization、staging dataset 用 production sample + format-preserving encryption。</p>
<h2 id="可重現性與版本化">可重現性與版本化</h2>
<p>Seed 資料需要版本化，跟 schema migration 對齊。當 DB schema 新增欄位或改型別，既有 seed 如果沒同步更新，integration test 會因資料問題失敗而非邏輯問題 — 這類 failure 的除錯成本高，因為錯誤訊息指向 schema 不符，團隊會懷疑是 migration bug 還是 seed bug。</p>
<p><strong>Seed migration</strong> 是把 seed 更新綁進 schema migration workflow 的做法：每次 DB migration 加一份對應的 seed migration。這讓 seed 狀態跟 schema 狀態同步演進，CI 跑 integration test 時永遠拿到匹配的組合。</p>
<p><strong>Fixture factory</strong> 用 factory pattern 生成測試資料，讓新增欄位自動帶 default。factory 的優勢是欄位變更只需改 factory 定義，不需要手動更新每個 fixture file — 這在高頻 schema 變更的服務中可以顯著降低 fixture 維護負擔。</p>
<p><strong>資料清理</strong> 策略決定 integration test 的隔離性。transaction rollback 最乾淨（每個 test case 跑在 transaction 內、結束後 rollback），但不適用於跨 transaction 的流程測試。truncate 較快但需要處理外鍵順序。獨立 DB per suite 隔離最強但成本最高 — 每個 test suite 用自己的 database instance。選擇時對齊 CI 的隔離需求（連到 <a href="/blog/backend/06-reliability/ci-pipeline/" data-link-title="6.1 CI pipeline" data-link-desc="CI pipeline 的分層策略、artifact 管理、flaky 治理與 release gate 輸入">6.1 CI pipeline</a> 的 environment 隔離段）。</p>
<h2 id="fixture-與-contract-testing-的整合">Fixture 與 contract testing 的整合</h2>
<p><a href="/blog/backend/06-reliability/contract-testing/" data-link-title="6.10 Contract Testing 與 Schema 演進" data-link-desc="把跨服務 / API / event schema 的隱性期待變成可驗證契約，控制演進相容性">Contract testing</a> 定義 schema shape，fixture factory 可以用 contract 作為資料生成的來源。當 contract 變更時（新增欄位、型別調整），fixture factory 自動更新生成邏輯，讓 test data 跟 contract 保持同步。</p>
<p>這個整合的價值是把「契約變更是否影響測試資料」從人工 review 變成自動化流程。<a href="/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/" data-link-title="Stripe：Idempotency 與零停機遷移的交易安全設計" data-link-desc="把 API 重試與資料遷移放在同一套安全模型，維持支付交易的一致結果。">Stripe 的交易正確性實踐</a>對此有額外要求：交易路徑的 test data 需要能重播到相同狀態，確保 <a href="/blog/backend/knowledge-cards/idempotency/" data-link-title="Idempotency" data-link-desc="說明同一操作執行多次時如何保持結果一致">idempotency</a> 驗證的資料基礎一致。</p>
<h2 id="案例對照">案例對照</h2>
<ul>
<li><a href="/blog/backend/06-reliability/cases/pinterest/cache-reliability-and-capacity-surprises/" data-link-title="Pinterest：快取可靠性與容量驚奇治理" data-link-desc="針對快取層失效與流量突增，建立容量緩衝、退化路徑與重建節奏。">Pinterest</a>：資料分佈差異改變 cache 命中率與瓶頸位置，staging dataset 若分佈偏離 production，壓測結論會失真。</li>
<li><a href="/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/" data-link-title="Stripe：Idempotency 與零停機遷移的交易安全設計" data-link-desc="把 API 重試與資料遷移放在同一套安全模型，維持支付交易的一致結果。">Stripe</a>：交易資料需要嚴格控制可重播性，fixture 與 seed 要能產出一致的 idempotency 驗證結果。</li>
</ul>
<h2 id="判讀訊號">判讀訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀條件</th>
          <th>行動建議</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>工程師為 debug 把 production data 拷到 local</td>
          <td>PII 暴露風險 — 需要遮罩管線而非直接複製</td>
          <td>建立遮罩 pipeline、禁止直接複製 production DB</td>
      </tr>
      <tr>
          <td>staging DB 含真實用戶 PII</td>
          <td>合規風險 — 需要用 tokenization 或 synthetic 替代</td>
          <td>導入 tokenization 工具或 synthetic generation</td>
      </tr>
      <tr>
          <td>fixture 跟 schema 漂移、測試常壞</td>
          <td>seed migration 未跟 schema migration 對齊</td>
          <td>每次 schema migration 同步更新 seed 版本</td>
      </tr>
      <tr>
          <td>新測試靠拷貼舊 fixture</td>
          <td>缺少 fixture factory — 變更範圍模糊、維護成本累積</td>
          <td>導入 factory pattern 自動帶 default</td>
      </tr>
      <tr>
          <td>production bug 重現不出</td>
          <td>staging dataset 分佈跟 production 差異太大 — 需更新或用 production sample</td>
          <td>定期用脫敏 production sample 更新 staging data</td>
      </tr>
  </tbody>
</table>
<h2 id="交接路由">交接路由</h2>
<ul>
<li><a href="/blog/backend/06-reliability/ci-pipeline/" data-link-title="6.1 CI pipeline" data-link-desc="CI pipeline 的分層策略、artifact 管理、flaky 治理與 release gate 輸入">6.1 CI pipeline</a>：test data 如何進入 fast / slow stage</li>
<li><a href="/blog/backend/06-reliability/contract-testing/" data-link-title="6.10 Contract Testing 與 Schema 演進" data-link-desc="把跨服務 / API / event schema 的隱性期待變成可驗證契約，控制演進相容性">6.10 contract testing</a>：contract 定義 fixture shape</li>
<li><a href="/blog/backend/06-reliability/migration-safety/" data-link-title="6.11 Migration Safety 與 DB Rollout" data-link-desc="把 schema migration 從一次性事件變成可逆、可漸進的 rollout 流程">6.11 migration safety</a>：seed migration 跟 schema migration 對齊</li>
<li><a href="/blog/backend/06-reliability/environment-parity/" data-link-title="6.15 Environment Parity 與漂移控制" data-link-desc="把 staging / preprod / prod 之間的差異視為一級風險，按漂移來源分類偵測與治理">6.15 environment parity</a>：production-like data 是 parity 的一部分</li>
<li><a href="/blog/backend/07-security-data-protection/" data-link-title="模組七：資安與資料保護" data-link-desc="以問題驅動方式擴充資安知識網：先定義服務環節問題，再以案例作為觸發式參考">07 資料保護</a>：PII 遮罩與最小揭露</li>
</ul>
]]></content:encoded></item><item><title>6.17 Feature Flag Governance</title><link>https://tarrragon.github.io/blog/backend/06-reliability/feature-flag-governance/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/feature-flag-governance/</guid><description>&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>Feature flag 在 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">release gate&lt;/a> 之後提供 runtime 層的細粒度控制。Flag governance 把這個控制從單次開關提升為有生命週期的 artifact，涵蓋灰度、實驗與緊急止血的風險管理。&lt;/p>
&lt;p>當 flag 變多，真正的風險是狀態分支不透明、技術債累積與權限混用。&lt;/p>
&lt;h2 id="核心判讀">核心判讀&lt;/h2>
&lt;p>Flag governance 的健康度先看旗標角色是否分離，再看移除與審計是否有固定流程。&lt;/p>
&lt;p>重點訊號包括：&lt;/p>
&lt;ul>
&lt;li>release / experiment / ops / permission 是否分流&lt;/li>
&lt;li>stale flag 是否有回收機制&lt;/li>
&lt;li>progressive rollout 是否有可觀測的 cohort&lt;/li>
&lt;li>flag 變更是否可審計、可追責&lt;/li>
&lt;/ul>
&lt;h2 id="flag-角色分類">Flag 角色分類&lt;/h2>
&lt;p>Flag 按用途分離，不同角色的 lifecycle、權限與治理策略差異顯著。混用會讓審計失真、移除困難、權限控制失效。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>角色&lt;/th>
 &lt;th>責任&lt;/th>
 &lt;th>Lifecycle 預期&lt;/th>
 &lt;th>Owner&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Release flag&lt;/td>
 &lt;td>控制新功能是否對使用者可見&lt;/td>
 &lt;td>天到週&lt;/td>
 &lt;td>功能團隊&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Experiment flag&lt;/td>
 &lt;td>控制 A/B test 流量分配&lt;/td>
 &lt;td>週到月&lt;/td>
 &lt;td>實驗平台團隊&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Ops flag&lt;/td>
 &lt;td>緊急止血、降級、流量限制&lt;/td>
 &lt;td>長期存在&lt;/td>
 &lt;td>SRE / 值班&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Permission flag&lt;/td>
 &lt;td>控制使用者 / 租戶功能存取&lt;/td>
 &lt;td>跟隨權限策略&lt;/td>
 &lt;td>產品 / IAM&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>&lt;strong>Release flag&lt;/strong> 上線後應在固定時限內收斂為 always-on 或移除。它的存在意義是灰度期間的安全網。灰度結束後，flag 的控制作用消失，只剩代碼分支 — 這段分支就是 flag debt 的來源。&lt;/p>
&lt;p>&lt;strong>Experiment flag&lt;/strong> 的 lifecycle 受實驗週期決定。實驗結束後，flag 應收斂為勝出變體的行為並移除。實驗 flag 的特殊風險是依賴統計引擎的流量分配 — 引擎異常時，flag 的行為取決於 fallback 配置。&lt;/p>
&lt;p>&lt;strong>Ops flag&lt;/strong> 是長期存在的 kill switch 與降級控制。它與其他三類 flag 的關鍵差異是觸發頻率低但影響範圍大 — 觸發時通常是事故情境，需要秒級生效與審計紀錄。ops flag 的設計需求見下方「Kill switch 設計」段。&lt;/p>
&lt;p>&lt;strong>Permission flag&lt;/strong> 本質是權限控制，應走 RBAC 或 entitlement 系統。當 permission flag 混入 feature flag 系統，功能存取權會繞過正式權限審核流程 — 修改一個 flag 值就能改變租戶的功能範圍，沒有對應的審計軌跡。判斷標準：如果 flag 的值決定「誰能用什麼功能」，它是 permission，應該從 feature flag 系統遷移到權限系統。&lt;/p>
&lt;h2 id="lifecycle-管理">Lifecycle 管理&lt;/h2>
&lt;p>Flag 的生命週期是 create → rollout → converge → remove。每個階段有明確的輸入與交付物。&lt;/p>
&lt;p>&lt;strong>Create&lt;/strong>：flag 建立時記錄 owner、用途分類（release / experiment / ops）、預計移除日期與關聯 ticket。這些 metadata 是後續治理的基礎 — 沒有 owner 的 flag 在移除階段會變成無人認領的 debt。&lt;/p>
&lt;p>&lt;strong>Rollout&lt;/strong>：progressive rollout 按 percentage、cohort 或 region 逐步放量。每一步有可觀測指標確認行為正常 — error rate、latency、business KPI。rollout 節奏跟 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate&lt;/a> 的放行條件對齊：gate 通過後用 flag 做細粒度控制，flag 異常時 gate 提供回退依據。&lt;/p>
&lt;p>&lt;strong>Converge&lt;/strong>：功能穩定後，flag 設定 100%（always-on）或 0%（移除功能）。此時 flag 已無控制作用，只是代碼中的條件分支。converge 階段是 flag 治理的關鍵轉折 — 很多 flag 停在這裡不再前進，持續佔用代碼路徑。&lt;/p></description><content:encoded><![CDATA[<h2 id="概念定位">概念定位</h2>
<p>Feature flag 在 <a href="/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">release gate</a> 之後提供 runtime 層的細粒度控制。Flag governance 把這個控制從單次開關提升為有生命週期的 artifact，涵蓋灰度、實驗與緊急止血的風險管理。</p>
<p>當 flag 變多，真正的風險是狀態分支不透明、技術債累積與權限混用。</p>
<h2 id="核心判讀">核心判讀</h2>
<p>Flag governance 的健康度先看旗標角色是否分離，再看移除與審計是否有固定流程。</p>
<p>重點訊號包括：</p>
<ul>
<li>release / experiment / ops / permission 是否分流</li>
<li>stale flag 是否有回收機制</li>
<li>progressive rollout 是否有可觀測的 cohort</li>
<li>flag 變更是否可審計、可追責</li>
</ul>
<h2 id="flag-角色分類">Flag 角色分類</h2>
<p>Flag 按用途分離，不同角色的 lifecycle、權限與治理策略差異顯著。混用會讓審計失真、移除困難、權限控制失效。</p>
<table>
  <thead>
      <tr>
          <th>角色</th>
          <th>責任</th>
          <th>Lifecycle 預期</th>
          <th>Owner</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Release flag</td>
          <td>控制新功能是否對使用者可見</td>
          <td>天到週</td>
          <td>功能團隊</td>
      </tr>
      <tr>
          <td>Experiment flag</td>
          <td>控制 A/B test 流量分配</td>
          <td>週到月</td>
          <td>實驗平台團隊</td>
      </tr>
      <tr>
          <td>Ops flag</td>
          <td>緊急止血、降級、流量限制</td>
          <td>長期存在</td>
          <td>SRE / 值班</td>
      </tr>
      <tr>
          <td>Permission flag</td>
          <td>控制使用者 / 租戶功能存取</td>
          <td>跟隨權限策略</td>
          <td>產品 / IAM</td>
      </tr>
  </tbody>
</table>
<p><strong>Release flag</strong> 上線後應在固定時限內收斂為 always-on 或移除。它的存在意義是灰度期間的安全網。灰度結束後，flag 的控制作用消失，只剩代碼分支 — 這段分支就是 flag debt 的來源。</p>
<p><strong>Experiment flag</strong> 的 lifecycle 受實驗週期決定。實驗結束後，flag 應收斂為勝出變體的行為並移除。實驗 flag 的特殊風險是依賴統計引擎的流量分配 — 引擎異常時，flag 的行為取決於 fallback 配置。</p>
<p><strong>Ops flag</strong> 是長期存在的 kill switch 與降級控制。它與其他三類 flag 的關鍵差異是觸發頻率低但影響範圍大 — 觸發時通常是事故情境，需要秒級生效與審計紀錄。ops flag 的設計需求見下方「Kill switch 設計」段。</p>
<p><strong>Permission flag</strong> 本質是權限控制，應走 RBAC 或 entitlement 系統。當 permission flag 混入 feature flag 系統，功能存取權會繞過正式權限審核流程 — 修改一個 flag 值就能改變租戶的功能範圍，沒有對應的審計軌跡。判斷標準：如果 flag 的值決定「誰能用什麼功能」，它是 permission，應該從 feature flag 系統遷移到權限系統。</p>
<h2 id="lifecycle-管理">Lifecycle 管理</h2>
<p>Flag 的生命週期是 create → rollout → converge → remove。每個階段有明確的輸入與交付物。</p>
<p><strong>Create</strong>：flag 建立時記錄 owner、用途分類（release / experiment / ops）、預計移除日期與關聯 ticket。這些 metadata 是後續治理的基礎 — 沒有 owner 的 flag 在移除階段會變成無人認領的 debt。</p>
<p><strong>Rollout</strong>：progressive rollout 按 percentage、cohort 或 region 逐步放量。每一步有可觀測指標確認行為正常 — error rate、latency、business KPI。rollout 節奏跟 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate</a> 的放行條件對齊：gate 通過後用 flag 做細粒度控制，flag 異常時 gate 提供回退依據。</p>
<p><strong>Converge</strong>：功能穩定後，flag 設定 100%（always-on）或 0%（移除功能）。此時 flag 已無控制作用，只是代碼中的條件分支。converge 階段是 flag 治理的關鍵轉折 — 很多 flag 停在這裡不再前進，持續佔用代碼路徑。</p>
<p><strong>Remove</strong>：移除 flag 代碼、清理條件分支、移除 flag 定義。移除動作困難的原因是 flag 可能被多處引用（server / client / config / test），每處都需要確認行為收斂到同一分支。自動化掃描（dead code detection、unused flag audit）能降低手動風險，但最終決策仍需要 flag owner 確認沒有殘留依賴。</p>
<h2 id="flag-debt-治理">Flag debt 治理</h2>
<p>每個未移除的 flag 讓測試需要覆蓋的狀態空間翻倍。10 個 stale flag 代表 1024 種潛在的狀態組合 — 實際測試覆蓋率遠低於這個數字，代碼行為的可預測性持續下降。</p>
<p><strong>TTL policy</strong>：flag 建立時設定預計移除日期。超過 TTL 且沒有活躍修改的 flag 自動標記為 debt，進入清理 backlog。TTL 按角色設定：release flag 兩週到一個月，experiment flag 與實驗週期對齊，ops flag 免 TTL 但需要年度 review。</p>
<p><strong>定期掃描</strong>：每月或每季掃描 stale flag（超過 TTL + 無活躍修改），生成清理 backlog。掃描結果對應到 flag owner，由 owner 決定是移除、延長 TTL 還是升級為 ops flag。無 owner 的 stale flag 是最高風險 — 沒有人能確認移除是否安全。</p>
<p><strong>Flag count dashboard</strong>：追蹤活躍 flag 數量趨勢。flag 數量持續上升是治理失敗的訊號 — 代表建立速度超過移除速度，debt 在累積。dashboard 按角色分類顯示，讓團隊知道 debt 集中在哪一類 flag。</p>
<h2 id="kill-switch-設計">Kill switch 設計</h2>
<p>Ops flag 作為事中止血工具，需要跟一般 feature flag 不同的設計約束。</p>
<p><strong>觸發延遲</strong>：kill switch 需要秒級生效。依賴 redeploy 才能生效的 flag 在事故中無法作為止血工具 — 部署流程本身需要數分鐘到數十分鐘。實作通常靠 flag evaluation service 的即時推送或短間隔 polling，讓 flag 值變更能在秒級傳播到所有 instance。</p>
<p><strong>權限控制</strong>：kill switch 的觸發權限應受控。值班人員與 SRE 有觸發權，一般開發者沒有。觸發記錄包含誰、什麼時間、因什麼原因觸發，接到 <a href="/blog/backend/08-incident-response/containment-recovery-strategy/" data-link-title="8.3 止血、降級與回復策略" data-link-desc="把短期止血與正式回復拆成可執行步驟">8.3 止血策略</a> 的決策 log。</p>
<p><strong>Fallback 行為明確</strong>：每個 kill switch 在觸發後的預期行為應事前定義。「關掉這個 flag 後會怎樣」的答案應寫在 flag 定義中，讓觸發者在壓力下可快速判斷副作用，而不是臨場推理。</p>
<h2 id="experimentation-平台可靠性">Experimentation 平台可靠性</h2>
<p>A/B test 平台本身是 feature flag 的下游消費者。平台的可用性直接影響所有走 experiment flag 的流量分配。</p>
<p>平台掛掉時，flag 的行為取決於 fallback 配置：default-on 會讓所有使用者看到實驗中的變體，default-off 會讓所有使用者回到 control group。兩者的商業影響完全不同，fallback 行為應在每個 experiment flag 建立時明確配置。</p>
<p>experimentation 平台的 SLO 應獨立定義。當平台自身的 <a href="/blog/backend/knowledge-cards/error-budget/" data-link-title="Error Budget" data-link-desc="說明 SLO 允許的失敗額度如何影響發版與可靠性投入">error budget</a> 消耗過快時，影響的是所有進行中的實驗的流量分配正確性。平台故障不只是「實驗暫停」— 如果 fallback 行為配置錯誤，使用者可能被導向尚未驗證的功能路徑。</p>
<h2 id="案例對照">案例對照</h2>
<ul>
<li><a href="/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/" data-link-title="Stripe：Idempotency 與零停機遷移的交易安全設計" data-link-desc="把 API 重試與資料遷移放在同一套安全模型，維持支付交易的一致結果。">Stripe</a>：progressive rollout 用 flag 控制 migration 的流量切換比例，每一步驗證交易正確性後再擴大，flag 的 rollout 節奏跟 migration safety 綁定。</li>
<li><a href="/blog/backend/06-reliability/cases/shopify/bfcm-capacity-and-game-day/" data-link-title="Shopify：BFCM 容量治理與 Game Day 驗證節奏" data-link-desc="把季節性流量峰值轉成年度可靠性流程，透過容量模型、演練與隔離策略提前吸收風險。">Shopify</a>：高峰流量期間 ops flag 用於細粒度降級控制 — 關閉非核心功能釋放容量給 checkout 路徑。flag 的降級策略在 game day 驗證，確認觸發後的行為符合預期。</li>
<li><a href="/blog/backend/06-reliability/cases/stripe/canary-deploy-and-progressive-rollout/" data-link-title="Stripe：Canary Deploy 與 Progressive Rollout 治理" data-link-desc="金流場景如何用交易指標驅動放行節奏：延遲確認、duplicate 偵測與自動回退。">Stripe S2</a>：progressive rollout 用 flag 控制 canary 放量比例，每一步用交易指標判斷是否繼續。flag 的 rollout 節奏跟金流風險的延遲確認窗綁定。</li>
</ul>
<h2 id="判讀訊號">判讀訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀條件</th>
          <th>行動建議</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>程式碼中存在 &gt; 6 個月未切換的 flag</td>
          <td>flag 已停在 converge 階段，應進入移除流程或升級為 ops flag</td>
          <td>啟動 stale flag 掃描 + 移除 sprint</td>
      </tr>
      <tr>
          <td>flag 移除流程靠 grep 跟人工 PR</td>
          <td>缺少自動化掃描，移除成本高導致 debt 累積</td>
          <td>導入 dead code detection 工具自動標記</td>
      </tr>
      <tr>
          <td>flag 實際分支跟預期不一致</td>
          <td>flag 狀態與代碼路徑脫鉤，通常在事故時才被發現</td>
          <td>建 flag 狀態 dashboard 定期對齊</td>
      </tr>
      <tr>
          <td>experimentation 平台掛掉影響所有 A/B 流量</td>
          <td>平台 fallback 行為未配置或未驗證</td>
          <td>配置 default-on/off fallback + 定期演練</td>
      </tr>
      <tr>
          <td>ops flag 跟 release flag 混在同系統、無權限隔離</td>
          <td>止血操作的審計軌跡與一般功能開關無法區分，事後回查困難</td>
          <td>分離 flag 系統或加 RBAC 權限隔離</td>
      </tr>
      <tr>
          <td>活躍 flag 數量每季持續上升</td>
          <td>建立速度超過移除速度，測試覆蓋的狀態空間在膨脹</td>
          <td>設 flag count budget、超額暫停新 flag 建立</td>
      </tr>
  </tbody>
</table>
<h2 id="交接路由">交接路由</h2>
<ul>
<li><a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate</a>：flag 是 gate 通過後的細粒度 rollout 控制</li>
<li><a href="/blog/backend/06-reliability/contract-testing/" data-link-title="6.10 Contract Testing 與 Schema 演進" data-link-desc="把跨服務 / API / event schema 的隱性期待變成可驗證契約，控制演進相容性">6.10 contract testing</a>：flag 不同分支的契約覆蓋</li>
<li><a href="/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13 perf regression gate</a>：flag 切換後的效能驗證</li>
<li><a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 reliability debt backlog</a>：stale flag 進入 debt 治理</li>
<li><a href="/blog/backend/07-security-data-protection/" data-link-title="模組七：資安與資料保護" data-link-desc="以問題驅動方式擴充資安知識網：先定義服務環節問題，再以案例作為觸發式參考">07 資安與資料保護</a>：permission flag 的權限約束</li>
<li><a href="/blog/backend/08-incident-response/containment-recovery-strategy/" data-link-title="8.3 止血、降級與回復策略" data-link-desc="把短期止血與正式回復拆成可執行步驟">8.3 止血策略</a>：ops flag 作為事中止血手段</li>
</ul>
]]></content:encoded></item><item><title>6.18 Reliability Metrics Governance</title><link>https://tarrragon.github.io/blog/backend/06-reliability/reliability-metrics-governance/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/reliability-metrics-governance/</guid><description>&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>Reliability metrics governance 確保團隊量測到的指標能反映真實的可靠性狀態。指標的價值在於引導討論與暴露趨勢，一旦指標被直接當成目標，治理就開始退化。&lt;/p>
&lt;h2 id="核心判讀">核心判讀&lt;/h2>
&lt;p>指標是否對準使用者感受、是否能驅動工程決策 — 這兩個問題決定 metrics governance 的有效性。&lt;/p>
&lt;p>判讀的核心問題：&lt;/p>
&lt;ul>
&lt;li>SLI 是否有明確觀測窗口與採樣邊界&lt;/li>
&lt;li>SLO 是否能轉成 release / alert / incident 決策&lt;/li>
&lt;li>DORA / SPACE / CFR 是否被混用成單一成績單&lt;/li>
&lt;li>metric drift 是否被記錄與校正&lt;/li>
&lt;/ul>
&lt;h2 id="dora-四指標">DORA 四指標&lt;/h2>
&lt;p>DORA 量測的是交付與可靠性流程的效率，四個指標各自回答不同問題。&lt;/p>
&lt;p>&lt;strong>Deploy frequency&lt;/strong> 量測交付節奏 — 團隊多頻繁把變更送到 production。高頻 deploy 通常代表小批次、低風險；但判讀陷阱是拆碎 deploy 只為衝頻率。辨別方式是同時看 deploy size distribution — 若平均 deploy 的變更量持續縮小但 frequency 持續上升，gaming 的可能性高。deploy frequency 要搭配 change failure rate 一起看，頻率高但 CFR 也高代表品質沒跟上。&lt;/p>
&lt;p>&lt;strong>Lead time for changes&lt;/strong> 量測從 commit 到 production 的時間。長 lead time 通常指向 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/ci-pipeline/" data-link-title="6.1 CI pipeline" data-link-desc="CI pipeline 的分層策略、artifact 管理、flaky 治理與 release gate 輸入">CI pipeline&lt;/a> bottleneck、approval queue 或 staging 排隊。判讀陷阱是把 lead time 壓短但跳過驗證步驟 — 縮短的時間可能來自移除 slow path 測試，表面效率提升但風險轉移到 production。改善 lead time 的投資方向先看 CI 分層（6.1）是否合理，再看 review queue 是否成為瓶頸。&lt;/p>
&lt;p>&lt;strong>Change failure rate (CFR)&lt;/strong> 量測 deploy 後需要 rollback 或 hotfix 的比率。CFR 是 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">release gate&lt;/a> 健康度的直接指標 — gate 有效時 CFR 應該維持穩定或下降。判讀陷阱是團隊避免標記 rollback 來壓低 CFR，或把 hotfix 歸類為「正常 deploy」。偵測方式是把 CFR 跟 customer complaint rate 做相關性分析 — 若 CFR 持續下降但客訴未減，代表量測漏洞存在。&lt;/p>
&lt;p>&lt;strong>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/mttr/" data-link-title="MTTR" data-link-desc="說明平均修復時間如何作為事故處理能力指標">MTTR&lt;/a>&lt;/strong> 量測從故障到恢復的時間。MTTR 的量測邊界需要明確定義：從 alert 觸發開始算、從 customer impact 開始算、到 recovery complete 還是到 root cause 修復。不同定義會產出完全不同的數字。判讀陷阱是延遲標記 incident 起始時間來壓低 MTTR。連到 &lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/" data-link-title="模組八：事故處理與復盤" data-link-desc="用 IR 領域詞彙建問題節點、以服務級案例庫累積事故脈絡，先建概念與案例庫再進實作交接">08 incident response&lt;/a> 的事故分級與復盤流程。&lt;/p>
&lt;h2 id="space-補充維度">SPACE 補充維度&lt;/h2>
&lt;p>DORA 偏重 delivery 效率，SPACE 補人因與協作維度。五個面向各捕捉 DORA 看不到的訊號。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>維度&lt;/th>
 &lt;th>量測重點&lt;/th>
 &lt;th>判讀價值&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Satisfaction&lt;/td>
 &lt;td>團隊對工具、流程、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/on-call/" data-link-title="On-Call" data-link-desc="說明值班制度如何承接告警、事故分級與升級流程">on-call&lt;/a> 負擔的滿意度&lt;/td>
 &lt;td>滿意度下降常先於效能指標退化&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Performance&lt;/td>
 &lt;td>code review 品質、bug escape rate&lt;/td>
 &lt;td>補 DORA 缺的品質維度&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Activity&lt;/td>
 &lt;td>commit / PR / deploy 頻率&lt;/td>
 &lt;td>activity 是描述性指標，不等於 productivity&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Communication&lt;/td>
 &lt;td>跨團隊協作效率、incident communication 品質&lt;/td>
 &lt;td>協作瓶頸在 DORA 中完全看不到&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Efficiency&lt;/td>
 &lt;td>flow state time、context switch frequency&lt;/td>
 &lt;td>高 context switch 會拖慢 lead time 但原因不在 CI&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>SPACE 同樣需要 governance。Satisfaction 被 KPI 化後團隊會避免誠實回饋；Activity 被當成 productivity 量測後會鼓勵 commit 拆碎。治理原則跟 DORA 相同：指標是討論的起點，不是績效的終點。&lt;/p></description><content:encoded><![CDATA[<h2 id="概念定位">概念定位</h2>
<p>Reliability metrics governance 確保團隊量測到的指標能反映真實的可靠性狀態。指標的價值在於引導討論與暴露趨勢，一旦指標被直接當成目標，治理就開始退化。</p>
<h2 id="核心判讀">核心判讀</h2>
<p>指標是否對準使用者感受、是否能驅動工程決策 — 這兩個問題決定 metrics governance 的有效性。</p>
<p>判讀的核心問題：</p>
<ul>
<li>SLI 是否有明確觀測窗口與採樣邊界</li>
<li>SLO 是否能轉成 release / alert / incident 決策</li>
<li>DORA / SPACE / CFR 是否被混用成單一成績單</li>
<li>metric drift 是否被記錄與校正</li>
</ul>
<h2 id="dora-四指標">DORA 四指標</h2>
<p>DORA 量測的是交付與可靠性流程的效率，四個指標各自回答不同問題。</p>
<p><strong>Deploy frequency</strong> 量測交付節奏 — 團隊多頻繁把變更送到 production。高頻 deploy 通常代表小批次、低風險；但判讀陷阱是拆碎 deploy 只為衝頻率。辨別方式是同時看 deploy size distribution — 若平均 deploy 的變更量持續縮小但 frequency 持續上升，gaming 的可能性高。deploy frequency 要搭配 change failure rate 一起看，頻率高但 CFR 也高代表品質沒跟上。</p>
<p><strong>Lead time for changes</strong> 量測從 commit 到 production 的時間。長 lead time 通常指向 <a href="/blog/backend/06-reliability/ci-pipeline/" data-link-title="6.1 CI pipeline" data-link-desc="CI pipeline 的分層策略、artifact 管理、flaky 治理與 release gate 輸入">CI pipeline</a> bottleneck、approval queue 或 staging 排隊。判讀陷阱是把 lead time 壓短但跳過驗證步驟 — 縮短的時間可能來自移除 slow path 測試，表面效率提升但風險轉移到 production。改善 lead time 的投資方向先看 CI 分層（6.1）是否合理，再看 review queue 是否成為瓶頸。</p>
<p><strong>Change failure rate (CFR)</strong> 量測 deploy 後需要 rollback 或 hotfix 的比率。CFR 是 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">release gate</a> 健康度的直接指標 — gate 有效時 CFR 應該維持穩定或下降。判讀陷阱是團隊避免標記 rollback 來壓低 CFR，或把 hotfix 歸類為「正常 deploy」。偵測方式是把 CFR 跟 customer complaint rate 做相關性分析 — 若 CFR 持續下降但客訴未減，代表量測漏洞存在。</p>
<p><strong><a href="/blog/backend/knowledge-cards/mttr/" data-link-title="MTTR" data-link-desc="說明平均修復時間如何作為事故處理能力指標">MTTR</a></strong> 量測從故障到恢復的時間。MTTR 的量測邊界需要明確定義：從 alert 觸發開始算、從 customer impact 開始算、到 recovery complete 還是到 root cause 修復。不同定義會產出完全不同的數字。判讀陷阱是延遲標記 incident 起始時間來壓低 MTTR。連到 <a href="/blog/backend/08-incident-response/" data-link-title="模組八：事故處理與復盤" data-link-desc="用 IR 領域詞彙建問題節點、以服務級案例庫累積事故脈絡，先建概念與案例庫再進實作交接">08 incident response</a> 的事故分級與復盤流程。</p>
<h2 id="space-補充維度">SPACE 補充維度</h2>
<p>DORA 偏重 delivery 效率，SPACE 補人因與協作維度。五個面向各捕捉 DORA 看不到的訊號。</p>
<table>
  <thead>
      <tr>
          <th>維度</th>
          <th>量測重點</th>
          <th>判讀價值</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Satisfaction</td>
          <td>團隊對工具、流程、<a href="/blog/backend/knowledge-cards/on-call/" data-link-title="On-Call" data-link-desc="說明值班制度如何承接告警、事故分級與升級流程">on-call</a> 負擔的滿意度</td>
          <td>滿意度下降常先於效能指標退化</td>
      </tr>
      <tr>
          <td>Performance</td>
          <td>code review 品質、bug escape rate</td>
          <td>補 DORA 缺的品質維度</td>
      </tr>
      <tr>
          <td>Activity</td>
          <td>commit / PR / deploy 頻率</td>
          <td>activity 是描述性指標，不等於 productivity</td>
      </tr>
      <tr>
          <td>Communication</td>
          <td>跨團隊協作效率、incident communication 品質</td>
          <td>協作瓶頸在 DORA 中完全看不到</td>
      </tr>
      <tr>
          <td>Efficiency</td>
          <td>flow state time、context switch frequency</td>
          <td>高 context switch 會拖慢 lead time 但原因不在 CI</td>
      </tr>
  </tbody>
</table>
<p>SPACE 同樣需要 governance。Satisfaction 被 KPI 化後團隊會避免誠實回饋；Activity 被當成 productivity 量測後會鼓勵 commit 拆碎。治理原則跟 DORA 相同：指標是討論的起點，不是績效的終點。</p>
<h2 id="指標選用與團隊階段">指標選用與團隊階段</h2>
<p>指標投資的 ROI 跟團隊規模正相關。團隊小時指標治理成本高，應集中在最少的關鍵指標。</p>
<table>
  <thead>
      <tr>
          <th>階段</th>
          <th>建議指標</th>
          <th>理由</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Startup（&lt; 10 人）</td>
          <td>deploy frequency + CFR</td>
          <td>兩個指標足以判讀交付節奏與品質平衡，其他指標 noise 太大</td>
      </tr>
      <tr>
          <td>Scale（10-100 人）</td>
          <td>完整 DORA</td>
          <td>加入 lead time + MTTR，開始治理跨團隊 baseline</td>
      </tr>
      <tr>
          <td>Mature（100+ 人）</td>
          <td>DORA + SPACE + trend</td>
          <td>完整框架加趨勢分析，composite metrics 需要專人維護</td>
      </tr>
  </tbody>
</table>
<p>baseline 對齊的判準是跟自己的歷史趨勢比，而非抄業界數字。DORA 報告的 elite / high / medium / low 分類提供方向參考，但直接套用會忽略產業、架構與團隊結構的差異。</p>
<h2 id="anti-gaming-與-goodharts-law">Anti-gaming 與 Goodhart&rsquo;s law</h2>
<p>當指標直接變成目標，量測的行為會改變被量測的對象。這就是 Goodhart&rsquo;s law 在工程指標上的實現。</p>
<p>常見 gaming 模式與偵測方式：</p>
<table>
  <thead>
      <tr>
          <th>Gaming 模式</th>
          <th>偵測方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>拆碎 deploy 衝 frequency</td>
          <td>deploy size distribution 出現異常小的 cluster</td>
      </tr>
      <tr>
          <td>延遲標記 incident 降 MTTR</td>
          <td>incident 起始時間 vs alert 觸發時間的 gap 分析</td>
      </tr>
      <tr>
          <td>避免 rollback 降 CFR</td>
          <td>CFR vs customer complaint rate 的相關性斷裂</td>
      </tr>
      <tr>
          <td>跳過 slow path 測試縮短 lead time</td>
          <td>lead time 下降同時 CFR 上升</td>
      </tr>
      <tr>
          <td>壓下同類 incident 不報</td>
          <td>incident recurrence rate 與 <a href="/blog/backend/knowledge-cards/post-incident-review/" data-link-title="Post-Incident Review" data-link-desc="說明事故後如何完成復盤、學習與改進閉環">post-incident review</a> 數量不匹配</td>
      </tr>
  </tbody>
</table>
<p>治理原則：指標是診斷工具，用來發現問題方向與引導團隊討論。指標跨團隊強制排名會讓 gaming 成為理性選擇 — 團隊會優化數字而非優化系統。有效做法是把指標用在團隊自身的趨勢追蹤，跨團隊只分享經驗與改善策略。</p>
<h2 id="跟-slo-的差異">跟 SLO 的差異</h2>
<p>SLO 是面向使用者的服務承諾 — 量測的是「我的服務給使用者什麼品質」。6.18 metrics 是面向團隊的工程能力量測 — 量測的是「我的交付與可靠性流程效率如何」。</p>
<p>兩者的消費者不同：SLO 的消費者是 product / business stakeholder 與 on-call 團隊；DORA / SPACE 的消費者是工程管理與團隊自身。治理節奏也不同：SLO 跟 <a href="/blog/backend/knowledge-cards/error-budget/" data-link-title="Error Budget" data-link-desc="說明 SLO 允許的失敗額度如何影響發版與可靠性投入">error budget</a> 政策綁定，burn rate 驅動即時決策；DORA 趨勢按月或按季 review。</p>
<p>混用的風險是 SLO 失去商業對齊的價值。當 SLO 被當成工程 KPI 而非使用者承諾，團隊會開始縮小 SLI 範圍或放寬目標來讓數字好看，SLO 政策的放行判讀也跟著失真。</p>
<h2 id="案例對照">案例對照</h2>
<ul>
<li><a href="/blog/backend/06-reliability/cases/google/error-budget-policy-and-release-gating/" data-link-title="Google：Error Budget 政策如何決定發布節奏" data-link-desc="把 SLO 消耗量轉成 release gate，讓可靠性與交付速度共用同一套決策語言。">Google：Error Budget 與 Release Gating</a>：SLO 與 DORA 的邊界在這個案例中最清楚 — error budget 是服務承諾的消耗量測，DORA 是交付流程的效率量測，兩者在 release gate 交會但責任不同。</li>
<li><a href="/blog/backend/06-reliability/cases/honeycomb/burn-rate-driven-reliability-operations/" data-link-title="Honeycomb：以 Burn Rate 驅動的可靠性操作" data-link-desc="把 SLO burn rate 直接連到值班決策與改善優先序，降低高噪音告警造成的判讀失真。">Honeycomb：Burn Rate 驅動可靠性</a>：用觀測資料驅動判讀，而非先設定指標再找資料。這個案例說明指標治理的起點是觀測能力，指標是觀測的摘要，觀測是指標的來源。</li>
<li><a href="/blog/backend/08-incident-response/cases/datadog/" data-link-title="Datadog" data-link-desc="Datadog 監控服務事故、客戶觀測落差">Datadog</a>：指標平台的可靠性直接影響事故判讀品質。當指標平台本身不穩定，所有基於它的 DORA / SLO 量測都會失真。</li>
</ul>
<h2 id="判讀訊號">判讀訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀條件</th>
          <th>行動建議</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>指標數字持續改善、客戶投訴未減</td>
          <td>量測覆蓋不足或 gaming — 先檢查 CFR vs complaint 相關性</td>
          <td>把 complaint 率加入 dashboard 交叉比對</td>
      </tr>
      <tr>
          <td>跨團隊強制排名</td>
          <td>gaming 風險高 — 改為團隊自身趨勢追蹤</td>
          <td>取消排名、改為各團隊獨立看自身 trend</td>
      </tr>
      <tr>
          <td>DORA 採集靠人工、滯後超過一個月</td>
          <td>指標失去即時性 — 自動化採集連到 CI / deploy pipeline</td>
          <td>串接 CI/CD pipeline 自動產出 DORA 資料</td>
      </tr>
      <tr>
          <td>指標無 owner、半年無人 review</td>
          <td>治理已停擺 — 指定 owner 與季度 review 節奏</td>
          <td>指定 metrics owner + 排入季度 review 議程</td>
      </tr>
      <tr>
          <td>deploy frequency 上升同時 CFR 上升</td>
          <td>速度與品質失衡 — 先補 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">release gate</a> 再追 frequency</td>
          <td>暫停追 frequency、先讓 CFR 回到 baseline</td>
      </tr>
      <tr>
          <td>MTTR 定義跨團隊不一致</td>
          <td>量測不可比 — 先統一量測邊界（alert → recovery complete）</td>
          <td>發布 MTTR 量測定義文件、統一 start/end 判準</td>
      </tr>
  </tbody>
</table>
<h2 id="交接路由">交接路由</h2>
<ul>
<li><a href="/blog/backend/06-reliability/ci-pipeline/" data-link-title="6.1 CI pipeline" data-link-desc="CI pipeline 的分層策略、artifact 管理、flaky 治理與 release gate 輸入">6.1 CI pipeline</a>：lead time 的主要改善入口</li>
<li><a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6 SLO / error budget</a>：商業承諾層的指標，跟 DORA 互補但責任不同</li>
<li><a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate</a>：CFR 是 gate 健康度訊號</li>
<li><a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 reliability debt backlog</a>：指標趨勢揭露的可靠性債</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 政策">04.6 SLI/SLO 訊號層</a>：指標的觀測來源</li>
<li><a href="/blog/backend/08-incident-response/post-incident-review/" data-link-title="8.5 復盤與改進追蹤" data-link-desc="把 RCA 與 action items 轉成可驗證閉環">08.5 post-incident review</a>：MTTR 計算的事件來源、指標漂移通常先在復盤裡被看見</li>
<li><a href="/blog/backend/08-incident-response/observability-reliability-incident-loop/" data-link-title="8.11 Observability / Reliability / Incident Response 閉環" data-link-desc="把 04 / 06 / 08 三個模組的雙向反饋串成可判讀循環，定義閉環健康度判讀訊號">08.11 觀測 / 可靠性 / 事故閉環</a>：指標治理回寫到三模組閉環</li>
</ul>
]]></content:encoded></item><item><title>6.19 Reliability Readiness Review</title><link>https://tarrragon.github.io/blog/backend/06-reliability/reliability-readiness-review/</link><pubDate>Sat, 02 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/reliability-readiness-review/</guid><description>&lt;h2 id="大綱">大綱&lt;/h2>
&lt;ul>
&lt;li>reliability readiness 的責任：確認服務能承受預期流量、依賴失效、資料變更與回復壓力&lt;/li>
&lt;li>檢查面向：SLO、capacity、dependency、rollback、data migration、on-call、runbook&lt;/li>
&lt;li>上線前門檻：核心路徑有 SLI、load test、rollback path、owner 與 alert&lt;/li>
&lt;li>重大變更門檻：migration、feature flag、dependency change、config rollout 的風險判讀&lt;/li>
&lt;li>高風險操作門檻：手動修資料、批次任務、backfill、區域切換&lt;/li>
&lt;li>跟 04 的交接：缺少訊號時回到 observability readiness&lt;/li>
&lt;li>跟 08 的交接：缺少事故節奏時回到 drills / runbook lifecycle&lt;/li>
&lt;li>反模式：release gate 只看 CI 綠燈；沒有 rollback rehearsal；容量假設沒有驗證&lt;/li>
&lt;/ul>
&lt;p>Reliability readiness review 的核心價值是把「上線前風險」前移成可討論的工程語言。只靠測試通過不代表服務可在真實流量與依賴波動下維持穩定，readiness 讓團隊在變更前先明確回答容量、回復、資料與值班四個問題。&lt;/p>
&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>Reliability readiness review 是把可靠性準備度轉成可檢查門檻的流程，責任是在服務承受 production 壓力前先找出可預期失效。&lt;/p>
&lt;p>這一頁處理的是準備度。readiness 要把訊號、容量、依賴、回復、資料與值班能力放在同一張檢查表中判讀。&lt;/p>
&lt;p>readiness 的目標是提高發布品質。當缺口被提前看見，團隊可以選擇補驗證、縮小範圍、延後發布或先加保護措施，避免把不確定性直接帶進 production。&lt;/p>
&lt;h2 id="核心判讀">核心判讀&lt;/h2>
&lt;p>判讀 reliability readiness 時，先看服務的核心失敗模式是否已被驗證，再看回復路徑是否可執行。&lt;/p>
&lt;p>重點訊號包括：&lt;/p>
&lt;ul>
&lt;li>核心 user journey 是否有 SLO、load baseline 與 alert&lt;/li>
&lt;li>主要 dependency 是否有 timeout、fallback 與 degradation plan&lt;/li>
&lt;li>rollback / failover 是否有演練紀錄&lt;/li>
&lt;li>migration / backfill 是否有停止條件與資料校驗&lt;/li>
&lt;li>on-call 是否有 runbook、owner 與 escalation policy&lt;/li>
&lt;/ul>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>檢查面向&lt;/th>
 &lt;th>最小可用判準&lt;/th>
 &lt;th>常見風險&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>服務健康&lt;/td>
 &lt;td>核心旅程有 SLO 與 alert&lt;/td>
 &lt;td>只看系統資源，忽略用戶結果&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>容量邊界&lt;/td>
 &lt;td>有 load baseline 與容量餘裕&lt;/td>
 &lt;td>流量上升時才發現瓶頸&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>回復路徑&lt;/td>
 &lt;td>rollback / failover 有演練紀錄&lt;/td>
 &lt;td>事故現場才第一次走流程&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>資料操作&lt;/td>
 &lt;td>migration 有校驗與停止條件&lt;/td>
 &lt;td>補資料操作擴大影響面&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>值班準備&lt;/td>
 &lt;td>on-call 有 runbook 與 escalation&lt;/td>
 &lt;td>事故當下才建立協作節奏&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="readiness-範圍">Readiness 範圍&lt;/h2>
&lt;p>Reliability readiness review 的範圍是服務進入 production 壓力前需要具備的最低可靠性條件。它不取代 CI、load test、release gate 或 incident drill，而是把這些控制面接成同一個放行判斷。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>範圍&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>對應控制面&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>服務健康&lt;/td>
 &lt;td>核心旅程是否有可靠性目標&lt;/td>
 &lt;td>SLO、SLI、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">burn rate&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>容量&lt;/td>
 &lt;td>預期流量與尖峰是否被驗證&lt;/td>
 &lt;td>load test、capacity model&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>依賴&lt;/td>
 &lt;td>下游失效是否有 timeout 與降級&lt;/td>
 &lt;td>dependency budget、fallback&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>資料&lt;/td>
 &lt;td>migration、backfill 是否可校驗&lt;/td>
 &lt;td>migration safety、test data&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>回復&lt;/td>
 &lt;td>rollback、failover 是否可執行&lt;/td>
 &lt;td>DR rehearsal、rollback rehearsal&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>操作&lt;/td>
 &lt;td>on-call 是否知道如何接住事故&lt;/td>
 &lt;td>runbook、escalation、drill&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>服務健康是 readiness 的第一層。核心 user journey 需要有 SLO、dashboard、alert 與 owner，讓團隊知道「服務是否仍在承諾範圍內」。&lt;/p>
&lt;p>容量是 readiness 的第二層。load baseline、throughput ceiling、queue lag、dependency saturation 與 cost threshold 都需要在上線前被看見，避免第一個尖峰才揭露瓶頸。&lt;/p>
&lt;p>依賴是 readiness 的第三層。每個關鍵 downstream 都需要 timeout、deadline、retry、fallback、circuit breaker 或 degradation plan，讓局部失效維持在可控範圍。&lt;/p></description><content:encoded><![CDATA[<h2 id="大綱">大綱</h2>
<ul>
<li>reliability readiness 的責任：確認服務能承受預期流量、依賴失效、資料變更與回復壓力</li>
<li>檢查面向：SLO、capacity、dependency、rollback、data migration、on-call、runbook</li>
<li>上線前門檻：核心路徑有 SLI、load test、rollback path、owner 與 alert</li>
<li>重大變更門檻：migration、feature flag、dependency change、config rollout 的風險判讀</li>
<li>高風險操作門檻：手動修資料、批次任務、backfill、區域切換</li>
<li>跟 04 的交接：缺少訊號時回到 observability readiness</li>
<li>跟 08 的交接：缺少事故節奏時回到 drills / runbook lifecycle</li>
<li>反模式：release gate 只看 CI 綠燈；沒有 rollback rehearsal；容量假設沒有驗證</li>
</ul>
<p>Reliability readiness review 的核心價值是把「上線前風險」前移成可討論的工程語言。只靠測試通過不代表服務可在真實流量與依賴波動下維持穩定，readiness 讓團隊在變更前先明確回答容量、回復、資料與值班四個問題。</p>
<h2 id="概念定位">概念定位</h2>
<p>Reliability readiness review 是把可靠性準備度轉成可檢查門檻的流程，責任是在服務承受 production 壓力前先找出可預期失效。</p>
<p>這一頁處理的是準備度。readiness 要把訊號、容量、依賴、回復、資料與值班能力放在同一張檢查表中判讀。</p>
<p>readiness 的目標是提高發布品質。當缺口被提前看見，團隊可以選擇補驗證、縮小範圍、延後發布或先加保護措施，避免把不確定性直接帶進 production。</p>
<h2 id="核心判讀">核心判讀</h2>
<p>判讀 reliability readiness 時，先看服務的核心失敗模式是否已被驗證，再看回復路徑是否可執行。</p>
<p>重點訊號包括：</p>
<ul>
<li>核心 user journey 是否有 SLO、load baseline 與 alert</li>
<li>主要 dependency 是否有 timeout、fallback 與 degradation plan</li>
<li>rollback / failover 是否有演練紀錄</li>
<li>migration / backfill 是否有停止條件與資料校驗</li>
<li>on-call 是否有 runbook、owner 與 escalation policy</li>
</ul>
<table>
  <thead>
      <tr>
          <th>檢查面向</th>
          <th>最小可用判準</th>
          <th>常見風險</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>服務健康</td>
          <td>核心旅程有 SLO 與 alert</td>
          <td>只看系統資源，忽略用戶結果</td>
      </tr>
      <tr>
          <td>容量邊界</td>
          <td>有 load baseline 與容量餘裕</td>
          <td>流量上升時才發現瓶頸</td>
      </tr>
      <tr>
          <td>回復路徑</td>
          <td>rollback / failover 有演練紀錄</td>
          <td>事故現場才第一次走流程</td>
      </tr>
      <tr>
          <td>資料操作</td>
          <td>migration 有校驗與停止條件</td>
          <td>補資料操作擴大影響面</td>
      </tr>
      <tr>
          <td>值班準備</td>
          <td>on-call 有 runbook 與 escalation</td>
          <td>事故當下才建立協作節奏</td>
      </tr>
  </tbody>
</table>
<h2 id="readiness-範圍">Readiness 範圍</h2>
<p>Reliability readiness review 的範圍是服務進入 production 壓力前需要具備的最低可靠性條件。它不取代 CI、load test、release gate 或 incident drill，而是把這些控制面接成同一個放行判斷。</p>
<table>
  <thead>
      <tr>
          <th>範圍</th>
          <th>核心問題</th>
          <th>對應控制面</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>服務健康</td>
          <td>核心旅程是否有可靠性目標</td>
          <td>SLO、SLI、<a href="/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">burn rate</a></td>
      </tr>
      <tr>
          <td>容量</td>
          <td>預期流量與尖峰是否被驗證</td>
          <td>load test、capacity model</td>
      </tr>
      <tr>
          <td>依賴</td>
          <td>下游失效是否有 timeout 與降級</td>
          <td>dependency budget、fallback</td>
      </tr>
      <tr>
          <td>資料</td>
          <td>migration、backfill 是否可校驗</td>
          <td>migration safety、test data</td>
      </tr>
      <tr>
          <td>回復</td>
          <td>rollback、failover 是否可執行</td>
          <td>DR rehearsal、rollback rehearsal</td>
      </tr>
      <tr>
          <td>操作</td>
          <td>on-call 是否知道如何接住事故</td>
          <td>runbook、escalation、drill</td>
      </tr>
  </tbody>
</table>
<p>服務健康是 readiness 的第一層。核心 user journey 需要有 SLO、dashboard、alert 與 owner，讓團隊知道「服務是否仍在承諾範圍內」。</p>
<p>容量是 readiness 的第二層。load baseline、throughput ceiling、queue lag、dependency saturation 與 cost threshold 都需要在上線前被看見，避免第一個尖峰才揭露瓶頸。</p>
<p>依賴是 readiness 的第三層。每個關鍵 downstream 都需要 timeout、deadline、retry、fallback、circuit breaker 或 degradation plan，讓局部失效維持在可控範圍。</p>
<p>資料是 readiness 的第四層。schema migration、backfill、online migration 與資料修復需要校驗、停止條件、rollback 或補償流程，讓資料風險能被事前判讀。</p>
<p>操作是 readiness 的最後一層。runbook、owner、escalation policy、incident intake 與 <a href="/blog/backend/knowledge-cards/incident-decision-log/" data-link-title="Incident Decision Log" data-link-desc="說明事故期間如何保留決策、證據、owner 與回退條件">decision log</a> 讓服務在失效時能被團隊接住。</p>
<h2 id="review-流程">Review 流程</h2>
<p>Reliability readiness review 的流程是從風險清單走向放行判斷。每個缺口都要被分類為阻擋、降級接受或後續改善，讓發布決策有清楚路由。</p>
<ol>
<li>定義本次上線或變更的服務承諾。</li>
<li>列出核心 failure mode、dependency、資料操作與回復路徑。</li>
<li>檢查 04 訊號是否足以支援判讀。</li>
<li>檢查 06 驗證是否足以支援放行。</li>
<li>檢查 08 值班與事故流程是否能接住失效。</li>
<li>對每個缺口指定 owner、處理路由與重新評估條件。</li>
</ol>
<p>服務承諾是 readiness review 的錨點。若本次變更影響 checkout、payment、message delivery 或 tenant migration，review 就要圍繞這些旅程的可靠性承諾，並把程式碼合併狀態視為其中一個輸入。</p>
<p>Failure mode 清單需要具體。依賴 timeout、queue lag、cache stampede、migration lock、feature flag misrouting、region failover 與 data reconciliation 都是不同失效模式，對應不同驗證與回復路由。</p>
<p>04 訊號是 readiness 的前提。若缺少 SLI、trace、log correlation 或 telemetry data quality，可靠性 review 只能停在推測；這類缺口應先回到 04.16 與 04.17。</p>
<p>08 流程是 readiness 的接手面。若 on-call 沒有 runbook、incident commander 不清楚啟動條件、status update 沒有節奏，可靠性缺口會在事故時轉成協作壓力。</p>
<h2 id="判讀訊號">判讀訊號</h2>
<ul>
<li>上線前只看 unit / integration test，沒有容量與回復判準</li>
<li>依賴失效時只能現場討論 fallback</li>
<li>migration 執行前沒有 rollback rehearsal</li>
<li>服務 owner 需要臨場補 RTO / RPO 或核心 SLO</li>
<li>on-call 第一次接觸 runbook 是事故當下</li>
</ul>
<p>典型情境是服務通過 CI 與 integration test 就上線，結果在流量尖峰時 dependency timeout 連鎖放大。若前一輪 readiness 已要求 load baseline、fallback 驗證與 rollback rehearsal，這類事故通常會降級成可控風險，維持在局部範圍。</p>
<h2 id="放行判斷">放行判斷</h2>
<p>Reliability readiness 的放行判斷需要區分「阻擋上線」與「帶限制上線」。這個區分讓團隊既能控制風險，也能在低風險缺口存在時保持交付節奏。</p>
<table>
  <thead>
      <tr>
          <th>結果</th>
          <th>判斷條件</th>
          <th>常見動作</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Pass</td>
          <td>核心路徑、容量、回復與值班皆達標</td>
          <td>正常進入 release gate</td>
      </tr>
      <tr>
          <td>Conditional pass</td>
          <td>缺口可被降級、人工查證或短期 runbook 承接</td>
          <td>記錄限制、owner 與補齊期限</td>
      </tr>
      <tr>
          <td>Block</td>
          <td>核心旅程、資料或回復路徑缺少判讀</td>
          <td>暫停發布，補驗證或縮小範圍</td>
      </tr>
      <tr>
          <td>Defer</td>
          <td>需求價值低於可靠性風險</td>
          <td>延後變更，先處理 reliability debt</td>
      </tr>
  </tbody>
</table>
<p>Pass 代表核心風險已有證據支撐。這不代表系統完美，而是代表本次發布或操作有足夠訊號、驗證與回復路由。</p>
<p>Conditional pass 適合處理可控缺口。例如某個低風險 batch job 缺少完整 trace，但已有 log query、manual replay 與 on-call owner，可以帶著明確限制上線。</p>
<p>Block 適合處理核心旅程與資料風險。payment migration 缺少 rollback rehearsal、tenant backfill 缺少校驗、核心 API 缺少 SLO alert，這些缺口會讓事故處理沒有可靠入口。</p>
<p>Defer 適合處理價值與風險不對稱的變更。若本次變更只是次要優化，但會暴露高風險 migration 或 dependency change，延後是合理的 reliability decision。</p>
<h2 id="常見反模式">常見反模式</h2>
<p>Reliability readiness 的反模式通常來自把測試通過視為 production 準備度。測試通過證明某些功能路徑可執行，readiness 則要證明服務能在真實壓力、依賴波動與事故流程下被接住。</p>
<table>
  <thead>
      <tr>
          <th>反模式</th>
          <th>表面現象</th>
          <th>修正方向</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>CI 綠燈即上線</td>
          <td>只看 test pass</td>
          <td>加入 SLO、capacity、rollback 判準</td>
      </tr>
      <tr>
          <td>容量假設無驗證</td>
          <td>靠估算決定尖峰承載</td>
          <td>補 load baseline 與容量餘裕</td>
      </tr>
      <tr>
          <td>Rollback 只寫文件</td>
          <td>回復流程沒有演練紀錄</td>
          <td>補 rollback rehearsal</td>
      </tr>
      <tr>
          <td>Migration 缺停止條件</td>
          <td>執行中才判斷是否暫停</td>
          <td>事前定義校驗、pause、fallback</td>
      </tr>
      <tr>
          <td>On-call 臨場接手</td>
          <td>事故時才找 owner 與 runbook</td>
          <td>補 drill 與 escalation route</td>
      </tr>
  </tbody>
</table>
<p>CI 綠燈即上線會讓可靠性停在程式正確性層。production 可靠性還包含容量、依賴、資料、回復與協作，這些條件需要各自的證據。</p>
<p>Rollback 只寫文件會在事故現場暴露落差。回復流程需要在類 production 條件下演練過，才能知道權限、資料、流量、相容性與通訊是否接得上。</p>
<h2 id="產業情境醫療系統">產業情境：醫療系統</h2>
<p>醫療系統上線前的 readiness review 需要額外的合規維度。可靠性準備度跟醫療法規準備度是同一個放行判斷的兩個面向，缺任何一個都應 block。</p>
<p>Readiness checklist 需要包含合規項目：PHI（受保護健康資訊）加密狀態、存取控制驗證、audit trail 完整性、backup encryption 驗證。這些項目跟可靠性項目（SLO、load baseline、rollback path）平行檢查，合規缺口的阻擋權重跟核心旅程缺口相同。</p>
<p>合規驗證跟可靠性驗證有時存在張力。為了 HIPAA compliance 加密所有 backup 會增加 restore 時間，<a href="/blog/backend/knowledge-cards/rto/" data-link-title="RTO" data-link-desc="說明恢復時間目標如何約束事故回復策略">RTO</a> 可能不符合臨床需求。為了最小資料揭露限制 staging 資料量會降低環境 parity。這類 trade-off 需要在 readiness review 中明確記錄，包含選擇理由與風險接受者。</p>
<p>醫療系統的 readiness review 需要臨床代表參與。技術 readiness 回答的是「系統能否穩定運作」，臨床 readiness 回答的是「臨床 workflow 能否安全繼續」。EMR 升級後的畫面配置變更、醫囑流程的步驟調整、報告格式的差異，這些在技術指標上可能正常，但在臨床操作上可能造成用藥錯誤或判讀延遲。</p>
<p>高風險變更（EMR 升級、PACS 遷移、醫囑系統切換）需要 go-live support window。變更後的前 24-72 小時維持加強值班，因為臨床問題的反饋延遲通常比技術指標長 — 護理站的操作異常可能在換班時才被回報，藥局的處方錯誤可能在調劑時才被發現。support window 的長度由臨床回饋延遲決定，技術團隊單獨設定容易低估。</p>
<h2 id="與-release-gate-的關係">與 Release Gate 的關係</h2>
<p>Reliability readiness review 是 release gate 的上游資料。readiness 負責整理風險與證據，release gate 負責根據政策做放行、暫停、縮小範圍或例外核准。</p>
<p>Readiness 結果應包含三種資訊：已驗證條件、已接受限制與阻擋缺口。Release gate 只看「通過 / 失敗」會遺失判讀脈絡；保留這三類資訊才能讓發布決策可復盤。</p>
<p>Readiness 也應回寫 reliability debt。每次 conditional pass 都代表團隊暫時接受一個缺口；若缺口反覆被接受，就應進入 <a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 Reliability Debt Backlog</a>。</p>
<h2 id="交接路由">交接路由</h2>
<ul>
<li>04.16 observability readiness：確認訊號可支援 readiness 判讀</li>
<li>06.2 load test：補容量與吞吐驗證</li>
<li>06.7 DR / rollback rehearsal：補回復路徑演練</li>
<li>06.8 release gate：把 readiness 結果變成放行條件</li>
<li>08.6 drills / on-call readiness：補值班與事故演練</li>
</ul>
]]></content:encoded></item><item><title>6.20 Experiment Safety Boundary</title><link>https://tarrragon.github.io/blog/backend/06-reliability/experiment-safety-boundary/</link><pubDate>Sat, 02 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/experiment-safety-boundary/</guid><description>&lt;h2 id="大綱">大綱&lt;/h2>
&lt;ul>
&lt;li>experiment safety boundary 的責任：讓可靠性實驗可控、可停、可回復&lt;/li>
&lt;li>實驗類型：chaos test、load test、failover drill、rollback rehearsal、DR drill&lt;/li>
&lt;li>blast radius：服務、tenant、region、dependency、資料範圍&lt;/li>
&lt;li>停止條件：SLO burn、error rate、latency、queue lag、customer impact、cost threshold&lt;/li>
&lt;li>權限約束：誰能啟動、誰能停止、誰能擴大範圍&lt;/li>
&lt;li>evidence 要求：假設、步驟、觀測訊號、結果、回復時間、action item&lt;/li>
&lt;li>跟 07 的交接：高風險演練需要權限與稽核約束&lt;/li>
&lt;li>反模式：直接在 production 打 chaos；缺停止條件；實驗 owner 與 incident commander 不清楚&lt;/li>
&lt;/ul>
&lt;p>Experiment safety boundary 的價值在於讓失敗驗證可重播、可停止、可回復。實驗越接近真實失效，對團隊越有學習價值；同時也越需要清楚邊界，避免「為了驗證韌性」而產生額外事故。&lt;/p>
&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>Experiment safety boundary 是定義可靠性實驗安全範圍的控制面，責任是讓團隊能主動驗證失敗，同時控制實驗造成的實際風險。&lt;/p>
&lt;p>這一頁處理的是實驗邊界。可靠性實驗的價值來自接近真實失效，但越接近真實，越需要明確 blast radius、停止條件與回復路徑。&lt;/p>
&lt;p>安全邊界是一組事前契約：誰能啟動、誰有停止權、觸發什麼門檻必須終止、終止後怎麼回復。契約存在時，團隊才能在實驗中保持速度，同時控制風險成本。&lt;/p>
&lt;h2 id="核心判讀">核心判讀&lt;/h2>
&lt;p>判讀 experiment safety 時，先看實驗假設是否明確，再看實驗失控時是否能立刻停止與回復。&lt;/p>
&lt;p>重點訊號包括：&lt;/p>
&lt;ul>
&lt;li>experiment hypothesis 是否連到具體 failure mode&lt;/li>
&lt;li>blast radius 是否限制 service、tenant、region 或 traffic percentage&lt;/li>
&lt;li>stop condition 是否連到 SLO / customer impact / cost&lt;/li>
&lt;li>rollback / failover 是否在實驗前準備好&lt;/li>
&lt;li>observer、executor、approver 是否分工清楚&lt;/li>
&lt;/ul>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>控制面&lt;/th>
 &lt;th>最小可用判準&lt;/th>
 &lt;th>失控信號&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>範圍控制&lt;/td>
 &lt;td>blast radius 限在服務 / 區域 / 流量百分比&lt;/td>
 &lt;td>影響擴散到非目標服務&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>停止條件&lt;/td>
 &lt;td>stop condition 連到 SLO / impact / cost&lt;/td>
 &lt;td>超門檻仍持續實驗&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>權限治理&lt;/td>
 &lt;td>啟動者、停止者、核准者分離&lt;/td>
 &lt;td>需要額外查證誰在操作&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>回復能力&lt;/td>
 &lt;td>rollback / failover 已預演&lt;/td>
 &lt;td>終止後回復時間失控&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>證據留存&lt;/td>
 &lt;td>hypothesis 與結果可回放&lt;/td>
 &lt;td>成功與失敗都不可重現&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="實驗類型">實驗類型&lt;/h2>
&lt;p>Experiment safety boundary 需要依實驗類型調整邊界。不同實驗打到的系統層不同，學習價值與實際風險也不同。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>實驗類型&lt;/th>
 &lt;th>驗證問題&lt;/th>
 &lt;th>主要邊界&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Chaos test&lt;/td>
 &lt;td>依賴、節點、網路失效是否可承受&lt;/td>
 &lt;td>service、region、dependency&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Load test&lt;/td>
 &lt;td>流量與資料量是否超過容量模型&lt;/td>
 &lt;td>traffic percentage、cost、quota&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Failover drill&lt;/td>
 &lt;td>切換流程是否可執行&lt;/td>
 &lt;td>region、data replication、routing&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Rollback rehearsal&lt;/td>
 &lt;td>回復到前一版本是否安全&lt;/td>
 &lt;td>version、migration、feature flag&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>DR drill&lt;/td>
 &lt;td>災難恢復是否符合 RTO / RPO&lt;/td>
 &lt;td>data scope、region、access&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Chaos test 的風險在於故障注入接近真實失效。它需要明確 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/steady-state/" data-link-title="Steady State" data-link-desc="說明可靠性實驗與事故恢復如何定義系統應維持的可接受狀態">steady state&lt;/a>、觀測訊號與停止條件，讓團隊知道實驗如何驗證韌性。&lt;/p>
&lt;p>Load test 的風險在於放大共享依賴。測試流量可能壓到 database、cache、broker、third-party API 或 observability pipeline，因此邊界要包含共享資源與成本上限。&lt;/p>
&lt;p>Failover drill 的風險在於切換後的長尾狀態。流量切過去只是第一步，團隊還需要看資料同步、cache warmup、queue drain、DNS / routing propagation 與客戶端行為。&lt;/p>
&lt;p>Rollback rehearsal 的風險在於資料與版本相容性。程式可回滾不代表 schema、message、cache、feature flag 與 client contract 都能同步回到安全狀態。&lt;/p>
&lt;p>DR drill 的風險在於權限、資料與外部通訊。災難恢復通常涉及高權限操作、備份還原與跨團隊協作，因此需要額外 audit trail 與 incident communication 準備。&lt;/p>
&lt;h2 id="boundary-契約">Boundary 契約&lt;/h2>
&lt;p>Experiment boundary 契約的責任是讓實驗在開始前就具備可停止、可回復與可復盤條件。契約應被寫成實驗 artifact，並納入可回查的操作紀錄。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>契約欄位&lt;/th>
 &lt;th>責任&lt;/th>
 &lt;th>判讀用途&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Hypothesis&lt;/td>
 &lt;td>說明要驗證的 failure mode&lt;/td>
 &lt;td>避免實驗變成任意故障注入&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Blast radius&lt;/td>
 &lt;td>限制服務、tenant、region 範圍&lt;/td>
 &lt;td>控制實際影響&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Steady state&lt;/td>
 &lt;td>定義實驗期間應維持的狀態&lt;/td>
 &lt;td>判斷實驗是否成功&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Stop condition&lt;/td>
 &lt;td>定義終止門檻&lt;/td>
 &lt;td>讓失控時能立刻停手&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Rollback path&lt;/td>
 &lt;td>定義回復步驟&lt;/td>
 &lt;td>降低終止後的恢復成本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Authority&lt;/td>
 &lt;td>定義啟動、停止與擴大權限&lt;/td>
 &lt;td>避免事中權責不清&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Evidence&lt;/td>
 &lt;td>定義要收集的觀測與決策紀錄&lt;/td>
 &lt;td>支援復盤與可重播&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Hypothesis 是實驗的錨點。好的假設會說明「當 dependency timeout 發生時，checkout 應進入 degraded mode，SLO &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">burn rate&lt;/a> 應維持在門檻內」，而不只是「關掉某個服務」。&lt;/p></description><content:encoded><![CDATA[<h2 id="大綱">大綱</h2>
<ul>
<li>experiment safety boundary 的責任：讓可靠性實驗可控、可停、可回復</li>
<li>實驗類型：chaos test、load test、failover drill、rollback rehearsal、DR drill</li>
<li>blast radius：服務、tenant、region、dependency、資料範圍</li>
<li>停止條件：SLO burn、error rate、latency、queue lag、customer impact、cost threshold</li>
<li>權限約束：誰能啟動、誰能停止、誰能擴大範圍</li>
<li>evidence 要求：假設、步驟、觀測訊號、結果、回復時間、action item</li>
<li>跟 07 的交接：高風險演練需要權限與稽核約束</li>
<li>反模式：直接在 production 打 chaos；缺停止條件；實驗 owner 與 incident commander 不清楚</li>
</ul>
<p>Experiment safety boundary 的價值在於讓失敗驗證可重播、可停止、可回復。實驗越接近真實失效，對團隊越有學習價值；同時也越需要清楚邊界，避免「為了驗證韌性」而產生額外事故。</p>
<h2 id="概念定位">概念定位</h2>
<p>Experiment safety boundary 是定義可靠性實驗安全範圍的控制面，責任是讓團隊能主動驗證失敗，同時控制實驗造成的實際風險。</p>
<p>這一頁處理的是實驗邊界。可靠性實驗的價值來自接近真實失效，但越接近真實，越需要明確 blast radius、停止條件與回復路徑。</p>
<p>安全邊界是一組事前契約：誰能啟動、誰有停止權、觸發什麼門檻必須終止、終止後怎麼回復。契約存在時，團隊才能在實驗中保持速度，同時控制風險成本。</p>
<h2 id="核心判讀">核心判讀</h2>
<p>判讀 experiment safety 時，先看實驗假設是否明確，再看實驗失控時是否能立刻停止與回復。</p>
<p>重點訊號包括：</p>
<ul>
<li>experiment hypothesis 是否連到具體 failure mode</li>
<li>blast radius 是否限制 service、tenant、region 或 traffic percentage</li>
<li>stop condition 是否連到 SLO / customer impact / cost</li>
<li>rollback / failover 是否在實驗前準備好</li>
<li>observer、executor、approver 是否分工清楚</li>
</ul>
<table>
  <thead>
      <tr>
          <th>控制面</th>
          <th>最小可用判準</th>
          <th>失控信號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>範圍控制</td>
          <td>blast radius 限在服務 / 區域 / 流量百分比</td>
          <td>影響擴散到非目標服務</td>
      </tr>
      <tr>
          <td>停止條件</td>
          <td>stop condition 連到 SLO / impact / cost</td>
          <td>超門檻仍持續實驗</td>
      </tr>
      <tr>
          <td>權限治理</td>
          <td>啟動者、停止者、核准者分離</td>
          <td>需要額外查證誰在操作</td>
      </tr>
      <tr>
          <td>回復能力</td>
          <td>rollback / failover 已預演</td>
          <td>終止後回復時間失控</td>
      </tr>
      <tr>
          <td>證據留存</td>
          <td>hypothesis 與結果可回放</td>
          <td>成功與失敗都不可重現</td>
      </tr>
  </tbody>
</table>
<h2 id="實驗類型">實驗類型</h2>
<p>Experiment safety boundary 需要依實驗類型調整邊界。不同實驗打到的系統層不同，學習價值與實際風險也不同。</p>
<table>
  <thead>
      <tr>
          <th>實驗類型</th>
          <th>驗證問題</th>
          <th>主要邊界</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Chaos test</td>
          <td>依賴、節點、網路失效是否可承受</td>
          <td>service、region、dependency</td>
      </tr>
      <tr>
          <td>Load test</td>
          <td>流量與資料量是否超過容量模型</td>
          <td>traffic percentage、cost、quota</td>
      </tr>
      <tr>
          <td>Failover drill</td>
          <td>切換流程是否可執行</td>
          <td>region、data replication、routing</td>
      </tr>
      <tr>
          <td>Rollback rehearsal</td>
          <td>回復到前一版本是否安全</td>
          <td>version、migration、feature flag</td>
      </tr>
      <tr>
          <td>DR drill</td>
          <td>災難恢復是否符合 RTO / RPO</td>
          <td>data scope、region、access</td>
      </tr>
  </tbody>
</table>
<p>Chaos test 的風險在於故障注入接近真實失效。它需要明確 <a href="/blog/backend/knowledge-cards/steady-state/" data-link-title="Steady State" data-link-desc="說明可靠性實驗與事故恢復如何定義系統應維持的可接受狀態">steady state</a>、觀測訊號與停止條件，讓團隊知道實驗如何驗證韌性。</p>
<p>Load test 的風險在於放大共享依賴。測試流量可能壓到 database、cache、broker、third-party API 或 observability pipeline，因此邊界要包含共享資源與成本上限。</p>
<p>Failover drill 的風險在於切換後的長尾狀態。流量切過去只是第一步，團隊還需要看資料同步、cache warmup、queue drain、DNS / routing propagation 與客戶端行為。</p>
<p>Rollback rehearsal 的風險在於資料與版本相容性。程式可回滾不代表 schema、message、cache、feature flag 與 client contract 都能同步回到安全狀態。</p>
<p>DR drill 的風險在於權限、資料與外部通訊。災難恢復通常涉及高權限操作、備份還原與跨團隊協作，因此需要額外 audit trail 與 incident communication 準備。</p>
<h2 id="boundary-契約">Boundary 契約</h2>
<p>Experiment boundary 契約的責任是讓實驗在開始前就具備可停止、可回復與可復盤條件。契約應被寫成實驗 artifact，並納入可回查的操作紀錄。</p>
<table>
  <thead>
      <tr>
          <th>契約欄位</th>
          <th>責任</th>
          <th>判讀用途</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Hypothesis</td>
          <td>說明要驗證的 failure mode</td>
          <td>避免實驗變成任意故障注入</td>
      </tr>
      <tr>
          <td>Blast radius</td>
          <td>限制服務、tenant、region 範圍</td>
          <td>控制實際影響</td>
      </tr>
      <tr>
          <td>Steady state</td>
          <td>定義實驗期間應維持的狀態</td>
          <td>判斷實驗是否成功</td>
      </tr>
      <tr>
          <td>Stop condition</td>
          <td>定義終止門檻</td>
          <td>讓失控時能立刻停手</td>
      </tr>
      <tr>
          <td>Rollback path</td>
          <td>定義回復步驟</td>
          <td>降低終止後的恢復成本</td>
      </tr>
      <tr>
          <td>Authority</td>
          <td>定義啟動、停止與擴大權限</td>
          <td>避免事中權責不清</td>
      </tr>
      <tr>
          <td>Evidence</td>
          <td>定義要收集的觀測與決策紀錄</td>
          <td>支援復盤與可重播</td>
      </tr>
  </tbody>
</table>
<p>Hypothesis 是實驗的錨點。好的假設會說明「當 dependency timeout 發生時，checkout 應進入 degraded mode，SLO <a href="/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">burn rate</a> 應維持在門檻內」，而不只是「關掉某個服務」。</p>
<p>Blast radius 需要同時包含技術範圍與客戶範圍。技術範圍是 service、region、cluster、dependency；客戶範圍是 tenant、plan、traffic percentage 或 internal-only cohort。</p>
<p>Stop condition 需要對應使用者影響。CPU 上升可以作為輔助訊號，但停止條件更應包含 SLO burn、error rate、latency、queue lag、customer ticket、成本與安全事件。</p>
<p>Authority 需要事前分清。executor 可以啟動實驗，observer 可以判讀訊號，incident commander 或 designated stop owner 必須有權直接終止實驗。</p>
<h2 id="判讀訊號">判讀訊號</h2>
<ul>
<li>chaos 實驗描述只有「打掉節點」，沒有 steady state 與停止條件</li>
<li>load test 影響共享 dependency，其他服務被連帶拖垮</li>
<li>DR drill 的停止擴大條件需要臨場討論</li>
<li>實驗成功但沒有 evidence，可重播性不足</li>
<li>實驗權限過寬，值班人員不知道誰在操作</li>
</ul>
<p>常見事故型場景是 load test 誤傷共享依賴，導致無關服務一起退化。若實驗前有 boundary 契約，至少會先限制流量比例、設定跨服務告警與 stop condition，讓問題停留在演練範圍內。</p>
<h2 id="stop-condition-設計">Stop Condition 設計</h2>
<p>Stop condition 的責任是把「什麼時候停」變成可觀測門檻。實驗期間不應靠臨場感覺判斷是否繼續，應根據預先同意的訊號停止或縮小範圍。</p>
<table>
  <thead>
      <tr>
          <th>停止條件</th>
          <th>常見門檻</th>
          <th>路由</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>SLO burn</td>
          <td>短窗 burn rate 超過 policy</td>
          <td>終止實驗，進 incident intake</td>
      </tr>
      <tr>
          <td>Customer impact</td>
          <td>ticket、RUM、synthetic probe 異常</td>
          <td>終止或降到 internal cohort</td>
      </tr>
      <tr>
          <td>Queue lag</td>
          <td>lag 超過 drain 能力</td>
          <td>暫停流量，啟動 drain plan</td>
      </tr>
      <tr>
          <td>Error rate</td>
          <td>目標服務或相鄰服務錯誤率上升</td>
          <td>縮小 blast radius</td>
      </tr>
      <tr>
          <td>Cost threshold</td>
          <td>cloud cost 或 observability cost 暴增</td>
          <td>終止 load / trace 擴張</td>
      </tr>
      <tr>
          <td>Security signal</td>
          <td>audit、WAF、IAM 異常</td>
          <td>停止實驗，轉 07 / 08 分流</td>
      </tr>
  </tbody>
</table>
<p>SLO burn 是最適合作為 stop condition 的可靠性訊號。它能把多個低層訊號聚合成使用者影響，並且直接接到 <a href="/blog/backend/knowledge-cards/error-budget/" data-link-title="Error Budget" data-link-desc="說明 SLO 允許的失敗額度如何影響發版與可靠性投入">error budget</a> 與 release policy。</p>
<p>Customer impact 是停止條件的高優先訊號。即使 backend 指標尚未超標，只要 RUM、synthetic probe、support ticket 或 status page evidence 顯示客戶受影響，實驗就應縮小或終止。</p>
<p>Security signal 需要獨立路由。若實驗觸發異常權限、audit log gap、WAF event 或資料外送風險，應停止 reliability experiment，改由 security / incident response 流程判讀。</p>
<h2 id="evidence-與復盤">Evidence 與復盤</h2>
<p>Experiment evidence 的責任是讓實驗結果可被重播、比較與回寫。一次實驗不論成功或失敗，都應產出可被後續 readiness、release gate 與 incident drill 使用的證據。</p>
<table>
  <thead>
      <tr>
          <th>Evidence 欄位</th>
          <th>責任</th>
          <th>後續用途</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Hypothesis</td>
          <td>保留原始假設</td>
          <td>判斷成功或失敗</td>
      </tr>
      <tr>
          <td>Timeline</td>
          <td>記錄開始、注入、停止、回復</td>
          <td>產生 incident / drill 時間線</td>
      </tr>
      <tr>
          <td>Signal set</td>
          <td>保存 dashboard、query、alert</td>
          <td>回寫 04 observability readiness</td>
      </tr>
      <tr>
          <td>Decision log</td>
          <td>保存停止、擴大、回復決策</td>
          <td>支援 08 <a href="/blog/backend/knowledge-cards/incident-decision-log/" data-link-title="Incident Decision Log" data-link-desc="說明事故期間如何保留決策、證據、owner 與回退條件">incident decision log</a></td>
      </tr>
      <tr>
          <td>Action items</td>
          <td>保存缺口與 owner</td>
          <td>進入 reliability debt backlog</td>
      </tr>
  </tbody>
</table>
<p>成功實驗也需要 evidence。成功代表某個假設在某個範圍內成立，未必代表所有流量、region、tenant 或依賴都安全；evidence 能保留適用範圍。</p>
<p>失敗實驗需要分清系統缺口與實驗缺口。系統缺口可能是 fallback 沒生效；實驗缺口可能是 stop condition 不清、dashboard 缺訊號或 owner 權限不足。兩者回寫路由不同。</p>
<h2 id="案例對照chaos--fit-的安全邊界設計">案例對照：Chaos / FIT 的安全邊界設計</h2>
<p>本章的 boundary 跟 stop condition 框架在 Netflix 三個 case 中各對應不同子問題：N1 給出單輪 chaos 的四元素、N2 給出時段選擇 guardrails、N3 給出實驗輸出的結構化欄位。三者連起來、安全邊界從「實驗執行階段」延伸到「證據交接階段」。</p>
<p>對應 <a href="/blog/backend/06-reliability/cases/netflix/steady-state-chaos-and-fit/" data-link-title="Netflix：Steady State、Chaos 與 FIT 的驗證路徑" data-link-desc="把故障注入從工具操作升級成可驗證流程：先定義穩態，再設計注入與回復條件。">N1 Netflix Steady State、Chaos 與 FIT</a>：揭露一輪有效 chaos 驗證的四元素 — Steady state（服務正常時應維持什麼行為）、Hypothesis（失效發生後仍應維持什麼）、Blast radius（實驗範圍怎麼限制）、Abort condition（何時立即停止）。</p>
<p>四元素中 Blast radius + Abort condition 直接對應本章的 boundary 契約跟 stop condition。Steady state 對應 <a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22 steady-state-definition</a>、Hypothesis 對應實驗設計層。</p>
<p>對應 <a href="/blog/backend/06-reliability/cases/netflix/chaos-monkey-business-hours-guardrails/" data-link-title="Netflix：Business-Hours Chaos 與 Guardrails" data-link-desc="Chaos Monkey 為何刻意在 business hours 執行：把即時應變能力納入驗證，並用 guardrails 限制實驗風險。">N2 Netflix Business-Hours Chaos 與 Guardrails</a>：揭露「business-hours chaos 跟 off-hours chaos 的選擇」— 工作時間執行能驗證即時應變能力跟通訊鏈條、但要在 guardrails 內（時段限制、實驗範圍限制、明確 abort trigger、事後回寫）。</p>
<p>Business-hours chaos 的核心價值是在 guardrails 內接近真實情境：人員在線可即時應變、依賴流量真實、通訊鏈條被測到。Off-hours 雖然短期風險低、但測到的多是「工具可執行」、不等於「服務可承受」。</p>
<p>對應 <a href="/blog/backend/06-reliability/cases/netflix/fit-failure-injection-evidence-handoff/" data-link-title="Netflix：FIT 證據交接與 Release Gate 回寫" data-link-desc="用 Failure Injection Testing 產出的證據直接驅動 release gate：把實驗結果轉成可放行、可凍結、可回退的決策欄位。">N3 Netflix FIT 證據交接</a>：揭露實驗輸出要結構化成四個決策欄位。四欄位分屬不同 release gate 階段 — rollout 決策類（steady-state impact、dependency drift）回答「能否繼續 rollout / blast radius 是否可接受」、事故處置類（abort trigger record、fallback result）回答「是否進入凍結與回退 / 事故時能否安全止血」。這四欄位讓 FIT 結果直接對應 release gate 的具體決策 — 不再倚賴主觀討論回到放行 / 凍結判斷。詳見 <a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23 verification-evidence-handoff</a> 跟 <a href="/blog/backend/06-reliability/rule-rollout-safety-gate/" data-link-title="6.24 規則推送安全閘門" data-link-desc="把規則、策略與控制面配置推送從部署步驟升級為可靠性 gate，避免小變更在秒級擴散成全網事故。">6.24 rule-rollout-safety-gate</a>。</p>
<h2 id="常見反模式">常見反模式</h2>
<p>Experiment safety 的反模式通常來自把可靠性實驗當成勇敢行為。可靠性實驗的價值在設計、控制與學習，風險承受只是需要被管理的成本。</p>
<table>
  <thead>
      <tr>
          <th>反模式</th>
          <th>表面現象</th>
          <th>修正方向</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>直接打 production chaos</td>
          <td>真實但邊界不清</td>
          <td>先定義 cohort、stop condition</td>
      </tr>
      <tr>
          <td>無 steady state</td>
          <td>只知道打壞了什麼</td>
          <td>補 6.22 穩態定義</td>
      </tr>
      <tr>
          <td>無 stop owner</td>
          <td>超門檻後仍等會議決定</td>
          <td>指定有權停止的人</td>
      </tr>
      <tr>
          <td>缺 evidence</td>
          <td>實驗做過但缺少重播材料</td>
          <td>保存 hypothesis、timeline、signal</td>
      </tr>
      <tr>
          <td>權限過寬</td>
          <td>任意工程師可擴大 blast radius</td>
          <td>啟動、停止、擴大權限分離</td>
      </tr>
  </tbody>
</table>
<p>直接打 production chaos 的問題是風險與學習常被混在一起。production 實驗可以有價值，但需要從小 cohort、清楚 stop condition 與完整 rollback path 開始。</p>
<p>缺 evidence 會讓實驗只留下口頭記憶。可靠性能力需要累積，實驗結果應能回寫到 readiness、release gate、runbook 與 incident drill。</p>
<h2 id="交接路由">交接路由</h2>
<ul>
<li>04.16 observability readiness：確認實驗可被觀測</li>
<li>06.4 chaos testing：定義故障注入場景</li>
<li>06.7 DR / rollback rehearsal：定義回復路徑</li>
<li>06.22 steady state definition：定義實驗前 <a href="/blog/backend/knowledge-cards/steady-state/" data-link-title="Steady State" data-link-desc="說明可靠性實驗與事故恢復如何定義系統應維持的可接受狀態">steady state</a></li>
<li>07.23 shared controls：接 containment、rollback、degradation 共用控制面</li>
<li>08.6 drills / on-call readiness：把實驗轉成值班演練</li>
</ul>
]]></content:encoded></item><item><title>Honeycomb：以 Burn Rate 驅動的可靠性操作</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/honeycomb/burn-rate-driven-reliability-operations/</link><pubDate>Thu, 07 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/honeycomb/burn-rate-driven-reliability-operations/</guid><description>&lt;p>Honeycomb 案例的核心責任是把可觀測訊號直接轉成可靠性決策。當團隊面對大量告警時，burn rate 提供比固定閾值更接近使用者體感的判讀方式。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>固定閾值告警在高變化流量下容易失真。團隊可能長時間處於告警疲勞，卻看不出真正侵蝕 SLO 的事件。&lt;/p>
&lt;h2 id="決策機制">決策機制&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>機制&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>交付結果&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Burn rate 警示&lt;/td>
 &lt;td>可靠性消耗速度是否異常&lt;/td>
 &lt;td>優先序判讀&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>SLO 驅動值班&lt;/td>
 &lt;td>哪些事件需要立即接手&lt;/td>
 &lt;td>響應節奏&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Tracing-first 分析&lt;/td>
 &lt;td>事件路徑如何定位&lt;/td>
 &lt;td>可追溯證據&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>fast burn&lt;/td>
 &lt;td>短期消耗是否超過容忍帶&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>slow burn&lt;/td>
 &lt;td>長期趨勢是否持續惡化&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/04-observability/sli-slo-signal/" data-link-title="4.6 SLI 量測與 SLO 訊號設計" data-link-desc="把可靠性目標的訊號從 metric 端設計好、餵給 6.6 SLO 政策">4.6&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>trace outlier path&lt;/td>
 &lt;td>關鍵路徑是否集中退化&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/04-observability/tracing-context/" data-link-title="4.3 tracing 與 context link" data-link-desc="整理 trace id、span 與跨服務 context propagation">4.3&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>先用 &lt;a href="https://tarrragon.github.io/blog/backend/04-observability/observability-evidence-package/" data-link-title="4.20 Observability Evidence Package" data-link-desc="把 log、metric、trace、audit 與資料品質限制包成可交接證據">4.20&lt;/a> 組證據，再在 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23&lt;/a> 回寫驗證條件。&lt;/p></description><content:encoded><![CDATA[<p>Honeycomb 案例的核心責任是把可觀測訊號直接轉成可靠性決策。當團隊面對大量告警時，burn rate 提供比固定閾值更接近使用者體感的判讀方式。</p>
<h2 id="問題場景">問題場景</h2>
<p>固定閾值告警在高變化流量下容易失真。團隊可能長時間處於告警疲勞，卻看不出真正侵蝕 SLO 的事件。</p>
<h2 id="決策機制">決策機制</h2>
<table>
  <thead>
      <tr>
          <th>機制</th>
          <th>核心問題</th>
          <th>交付結果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Burn rate 警示</td>
          <td>可靠性消耗速度是否異常</td>
          <td>優先序判讀</td>
      </tr>
      <tr>
          <td>SLO 驅動值班</td>
          <td>哪些事件需要立即接手</td>
          <td>響應節奏</td>
      </tr>
      <tr>
          <td>Tracing-first 分析</td>
          <td>事件路徑如何定位</td>
          <td>可追溯證據</td>
      </tr>
  </tbody>
</table>
<h2 id="可觀測訊號">可觀測訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>fast burn</td>
          <td>短期消耗是否超過容忍帶</td>
          <td><a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6</a></td>
      </tr>
      <tr>
          <td>slow burn</td>
          <td>長期趨勢是否持續惡化</td>
          <td><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</a></td>
      </tr>
      <tr>
          <td>trace outlier path</td>
          <td>關鍵路徑是否集中退化</td>
          <td><a href="/blog/backend/04-observability/tracing-context/" data-link-title="4.3 tracing 與 context link" data-link-desc="整理 trace id、span 與跨服務 context propagation">4.3</a></td>
      </tr>
  </tbody>
</table>
<h2 id="下一步路由">下一步路由</h2>
<p>先用 <a href="/blog/backend/04-observability/observability-evidence-package/" data-link-title="4.20 Observability Evidence Package" data-link-desc="把 log、metric、trace、audit 與資料品質限制包成可交接證據">4.20</a> 組證據，再在 <a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23</a> 回寫驗證條件。</p>
]]></content:encoded></item><item><title>Netflix：Steady State、Chaos 與 FIT 的驗證路徑</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/netflix/steady-state-chaos-and-fit/</link><pubDate>Thu, 07 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/netflix/steady-state-chaos-and-fit/</guid><description>&lt;p>Netflix chaos 實踐的核心責任是驗證「服務在失效條件下是否仍維持 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/steady-state/" data-link-title="Steady State" data-link-desc="說明可靠性實驗與事故恢復如何定義系統應維持的可接受狀態">steady state&lt;/a>」。重點是注入後能否用明確訊號證明系統仍可服務，故障注入數量是次要考量。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>許多團隊會做壓測與演練，但演練設計常停在工具層：kill instance、斷連線、延遲注入。這些動作本身不會自動產生可靠性結論。若沒有 steady state 與停止條件，演練只會留下「有做過 chaos」的紀錄。&lt;/p>
&lt;p>Netflix 的價值在於把 chaos 轉成科學化驗證循環：先定義穩態，再設計可證偽的假設。&lt;/p>
&lt;h2 id="決策機制">決策機制&lt;/h2>
&lt;p>一輪有效的 chaos 驗證要同時具備四個元素。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>元素&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>交付結果&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Steady state&lt;/td>
 &lt;td>服務正常時應維持什麼行為&lt;/td>
 &lt;td>穩態指標&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Hypothesis&lt;/td>
 &lt;td>失效發生後仍應維持什麼&lt;/td>
 &lt;td>可證偽假設&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Blast radius&lt;/td>
 &lt;td>實驗範圍怎麼限制&lt;/td>
 &lt;td>實驗邊界&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Abort condition&lt;/td>
 &lt;td>何時立即停止&lt;/td>
 &lt;td>風險切斷條件&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>FIT（Failure Injection Testing）把注入粒度推進到 request path，讓測試更接近真實依賴路徑。這讓團隊能在不擴大範圍的前提下，驗證高價值路徑的容錯能力。&lt;/p>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>steady-state SLI&lt;/td>
 &lt;td>注入後是否維持服務承諾&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>abort trigger count&lt;/td>
 &lt;td>停止條件是否可執行&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>fallback success ratio&lt;/td>
 &lt;td>降級與替代路徑是否有效&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/containment-recovery-strategy/" data-link-title="8.3 止血、降級與回復策略" data-link-desc="把短期止血與正式回復拆成可執行步驟">8.3&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>trace degradation pattern&lt;/td>
 &lt;td>退化是否集中於預期依賴&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/04-observability/tracing-context/" data-link-title="4.3 tracing 與 context link" data-link-desc="整理 trace id、span 與跨服務 context propagation">4.3&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="常見陷阱">常見陷阱&lt;/h2>
&lt;p>最常見錯誤是把 chaos 視為「故障越大越好」。這會把演練從驗證流程變成壓力展示，增加真實風險卻不提升可學習性。有效做法是用最小 blast radius 驗證最高價值假設，然後逐步放大。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>若要把本案例落地，先寫 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22&lt;/a> 的穩態欄位，再在 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20&lt;/a> 定義停止條件。案例輸出的證據交給 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/incident-evidence-write-back/" data-link-title="8.22 Incident Evidence Write-back" data-link-desc="把事故證據、決策與復盤結論回寫到 observability、reliability 與 runbook">8.22&lt;/a>。&lt;/p></description><content:encoded><![CDATA[<p>Netflix chaos 實踐的核心責任是驗證「服務在失效條件下是否仍維持 <a href="/blog/backend/knowledge-cards/steady-state/" data-link-title="Steady State" data-link-desc="說明可靠性實驗與事故恢復如何定義系統應維持的可接受狀態">steady state</a>」。重點是注入後能否用明確訊號證明系統仍可服務，故障注入數量是次要考量。</p>
<h2 id="問題場景">問題場景</h2>
<p>許多團隊會做壓測與演練，但演練設計常停在工具層：kill instance、斷連線、延遲注入。這些動作本身不會自動產生可靠性結論。若沒有 steady state 與停止條件，演練只會留下「有做過 chaos」的紀錄。</p>
<p>Netflix 的價值在於把 chaos 轉成科學化驗證循環：先定義穩態，再設計可證偽的假設。</p>
<h2 id="決策機制">決策機制</h2>
<p>一輪有效的 chaos 驗證要同時具備四個元素。</p>
<table>
  <thead>
      <tr>
          <th>元素</th>
          <th>核心問題</th>
          <th>交付結果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Steady state</td>
          <td>服務正常時應維持什麼行為</td>
          <td>穩態指標</td>
      </tr>
      <tr>
          <td>Hypothesis</td>
          <td>失效發生後仍應維持什麼</td>
          <td>可證偽假設</td>
      </tr>
      <tr>
          <td>Blast radius</td>
          <td>實驗範圍怎麼限制</td>
          <td>實驗邊界</td>
      </tr>
      <tr>
          <td>Abort condition</td>
          <td>何時立即停止</td>
          <td>風險切斷條件</td>
      </tr>
  </tbody>
</table>
<p>FIT（Failure Injection Testing）把注入粒度推進到 request path，讓測試更接近真實依賴路徑。這讓團隊能在不擴大範圍的前提下，驗證高價值路徑的容錯能力。</p>
<h2 id="可觀測訊號">可觀測訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>steady-state SLI</td>
          <td>注入後是否維持服務承諾</td>
          <td><a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22</a></td>
      </tr>
      <tr>
          <td>abort trigger count</td>
          <td>停止條件是否可執行</td>
          <td><a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20</a></td>
      </tr>
      <tr>
          <td>fallback success ratio</td>
          <td>降級與替代路徑是否有效</td>
          <td><a href="/blog/backend/08-incident-response/containment-recovery-strategy/" data-link-title="8.3 止血、降級與回復策略" data-link-desc="把短期止血與正式回復拆成可執行步驟">8.3</a></td>
      </tr>
      <tr>
          <td>trace degradation pattern</td>
          <td>退化是否集中於預期依賴</td>
          <td><a href="/blog/backend/04-observability/tracing-context/" data-link-title="4.3 tracing 與 context link" data-link-desc="整理 trace id、span 與跨服務 context propagation">4.3</a></td>
      </tr>
  </tbody>
</table>
<h2 id="常見陷阱">常見陷阱</h2>
<p>最常見錯誤是把 chaos 視為「故障越大越好」。這會把演練從驗證流程變成壓力展示，增加真實風險卻不提升可學習性。有效做法是用最小 blast radius 驗證最高價值假設，然後逐步放大。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>若要把本案例落地，先寫 <a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22</a> 的穩態欄位，再在 <a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20</a> 定義停止條件。案例輸出的證據交給 <a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23</a> 與 <a href="/blog/backend/08-incident-response/incident-evidence-write-back/" data-link-title="8.22 Incident Evidence Write-back" data-link-desc="把事故證據、決策與復盤結論回寫到 observability、reliability 與 runbook">8.22</a>。</p>
]]></content:encoded></item><item><title>6.21 Reliability Debt Backlog</title><link>https://tarrragon.github.io/blog/backend/06-reliability/reliability-debt-backlog/</link><pubDate>Sat, 02 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/reliability-debt-backlog/</guid><description>&lt;h2 id="大綱">大綱&lt;/h2>
&lt;ul>
&lt;li>reliability debt 的責任：把可靠性缺口從口頭風險變成可管理 backlog&lt;/li>
&lt;li>來源：post-incident review、game day、load test、chaos、on-call toil、customer ticket&lt;/li>
&lt;li>debt 類型：missing automation、weak rollback、manual recovery、fragile dependency、observability gap&lt;/li>
&lt;li>欄位：impact、frequency、owner、evidence、mitigation、target state、closure signal&lt;/li>
&lt;li>排序方式：SLO 影響、事故重複率、toil 成本、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/blast-radius/" data-link-title="Blast Radius" data-link-desc="說明事故影響面如何估算與隔離">blast radius&lt;/a>、修復成本&lt;/li>
&lt;li>關閉條件：測試、演練、runbook 更新、alert 改善、manual step 移除&lt;/li>
&lt;li>跟 08 的交接：PIR action item 進 reliability debt，集中成可追蹤工作&lt;/li>
&lt;li>反模式：每次復盤都列改善，三個月後仍 open；toil 沒有量化；debt 無 owner&lt;/li>
&lt;/ul>
&lt;p>Reliability debt backlog 的重點是把「事故教訓」轉成「可交付工作」。沒有 backlog，團隊每次復盤都會得到相似結論；有 backlog，才有辦法把缺口排序、分派、驗收並逐步關閉。&lt;/p>
&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>Reliability debt backlog 是管理可靠性缺口的工作佇列，責任是把反覆事故、演練缺口與手動修復轉成可排序、可驗證、可關閉的工程工作。&lt;/p>
&lt;p>這一頁處理的是債務治理。可靠性問題常以事故、值班疲勞與手動操作出現；backlog 讓這些訊號進入產品與工程排程。&lt;/p>
&lt;p>debt backlog 也提供跨團隊溝通語言。平台、服務、SRE 與產品可以用同一組欄位討論優先序，讓決策建立在同一批證據與欄位定義上。&lt;/p>
&lt;h2 id="核心判讀">核心判讀&lt;/h2>
&lt;p>判讀 reliability debt 時，先看缺口是否有 evidence，再看關閉條件是否可驗證。&lt;/p>
&lt;p>重點訊號包括：&lt;/p>
&lt;ul>
&lt;li>debt 是否連到事故、演練或 toil 證據&lt;/li>
&lt;li>owner 是否能決定修復方案與排程&lt;/li>
&lt;li>impact 是否能對應 SLO、customer impact 或 on-call cost&lt;/li>
&lt;li>mitigation 是否只降低風險，或真正移除根因&lt;/li>
&lt;li>closure signal 是否能由測試、演練或監控證明&lt;/li>
&lt;/ul>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>欄位&lt;/th>
 &lt;th>目的&lt;/th>
 &lt;th>驗收重點&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Impact / Frequency&lt;/td>
 &lt;td>定義業務與技術代價&lt;/td>
 &lt;td>是否可量化到 SLO / toil / 客訴&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Owner / Due&lt;/td>
 &lt;td>明確責任與時程&lt;/td>
 &lt;td>是否有人可決策與執行&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Evidence&lt;/td>
 &lt;td>連回事故或演練證據&lt;/td>
 &lt;td>是否能追溯原始問題&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Mitigation / Target&lt;/td>
 &lt;td>區分短期止血與長期修法&lt;/td>
 &lt;td>是否避免只補 workaround&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Closure Signal&lt;/td>
 &lt;td>定義完成條件&lt;/td>
 &lt;td>是否可由測試或演練驗證&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="判讀訊號">判讀訊號&lt;/h2>
&lt;ul>
&lt;li>同類事故重複發生，但每次 action item 都重新命名&lt;/li>
&lt;li>on-call 反覆手動修同一個問題&lt;/li>
&lt;li>runbook 記錄 workaround，但沒有工程化任務&lt;/li>
&lt;li>debt backlog 只有優先級，缺少 impact / evidence / closure&lt;/li>
&lt;li>reliability 工作永遠輸給 feature，但事故成本持續上升&lt;/li>
&lt;/ul>
&lt;p>實務上最常見的失敗模式是 action item 全留在會議筆記。三個月後同類事故再發生，團隊才重新開同一張單。把 PIR 直接轉進 debt backlog，才能讓「是否真的改善」變成可驗證事實。&lt;/p>
&lt;h2 id="action-item-分級跟-release-gate-綁定">Action Item 分級跟 Release Gate 綁定&lt;/h2>
&lt;p>Action item 分級的核心責任是給每個改進項匹配的強制力：高風險者進 release gate 綁定、中風險者進 backlog 落地節點、低風險者保留追蹤節點。三類風險（重複事故、影響面放大、診斷效率）各需不同強制力、沒有分級時所有改進項並列競爭資源、強制力被攤平。&lt;/p>
&lt;p>對應 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/google/postmortem-action-item-closure-governance/" data-link-title="Google：Postmortem Action Item Closure 治理" data-link-desc="把 blameless postmortem 從會議文件變成可追蹤的可靠性治理機制：action item 分級、完成條件與回寫節奏。">G2 Google Postmortem Action Item Closure 治理&lt;/a>：揭露三層機制對應上述三類風險 — action item 分級（P0/P1/P2）、可驗證完成條件（不是「優化」「強化」抽象字）、closure 進固定 review cadence。&lt;/p>
&lt;p>P0/P1/P2 分級的核心價值是「給高風險 action item 強制力」：&lt;/p>
&lt;ul>
&lt;li>P0 重複事故高機率再發生：下個 release 週期前完成並驗證&lt;/li>
&lt;li>P1 會放大事故影響面：要有落地日期跟 gate 條件&lt;/li>
&lt;li>P2 提升診斷或操作效率：可排 backlog、但保留追蹤節點&lt;/li>
&lt;/ul>
&lt;p>最關鍵的綁定是 &lt;strong>P0/P1 直接掛到 release gate&lt;/strong>：未完成時不得放行關聯變更。這層綁定才讓分級從「backlog 優先序」升級為「工程強制力」 — P0/P1 直接決定 release 是否放行、未完成的 action item 直接是放行條件缺口。詳見 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/release-gate/#%e8%ae%8a%e6%9b%b4%e5%88%86%e5%b1%a4%e8%b7%9f-gate-%e6%94%bf%e7%ad%96" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate 變更分層&lt;/a>。&lt;/p></description><content:encoded><![CDATA[<h2 id="大綱">大綱</h2>
<ul>
<li>reliability debt 的責任：把可靠性缺口從口頭風險變成可管理 backlog</li>
<li>來源：post-incident review、game day、load test、chaos、on-call toil、customer ticket</li>
<li>debt 類型：missing automation、weak rollback、manual recovery、fragile dependency、observability gap</li>
<li>欄位：impact、frequency、owner、evidence、mitigation、target state、closure signal</li>
<li>排序方式：SLO 影響、事故重複率、toil 成本、<a href="/blog/backend/knowledge-cards/blast-radius/" data-link-title="Blast Radius" data-link-desc="說明事故影響面如何估算與隔離">blast radius</a>、修復成本</li>
<li>關閉條件：測試、演練、runbook 更新、alert 改善、manual step 移除</li>
<li>跟 08 的交接：PIR action item 進 reliability debt，集中成可追蹤工作</li>
<li>反模式：每次復盤都列改善，三個月後仍 open；toil 沒有量化；debt 無 owner</li>
</ul>
<p>Reliability debt backlog 的重點是把「事故教訓」轉成「可交付工作」。沒有 backlog，團隊每次復盤都會得到相似結論；有 backlog，才有辦法把缺口排序、分派、驗收並逐步關閉。</p>
<h2 id="概念定位">概念定位</h2>
<p>Reliability debt backlog 是管理可靠性缺口的工作佇列，責任是把反覆事故、演練缺口與手動修復轉成可排序、可驗證、可關閉的工程工作。</p>
<p>這一頁處理的是債務治理。可靠性問題常以事故、值班疲勞與手動操作出現；backlog 讓這些訊號進入產品與工程排程。</p>
<p>debt backlog 也提供跨團隊溝通語言。平台、服務、SRE 與產品可以用同一組欄位討論優先序，讓決策建立在同一批證據與欄位定義上。</p>
<h2 id="核心判讀">核心判讀</h2>
<p>判讀 reliability debt 時，先看缺口是否有 evidence，再看關閉條件是否可驗證。</p>
<p>重點訊號包括：</p>
<ul>
<li>debt 是否連到事故、演練或 toil 證據</li>
<li>owner 是否能決定修復方案與排程</li>
<li>impact 是否能對應 SLO、customer impact 或 on-call cost</li>
<li>mitigation 是否只降低風險，或真正移除根因</li>
<li>closure signal 是否能由測試、演練或監控證明</li>
</ul>
<table>
  <thead>
      <tr>
          <th>欄位</th>
          <th>目的</th>
          <th>驗收重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Impact / Frequency</td>
          <td>定義業務與技術代價</td>
          <td>是否可量化到 SLO / toil / 客訴</td>
      </tr>
      <tr>
          <td>Owner / Due</td>
          <td>明確責任與時程</td>
          <td>是否有人可決策與執行</td>
      </tr>
      <tr>
          <td>Evidence</td>
          <td>連回事故或演練證據</td>
          <td>是否能追溯原始問題</td>
      </tr>
      <tr>
          <td>Mitigation / Target</td>
          <td>區分短期止血與長期修法</td>
          <td>是否避免只補 workaround</td>
      </tr>
      <tr>
          <td>Closure Signal</td>
          <td>定義完成條件</td>
          <td>是否可由測試或演練驗證</td>
      </tr>
  </tbody>
</table>
<h2 id="判讀訊號">判讀訊號</h2>
<ul>
<li>同類事故重複發生，但每次 action item 都重新命名</li>
<li>on-call 反覆手動修同一個問題</li>
<li>runbook 記錄 workaround，但沒有工程化任務</li>
<li>debt backlog 只有優先級，缺少 impact / evidence / closure</li>
<li>reliability 工作永遠輸給 feature，但事故成本持續上升</li>
</ul>
<p>實務上最常見的失敗模式是 action item 全留在會議筆記。三個月後同類事故再發生，團隊才重新開同一張單。把 PIR 直接轉進 debt backlog，才能讓「是否真的改善」變成可驗證事實。</p>
<h2 id="action-item-分級跟-release-gate-綁定">Action Item 分級跟 Release Gate 綁定</h2>
<p>Action item 分級的核心責任是給每個改進項匹配的強制力：高風險者進 release gate 綁定、中風險者進 backlog 落地節點、低風險者保留追蹤節點。三類風險（重複事故、影響面放大、診斷效率）各需不同強制力、沒有分級時所有改進項並列競爭資源、強制力被攤平。</p>
<p>對應 <a href="/blog/backend/06-reliability/cases/google/postmortem-action-item-closure-governance/" data-link-title="Google：Postmortem Action Item Closure 治理" data-link-desc="把 blameless postmortem 從會議文件變成可追蹤的可靠性治理機制：action item 分級、完成條件與回寫節奏。">G2 Google Postmortem Action Item Closure 治理</a>：揭露三層機制對應上述三類風險 — action item 分級（P0/P1/P2）、可驗證完成條件（不是「優化」「強化」抽象字）、closure 進固定 review cadence。</p>
<p>P0/P1/P2 分級的核心價值是「給高風險 action item 強制力」：</p>
<ul>
<li>P0 重複事故高機率再發生：下個 release 週期前完成並驗證</li>
<li>P1 會放大事故影響面：要有落地日期跟 gate 條件</li>
<li>P2 提升診斷或操作效率：可排 backlog、但保留追蹤節點</li>
</ul>
<p>最關鍵的綁定是 <strong>P0/P1 直接掛到 release gate</strong>：未完成時不得放行關聯變更。這層綁定才讓分級從「backlog 優先序」升級為「工程強制力」 — P0/P1 直接決定 release 是否放行、未完成的 action item 直接是放行條件缺口。詳見 <a href="/blog/backend/06-reliability/release-gate/#%e8%ae%8a%e6%9b%b4%e5%88%86%e5%b1%a4%e8%b7%9f-gate-%e6%94%bf%e7%ad%96" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 release gate 變更分層</a>。</p>
<p>整體 reliability 訊號量化（含 toil ratio、closure rate、debt 趨勢）由 <a href="/blog/backend/06-reliability/reliability-metrics-governance/" data-link-title="6.18 Reliability Metrics Governance" data-link-desc="DORA / SPACE 指標的選用、量測陷阱、anti-gaming 與團隊階段適配">6.18 reliability-metrics-governance</a> 處理。</p>
<h2 id="toil-budget把重複手動工作變成預算問題">Toil Budget：把重複手動工作變成預算問題</h2>
<p>Toil budget 是把重複手動工作量化成預算、用 closure 機制強制超標部分轉投自動化的治理工具。Toil 沒被當預算治理時、會吸收 SRE 時間、把可靠性改進工作擠掉。</p>
<p>對應 <a href="/blog/backend/06-reliability/cases/google/toil-budget-and-automation-investment-policy/" data-link-title="Google：Toil Budget 與 Automation 投資政策" data-link-desc="把 toil 從感受問題轉成預算問題：用時間配比與自動化回報機制，避免 on-call 壓力長期侵蝕可靠性工程。">G3 Google Toil Budget 與 Automation 投資政策</a>：揭露四個機制 — toil 分類（哪些屬可自動化）、時間配比（Google SRE 經驗值 50%、組織應依自身 toil 性質校準、不是普世門檻）、超標處理（凍結部分 feature、轉投自動化）、改善驗證（closure 指標跟 evidence）。前兩項屬「測量設計」（toil 是什麼 + 多少算超標）、後兩項屬「治理動作」（超標後做什麼 + 改善如何驗證）。</p>
<p>Toil budget 跟 reliability debt backlog 是兩個面向：</p>
<ul>
<li>Reliability debt backlog：管「失效缺口」（事故 / 演練揭露的工程化任務）</li>
<li>Toil budget：管「日常壓力」（on-call 反覆手動工作的時間成本）</li>
</ul>
<p>兩者要共用同一個 closure 機制：toil 超標時、超標部分強制轉投自動化、進 debt backlog 排序、按完成條件驗收。這層綁定讓 toil 超標自動觸發改善排程：超標 ratio 進日常輸入信號、相關 feature 凍結、自動化工作進 debt backlog 排序、按完成條件驗收。把 toil ratio 當日常治理輸入、而非 on-call burnout 後的事後指標。</p>
<h2 id="交接路由">交接路由</h2>
<ul>
<li>04.8 signal governance loop：把觀測缺口變成 debt</li>
<li>06.8 release gate：高風險 debt 可成為 freeze 條件</li>
<li>06.18 reliability metrics governance：量化 debt 趨勢</li>
<li>08.5 post-incident review：PIR action items 的上游來源</li>
<li>08.13 repeated incident / toil：反覆事故與 toil 的事故端入口</li>
</ul>
]]></content:encoded></item><item><title>Spotify</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/spotify/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/spotify/</guid><description>&lt;p>Spotify 是音樂串流平台、squad-based 組織模型對 SRE 實踐有特殊影響、chaos engineering 文章是 mid-size company 採用 chaos 的代表。&lt;/p>
&lt;h2 id="規劃重點">規劃重點&lt;/h2>
&lt;ul>
&lt;li>Squad-based SRE：分散式組織下的可靠性責任分配&lt;/li>
&lt;li>Backstage：開源開發者平台的可靠性整合&lt;/li>
&lt;li>Chaos engineering 採用過程：從 zero 到 mature 的實踐軌跡&lt;/li>
&lt;li>Streaming infrastructure：高頻寬媒體的可靠性挑戰&lt;/li>
&lt;/ul>
&lt;h2 id="預計收錄實踐">預計收錄實踐&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>議題&lt;/th>
 &lt;th>教學重點&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Backstage&lt;/td>
 &lt;td>service catalog + reliability metadata&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Squad SRE&lt;/td>
 &lt;td>分散組織的可靠性責任&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Chaos Engineering Adoption&lt;/td>
 &lt;td>Spotify 的 chaos 起步歷程&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>CDN / Streaming Resilience&lt;/td>
 &lt;td>媒體串流的失敗模式&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="案例定位">案例定位&lt;/h2>
&lt;p>Spotify 這個案例在講的是平台工程如何把可靠性散到每個 squad，又把共同能力集中到 Backstage 這類基礎設施。讀者先抓 squad-based SRE、service catalog 與 declarative infrastructure 的關係，再看它們怎麼支撐大型串流平台。&lt;/p>
&lt;h2 id="判讀重點">判讀重點&lt;/h2>
&lt;p>當組織採用分散責任模型時，可靠性不再只靠中央團隊，而是靠平台把常見能力做成共同元件。當 fleet 或 streaming 基礎設施需要治理時，重點是 catalog 與 control plane 是否讓團隊看得到、管得動。&lt;/p>
&lt;h2 id="可操作判準">可操作判準&lt;/h2>
&lt;ul>
&lt;li>能否把 service catalog 跟 reliability metadata 接起來&lt;/li>
&lt;li>能否說清楚 squad 與平台各自負責什麼&lt;/li>
&lt;li>能否用 declarative infrastructure 管 fleet 變化&lt;/li>
&lt;li>能否在 chaos 採用時保住平台一致性&lt;/li>
&lt;/ul>
&lt;h2 id="與其他案例的關係">與其他案例的關係&lt;/h2>
&lt;p>Spotify 的重點是把可靠性做成平台能力，這和 LinkedIn 的 operability、Honeycomb 的 observability、Meta 的 control plane 治理屬於相近抽象層。不同的是 Spotify 更強調組織分工，所以很適合拿來說明平台如何支撐分散團隊。&lt;/p>
&lt;h2 id="代表樣本">代表樣本&lt;/h2>
&lt;ul>
&lt;li>Backstage 將 service catalog 與 reliability metadata 整合成平台入口。&lt;/li>
&lt;li>declarative infrastructure 讓 fleet 管理變成可重現的控制流程。&lt;/li>
&lt;li>squad-based SRE 讓責任分散到服務團隊。&lt;/li>
&lt;li>chaos engineering adoption 讓平台能力和演練節奏一起成熟。&lt;/li>
&lt;li>streaming resilience 讓高頻寬服務的失敗模式能被平台化管理。&lt;/li>
&lt;li>service catalog 讓可靠性資訊跟服務拓撲一起被看見。&lt;/li>
&lt;li>fleet management 讓大規模機器與服務狀態保持一致。&lt;/li>
&lt;li>catalog-driven ops 讓平台資訊成為日常營運入口。&lt;/li>
&lt;/ul>
&lt;h2 id="章節列表">章節列表&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>章節&lt;/th>
 &lt;th>主題&lt;/th>
 &lt;th>核心責任&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/spotify/platform-engineering-and-reliability-contracts/" data-link-title="Spotify：平台工程與可靠性契約" data-link-desc="用平台契約統一服務團隊的可靠性最低標準，降低跨團隊變更造成的隱性風險。">SP1&lt;/a>&lt;/td>
 &lt;td>平台工程與可靠性契約&lt;/td>
 &lt;td>讓分散團隊共用可靠性基線與交付契約&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/spotify/backstage-service-catalog-and-reliability-metadata/" data-link-title="Spotify：Backstage Service Catalog 與 Reliability Metadata" data-link-desc="用 service catalog 治理分散團隊的可靠性資訊：ownership、SLO 狀態、依賴圖與 runbook 的單一入口。">SP2&lt;/a>&lt;/td>
 &lt;td>Backstage Service Catalog 與 Reliability Metadata&lt;/td>
 &lt;td>用 service catalog 治理分散團隊的可靠性資訊&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://engineering.atspotify.com/about/">About | Spotify Engineering&lt;/a>：Spotify Engineering 與 Backstage 的官方入口。&lt;/li>
&lt;li>&lt;a href="https://backstage.io/blog/2020/03/16/announcing-backstage">Announcing Backstage&lt;/a>：Backstage 的開源宣布與背景。&lt;/li>
&lt;li>&lt;a href="https://backstage.io/docs/overview/technical-overview">Technical overview&lt;/a>：Backstage 的技術總覽與 catalog/portal 說明。&lt;/li>
&lt;li>&lt;a href="https://engineering.atspotify.com/2023/05/fleet-management-at-spotify-part-2-the-path-to-declarative-infrastructure/">Fleet Management at Spotify (Part 2): The Path to Declarative Infrastructure&lt;/a>：大規模 fleet 與控制面的治理。&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Spotify 是音樂串流平台、squad-based 組織模型對 SRE 實踐有特殊影響、chaos engineering 文章是 mid-size company 採用 chaos 的代表。</p>
<h2 id="規劃重點">規劃重點</h2>
<ul>
<li>Squad-based SRE：分散式組織下的可靠性責任分配</li>
<li>Backstage：開源開發者平台的可靠性整合</li>
<li>Chaos engineering 採用過程：從 zero 到 mature 的實踐軌跡</li>
<li>Streaming infrastructure：高頻寬媒體的可靠性挑戰</li>
</ul>
<h2 id="預計收錄實踐">預計收錄實踐</h2>
<table>
  <thead>
      <tr>
          <th>議題</th>
          <th>教學重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Backstage</td>
          <td>service catalog + reliability metadata</td>
      </tr>
      <tr>
          <td>Squad SRE</td>
          <td>分散組織的可靠性責任</td>
      </tr>
      <tr>
          <td>Chaos Engineering Adoption</td>
          <td>Spotify 的 chaos 起步歷程</td>
      </tr>
      <tr>
          <td>CDN / Streaming Resilience</td>
          <td>媒體串流的失敗模式</td>
      </tr>
  </tbody>
</table>
<h2 id="案例定位">案例定位</h2>
<p>Spotify 這個案例在講的是平台工程如何把可靠性散到每個 squad，又把共同能力集中到 Backstage 這類基礎設施。讀者先抓 squad-based SRE、service catalog 與 declarative infrastructure 的關係，再看它們怎麼支撐大型串流平台。</p>
<h2 id="判讀重點">判讀重點</h2>
<p>當組織採用分散責任模型時，可靠性不再只靠中央團隊，而是靠平台把常見能力做成共同元件。當 fleet 或 streaming 基礎設施需要治理時，重點是 catalog 與 control plane 是否讓團隊看得到、管得動。</p>
<h2 id="可操作判準">可操作判準</h2>
<ul>
<li>能否把 service catalog 跟 reliability metadata 接起來</li>
<li>能否說清楚 squad 與平台各自負責什麼</li>
<li>能否用 declarative infrastructure 管 fleet 變化</li>
<li>能否在 chaos 採用時保住平台一致性</li>
</ul>
<h2 id="與其他案例的關係">與其他案例的關係</h2>
<p>Spotify 的重點是把可靠性做成平台能力，這和 LinkedIn 的 operability、Honeycomb 的 observability、Meta 的 control plane 治理屬於相近抽象層。不同的是 Spotify 更強調組織分工，所以很適合拿來說明平台如何支撐分散團隊。</p>
<h2 id="代表樣本">代表樣本</h2>
<ul>
<li>Backstage 將 service catalog 與 reliability metadata 整合成平台入口。</li>
<li>declarative infrastructure 讓 fleet 管理變成可重現的控制流程。</li>
<li>squad-based SRE 讓責任分散到服務團隊。</li>
<li>chaos engineering adoption 讓平台能力和演練節奏一起成熟。</li>
<li>streaming resilience 讓高頻寬服務的失敗模式能被平台化管理。</li>
<li>service catalog 讓可靠性資訊跟服務拓撲一起被看見。</li>
<li>fleet management 讓大規模機器與服務狀態保持一致。</li>
<li>catalog-driven ops 讓平台資訊成為日常營運入口。</li>
</ul>
<h2 id="章節列表">章節列表</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/spotify/platform-engineering-and-reliability-contracts/" data-link-title="Spotify：平台工程與可靠性契約" data-link-desc="用平台契約統一服務團隊的可靠性最低標準，降低跨團隊變更造成的隱性風險。">SP1</a></td>
          <td>平台工程與可靠性契約</td>
          <td>讓分散團隊共用可靠性基線與交付契約</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/spotify/backstage-service-catalog-and-reliability-metadata/" data-link-title="Spotify：Backstage Service Catalog 與 Reliability Metadata" data-link-desc="用 service catalog 治理分散團隊的可靠性資訊：ownership、SLO 狀態、依賴圖與 runbook 的單一入口。">SP2</a></td>
          <td>Backstage Service Catalog 與 Reliability Metadata</td>
          <td>用 service catalog 治理分散團隊的可靠性資訊</td>
      </tr>
  </tbody>
</table>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://engineering.atspotify.com/about/">About | Spotify Engineering</a>：Spotify Engineering 與 Backstage 的官方入口。</li>
<li><a href="https://backstage.io/blog/2020/03/16/announcing-backstage">Announcing Backstage</a>：Backstage 的開源宣布與背景。</li>
<li><a href="https://backstage.io/docs/overview/technical-overview">Technical overview</a>：Backstage 的技術總覽與 catalog/portal 說明。</li>
<li><a href="https://engineering.atspotify.com/2023/05/fleet-management-at-spotify-part-2-the-path-to-declarative-infrastructure/">Fleet Management at Spotify (Part 2): The Path to Declarative Infrastructure</a>：大規模 fleet 與控制面的治理。</li>
</ul>
]]></content:encoded></item><item><title>Netflix：Business-Hours Chaos 與 Guardrails</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/netflix/chaos-monkey-business-hours-guardrails/</link><pubDate>Thu, 07 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/netflix/chaos-monkey-business-hours-guardrails/</guid><description>&lt;p>Netflix 把 Chaos Monkey 放在 business hours 執行，核心責任是同時驗證系統韌性與團隊反應能力。若只在離峰或隔離環境跑故障注入，很多真實依賴與協作問題不會被看見。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>團隊常把 chaos 排在低流量時段，理由是比較安全。這種做法雖然降低短期風險，但也降低驗證價值：人員不在位、依賴流量特徵不同、通訊鏈條沒被真正測到。最後得到的是工具可執行，不是服務可承受。&lt;/p>
&lt;h2 id="驗證機制">驗證機制&lt;/h2>
&lt;p>Business-hours chaos 是把風險放進 guardrails 內驗證，風險範圍是收斂的。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>機制&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>控制方式&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>時段限制&lt;/td>
 &lt;td>事故處理人力是否在線&lt;/td>
 &lt;td>僅在可支援時段啟動&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>實驗範圍限制&lt;/td>
 &lt;td>是否影響過大 blast radius&lt;/td>
 &lt;td>先從小範圍服務群組啟動&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>停止條件&lt;/td>
 &lt;td>何時立即結束實驗&lt;/td>
 &lt;td>明確 abort trigger 與 rollback 路徑&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>事後回寫&lt;/td>
 &lt;td>是否有把結果回寫到工程控制面&lt;/td>
 &lt;td>固定接 [8.22 evidence write-back]&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這個機制的本質是「在可控邊界內接近真實情境」，而不是追求更大故障。&lt;/p>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>abort trigger latency&lt;/td>
 &lt;td>停止條件是否能即時生效&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>on-call handoff quality&lt;/td>
 &lt;td>值班與指揮鏈條是否順暢&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/incident-command-roles/" data-link-title="8.2 事故指揮與角色分工" data-link-desc="定義 incident commander 與跨角色協作責任">8.2&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>steady-state drift&lt;/td>
 &lt;td>實驗期間是否偏離穩態&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>communication lag&lt;/td>
 &lt;td>內外部更新是否跟上變化&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/incident-communication/" data-link-title="8.4 事故通訊與狀態更新" data-link-desc="建立內外部通報節奏與狀態更新格式">8.4&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="常見陷阱">常見陷阱&lt;/h2>
&lt;p>常見誤解是「business hours chaos 比較危險，所以應該避免」。真正風險在於沒有 guardrails，而不是時段本身。若有明確範圍、停止條件與值班協調，business-hours 測到的結果反而更接近真實事故。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>先在 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/reliability-readiness-review/" data-link-title="6.19 Reliability Readiness Review" data-link-desc="把上線前、重大變更前與高風險操作前的可靠性準備度變成可檢查門檻">6.19 Reliability Readiness Review&lt;/a> 檢查實驗前置條件，再到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20&lt;/a> 寫 guardrails 與 abort 條件。實驗結果回寫 &lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/drills-and-oncall-readiness/" data-link-title="8.6 演練與值班能力建設" data-link-desc="用演練與值班訓練提升事故反應品質">8.6 Drills and On-call Readiness&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/incident-evidence-write-back/" data-link-title="8.22 Incident Evidence Write-back" data-link-desc="把事故證據、決策與復盤結論回寫到 observability、reliability 與 runbook">8.22&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://github.com/Netflix/SimianArmy/wiki/Chaos-Monkey">Netflix/SimianArmy Wiki: Chaos Monkey&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/Netflix/chaosmonkey">Netflix/chaosmonkey&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Netflix 把 Chaos Monkey 放在 business hours 執行，核心責任是同時驗證系統韌性與團隊反應能力。若只在離峰或隔離環境跑故障注入，很多真實依賴與協作問題不會被看見。</p>
<h2 id="問題場景">問題場景</h2>
<p>團隊常把 chaos 排在低流量時段，理由是比較安全。這種做法雖然降低短期風險，但也降低驗證價值：人員不在位、依賴流量特徵不同、通訊鏈條沒被真正測到。最後得到的是工具可執行，不是服務可承受。</p>
<h2 id="驗證機制">驗證機制</h2>
<p>Business-hours chaos 是把風險放進 guardrails 內驗證，風險範圍是收斂的。</p>
<table>
  <thead>
      <tr>
          <th>機制</th>
          <th>核心問題</th>
          <th>控制方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>時段限制</td>
          <td>事故處理人力是否在線</td>
          <td>僅在可支援時段啟動</td>
      </tr>
      <tr>
          <td>實驗範圍限制</td>
          <td>是否影響過大 blast radius</td>
          <td>先從小範圍服務群組啟動</td>
      </tr>
      <tr>
          <td>停止條件</td>
          <td>何時立即結束實驗</td>
          <td>明確 abort trigger 與 rollback 路徑</td>
      </tr>
      <tr>
          <td>事後回寫</td>
          <td>是否有把結果回寫到工程控制面</td>
          <td>固定接 [8.22 evidence write-back]</td>
      </tr>
  </tbody>
</table>
<p>這個機制的本質是「在可控邊界內接近真實情境」，而不是追求更大故障。</p>
<h2 id="可觀測訊號">可觀測訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>abort trigger latency</td>
          <td>停止條件是否能即時生效</td>
          <td><a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20</a></td>
      </tr>
      <tr>
          <td>on-call handoff quality</td>
          <td>值班與指揮鏈條是否順暢</td>
          <td><a href="/blog/backend/08-incident-response/incident-command-roles/" data-link-title="8.2 事故指揮與角色分工" data-link-desc="定義 incident commander 與跨角色協作責任">8.2</a></td>
      </tr>
      <tr>
          <td>steady-state drift</td>
          <td>實驗期間是否偏離穩態</td>
          <td><a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22</a></td>
      </tr>
      <tr>
          <td>communication lag</td>
          <td>內外部更新是否跟上變化</td>
          <td><a href="/blog/backend/08-incident-response/incident-communication/" data-link-title="8.4 事故通訊與狀態更新" data-link-desc="建立內外部通報節奏與狀態更新格式">8.4</a></td>
      </tr>
  </tbody>
</table>
<h2 id="常見陷阱">常見陷阱</h2>
<p>常見誤解是「business hours chaos 比較危險，所以應該避免」。真正風險在於沒有 guardrails，而不是時段本身。若有明確範圍、停止條件與值班協調，business-hours 測到的結果反而更接近真實事故。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>先在 <a href="/blog/backend/06-reliability/reliability-readiness-review/" data-link-title="6.19 Reliability Readiness Review" data-link-desc="把上線前、重大變更前與高風險操作前的可靠性準備度變成可檢查門檻">6.19 Reliability Readiness Review</a> 檢查實驗前置條件，再到 <a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20</a> 寫 guardrails 與 abort 條件。實驗結果回寫 <a href="/blog/backend/08-incident-response/drills-and-oncall-readiness/" data-link-title="8.6 演練與值班能力建設" data-link-desc="用演練與值班訓練提升事故反應品質">8.6 Drills and On-call Readiness</a> 與 <a href="/blog/backend/08-incident-response/incident-evidence-write-back/" data-link-title="8.22 Incident Evidence Write-back" data-link-desc="把事故證據、決策與復盤結論回寫到 observability、reliability 與 runbook">8.22</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://github.com/Netflix/SimianArmy/wiki/Chaos-Monkey">Netflix/SimianArmy Wiki: Chaos Monkey</a></li>
<li><a href="https://github.com/Netflix/chaosmonkey">Netflix/chaosmonkey</a></li>
</ul>
]]></content:encoded></item><item><title>6.22 Steady State Definition</title><link>https://tarrragon.github.io/blog/backend/06-reliability/steady-state-definition/</link><pubDate>Sat, 02 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/steady-state-definition/</guid><description>&lt;h2 id="大綱">大綱&lt;/h2>
&lt;ul>
&lt;li>steady state 的責任：定義實驗期間系統應維持的可接受狀態&lt;/li>
&lt;li>穩態來源：SLO、business KPI、queue lag、error rate、latency、throughput、customer impact&lt;/li>
&lt;li>可接受退化：degradation mode、fallback、load shedding、partial outage&lt;/li>
&lt;li>實驗假設：故障注入後哪些訊號應保持穩定，哪些訊號可暫時退化&lt;/li>
&lt;li>觀測要求：dashboard、alert、trace、synthetic probe、client-side signal&lt;/li>
&lt;li>跟 chaos 的關係：沒有 steady state，chaos 只能證明系統被打壞&lt;/li>
&lt;li>跟 incident response 的關係：steady state 也定義事故恢復完成條件&lt;/li>
&lt;li>反模式：只定義故障動作，不定義成功條件；只看 server 指標，不看使用者影響&lt;/li>
&lt;/ul>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/steady-state/" data-link-title="Steady State" data-link-desc="說明可靠性實驗與事故恢復如何定義系統應維持的可接受狀態">Steady state&lt;/a> definition 的價值是讓實驗與事故有共同終點。穩態定義建立後，團隊可以同時回答「壞到什麼程度可接受」與「什麼時候算恢復」。&lt;/p>
&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>Steady state definition 是可靠性實驗的成功條件，責任是讓團隊知道故障發生後系統應該維持什麼服務能力。&lt;/p>
&lt;p>這一頁處理的是穩態定義。Chaos、failover 與 DR drill 都需要先定義系統的可接受狀態，才能判斷實驗是在驗證韌性，還是在製造混亂。&lt;/p>
&lt;p>穩態是一組服務承諾，通常同時包含成功率、延遲、資料正確性與使用者影響，並對應不同故障情境下的可接受退化範圍。&lt;/p>
&lt;h2 id="核心判讀">核心判讀&lt;/h2>
&lt;p>判讀 steady state 時，先看穩態是否貼近使用者，再看退化是否有明確邊界。&lt;/p>
&lt;p>重點訊號包括：&lt;/p>
&lt;ul>
&lt;li>steady state 是否包含 success rate、latency、queue lag 與 user impact&lt;/li>
&lt;li>degraded mode 是否說明哪些功能保留、哪些功能暫停&lt;/li>
&lt;li>stop condition 是否連到 steady state breach&lt;/li>
&lt;li>dashboard 是否能同時呈現系統指標與使用者旅程&lt;/li>
&lt;li>recovery complete 是否有可量測門檻&lt;/li>
&lt;/ul>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>穩態元素&lt;/th>
 &lt;th>最小可用判準&lt;/th>
 &lt;th>判讀價值&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>服務成功&lt;/td>
 &lt;td>success rate / &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/error-budget/" data-link-title="Error Budget" data-link-desc="說明 SLO 允許的失敗額度如何影響發版與可靠性投入">error budget&lt;/a> 在可接受範圍&lt;/td>
 &lt;td>判斷是否需要升級事故&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>體驗延遲&lt;/td>
 &lt;td>latency 與 queue lag 在門檻內&lt;/td>
 &lt;td>判斷是否進入 degraded mode&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>資料正確&lt;/td>
 &lt;td>無資料遺失或可接受補償策略&lt;/td>
 &lt;td>判斷是否可宣告恢復&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>恢復條件&lt;/td>
 &lt;td>recovery complete 有量測閾值&lt;/td>
 &lt;td>判斷事故何時可關閉&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="穩態來源">穩態來源&lt;/h2>
&lt;p>Steady state 的來源是服務承諾與操作訊號。它需要把 SLO、business KPI、系統指標與客戶感知訊號放在同一個判讀模型中。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>來源&lt;/th>
 &lt;th>責任&lt;/th>
 &lt;th>常見訊號&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>SLO / SLI&lt;/td>
 &lt;td>定義可靠性承諾&lt;/td>
 &lt;td>success rate、latency、freshness&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Business KPI&lt;/td>
 &lt;td>定義業務結果是否維持&lt;/td>
 &lt;td>checkout success、order volume&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Queue / async&lt;/td>
 &lt;td>定義背景流程是否可追上&lt;/td>
 &lt;td>queue lag、DLQ、retry rate&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Client signal&lt;/td>
 &lt;td>定義使用者感知是否正常&lt;/td>
 &lt;td>RUM、synthetic probe、mobile error&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Data signal&lt;/td>
 &lt;td>定義資料是否正確且可回復&lt;/td>
 &lt;td>reconciliation、replication lag&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>SLO / SLI 是 steady state 的主要來源。它們讓實驗與事故判讀有共同基準，避免每次演練都重新討論什麼算可接受狀態。&lt;/p>
&lt;p>Business KPI 能補足純技術指標的盲區。checkout success、payment authorization、message delivery、document publish 與 invoice generation 這些業務結果，能直接反映使用者旅程是否維持。&lt;/p>
&lt;p>Queue / async 訊號能保護延遲性風險。同步 API 可能恢復，但 queue lag、DLQ、retry storm 或 backfill backlog 仍在累積；steady state 應包含這些後段壓力。&lt;/p>
&lt;p>Client signal 能補 server-side 盲區。CDN、mobile network、browser runtime、third-party script 與 regional routing 可能讓 server 看起來健康，但使用者仍感知到失敗。&lt;/p>
&lt;p>Data signal 能保護正確性。failover、migration、replay 與 DR drill 都需要確認資料沒有遺失，或至少有明確補償與 reconciliation 路徑。&lt;/p></description><content:encoded><![CDATA[<h2 id="大綱">大綱</h2>
<ul>
<li>steady state 的責任：定義實驗期間系統應維持的可接受狀態</li>
<li>穩態來源：SLO、business KPI、queue lag、error rate、latency、throughput、customer impact</li>
<li>可接受退化：degradation mode、fallback、load shedding、partial outage</li>
<li>實驗假設：故障注入後哪些訊號應保持穩定，哪些訊號可暫時退化</li>
<li>觀測要求：dashboard、alert、trace、synthetic probe、client-side signal</li>
<li>跟 chaos 的關係：沒有 steady state，chaos 只能證明系統被打壞</li>
<li>跟 incident response 的關係：steady state 也定義事故恢復完成條件</li>
<li>反模式：只定義故障動作，不定義成功條件；只看 server 指標，不看使用者影響</li>
</ul>
<p><a href="/blog/backend/knowledge-cards/steady-state/" data-link-title="Steady State" data-link-desc="說明可靠性實驗與事故恢復如何定義系統應維持的可接受狀態">Steady state</a> definition 的價值是讓實驗與事故有共同終點。穩態定義建立後，團隊可以同時回答「壞到什麼程度可接受」與「什麼時候算恢復」。</p>
<h2 id="概念定位">概念定位</h2>
<p>Steady state definition 是可靠性實驗的成功條件，責任是讓團隊知道故障發生後系統應該維持什麼服務能力。</p>
<p>這一頁處理的是穩態定義。Chaos、failover 與 DR drill 都需要先定義系統的可接受狀態，才能判斷實驗是在驗證韌性，還是在製造混亂。</p>
<p>穩態是一組服務承諾，通常同時包含成功率、延遲、資料正確性與使用者影響，並對應不同故障情境下的可接受退化範圍。</p>
<h2 id="核心判讀">核心判讀</h2>
<p>判讀 steady state 時，先看穩態是否貼近使用者，再看退化是否有明確邊界。</p>
<p>重點訊號包括：</p>
<ul>
<li>steady state 是否包含 success rate、latency、queue lag 與 user impact</li>
<li>degraded mode 是否說明哪些功能保留、哪些功能暫停</li>
<li>stop condition 是否連到 steady state breach</li>
<li>dashboard 是否能同時呈現系統指標與使用者旅程</li>
<li>recovery complete 是否有可量測門檻</li>
</ul>
<table>
  <thead>
      <tr>
          <th>穩態元素</th>
          <th>最小可用判準</th>
          <th>判讀價值</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>服務成功</td>
          <td>success rate / <a href="/blog/backend/knowledge-cards/error-budget/" data-link-title="Error Budget" data-link-desc="說明 SLO 允許的失敗額度如何影響發版與可靠性投入">error budget</a> 在可接受範圍</td>
          <td>判斷是否需要升級事故</td>
      </tr>
      <tr>
          <td>體驗延遲</td>
          <td>latency 與 queue lag 在門檻內</td>
          <td>判斷是否進入 degraded mode</td>
      </tr>
      <tr>
          <td>資料正確</td>
          <td>無資料遺失或可接受補償策略</td>
          <td>判斷是否可宣告恢復</td>
      </tr>
      <tr>
          <td>恢復條件</td>
          <td>recovery complete 有量測閾值</td>
          <td>判斷事故何時可關閉</td>
      </tr>
  </tbody>
</table>
<h2 id="穩態來源">穩態來源</h2>
<p>Steady state 的來源是服務承諾與操作訊號。它需要把 SLO、business KPI、系統指標與客戶感知訊號放在同一個判讀模型中。</p>
<table>
  <thead>
      <tr>
          <th>來源</th>
          <th>責任</th>
          <th>常見訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>SLO / SLI</td>
          <td>定義可靠性承諾</td>
          <td>success rate、latency、freshness</td>
      </tr>
      <tr>
          <td>Business KPI</td>
          <td>定義業務結果是否維持</td>
          <td>checkout success、order volume</td>
      </tr>
      <tr>
          <td>Queue / async</td>
          <td>定義背景流程是否可追上</td>
          <td>queue lag、DLQ、retry rate</td>
      </tr>
      <tr>
          <td>Client signal</td>
          <td>定義使用者感知是否正常</td>
          <td>RUM、synthetic probe、mobile error</td>
      </tr>
      <tr>
          <td>Data signal</td>
          <td>定義資料是否正確且可回復</td>
          <td>reconciliation、replication lag</td>
      </tr>
  </tbody>
</table>
<p>SLO / SLI 是 steady state 的主要來源。它們讓實驗與事故判讀有共同基準，避免每次演練都重新討論什麼算可接受狀態。</p>
<p>Business KPI 能補足純技術指標的盲區。checkout success、payment authorization、message delivery、document publish 與 invoice generation 這些業務結果，能直接反映使用者旅程是否維持。</p>
<p>Queue / async 訊號能保護延遲性風險。同步 API 可能恢復，但 queue lag、DLQ、retry storm 或 backfill backlog 仍在累積；steady state 應包含這些後段壓力。</p>
<p>Client signal 能補 server-side 盲區。CDN、mobile network、browser runtime、third-party script 與 regional routing 可能讓 server 看起來健康，但使用者仍感知到失敗。</p>
<p>Data signal 能保護正確性。failover、migration、replay 與 DR drill 都需要確認資料沒有遺失，或至少有明確補償與 reconciliation 路徑。</p>
<h2 id="產業情境遊戲服務的穩態定義">產業情境：遊戲服務的穩態定義</h2>
<p>遊戲伺服器的穩態指標跟一般 web service 有結構性差異。即時互動遊戲的關鍵衡量是 tick rate stability（伺服器每秒處理的遊戲邏輯循環數）和 player session continuity（玩家連線不中斷），HTTP success rate 只能反映 API 層健康，無法代表 gameplay 品質。</p>
<p>穩態訊號需要覆蓋四個面向：tick rate 維持在目標頻率（如 64 tick/s 的射擊遊戲降到 32 就會被感知）、matchmaking latency 在可接受範圍、session persistence 不因後端變更掉線、state synchronization lag 不讓玩家看到不一致的遊戲狀態。</p>
<p>遊戲的高峰型態跟電商不同。峰值可能是新版本上線首日、季賽開始或限時活動開放，持續時間通常以天計（而非 BFCM 的數小時），流量曲線有明顯的日週期（每日晚間尖峰）。workload model 需要反映這種「多日高原 + 日內尖峰」的形狀，而非單一爆量。</p>
<p>Degraded mode 的定義需要區分核心 gameplay loop 與周邊系統。排行榜、成就系統、社交功能、商城可以暫停或降級，但核心對戰邏輯必須維持。玩家對 gameplay 中斷的容忍度遠低於周邊功能 — 排行榜延遲更新是可接受的退化，比賽中角色動作不同步則直接導致玩家離開。</p>
<p>穩態 breach 的判準對應兩個升級門檻：tick rate 低於感知門檻時，遊戲體驗開始劣化，需要啟動 load shedding 或關閉新 match 入口；session drop rate 超過門檻時，代表大量玩家掉線，需要升級事故等級並啟動 rollback。</p>
<h2 id="產業情境saas-與-b2b-服務的穩態定義">產業情境：SaaS 與 B2B 服務的穩態定義</h2>
<p>SaaS 服務的穩態需要按租戶層級定義。全域指標健康但特定租戶劣化的情境在多租戶系統很常見 — 全域 error rate 正常但某個大客戶的 latency 已超出其 SLA 承諾，只用全域穩態定義會讓這類局部退化被平均值隱藏。</p>
<p>租戶級 SLI 是 SaaS 穩態定義的核心擴充。按 tenant_id label 拆分 SLI（success rate / latency / queue lag），讓穩態判讀能對齊個別客戶的 SLA 承諾。enterprise 客戶的穩態門檻通常比 self-serve 更嚴格，拆分後才能分別判讀。拆分的成本是 cardinality 上升（每個 SLI × 租戶數），需要搭配 recording rule 或 rollup 控制 Prometheus / metrics backend 的壓力。</p>
<p>Noisy neighbor 是 SaaS 穩態的特有威脅。一個租戶的流量爆增或異常 query pattern 會拖垮共享資源（DB connection pool / cache throughput / queue depth），其他租戶的穩態被連帶破壞。穩態定義需要包含「單租戶資源消耗不超過共享資源配額的 X%」的條件，X 的值取決於隔離策略的強度 — <a href="/blog/backend/06-reliability/cases/amazon/shuffle-sharding-and-cell-boundary/" data-link-title="Amazon：Shuffle Sharding 與 Cell 邊界的失效局部化" data-link-desc="用 cell 與 shuffle sharding 將多租戶故障限制在局部，讓恢復策略可分批執行。">Amazon A1 的 shuffle sharding</a> 讓租戶間擴散受限於 shard 重疊機率，<a href="/blog/backend/06-reliability/cases/shopify/pod-architecture-and-resiliency-matrix/" data-link-title="Shopify：Pod Architecture 與 Resiliency Matrix" data-link-desc="多租戶隔離與系統化失敗模式盤點：pod 邊界控制擴散、resiliency matrix 驅動演練。">Shopify H2 的 pod 隔離</a> 讓租戶群組有獨立 pod 的穩態邊界。</p>
<p>Chaos 實驗在 SaaS 場景需要同時驗證全域穩態與租戶穩態。注入 DB latency 後，全域 success rate 可能只掉 0.1%（被其他健康租戶稀釋），但受影響的租戶群組可能已經 breach SLA。實驗的 steady state probe 需要同時查詢全域 SLI 和 top-N 租戶 SLI，才能判斷退化是否在可接受範圍。</p>
<h2 id="可接受退化">可接受退化</h2>
<p>可接受退化的責任是定義故障期間哪些能力要維持、哪些能力可以暫停、哪些能力需要補償。它讓團隊在壓力下有一致的降級語言。</p>
<table>
  <thead>
      <tr>
          <th>退化模式</th>
          <th>適用情境</th>
          <th>穩態判準</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Read-only mode</td>
          <td>寫入風險高、讀取仍可服務</td>
          <td>讀取成功率維持，寫入明確暫停</td>
      </tr>
      <tr>
          <td>Fallback</td>
          <td>下游依賴失效</td>
          <td>使用替代資料，標示 freshness 限制</td>
      </tr>
      <tr>
          <td>Load shedding</td>
          <td>流量超過容量</td>
          <td>保核心旅程，拒絕低優先請求</td>
      </tr>
      <tr>
          <td>Partial outage</td>
          <td>區域、tenant 或功能局部受影響</td>
          <td>影響範圍可界定且持續收斂</td>
      </tr>
      <tr>
          <td>Manual recovery</td>
          <td>自動回復不足</td>
          <td>人工步驟有 owner、timeline、證據</td>
      </tr>
  </tbody>
</table>
<p>Read-only mode 適合保護資料正確性。若寫入路徑風險高，暫停寫入但保留查詢，可以讓服務維持部分價值，同時避免資料修復成本擴大。</p>
<p>Fallback 適合吸收下游失效。fallback 需要明確資料新鮮度、適用功能與使用者提示，讓服務承諾暫時降到可接受範圍。</p>
<p>Load shedding 適合處理容量壓力。它需要先定義核心旅程與低優先請求，讓系統在高壓下保住最重要的使用者結果。</p>
<p>Partial outage 適合處理 <a href="/blog/backend/knowledge-cards/blast-radius/" data-link-title="Blast Radius" data-link-desc="說明事故影響面如何估算與隔離">blast radius</a> 已被限制的事故。穩態定義應說明受影響 region、tenant、功能與預期恢復路徑，避免把局部可控誤讀成全域恢復。</p>
<h2 id="判讀訊號">判讀訊號</h2>
<ul>
<li>chaos 實驗只記錄「節點被關掉」，沒有記錄服務是否維持</li>
<li>failover 後 server healthy，但用戶核心流程仍失敗</li>
<li>degraded mode 啟動後，團隊不知道何時能解除</li>
<li>recovery 宣告依賴人工感覺，而非 SLO / synthetic probe / queue drain</li>
<li>事故與演練使用不同的恢復完成定義</li>
</ul>
<p>典型場景是 failover 後基礎 health check 全綠，但核心交易成功率仍低於承諾。若 steady state 只看系統健康，團隊會過早宣告恢復；若 steady state 包含 user journey，則會持續修復直到服務承諾回線。</p>
<h2 id="實驗假設">實驗假設</h2>
<p>Steady state 是 experiment hypothesis 的成功條件。故障注入前，團隊要先寫清楚哪些訊號應維持、哪些訊號可退化、退化多久仍可接受。</p>
<table>
  <thead>
      <tr>
          <th>假設欄位</th>
          <th>責任</th>
          <th>範例</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Injected failure</td>
          <td>說明要注入的失效</td>
          <td>關閉一個 cache node</td>
      </tr>
      <tr>
          <td>Expected behavior</td>
          <td>說明系統應如何吸收</td>
          <td>request latency 短暫上升</td>
      </tr>
      <tr>
          <td>Stable signals</td>
          <td>說明應維持穩定的訊號</td>
          <td>checkout success rate 維持門檻</td>
      </tr>
      <tr>
          <td>Allowed degradation</td>
          <td>說明可接受退化</td>
          <td>p99 latency 10 分鐘內回線</td>
      </tr>
      <tr>
          <td>Stop condition</td>
          <td>說明何時終止</td>
          <td>error budget burn 超門檻</td>
      </tr>
      <tr>
          <td>Recovery complete</td>
          <td>說明何時算恢復</td>
          <td>queue lag drain 到基準線</td>
      </tr>
  </tbody>
</table>
<p>Injected failure 只是實驗輸入。可靠性實驗真正要驗證的是 expected behavior，也就是系統面對失效時是否維持約定服務能力。</p>
<p>Stable signals 需要同時包含 server-side 與 user-facing 訊號。pod healthy、CPU 正常、database 可連線都很有用，但最後仍要回到核心旅程是否成功。</p>
<p>Allowed degradation 能避免過度反應。某些實驗預期會造成短暫 latency 上升或 fallback 啟動，只要在可接受時間窗內回線，就代表系統符合預期。</p>
<p>Recovery complete 應該可量測。queue lag drain、error rate 回到 baseline、synthetic probe 連續通過、reconciliation 完成，都比「看起來好了」更適合作為關閉條件。</p>
<h2 id="事故恢復">事故恢復</h2>
<p>Steady state 也是事故恢復宣告的共同基準。事故處理需要知道服務何時從 containment 進入 recovery，何時可以對內外部宣告恢復，何時進入 post-incident review。</p>
<table>
  <thead>
      <tr>
          <th>階段</th>
          <th>Steady state 責任</th>
          <th>事故決策</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Triage</td>
          <td>判斷是否已偏離穩態</td>
          <td>啟動或升級 incident</td>
      </tr>
      <tr>
          <td>Containment</td>
          <td>判斷退化是否維持在可接受範圍</td>
          <td>降級、限流、切換</td>
      </tr>
      <tr>
          <td>Recovery</td>
          <td>判斷核心旅程是否回到門檻</td>
          <td>宣告服務恢復</td>
      </tr>
      <tr>
          <td>Review</td>
          <td>判斷穩態定義是否足以支援判讀</td>
          <td>回寫 SLO、dashboard、runbook</td>
      </tr>
  </tbody>
</table>
<p>Triage 階段，steady state 幫助團隊把異常轉成事故門檻。若 success rate、latency、queue lag 或 customer impact 偏離穩態，就有足夠理由啟動分級。</p>
<p>Containment 階段，steady state 幫助團隊判斷退化策略是否有效。fallback、load shedding 或 read-only mode 啟動後，團隊要看核心旅程是否回到可接受範圍。</p>
<p>Recovery 階段，steady state 幫助團隊避免過早關閉事故。基礎 health check 回綠只是其中一個訊號，核心旅程、資料正確性與長尾 backlog 都要回到門檻。</p>
<p>Review 階段，steady state 會回寫到 04 與 06。若事故期間發現穩態指標缺失、門檻過鬆或 dashboard 不支援判讀，就要回到 SLO、observability readiness 或 reliability readiness。</p>
<h2 id="常見反模式">常見反模式</h2>
<p>Steady state 的反模式通常來自只定義故障動作，缺少成功條件。成功條件能讓 chaos、failover 與 DR drill 證明系統如何承受失效，而不只證明系統被打壞。</p>
<table>
  <thead>
      <tr>
          <th>反模式</th>
          <th>表面現象</th>
          <th>修正方向</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>只定義故障動作</td>
          <td>實驗說明只有關機、斷線、切流量</td>
          <td>補 stable signals 與成功條件</td>
      </tr>
      <tr>
          <td>只看 server 指標</td>
          <td>health check 綠燈就宣告恢復</td>
          <td>加入 user journey 與 client signal</td>
      </tr>
      <tr>
          <td>退化模式無邊界</td>
          <td>fallback 啟動後無時間窗與限制</td>
          <td>定義 allowed degradation</td>
      </tr>
      <tr>
          <td>恢復完成靠感覺</td>
          <td>IC 以主觀判斷關閉事故</td>
          <td>定義 recovery complete metric</td>
      </tr>
      <tr>
          <td>實驗與事故標準不同</td>
          <td>drill 通過但事故時用另一套門檻</td>
          <td>共用 steady state 與 runbook</td>
      </tr>
  </tbody>
</table>
<p>只看 server 指標會讓恢復宣告偏早。服務健康需要同時看基礎設施、後端旅程、client-side signal 與資料正確性，才能支援對外通訊。</p>
<p>退化模式無邊界會讓 fallback 變成隱性事故。fallback 可用時，團隊仍需要知道資料新鮮度、功能限制、時間窗與客戶影響。</p>
<h2 id="交接路由">交接路由</h2>
<ul>
<li>04.6 SLI/SLO signal：把穩態轉成可量測訊號</li>
<li>04.10 client-side / synthetic / RUM：補使用者感知訊號</li>
<li>06.4 chaos testing：把 steady state 作為實驗前提</li>
<li>06.7 DR / rollback rehearsal：把 steady state 作為恢復完成條件</li>
<li>08.3 containment / recovery：事故恢復宣告使用同一組穩態門檻</li>
</ul>
]]></content:encoded></item><item><title>Pinterest</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/pinterest/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/pinterest/</guid><description>&lt;p>Pinterest 是視覺探索平台、capacity planning 與儲存架構的工程文章揭露大規模 data-heavy service 的可靠性挑戰。&lt;/p>
&lt;h2 id="規劃重點">規劃重點&lt;/h2>
&lt;ul>
&lt;li>Storage Capacity：HBase / TiDB 等 stateful 系統的 capacity model&lt;/li>
&lt;li>Cache Strategies：Memcache / Redis 大規模部署的 failure mode&lt;/li>
&lt;li>Scaling Patterns：visual search 等高運算服務的可靠性&lt;/li>
&lt;li>Migration Reliability：跨 storage backend migration 的零事故設計&lt;/li>
&lt;/ul>
&lt;h2 id="預計收錄實踐">預計收錄實踐&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>議題&lt;/th>
 &lt;th>教學重點&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Storage Migration&lt;/td>
 &lt;td>HBase → TiDB 等大規模 migration 設計&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Cache Reliability&lt;/td>
 &lt;td>hot key、thundering herd 的工程處理&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Capacity Planning&lt;/td>
 &lt;td>data-heavy service 的容量預測&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>ML Serving Resilience&lt;/td>
 &lt;td>推薦系統的可靠性需求&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="案例定位">案例定位&lt;/h2>
&lt;p>Pinterest 這個案例在講的是資料密集型服務如何透過 storage migration 與容量規劃維持可用性。讀者先抓 HBase、TiDB、zero downtime migration 與 RocksDB 這些原語，再把它們視為資料平台演進的路徑。&lt;/p>
&lt;h2 id="判讀重點">判讀重點&lt;/h2>
&lt;p>當儲存後端需要退役或升級時，重點是如何在搬移過程中維持服務穩定，把資料搬過去只是其中一環。當推薦或搜尋系統吃到熱點流量時，cache 與 capacity 的設計要先保住查詢路徑，再處理最佳化。&lt;/p>
&lt;h2 id="可操作判準">可操作判準&lt;/h2>
&lt;ul>
&lt;li>能否把 storage migration 拆成不中斷的階段&lt;/li>
&lt;li>能否指出 hot key 與 thundering herd 的風險位置&lt;/li>
&lt;li>能否讓 data platform 的容量模型跟業務成長對齊&lt;/li>
&lt;li>能否把 migration 成果寫成可重複的工程模式&lt;/li>
&lt;/ul>
&lt;h2 id="與其他案例的關係">與其他案例的關係&lt;/h2>
&lt;p>Pinterest 把資料平台演進和可靠性綁在一起，和 Shopify 的峰值準備、GitHub 的資料一致性、Meta 的大規模 storage 實踐都有對照價值。這頁最重要的訊息是：migration 是維持服務語義的持續變更，用搬家的心態做會忽略穩定性。&lt;/p>
&lt;h2 id="代表樣本">代表樣本&lt;/h2>
&lt;ul>
&lt;li>HBase → TiDB migration 展示零停機遷移如何保住線上讀寫。&lt;/li>
&lt;li>RocksDB wide column database 代表新 storage backend 如何接手舊系統的壓力。&lt;/li>
&lt;li>cache strategies 讓熱點流量不直接壓垮主存儲。&lt;/li>
&lt;li>capacity planning 把資料密集型服務的擴容節奏固定下來。&lt;/li>
&lt;li>ML serving resilience 讓推薦系統在資料平台變動時仍能維持體感。&lt;/li>
&lt;li>zero-downtime migration 讓線上變更從一次性事件變成可管理流程。&lt;/li>
&lt;li>hot key mitigation 讓快取與查詢壓力不會一起炸開。&lt;/li>
&lt;li>storage backend migration 讓資料平台可以分階段換血。&lt;/li>
&lt;/ul>
&lt;h2 id="章節列表">章節列表&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>章節&lt;/th>
 &lt;th>主題&lt;/th>
 &lt;th>核心責任&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/pinterest/cache-reliability-and-capacity-surprises/" data-link-title="Pinterest：快取可靠性與容量驚奇治理" data-link-desc="針對快取層失效與流量突增，建立容量緩衝、退化路徑與重建節奏。">P1&lt;/a>&lt;/td>
 &lt;td>快取可靠性與容量驚奇&lt;/td>
 &lt;td>在命中率崩落時維持可回復節奏與容量緩衝&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/pinterest/storage-migration-and-data-infrastructure-reliability/" data-link-title="Pinterest：Storage Migration 與 Data Infrastructure Reliability" data-link-desc="大規模儲存遷移的可靠性設計：用 dual-write、shadow read 與 staged cutover 讓 PB 級資料基礎設施變更可漸進、可驗證、可回退。">P2&lt;/a>&lt;/td>
 &lt;td>Storage Migration 與 Data Infrastructure&lt;/td>
 &lt;td>大規模儲存遷移的漸進驗證與 dual-write / shadow read&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://medium.com/pinterest-engineering/hbase-deprecation-at-pinterest-8a99e6c8e6b7">HBase Deprecation at Pinterest&lt;/a>：HBase 退役與新 storage 方向。&lt;/li>
&lt;li>&lt;a href="https://medium.com/pinterest-engineering/tidb-adoption-at-pinterest-1130ab787a10">TiDB Adoption at Pinterest&lt;/a>：TiDB 選型與 migration 脈絡。&lt;/li>
&lt;li>&lt;a href="https://medium.com/pinterest-engineering/online-data-migration-from-hbase-to-tidb-with-zero-downtime-43f0fb474b84">Online Data Migration from HBase to TiDB with Zero Downtime&lt;/a>：零停機遷移的具體實作。&lt;/li>
&lt;li>&lt;a href="https://medium.com/pinterest-engineering/building-pinterests-new-wide-column-database-using-rocksdb-f5277ee4e3d2">Building Pinterest’s new wide column database using RocksDB&lt;/a>：新 wide column database 的工程脈絡。&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Pinterest 是視覺探索平台、capacity planning 與儲存架構的工程文章揭露大規模 data-heavy service 的可靠性挑戰。</p>
<h2 id="規劃重點">規劃重點</h2>
<ul>
<li>Storage Capacity：HBase / TiDB 等 stateful 系統的 capacity model</li>
<li>Cache Strategies：Memcache / Redis 大規模部署的 failure mode</li>
<li>Scaling Patterns：visual search 等高運算服務的可靠性</li>
<li>Migration Reliability：跨 storage backend migration 的零事故設計</li>
</ul>
<h2 id="預計收錄實踐">預計收錄實踐</h2>
<table>
  <thead>
      <tr>
          <th>議題</th>
          <th>教學重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Storage Migration</td>
          <td>HBase → TiDB 等大規模 migration 設計</td>
      </tr>
      <tr>
          <td>Cache Reliability</td>
          <td>hot key、thundering herd 的工程處理</td>
      </tr>
      <tr>
          <td>Capacity Planning</td>
          <td>data-heavy service 的容量預測</td>
      </tr>
      <tr>
          <td>ML Serving Resilience</td>
          <td>推薦系統的可靠性需求</td>
      </tr>
  </tbody>
</table>
<h2 id="案例定位">案例定位</h2>
<p>Pinterest 這個案例在講的是資料密集型服務如何透過 storage migration 與容量規劃維持可用性。讀者先抓 HBase、TiDB、zero downtime migration 與 RocksDB 這些原語，再把它們視為資料平台演進的路徑。</p>
<h2 id="判讀重點">判讀重點</h2>
<p>當儲存後端需要退役或升級時，重點是如何在搬移過程中維持服務穩定，把資料搬過去只是其中一環。當推薦或搜尋系統吃到熱點流量時，cache 與 capacity 的設計要先保住查詢路徑，再處理最佳化。</p>
<h2 id="可操作判準">可操作判準</h2>
<ul>
<li>能否把 storage migration 拆成不中斷的階段</li>
<li>能否指出 hot key 與 thundering herd 的風險位置</li>
<li>能否讓 data platform 的容量模型跟業務成長對齊</li>
<li>能否把 migration 成果寫成可重複的工程模式</li>
</ul>
<h2 id="與其他案例的關係">與其他案例的關係</h2>
<p>Pinterest 把資料平台演進和可靠性綁在一起，和 Shopify 的峰值準備、GitHub 的資料一致性、Meta 的大規模 storage 實踐都有對照價值。這頁最重要的訊息是：migration 是維持服務語義的持續變更，用搬家的心態做會忽略穩定性。</p>
<h2 id="代表樣本">代表樣本</h2>
<ul>
<li>HBase → TiDB migration 展示零停機遷移如何保住線上讀寫。</li>
<li>RocksDB wide column database 代表新 storage backend 如何接手舊系統的壓力。</li>
<li>cache strategies 讓熱點流量不直接壓垮主存儲。</li>
<li>capacity planning 把資料密集型服務的擴容節奏固定下來。</li>
<li>ML serving resilience 讓推薦系統在資料平台變動時仍能維持體感。</li>
<li>zero-downtime migration 讓線上變更從一次性事件變成可管理流程。</li>
<li>hot key mitigation 讓快取與查詢壓力不會一起炸開。</li>
<li>storage backend migration 讓資料平台可以分階段換血。</li>
</ul>
<h2 id="章節列表">章節列表</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/pinterest/cache-reliability-and-capacity-surprises/" data-link-title="Pinterest：快取可靠性與容量驚奇治理" data-link-desc="針對快取層失效與流量突增，建立容量緩衝、退化路徑與重建節奏。">P1</a></td>
          <td>快取可靠性與容量驚奇</td>
          <td>在命中率崩落時維持可回復節奏與容量緩衝</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/pinterest/storage-migration-and-data-infrastructure-reliability/" data-link-title="Pinterest：Storage Migration 與 Data Infrastructure Reliability" data-link-desc="大規模儲存遷移的可靠性設計：用 dual-write、shadow read 與 staged cutover 讓 PB 級資料基礎設施變更可漸進、可驗證、可回退。">P2</a></td>
          <td>Storage Migration 與 Data Infrastructure</td>
          <td>大規模儲存遷移的漸進驗證與 dual-write / shadow read</td>
      </tr>
  </tbody>
</table>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://medium.com/pinterest-engineering/hbase-deprecation-at-pinterest-8a99e6c8e6b7">HBase Deprecation at Pinterest</a>：HBase 退役與新 storage 方向。</li>
<li><a href="https://medium.com/pinterest-engineering/tidb-adoption-at-pinterest-1130ab787a10">TiDB Adoption at Pinterest</a>：TiDB 選型與 migration 脈絡。</li>
<li><a href="https://medium.com/pinterest-engineering/online-data-migration-from-hbase-to-tidb-with-zero-downtime-43f0fb474b84">Online Data Migration from HBase to TiDB with Zero Downtime</a>：零停機遷移的具體實作。</li>
<li><a href="https://medium.com/pinterest-engineering/building-pinterests-new-wide-column-database-using-rocksdb-f5277ee4e3d2">Building Pinterest’s new wide column database using RocksDB</a>：新 wide column database 的工程脈絡。</li>
</ul>
]]></content:encoded></item><item><title>Netflix：FIT 證據交接與 Release Gate 回寫</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/netflix/fit-failure-injection-evidence-handoff/</link><pubDate>Fri, 08 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/netflix/fit-failure-injection-evidence-handoff/</guid><description>&lt;p>FIT（Failure Injection Testing）的核心責任是產生可決策的證據，故障演示只是過程。當實驗結果無法直接回答「能不能放行」，FIT 就只是測試活動，不是可靠性控制面。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>團隊常在故障注入後留下 dashboard 截圖與結論摘要，但 release decision 仍靠主觀討論。這種斷裂會讓同類風險反覆出現，因為每次都在重新辯論，而不是沿用同一套 evidence 欄位。&lt;/p>
&lt;h2 id="決策機制">決策機制&lt;/h2>
&lt;p>要讓 FIT 成為 release gate 輸入，必須把實驗輸出結構化成決策欄位。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>欄位&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>決策用途&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>steady-state impact&lt;/td>
 &lt;td>注入後是否仍維持服務承諾&lt;/td>
 &lt;td>判斷能否繼續 rollout&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>abort trigger record&lt;/td>
 &lt;td>停止條件是否被觸發、何時觸發&lt;/td>
 &lt;td>判斷是否進入凍結與回退&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>fallback result&lt;/td>
 &lt;td>降級路徑是否可用、恢復是否收斂&lt;/td>
 &lt;td>判斷事故時能否安全止血&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>dependency drift&lt;/td>
 &lt;td>受影響依賴是否落在預期範圍&lt;/td>
 &lt;td>判斷 blast radius 是否可接受&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>verification evidence&lt;/td>
 &lt;td>證據是否足以支持 release&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>rule rollout anomaly&lt;/td>
 &lt;td>規則推送後是否偏離預期&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/rule-rollout-safety-gate/" data-link-title="6.24 規則推送安全閘門" data-link-desc="把規則、策略與控制面配置推送從部署步驟升級為可靠性 gate，避免小變更在秒級擴散成全網事故。">6.24&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>incident decision lag&lt;/td>
 &lt;td>事故時是否可快速調用證據&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/incident-decision-log/" data-link-title="8.19 Incident Decision Log" data-link-desc="把事中假設、決策、證據、回退條件與責任人留下可復盤紀錄">8.19&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>evidence write-back&lt;/td>
 &lt;td>教訓是否回寫成下次驗證輸入&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/incident-evidence-write-back/" data-link-title="8.22 Incident Evidence Write-back" data-link-desc="把事故證據、決策與復盤結論回寫到 observability、reliability 與 runbook">8.22&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="常見陷阱">常見陷阱&lt;/h2>
&lt;p>最常見錯誤是把 FIT 報告寫成敘事文件，沒有決策欄位，導致放行時無法直接引用。另一個錯誤是只記錄成功路徑，忽略 abort trigger 與 fallback 失敗，讓風險被低估。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>先把 FIT 輸出整理到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23 Verification Evidence Handoff&lt;/a>，再接到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/rule-rollout-safety-gate/" data-link-title="6.24 規則推送安全閘門" data-link-desc="把規則、策略與控制面配置推送從部署步驟升級為可靠性 gate，避免小變更在秒級擴散成全網事故。">6.24 Rule Rollout Safety Gate&lt;/a> 做放行判斷。事故發生時由 &lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/incident-decision-log/" data-link-title="8.19 Incident Decision Log" data-link-desc="把事中假設、決策、證據、回退條件與責任人留下可復盤紀錄">8.19&lt;/a> 快速提取決策證據，最後回寫 &lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/incident-evidence-write-back/" data-link-title="8.22 Incident Evidence Write-back" data-link-desc="把事故證據、決策與復盤結論回寫到 observability、reliability 與 runbook">8.22&lt;/a>。&lt;/p>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://netflixtechblog.com/fit-failure-injection-testing-35d8e2a9bb2e">FIT: Failure Injection Testing&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://github.com/Netflix/chaosmonkey">Netflix/chaosmonkey&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>FIT（Failure Injection Testing）的核心責任是產生可決策的證據，故障演示只是過程。當實驗結果無法直接回答「能不能放行」，FIT 就只是測試活動，不是可靠性控制面。</p>
<h2 id="問題場景">問題場景</h2>
<p>團隊常在故障注入後留下 dashboard 截圖與結論摘要，但 release decision 仍靠主觀討論。這種斷裂會讓同類風險反覆出現，因為每次都在重新辯論，而不是沿用同一套 evidence 欄位。</p>
<h2 id="決策機制">決策機制</h2>
<p>要讓 FIT 成為 release gate 輸入，必須把實驗輸出結構化成決策欄位。</p>
<table>
  <thead>
      <tr>
          <th>欄位</th>
          <th>核心問題</th>
          <th>決策用途</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>steady-state impact</td>
          <td>注入後是否仍維持服務承諾</td>
          <td>判斷能否繼續 rollout</td>
      </tr>
      <tr>
          <td>abort trigger record</td>
          <td>停止條件是否被觸發、何時觸發</td>
          <td>判斷是否進入凍結與回退</td>
      </tr>
      <tr>
          <td>fallback result</td>
          <td>降級路徑是否可用、恢復是否收斂</td>
          <td>判斷事故時能否安全止血</td>
      </tr>
      <tr>
          <td>dependency drift</td>
          <td>受影響依賴是否落在預期範圍</td>
          <td>判斷 blast radius 是否可接受</td>
      </tr>
  </tbody>
</table>
<h2 id="可觀測訊號">可觀測訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>verification evidence</td>
          <td>證據是否足以支持 release</td>
          <td><a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23</a></td>
      </tr>
      <tr>
          <td>rule rollout anomaly</td>
          <td>規則推送後是否偏離預期</td>
          <td><a href="/blog/backend/06-reliability/rule-rollout-safety-gate/" data-link-title="6.24 規則推送安全閘門" data-link-desc="把規則、策略與控制面配置推送從部署步驟升級為可靠性 gate，避免小變更在秒級擴散成全網事故。">6.24</a></td>
      </tr>
      <tr>
          <td>incident decision lag</td>
          <td>事故時是否可快速調用證據</td>
          <td><a href="/blog/backend/08-incident-response/incident-decision-log/" data-link-title="8.19 Incident Decision Log" data-link-desc="把事中假設、決策、證據、回退條件與責任人留下可復盤紀錄">8.19</a></td>
      </tr>
      <tr>
          <td>evidence write-back</td>
          <td>教訓是否回寫成下次驗證輸入</td>
          <td><a href="/blog/backend/08-incident-response/incident-evidence-write-back/" data-link-title="8.22 Incident Evidence Write-back" data-link-desc="把事故證據、決策與復盤結論回寫到 observability、reliability 與 runbook">8.22</a></td>
      </tr>
  </tbody>
</table>
<h2 id="常見陷阱">常見陷阱</h2>
<p>最常見錯誤是把 FIT 報告寫成敘事文件，沒有決策欄位，導致放行時無法直接引用。另一個錯誤是只記錄成功路徑，忽略 abort trigger 與 fallback 失敗，讓風險被低估。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>先把 FIT 輸出整理到 <a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23 Verification Evidence Handoff</a>，再接到 <a href="/blog/backend/06-reliability/rule-rollout-safety-gate/" data-link-title="6.24 規則推送安全閘門" data-link-desc="把規則、策略與控制面配置推送從部署步驟升級為可靠性 gate，避免小變更在秒級擴散成全網事故。">6.24 Rule Rollout Safety Gate</a> 做放行判斷。事故發生時由 <a href="/blog/backend/08-incident-response/incident-decision-log/" data-link-title="8.19 Incident Decision Log" data-link-desc="把事中假設、決策、證據、回退條件與責任人留下可復盤紀錄">8.19</a> 快速提取決策證據，最後回寫 <a href="/blog/backend/08-incident-response/incident-evidence-write-back/" data-link-title="8.22 Incident Evidence Write-back" data-link-desc="把事故證據、決策與復盤結論回寫到 observability、reliability 與 runbook">8.22</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://netflixtechblog.com/fit-failure-injection-testing-35d8e2a9bb2e">FIT: Failure Injection Testing</a></li>
<li><a href="https://github.com/Netflix/chaosmonkey">Netflix/chaosmonkey</a></li>
</ul>
]]></content:encoded></item><item><title>6.23 Verification Evidence Handoff</title><link>https://tarrragon.github.io/blog/backend/06-reliability/verification-evidence-handoff/</link><pubDate>Sat, 02 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/verification-evidence-handoff/</guid><description>&lt;h2 id="大綱">大綱&lt;/h2>
&lt;ul>
&lt;li>verification evidence handoff 的責任：把可靠性驗證結果交給 release gate、runbook 與 incident response&lt;/li>
&lt;li>來源：SLO policy、load test、chaos experiment、DR drill、rollback rehearsal、readiness review&lt;/li>
&lt;li>欄位：hypothesis、steady state、result、scope、evidence package、decision、owner、next route&lt;/li>
&lt;li>跟 4.20 的關係：使用同一 evidence package 格式承接 observability 證據&lt;/li>
&lt;li>跟 8.22 的關係：事故復盤會回寫新的驗證題目與證據缺口&lt;/li>
&lt;li>反模式：驗證做完只留結論；load test 圖表沒有 workload；chaos 成功但沒有 runbook 回寫&lt;/li>
&lt;/ul>
&lt;p>Verification evidence handoff 的核心是把可靠性驗證從「做過測試」升級成「留下可用證據」。驗證結果需要能進 release gate、runbook、incident drill 與 post-incident review，才會形成跨模組閉環。&lt;/p>
&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>Verification evidence handoff 是可靠性模組交給發布與事故流程的證據交接，責任是讓 SLO、load、chaos、DR 與 readiness 結果能被決策使用。&lt;/p>
&lt;p>這一頁處理的是驗證結果的交付格式。6.20 定義實驗安全邊界，6.22 定義 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/steady-state/" data-link-title="Steady State" data-link-desc="說明可靠性實驗與事故恢復如何定義系統應維持的可接受狀態">steady state&lt;/a>；本章把這些驗證輸出整理成可以被 05 release、08 incident response 與 04 observability 回寫使用的 artifact。&lt;/p>
&lt;p>驗證證據的價值在於支援未來決策。一次 load test 的圖表、一次 chaos 成功、一次 DR drill 通過，如果沒有 hypothesis、scope、steady state、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/evidence-package/" data-link-title="Evidence Package" data-link-desc="說明觀測、驗證與事故流程如何把證據包成可交接、可回放的 artifact">evidence package&lt;/a> 與 action item，後續團隊很難知道它證明了什麼。&lt;/p>
&lt;h2 id="handoff-欄位">Handoff 欄位&lt;/h2>
&lt;p>Verification evidence handoff 的欄位要同時保存驗證前提、觀測證據與決策結果。欄位的目標是讓下游能判斷「這個驗證能支持哪個決策」。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>欄位&lt;/th>
 &lt;th>責任&lt;/th>
 &lt;th>判讀用途&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Hypothesis&lt;/td>
 &lt;td>說明要驗證的 failure mode&lt;/td>
 &lt;td>判斷實驗是否回答原問題&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Scope&lt;/td>
 &lt;td>標示服務、tenant、region 範圍&lt;/td>
 &lt;td>防止把局部結果外推&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Steady state&lt;/td>
 &lt;td>定義成功條件&lt;/td>
 &lt;td>判斷是否通過&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Workload / Fault&lt;/td>
 &lt;td>記錄流量模型或故障注入&lt;/td>
 &lt;td>支援重播&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Evidence package&lt;/td>
 &lt;td>連到 log、metric、trace&lt;/td>
 &lt;td>支援查證與 handoff&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Result&lt;/td>
 &lt;td>pass、conditional、fail&lt;/td>
 &lt;td>接 release gate 與 readiness&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Decision&lt;/td>
 &lt;td>放行、阻擋、補驗證、補 runbook&lt;/td>
 &lt;td>把結果轉成動作&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Owner&lt;/td>
 &lt;td>指定後續責任人&lt;/td>
 &lt;td>支援 action item closure&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Hypothesis 欄位讓驗證聚焦。&lt;code>打掉 node&lt;/code> 只是操作；&lt;code>打掉一個 worker 後 queue lag 應在 10 分鐘內回到 baseline&lt;/code> 才是可判讀假設。&lt;/p>
&lt;p>Scope 欄位保護結論邊界。internal traffic、single tenant、one region、10% production traffic 與 full production 都是不同證據強度，handoff 需要明確標示。&lt;/p>
&lt;p>Evidence package 欄位接 4.20。驗證結果應保存 dashboard、query、trace、log、client-side signal、time range 與 data quality 限制，讓 release gate 或 incident response 可以回放。&lt;/p>
&lt;p>Result 欄位需要分層。Pass 代表在指定 scope 內符合 steady state；conditional 代表可接受但有缺口；fail 代表需要補設計、補訊號、補 runbook 或阻擋 release。&lt;/p>
&lt;h2 id="驗證來源">驗證來源&lt;/h2>
&lt;p>Verification evidence 的來源分成政策、容量、故障、回復與準備度。不同來源回答的決策問題不同。&lt;/p></description><content:encoded><![CDATA[<h2 id="大綱">大綱</h2>
<ul>
<li>verification evidence handoff 的責任：把可靠性驗證結果交給 release gate、runbook 與 incident response</li>
<li>來源：SLO policy、load test、chaos experiment、DR drill、rollback rehearsal、readiness review</li>
<li>欄位：hypothesis、steady state、result、scope、evidence package、decision、owner、next route</li>
<li>跟 4.20 的關係：使用同一 evidence package 格式承接 observability 證據</li>
<li>跟 8.22 的關係：事故復盤會回寫新的驗證題目與證據缺口</li>
<li>反模式：驗證做完只留結論；load test 圖表沒有 workload；chaos 成功但沒有 runbook 回寫</li>
</ul>
<p>Verification evidence handoff 的核心是把可靠性驗證從「做過測試」升級成「留下可用證據」。驗證結果需要能進 release gate、runbook、incident drill 與 post-incident review，才會形成跨模組閉環。</p>
<h2 id="概念定位">概念定位</h2>
<p>Verification evidence handoff 是可靠性模組交給發布與事故流程的證據交接，責任是讓 SLO、load、chaos、DR 與 readiness 結果能被決策使用。</p>
<p>這一頁處理的是驗證結果的交付格式。6.20 定義實驗安全邊界，6.22 定義 <a href="/blog/backend/knowledge-cards/steady-state/" data-link-title="Steady State" data-link-desc="說明可靠性實驗與事故恢復如何定義系統應維持的可接受狀態">steady state</a>；本章把這些驗證輸出整理成可以被 05 release、08 incident response 與 04 observability 回寫使用的 artifact。</p>
<p>驗證證據的價值在於支援未來決策。一次 load test 的圖表、一次 chaos 成功、一次 DR drill 通過，如果沒有 hypothesis、scope、steady state、<a href="/blog/backend/knowledge-cards/evidence-package/" data-link-title="Evidence Package" data-link-desc="說明觀測、驗證與事故流程如何把證據包成可交接、可回放的 artifact">evidence package</a> 與 action item，後續團隊很難知道它證明了什麼。</p>
<h2 id="handoff-欄位">Handoff 欄位</h2>
<p>Verification evidence handoff 的欄位要同時保存驗證前提、觀測證據與決策結果。欄位的目標是讓下游能判斷「這個驗證能支持哪個決策」。</p>
<table>
  <thead>
      <tr>
          <th>欄位</th>
          <th>責任</th>
          <th>判讀用途</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Hypothesis</td>
          <td>說明要驗證的 failure mode</td>
          <td>判斷實驗是否回答原問題</td>
      </tr>
      <tr>
          <td>Scope</td>
          <td>標示服務、tenant、region 範圍</td>
          <td>防止把局部結果外推</td>
      </tr>
      <tr>
          <td>Steady state</td>
          <td>定義成功條件</td>
          <td>判斷是否通過</td>
      </tr>
      <tr>
          <td>Workload / Fault</td>
          <td>記錄流量模型或故障注入</td>
          <td>支援重播</td>
      </tr>
      <tr>
          <td>Evidence package</td>
          <td>連到 log、metric、trace</td>
          <td>支援查證與 handoff</td>
      </tr>
      <tr>
          <td>Result</td>
          <td>pass、conditional、fail</td>
          <td>接 release gate 與 readiness</td>
      </tr>
      <tr>
          <td>Decision</td>
          <td>放行、阻擋、補驗證、補 runbook</td>
          <td>把結果轉成動作</td>
      </tr>
      <tr>
          <td>Owner</td>
          <td>指定後續責任人</td>
          <td>支援 action item closure</td>
      </tr>
  </tbody>
</table>
<p>Hypothesis 欄位讓驗證聚焦。<code>打掉 node</code> 只是操作；<code>打掉一個 worker 後 queue lag 應在 10 分鐘內回到 baseline</code> 才是可判讀假設。</p>
<p>Scope 欄位保護結論邊界。internal traffic、single tenant、one region、10% production traffic 與 full production 都是不同證據強度，handoff 需要明確標示。</p>
<p>Evidence package 欄位接 4.20。驗證結果應保存 dashboard、query、trace、log、client-side signal、time range 與 data quality 限制，讓 release gate 或 incident response 可以回放。</p>
<p>Result 欄位需要分層。Pass 代表在指定 scope 內符合 steady state；conditional 代表可接受但有缺口；fail 代表需要補設計、補訊號、補 runbook 或阻擋 release。</p>
<h2 id="驗證來源">驗證來源</h2>
<p>Verification evidence 的來源分成政策、容量、故障、回復與準備度。不同來源回答的決策問題不同。</p>
<table>
  <thead>
      <tr>
          <th>來源</th>
          <th>回答問題</th>
          <th>交接對象</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>SLO / Error Budget</td>
          <td>可靠性目標是否仍有風險餘額</td>
          <td>release gate、severity trigger</td>
      </tr>
      <tr>
          <td>Load test</td>
          <td>workload 是否覆蓋容量與成本壓力</td>
          <td>capacity plan、release gate</td>
      </tr>
      <tr>
          <td>Chaos experiment</td>
          <td>failure mode 是否可被吸收</td>
          <td>runbook、incident drill</td>
      </tr>
      <tr>
          <td>DR drill</td>
          <td>RTO / RPO 是否可達</td>
          <td>business continuity、IR</td>
      </tr>
      <tr>
          <td>Rollback rehearsal</td>
          <td>版本或資料回復是否可執行</td>
          <td>deployment platform、incident</td>
      </tr>
      <tr>
          <td>Readiness review</td>
          <td>上線前風險是否已被判讀</td>
          <td>release gate、service owner</td>
      </tr>
  </tbody>
</table>
<p>SLO evidence 適合支援變更節奏。當 <a href="/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">burn rate</a> 上升或 <a href="/blog/backend/knowledge-cards/error-budget/" data-link-title="Error Budget" data-link-desc="說明 SLO 允許的失敗額度如何影響發版與可靠性投入">error budget</a> 緊張，release gate 需要知道哪些 user journey 受影響、資料品質是否可信、freeze 是否觸發。</p>
<p>Load test evidence 適合支援容量與成本決策。它要保留 workload model、traffic shape、data volume、dependency saturation、cost threshold 與觀測限制。</p>
<p>Chaos evidence 適合支援 incident drill。它要保留 injected failure、steady state、stop condition、<a href="/blog/backend/knowledge-cards/blast-radius/" data-link-title="Blast Radius" data-link-desc="說明事故影響面如何估算與隔離">blast radius</a>、<a href="/blog/backend/knowledge-cards/incident-decision-log/" data-link-title="Incident Decision Log" data-link-desc="說明事故期間如何保留決策、證據、owner 與回退條件">decision log</a> 與 action item。</p>
<p>DR evidence 適合支援恢復承諾。它要保留切換步驟、資料同步、RTO / RPO、權限、通訊節奏與回復完成條件。</p>
<p>Rollback evidence 適合支援事故止血。它要保留版本、migration、feature flag、client compatibility、cache 與資料相容性。</p>
<h2 id="交接流程">交接流程</h2>
<p>Verification handoff 的流程是從驗證結果走向下游決策。每個結果都要明確路由，讓測試報告轉成 release、runbook 或 incident drill 的輸入。</p>
<ol>
<li>把驗證結果整理成 handoff 欄位。</li>
<li>附上 4.20 evidence package 與 data quality 限制。</li>
<li>判斷 result：pass、conditional、fail。</li>
<li>把 pass 送入 release gate 或 runbook。</li>
<li>把 conditional 送入 reliability debt 或 follow-up。</li>
<li>把 fail 送入 block、補驗證、補 observability 或 incident drill。</li>
</ol>
<p>Pass 的責任是支持後續放行。Pass 需要同時保留 scope，避免「小範圍通過」被誤用成「全域安全」。</p>
<p>Conditional 的責任是保留風險借款。若驗證結果可接受但缺 trace、runbook、owner 或資料校驗，應進入 reliability debt backlog，並設定 closure signal。</p>
<p>Fail 的責任是阻止風險下流。Fail 不只代表測試失敗，也可能代表 steady state 定義錯誤、evidence 不足、blast radius 過大或 stop condition 不清。</p>
<h2 id="常見反模式">常見反模式</h2>
<p>Verification evidence handoff 的反模式通常來自把驗證結果寫成結論，而沒有保留判讀過程。下游需要知道結論成立的條件。</p>
<table>
  <thead>
      <tr>
          <th>反模式</th>
          <th>表面現象</th>
          <th>修正方向</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>只寫 pass / fail</td>
          <td>release gate 看不到證據</td>
          <td>補 hypothesis、scope、evidence</td>
      </tr>
      <tr>
          <td>Load 圖表缺 workload</td>
          <td>圖表存在但缺少重播條件</td>
          <td>保存 traffic shape 與 data volume</td>
      </tr>
      <tr>
          <td>Chaos 成功無 runbook</td>
          <td>實驗有效但事故時用不上</td>
          <td>回寫 runbook 與 drill</td>
      </tr>
      <tr>
          <td>DR 通過缺 RTO / RPO</td>
          <td>切換完成但缺少承諾對齊</td>
          <td>保存 recovery timeline</td>
      </tr>
      <tr>
          <td>Conditional 無關閉條件</td>
          <td>風險借款長期存在</td>
          <td>設定 owner 與 closure signal</td>
      </tr>
  </tbody>
</table>
<p>只寫 pass / fail 會讓驗證證據失去工程價值。Pass 要說明在什麼範圍、什麼假設、什麼資料品質下成立；fail 要說明哪個控制面失效。</p>
<p>Conditional 無關閉條件會讓可靠性債累積。每個 conditional handoff 都需要 owner、期限、closure signal 與重新評估條件。</p>
<h2 id="交接路由">交接路由</h2>
<ul>
<li>4.20 observability evidence package：承接 log、metric、trace 與 data quality</li>
<li>6.8 release gate：把驗證結果轉成放行、阻擋或例外</li>
<li>6.20 experiment safety：保存 blast radius、stop condition 與權限</li>
<li>6.21 reliability debt backlog：承接 conditional 與 follow-up</li>
<li>8.6 drills / on-call readiness：把驗證結果轉成值班演練</li>
<li>8.22 incident evidence write-back：承接事故後新增的驗證題目</li>
</ul>
]]></content:encoded></item><item><title>Meta / Facebook</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/meta/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/meta/</guid><description>&lt;p>Meta（前 Facebook）是超大規模分散式系統的代表、2021-10 BGP 全球失效事故是大規模事故敘事的教學標竿。Engineering blog 公開的 reliability 文章涵蓋 region failover、cell architecture 等深度實踐。&lt;/p>
&lt;h2 id="規劃重點">規劃重點&lt;/h2>
&lt;ul>
&lt;li>BGP 與 DNS 自我封鎖：2021-10 事故揭露的內部依賴鎖死&lt;/li>
&lt;li>Region Failover：超大規模服務的跨區切換挑戰&lt;/li>
&lt;li>Cell Architecture：Facebook 規模下的 cell 設計&lt;/li>
&lt;li>Storm：Internal incident management 系統公開的設計&lt;/li>
&lt;/ul>
&lt;h2 id="預計收錄實踐">預計收錄實踐&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>議題&lt;/th>
 &lt;th>教學重點&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>2021-10 BGP 事故&lt;/td>
 &lt;td>配置變更鎖死自己、recovery 工具失效&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Region Failover&lt;/td>
 &lt;td>超大規模 traffic shift 的設計&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Storm IM System&lt;/td>
 &lt;td>內部 IR 工具的揭露&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Reliability Reviews&lt;/td>
 &lt;td>服務級可靠性審查制度&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="案例定位">案例定位&lt;/h2>
&lt;p>Meta 這個案例在講的是超大規模系統如何面對全球級網路與控制面事故。讀者先抓 BGP、自我封鎖、region failover 與 MySQL Raft 這些原語，再把它們當成超大規模恢復能力的組件。&lt;/p>
&lt;h2 id="判讀重點">判讀重點&lt;/h2>
&lt;p>當外部路由或內部配置互相牽制時，事故會把恢復工具一起拖進失效狀態。當服務開始做更快的 failover 投資時，真正要看的是它是否能縮短恢復時間並降低手動介入成本，單點工具層面的評估遠遠不夠。&lt;/p>
&lt;h2 id="可操作判準">可操作判準&lt;/h2>
&lt;ul>
&lt;li>能否分辨事故是路由、配置還是服務層問題&lt;/li>
&lt;li>能否說明 region failover 的前置條件&lt;/li>
&lt;li>能否把 IR 工具與對外說明串成一致時間線&lt;/li>
&lt;li>能否把資料庫 failover 投資對應到恢復時間縮短&lt;/li>
&lt;/ul>
&lt;h2 id="與其他案例的關係">與其他案例的關係&lt;/h2>
&lt;p>Meta 的價值在於把超大規模網路事故和恢復工具放在一起看，這和 AWS S3、GCP、Cloudflare 都是在談「控制面出事時會擴散多遠」。如果先讀 Meta，再回看其他案例，會更容易看出 region failover 和 route propagation 的真正成本。&lt;/p>
&lt;h2 id="代表樣本">代表樣本&lt;/h2>
&lt;ul>
&lt;li>2021-10 BGP 事故顯示一個控制面變更可以讓整個公司失去對外可見性。&lt;/li>
&lt;li>MySQL Raft 代表的是把資料庫 failover 工具化，縮短人工介入時間。&lt;/li>
&lt;li>region failover 顯示超大規模 traffic shift 的成本。&lt;/li>
&lt;li>reliability reviews 讓服務級風險在變更前先被看見。&lt;/li>
&lt;li>cell architecture 讓大規模服務把故障切成可管理的單位。&lt;/li>
&lt;li>Storm 代表內部 incident management 工具如何支撐跨團隊協作。&lt;/li>
&lt;li>DNS 自我封鎖讓內外部控制面一起失效。&lt;/li>
&lt;li>traffic shift 讓恢復不只是切流量，而是管理整個依賴網。&lt;/li>
&lt;/ul>
&lt;h2 id="章節列表">章節列表&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>章節&lt;/th>
 &lt;th>主題&lt;/th>
 &lt;th>核心責任&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/meta/region-failover-and-reliability-boundaries/" data-link-title="Meta：Region Failover 與可靠性邊界" data-link-desc="把跨區故障視為邊界治理問題，透過分區隔離與回復順序控制失效擴散。">M1&lt;/a>&lt;/td>
 &lt;td>Region Failover 邊界治理&lt;/td>
 &lt;td>把跨區故障擴散限制在可回復邊界內&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/meta/bgp-control-plane-recovery-ordering/" data-link-title="Meta：BGP 事故與控制面恢復順序" data-link-desc="當回復工具依賴已故障的系統：2021-10 事故揭露控制面恢復順序與 out-of-band 存取的設計約束。">M2&lt;/a>&lt;/td>
 &lt;td>BGP 事故與控制面恢復順序&lt;/td>
 &lt;td>恢復工具依賴已故障系統時的恢復順序與 out-of-band 設計&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://engineering.fb.com/2021/10/05/networking-traffic/outage-details/">More details about the October 4 outage&lt;/a>：Meta 2021-10 outage 的技術回顧。&lt;/li>
&lt;li>&lt;a href="https://engineering.fb.com/2021/10/04/networking-traffic/outage/">Update about the October 4th outage&lt;/a>：事故初始公開說明。&lt;/li>
&lt;li>&lt;a href="https://engineering.fb.com/2023/05/16/data-infrastructure/mysql-raft-meta/">Building and deploying MySQL Raft at Meta&lt;/a>：更快 failover 與可靠性投資。&lt;/li>
&lt;li>&lt;a href="https://engineering.fb.com/2014/06/05/core-infra/hydrabase-the-evolution-of-hbase-facebook/">HydraBase – The evolution of HBase@Facebook&lt;/a>：分散式儲存與 failover 的早期實踐。&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Meta（前 Facebook）是超大規模分散式系統的代表、2021-10 BGP 全球失效事故是大規模事故敘事的教學標竿。Engineering blog 公開的 reliability 文章涵蓋 region failover、cell architecture 等深度實踐。</p>
<h2 id="規劃重點">規劃重點</h2>
<ul>
<li>BGP 與 DNS 自我封鎖：2021-10 事故揭露的內部依賴鎖死</li>
<li>Region Failover：超大規模服務的跨區切換挑戰</li>
<li>Cell Architecture：Facebook 規模下的 cell 設計</li>
<li>Storm：Internal incident management 系統公開的設計</li>
</ul>
<h2 id="預計收錄實踐">預計收錄實踐</h2>
<table>
  <thead>
      <tr>
          <th>議題</th>
          <th>教學重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>2021-10 BGP 事故</td>
          <td>配置變更鎖死自己、recovery 工具失效</td>
      </tr>
      <tr>
          <td>Region Failover</td>
          <td>超大規模 traffic shift 的設計</td>
      </tr>
      <tr>
          <td>Storm IM System</td>
          <td>內部 IR 工具的揭露</td>
      </tr>
      <tr>
          <td>Reliability Reviews</td>
          <td>服務級可靠性審查制度</td>
      </tr>
  </tbody>
</table>
<h2 id="案例定位">案例定位</h2>
<p>Meta 這個案例在講的是超大規模系統如何面對全球級網路與控制面事故。讀者先抓 BGP、自我封鎖、region failover 與 MySQL Raft 這些原語，再把它們當成超大規模恢復能力的組件。</p>
<h2 id="判讀重點">判讀重點</h2>
<p>當外部路由或內部配置互相牽制時，事故會把恢復工具一起拖進失效狀態。當服務開始做更快的 failover 投資時，真正要看的是它是否能縮短恢復時間並降低手動介入成本，單點工具層面的評估遠遠不夠。</p>
<h2 id="可操作判準">可操作判準</h2>
<ul>
<li>能否分辨事故是路由、配置還是服務層問題</li>
<li>能否說明 region failover 的前置條件</li>
<li>能否把 IR 工具與對外說明串成一致時間線</li>
<li>能否把資料庫 failover 投資對應到恢復時間縮短</li>
</ul>
<h2 id="與其他案例的關係">與其他案例的關係</h2>
<p>Meta 的價值在於把超大規模網路事故和恢復工具放在一起看，這和 AWS S3、GCP、Cloudflare 都是在談「控制面出事時會擴散多遠」。如果先讀 Meta，再回看其他案例，會更容易看出 region failover 和 route propagation 的真正成本。</p>
<h2 id="代表樣本">代表樣本</h2>
<ul>
<li>2021-10 BGP 事故顯示一個控制面變更可以讓整個公司失去對外可見性。</li>
<li>MySQL Raft 代表的是把資料庫 failover 工具化，縮短人工介入時間。</li>
<li>region failover 顯示超大規模 traffic shift 的成本。</li>
<li>reliability reviews 讓服務級風險在變更前先被看見。</li>
<li>cell architecture 讓大規模服務把故障切成可管理的單位。</li>
<li>Storm 代表內部 incident management 工具如何支撐跨團隊協作。</li>
<li>DNS 自我封鎖讓內外部控制面一起失效。</li>
<li>traffic shift 讓恢復不只是切流量，而是管理整個依賴網。</li>
</ul>
<h2 id="章節列表">章節列表</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/meta/region-failover-and-reliability-boundaries/" data-link-title="Meta：Region Failover 與可靠性邊界" data-link-desc="把跨區故障視為邊界治理問題，透過分區隔離與回復順序控制失效擴散。">M1</a></td>
          <td>Region Failover 邊界治理</td>
          <td>把跨區故障擴散限制在可回復邊界內</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/cases/meta/bgp-control-plane-recovery-ordering/" data-link-title="Meta：BGP 事故與控制面恢復順序" data-link-desc="當回復工具依賴已故障的系統：2021-10 事故揭露控制面恢復順序與 out-of-band 存取的設計約束。">M2</a></td>
          <td>BGP 事故與控制面恢復順序</td>
          <td>恢復工具依賴已故障系統時的恢復順序與 out-of-band 設計</td>
      </tr>
  </tbody>
</table>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://engineering.fb.com/2021/10/05/networking-traffic/outage-details/">More details about the October 4 outage</a>：Meta 2021-10 outage 的技術回顧。</li>
<li><a href="https://engineering.fb.com/2021/10/04/networking-traffic/outage/">Update about the October 4th outage</a>：事故初始公開說明。</li>
<li><a href="https://engineering.fb.com/2023/05/16/data-infrastructure/mysql-raft-meta/">Building and deploying MySQL Raft at Meta</a>：更快 failover 與可靠性投資。</li>
<li><a href="https://engineering.fb.com/2014/06/05/core-infra/hydrabase-the-evolution-of-hbase-facebook/">HydraBase – The evolution of HBase@Facebook</a>：分散式儲存與 failover 的早期實踐。</li>
</ul>
]]></content:encoded></item><item><title>6.24 規則推送安全閘門</title><link>https://tarrragon.github.io/blog/backend/06-reliability/rule-rollout-safety-gate/</link><pubDate>Thu, 07 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/rule-rollout-safety-gate/</guid><description>&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>規則推送安全閘門（rule rollout safety gate）的核心責任是防止控制面錯誤快速擴散到資料面。這個閘門是補上「規則與配置類變更」特有風險，跟既有 release gate 互補而非取代：變更體積小、覆蓋範圍大、擴散速度快。&lt;/p>
&lt;p>當變更屬於 WAF rule、routing policy、token/policy、或 Addressing API 相關設定時，判讀重點從程式碼正確性轉為擴散控制。這類變更即使 diff 很短，也可能在數十秒內影響跨區域流量與多產品控制面。&lt;/p>
&lt;h2 id="適用場景">適用場景&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>場景&lt;/th>
 &lt;th>典型風險&lt;/th>
 &lt;th>為何需要獨立 gate&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>WAF / regex 規則更新&lt;/td>
 &lt;td>高計算成本規則拖垮 edge runtime&lt;/td>
 &lt;td>CI 綠燈無法代表 runtime 成本安全&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Routing / BYOIP 相關設定變更&lt;/td>
 &lt;td>prefix withdrawal 造成服務不可達&lt;/td>
 &lt;td>單一 API 查詢語意錯誤可全網擴散&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Token / policy 控制面變更&lt;/td>
 &lt;td>多產品授權連鎖失效&lt;/td>
 &lt;td>shared dependency 失效會誤導排障路徑&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>共享控制面批次清理任務&lt;/td>
 &lt;td>批量誤刪或批量撤告&lt;/td>
 &lt;td>需要數量閘門與緊急停機機制&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="產業情境遊戲服務的規則推送安全">產業情境：遊戲服務的規則推送安全&lt;/h2>
&lt;p>遊戲的規則推送（平衡性調整、反作弊規則、賽季設定、經濟系統參數）有特殊的擴散特性：影響面是全體玩家、生效時機即時、且玩家行為會立刻適應規則變更。&lt;/p>
&lt;p>規則推送的 blast radius 預設是全體在線玩家。一次平衡性調整會立刻改變所有正在進行的比賽的角色強度、裝備數值或技能效果。跟一般 feature flag 的 percentage rollout 不同，遊戲平衡性需要所有玩家看到相同規則，否則同場比賽的公平性會被破壞。&lt;/p>
&lt;p>推送時機需要跟 match lifecycle 對齊。在進行中的比賽套用新規則會造成不公平 — 某隊在舊規則下建立的優勢可能在新規則下消失。安全做法是在 match boundary（比賽開始或結束時）切換，讓新規則只套用到新開始的比賽。這要求規則推送系統能區分「已開始的 match」和「即將開始的 match」。&lt;/p>
&lt;p>回退條件需要綁定遊戲特有的 KPI。active player count 下降超過門檻、match completion rate 異常降低（玩家中途離開）、player report rate 上升（玩家回報異常體驗）、in-game economy anomaly（虛擬貨幣或道具流通量異常）都是規則推送出問題的訊號。這些 KPI 的 feedback loop 比一般服務長 — 玩家行為的適應需要數小時到數天才會穩定，短窗觀察可能漏掉延遲暴露的問題。&lt;/p>
&lt;p>反作弊規則的推送有額外約束：規則內容本身是機密的，推送失敗後不能在 log 或 alert 中暴露規則細節，回退也必須在不洩漏偵測邏輯的前提下進行。&lt;/p>
&lt;h2 id="gate-檢查層">Gate 檢查層&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>層級&lt;/th>
 &lt;th>Gate 問題&lt;/th>
 &lt;th>不通過訊號&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Query / API 語意&lt;/td>
 &lt;td>查詢參數有沒有安全預設與錯誤拒絕策略&lt;/td>
 &lt;td>空參數回傳全量、模糊布林語意&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Rule 計算成本&lt;/td>
 &lt;td>規則是否通過代表性 payload 成本檢查&lt;/td>
 &lt;td>單一規則可造成 CPU 熱點&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>推送策略&lt;/td>
 &lt;td>是否採分群 rollout 並設即時回退條件&lt;/td>
 &lt;td>一次推全域、無分區觀測閘門&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Withdrawal 限速&lt;/td>
 &lt;td>批次撤告 / 刪除是否有數量與速率限制&lt;/td>
 &lt;td>單次操作可影響大量 prefixes 或 bindings&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Shared dependency&lt;/td>
 &lt;td>是否識別跨產品共享控制點&lt;/td>
 &lt;td>多產品同時異常卻無 shared root 判讀&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Evidence 與回寫&lt;/td>
 &lt;td>事故後是否可回放決策、查證恢復路徑與狀態差異&lt;/td>
 &lt;td>決策只留結論，缺假設與驗證證據&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="判讀訊號">判讀訊號&lt;/h2>
&lt;ul>
&lt;li>控制面變更後，多區域同時出現 5xx / timeout / auth 失敗&lt;/li>
&lt;li>指標在秒級惡化，且與最新規則或 policy 變更高度同時&lt;/li>
&lt;li>回退後短時間明顯回穩，顯示變更與故障高度關聯&lt;/li>
&lt;li>部分服務可自助恢復、部分服務需人工修復，代表狀態損壞分層&lt;/li>
&lt;li>事故中出現「每個產品都在修自己的症狀」，代表 shared dependency 沒被先識別&lt;/li>
&lt;/ul>
&lt;h2 id="最低可執行-gate">最低可執行 Gate&lt;/h2>
&lt;ol>
&lt;li>&lt;strong>變更分類&lt;/strong>：將規則/配置/控制面 API 變更標為 &lt;code>high-blast-radius change&lt;/code>。&lt;/li>
&lt;li>&lt;strong>語意檢查&lt;/strong>：對 query 參數、空值與預設行為做拒絕式驗證。&lt;/li>
&lt;li>&lt;strong>成本檢查&lt;/strong>：用代表性 payload 跑 rule-level CPU / latency 測試。&lt;/li>
&lt;li>&lt;strong>分批推送&lt;/strong>：至少分成小流量群組與全量兩階段，且每階段有回退條件。&lt;/li>
&lt;li>&lt;strong>撤告保護&lt;/strong>：對 withdrawal / delete 設速率與數量上限，超限自動中止。&lt;/li>
&lt;li>&lt;strong>決策紀錄&lt;/strong>：事故期間保留假設、證據、回退門檻、owner 與狀態差異。&lt;/li>
&lt;/ol>
&lt;h2 id="反模式">反模式&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>反模式&lt;/th>
 &lt;th>風險結果&lt;/th>
 &lt;th>修法&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>把規則推送當一般配置&lt;/td>
 &lt;td>低估擴散速度與影響面&lt;/td>
 &lt;td>強制走高風險變更 gate&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>只看 CI / lint&lt;/td>
 &lt;td>無法捕捉 runtime 計算成本風險&lt;/td>
 &lt;td>補 rule replay 與成本基線&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>全域一次推送&lt;/td>
 &lt;td>擴散太快，回退窗口太短&lt;/td>
 &lt;td>改 staged rollout + 即時回退閘門&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>事故後只寫事後摘要&lt;/td>
 &lt;td>無法復盤決策與恢復路徑&lt;/td>
 &lt;td>補 decision log + evidence package&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>未分離 desired/actual state&lt;/td>
 &lt;td>壞狀態難以回到已知穩定點&lt;/td>
 &lt;td>引入 snapshot 與 staged state restore&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="案例回扣">案例回扣&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/cases/cloudflare/2019-regex-cpu-outage/" data-link-title="Cloudflare 2019 Regex CPU Outage" data-link-desc="2019-07-02 Cloudflare WAF 規則更新導致全球 CPU 飆升的事故解析：觸發條件、擴散機制、止血決策與可回寫控制面。">Cloudflare 2019 Regex CPU Outage&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/cases/cloudflare/2023-control-plane-token-incident/" data-link-title="Cloudflare 2023 Control Plane Token Incident" data-link-desc="2023-01-24 Cloudflare service token 錯誤變更導致多產品連鎖影響的事故解析：信任邊界、擴散機制、止血策略與流程回寫。">Cloudflare 2023 Control Plane Token Incident&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/cases/cloudflare/2026-byoip-bgp-withdrawal/" data-link-title="Cloudflare 2026 BYOIP BGP Withdrawal" data-link-desc="2026-02-20 Cloudflare BYOIP prefixes 被非預期撤告的事故解析：Addressing API bug、BGP withdrawal、狀態恢復與控制面回寫。">Cloudflare 2026 BYOIP BGP Withdrawal&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>這三個案例對應同一個上位問題：控制面小變更如何在短時間擴散成全網事故。不同事故只是擴散路徑不同，gate 核心都是「先限制擴散、再修復功能」。&lt;/p></description><content:encoded><![CDATA[<h2 id="概念定位">概念定位</h2>
<p>規則推送安全閘門（rule rollout safety gate）的核心責任是防止控制面錯誤快速擴散到資料面。這個閘門是補上「規則與配置類變更」特有風險，跟既有 release gate 互補而非取代：變更體積小、覆蓋範圍大、擴散速度快。</p>
<p>當變更屬於 WAF rule、routing policy、token/policy、或 Addressing API 相關設定時，判讀重點從程式碼正確性轉為擴散控制。這類變更即使 diff 很短，也可能在數十秒內影響跨區域流量與多產品控制面。</p>
<h2 id="適用場景">適用場景</h2>
<table>
  <thead>
      <tr>
          <th>場景</th>
          <th>典型風險</th>
          <th>為何需要獨立 gate</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>WAF / regex 規則更新</td>
          <td>高計算成本規則拖垮 edge runtime</td>
          <td>CI 綠燈無法代表 runtime 成本安全</td>
      </tr>
      <tr>
          <td>Routing / BYOIP 相關設定變更</td>
          <td>prefix withdrawal 造成服務不可達</td>
          <td>單一 API 查詢語意錯誤可全網擴散</td>
      </tr>
      <tr>
          <td>Token / policy 控制面變更</td>
          <td>多產品授權連鎖失效</td>
          <td>shared dependency 失效會誤導排障路徑</td>
      </tr>
      <tr>
          <td>共享控制面批次清理任務</td>
          <td>批量誤刪或批量撤告</td>
          <td>需要數量閘門與緊急停機機制</td>
      </tr>
  </tbody>
</table>
<h2 id="產業情境遊戲服務的規則推送安全">產業情境：遊戲服務的規則推送安全</h2>
<p>遊戲的規則推送（平衡性調整、反作弊規則、賽季設定、經濟系統參數）有特殊的擴散特性：影響面是全體玩家、生效時機即時、且玩家行為會立刻適應規則變更。</p>
<p>規則推送的 blast radius 預設是全體在線玩家。一次平衡性調整會立刻改變所有正在進行的比賽的角色強度、裝備數值或技能效果。跟一般 feature flag 的 percentage rollout 不同，遊戲平衡性需要所有玩家看到相同規則，否則同場比賽的公平性會被破壞。</p>
<p>推送時機需要跟 match lifecycle 對齊。在進行中的比賽套用新規則會造成不公平 — 某隊在舊規則下建立的優勢可能在新規則下消失。安全做法是在 match boundary（比賽開始或結束時）切換，讓新規則只套用到新開始的比賽。這要求規則推送系統能區分「已開始的 match」和「即將開始的 match」。</p>
<p>回退條件需要綁定遊戲特有的 KPI。active player count 下降超過門檻、match completion rate 異常降低（玩家中途離開）、player report rate 上升（玩家回報異常體驗）、in-game economy anomaly（虛擬貨幣或道具流通量異常）都是規則推送出問題的訊號。這些 KPI 的 feedback loop 比一般服務長 — 玩家行為的適應需要數小時到數天才會穩定，短窗觀察可能漏掉延遲暴露的問題。</p>
<p>反作弊規則的推送有額外約束：規則內容本身是機密的，推送失敗後不能在 log 或 alert 中暴露規則細節，回退也必須在不洩漏偵測邏輯的前提下進行。</p>
<h2 id="gate-檢查層">Gate 檢查層</h2>
<table>
  <thead>
      <tr>
          <th>層級</th>
          <th>Gate 問題</th>
          <th>不通過訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Query / API 語意</td>
          <td>查詢參數有沒有安全預設與錯誤拒絕策略</td>
          <td>空參數回傳全量、模糊布林語意</td>
      </tr>
      <tr>
          <td>Rule 計算成本</td>
          <td>規則是否通過代表性 payload 成本檢查</td>
          <td>單一規則可造成 CPU 熱點</td>
      </tr>
      <tr>
          <td>推送策略</td>
          <td>是否採分群 rollout 並設即時回退條件</td>
          <td>一次推全域、無分區觀測閘門</td>
      </tr>
      <tr>
          <td>Withdrawal 限速</td>
          <td>批次撤告 / 刪除是否有數量與速率限制</td>
          <td>單次操作可影響大量 prefixes 或 bindings</td>
      </tr>
      <tr>
          <td>Shared dependency</td>
          <td>是否識別跨產品共享控制點</td>
          <td>多產品同時異常卻無 shared root 判讀</td>
      </tr>
      <tr>
          <td>Evidence 與回寫</td>
          <td>事故後是否可回放決策、查證恢復路徑與狀態差異</td>
          <td>決策只留結論，缺假設與驗證證據</td>
      </tr>
  </tbody>
</table>
<h2 id="判讀訊號">判讀訊號</h2>
<ul>
<li>控制面變更後，多區域同時出現 5xx / timeout / auth 失敗</li>
<li>指標在秒級惡化，且與最新規則或 policy 變更高度同時</li>
<li>回退後短時間明顯回穩，顯示變更與故障高度關聯</li>
<li>部分服務可自助恢復、部分服務需人工修復，代表狀態損壞分層</li>
<li>事故中出現「每個產品都在修自己的症狀」，代表 shared dependency 沒被先識別</li>
</ul>
<h2 id="最低可執行-gate">最低可執行 Gate</h2>
<ol>
<li><strong>變更分類</strong>：將規則/配置/控制面 API 變更標為 <code>high-blast-radius change</code>。</li>
<li><strong>語意檢查</strong>：對 query 參數、空值與預設行為做拒絕式驗證。</li>
<li><strong>成本檢查</strong>：用代表性 payload 跑 rule-level CPU / latency 測試。</li>
<li><strong>分批推送</strong>：至少分成小流量群組與全量兩階段，且每階段有回退條件。</li>
<li><strong>撤告保護</strong>：對 withdrawal / delete 設速率與數量上限，超限自動中止。</li>
<li><strong>決策紀錄</strong>：事故期間保留假設、證據、回退門檻、owner 與狀態差異。</li>
</ol>
<h2 id="反模式">反模式</h2>
<table>
  <thead>
      <tr>
          <th>反模式</th>
          <th>風險結果</th>
          <th>修法</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>把規則推送當一般配置</td>
          <td>低估擴散速度與影響面</td>
          <td>強制走高風險變更 gate</td>
      </tr>
      <tr>
          <td>只看 CI / lint</td>
          <td>無法捕捉 runtime 計算成本風險</td>
          <td>補 rule replay 與成本基線</td>
      </tr>
      <tr>
          <td>全域一次推送</td>
          <td>擴散太快，回退窗口太短</td>
          <td>改 staged rollout + 即時回退閘門</td>
      </tr>
      <tr>
          <td>事故後只寫事後摘要</td>
          <td>無法復盤決策與恢復路徑</td>
          <td>補 decision log + evidence package</td>
      </tr>
      <tr>
          <td>未分離 desired/actual state</td>
          <td>壞狀態難以回到已知穩定點</td>
          <td>引入 snapshot 與 staged state restore</td>
      </tr>
  </tbody>
</table>
<h2 id="案例回扣">案例回扣</h2>
<ul>
<li><a href="/blog/backend/08-incident-response/cases/cloudflare/2019-regex-cpu-outage/" data-link-title="Cloudflare 2019 Regex CPU Outage" data-link-desc="2019-07-02 Cloudflare WAF 規則更新導致全球 CPU 飆升的事故解析：觸發條件、擴散機制、止血決策與可回寫控制面。">Cloudflare 2019 Regex CPU Outage</a></li>
<li><a href="/blog/backend/08-incident-response/cases/cloudflare/2023-control-plane-token-incident/" data-link-title="Cloudflare 2023 Control Plane Token Incident" data-link-desc="2023-01-24 Cloudflare service token 錯誤變更導致多產品連鎖影響的事故解析：信任邊界、擴散機制、止血策略與流程回寫。">Cloudflare 2023 Control Plane Token Incident</a></li>
<li><a href="/blog/backend/08-incident-response/cases/cloudflare/2026-byoip-bgp-withdrawal/" data-link-title="Cloudflare 2026 BYOIP BGP Withdrawal" data-link-desc="2026-02-20 Cloudflare BYOIP prefixes 被非預期撤告的事故解析：Addressing API bug、BGP withdrawal、狀態恢復與控制面回寫。">Cloudflare 2026 BYOIP BGP Withdrawal</a></li>
</ul>
<p>這三個案例對應同一個上位問題：控制面小變更如何在短時間擴散成全網事故。不同事故只是擴散路徑不同，gate 核心都是「先限制擴散、再修復功能」。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li><code>04</code>： <a href="/blog/backend/04-observability/telemetry-data-quality/" data-link-title="4.17 Telemetry Data Quality" data-link-desc="把 missing signal、schema drift、sampling bias 與 timestamp skew 變成資料品質問題">4.17 Telemetry Data Quality</a></li>
<li><code>06</code>： <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate</a></li>
<li><code>06</code>： <a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 Experiment Safety Boundary</a></li>
<li><code>06</code>： <a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23 Verification Evidence Handoff</a></li>
<li><code>08</code>： <a href="/blog/backend/08-incident-response/incident-decision-log/" data-link-title="8.19 Incident Decision Log" data-link-desc="把事中假設、決策、證據、回退條件與責任人留下可復盤紀錄">8.19 Incident Decision Log</a></li>
<li><code>08</code>： <a href="/blog/backend/08-incident-response/incident-evidence-write-back/" data-link-title="8.22 Incident Evidence Write-back" data-link-desc="把事故證據、決策與復盤結論回寫到 observability、reliability 與 runbook">8.22 Incident Evidence Write-back</a></li>
</ul>
]]></content:encoded></item><item><title>6.25 Provider Dependency Release Gate 實作示範</title><link>https://tarrragon.github.io/blog/backend/06-reliability/provider-dependency-release-gate/</link><pubDate>Fri, 08 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/provider-dependency-release-gate/</guid><description>&lt;p>Provider dependency release gate 的核心責任是把第三方依賴風險轉成可驗證放行條件，避免變更在高不確定性下直接擴散。&lt;/p>
&lt;h2 id="服務路徑與風險模型">服務路徑與風險模型&lt;/h2>
&lt;p>示範路徑是 checkout API 切換 payment provider timeout/retry 設定。這類變更看起來只改 config，但會直接影響交易成功率、延遲與重試風暴。&lt;/p>
&lt;p>gate 應固定五欄：&lt;code>Gate decision&lt;/code>、&lt;code>Checks&lt;/code>、&lt;code>Stop condition&lt;/code>、&lt;code>Rollback window&lt;/code>、&lt;code>Owner&lt;/code>。欄位先成立，再討論工具落地。&lt;/p>
&lt;p>以 payment provider timeout 調整為例，五欄的具體內容：&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>欄位&lt;/th>
 &lt;th>範例值&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Gate decision&lt;/td>
 &lt;td>proceed / hold / rollback — 每批 canary 結束時做一次判定&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Checks&lt;/td>
 &lt;td>checkout success rate &amp;gt; 99.5%、provider timeout ratio &amp;lt; 2%、duplicate charge = 0、error budget remaining &amp;gt; 30%&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Stop condition&lt;/td>
 &lt;td>error rate 超門檻、latency p99 超過基線 2 倍、provider timeout ratio &amp;gt; 5%，任一觸發即停止擴批&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Rollback window&lt;/td>
 &lt;td>15 min — config-only 變更無 schema 衝突，超過 15 min 後交易資料可能依賴新設定&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Owner&lt;/td>
 &lt;td>checkout team lead，負責每批 go/no-go 與 rollback 決策&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Checks 欄位的數值來自歷史 baseline，每次變更前從 production 最近 7 天取值。baseline 偏移超過 10% 時，先校準再啟動 canary。&lt;/p>
&lt;h2 id="實作步驟">實作步驟&lt;/h2>
&lt;ol>
&lt;li>定義放行前檢查：checkout 成功率、provider timeout 比率、duplicate charge 監控、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/error-budget/" data-link-title="Error Budget" data-link-desc="說明 SLO 允許的失敗額度如何影響發版與可靠性投入">error budget&lt;/a> 餘量。&lt;/li>
&lt;li>設定 canary 節奏：1% -&amp;gt; 5% -&amp;gt; 25% -&amp;gt; 100%，每批觀察固定時間窗。&lt;/li>
&lt;li>為每批設定 stop condition：error rate、latency、provider timeout 任一超門檻即停止擴大。&lt;/li>
&lt;li>設定 rollback window：例如 15 分鐘內可無資料格式衝突地回退設定。&lt;/li>
&lt;li>把每批結果寫入 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23 Verification Evidence Handoff&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/incident-decision-log/" data-link-title="8.19 Incident Decision Log" data-link-desc="把事中假設、決策、證據、回退條件與責任人留下可復盤紀錄">8.19 Incident Decision Log&lt;/a>。&lt;/li>
&lt;/ol>
&lt;h3 id="canary-節奏與觀察窗">Canary 節奏與觀察窗&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>批次&lt;/th>
 &lt;th>流量比例&lt;/th>
 &lt;th>觀察窗&lt;/th>
 &lt;th>Go/no-go 判斷依據&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>B1&lt;/td>
 &lt;td>1%&lt;/td>
 &lt;td>30 min&lt;/td>
 &lt;td>checks 全過、stop condition 未觸發&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>B2&lt;/td>
 &lt;td>5%&lt;/td>
 &lt;td>1 h&lt;/td>
 &lt;td>B1 指標持平、無 duplicate charge、無客訴&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>B3&lt;/td>
 &lt;td>25%&lt;/td>
 &lt;td>2 h&lt;/td>
 &lt;td>B2 指標持平、error budget 消耗速度未加快&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>B4&lt;/td>
 &lt;td>100%&lt;/td>
 &lt;td>持續觀測&lt;/td>
 &lt;td>B3 指標持平、跨區結果一致，進入持續觀測而非一次性放行&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Payment 類變更的觀察窗比一般 config 變更長，原因有兩個。第一，交易確認有延遲 — provider 回傳 settlement 結果可能在數分鐘到數小時後，短觀察窗無法看到完整的交易結果分佈。第二，退款與爭議申請通常在交易後數小時甚至數天才出現，B3 階段需要持續追蹤退款率趨勢，確認新設定沒有引發 provider 層的異常判定。&lt;/p></description><content:encoded><![CDATA[<p>Provider dependency release gate 的核心責任是把第三方依賴風險轉成可驗證放行條件，避免變更在高不確定性下直接擴散。</p>
<h2 id="服務路徑與風險模型">服務路徑與風險模型</h2>
<p>示範路徑是 checkout API 切換 payment provider timeout/retry 設定。這類變更看起來只改 config，但會直接影響交易成功率、延遲與重試風暴。</p>
<p>gate 應固定五欄：<code>Gate decision</code>、<code>Checks</code>、<code>Stop condition</code>、<code>Rollback window</code>、<code>Owner</code>。欄位先成立，再討論工具落地。</p>
<p>以 payment provider timeout 調整為例，五欄的具體內容：</p>
<table>
  <thead>
      <tr>
          <th>欄位</th>
          <th>範例值</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Gate decision</td>
          <td>proceed / hold / rollback — 每批 canary 結束時做一次判定</td>
      </tr>
      <tr>
          <td>Checks</td>
          <td>checkout success rate &gt; 99.5%、provider timeout ratio &lt; 2%、duplicate charge = 0、error budget remaining &gt; 30%</td>
      </tr>
      <tr>
          <td>Stop condition</td>
          <td>error rate 超門檻、latency p99 超過基線 2 倍、provider timeout ratio &gt; 5%，任一觸發即停止擴批</td>
      </tr>
      <tr>
          <td>Rollback window</td>
          <td>15 min — config-only 變更無 schema 衝突，超過 15 min 後交易資料可能依賴新設定</td>
      </tr>
      <tr>
          <td>Owner</td>
          <td>checkout team lead，負責每批 go/no-go 與 rollback 決策</td>
      </tr>
  </tbody>
</table>
<p>Checks 欄位的數值來自歷史 baseline，每次變更前從 production 最近 7 天取值。baseline 偏移超過 10% 時，先校準再啟動 canary。</p>
<h2 id="實作步驟">實作步驟</h2>
<ol>
<li>定義放行前檢查：checkout 成功率、provider timeout 比率、duplicate charge 監控、<a href="/blog/backend/knowledge-cards/error-budget/" data-link-title="Error Budget" data-link-desc="說明 SLO 允許的失敗額度如何影響發版與可靠性投入">error budget</a> 餘量。</li>
<li>設定 canary 節奏：1% -&gt; 5% -&gt; 25% -&gt; 100%，每批觀察固定時間窗。</li>
<li>為每批設定 stop condition：error rate、latency、provider timeout 任一超門檻即停止擴大。</li>
<li>設定 rollback window：例如 15 分鐘內可無資料格式衝突地回退設定。</li>
<li>把每批結果寫入 <a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23 Verification Evidence Handoff</a> 與 <a href="/blog/backend/08-incident-response/incident-decision-log/" data-link-title="8.19 Incident Decision Log" data-link-desc="把事中假設、決策、證據、回退條件與責任人留下可復盤紀錄">8.19 Incident Decision Log</a>。</li>
</ol>
<h3 id="canary-節奏與觀察窗">Canary 節奏與觀察窗</h3>
<table>
  <thead>
      <tr>
          <th>批次</th>
          <th>流量比例</th>
          <th>觀察窗</th>
          <th>Go/no-go 判斷依據</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>B1</td>
          <td>1%</td>
          <td>30 min</td>
          <td>checks 全過、stop condition 未觸發</td>
      </tr>
      <tr>
          <td>B2</td>
          <td>5%</td>
          <td>1 h</td>
          <td>B1 指標持平、無 duplicate charge、無客訴</td>
      </tr>
      <tr>
          <td>B3</td>
          <td>25%</td>
          <td>2 h</td>
          <td>B2 指標持平、error budget 消耗速度未加快</td>
      </tr>
      <tr>
          <td>B4</td>
          <td>100%</td>
          <td>持續觀測</td>
          <td>B3 指標持平、跨區結果一致，進入持續觀測而非一次性放行</td>
      </tr>
  </tbody>
</table>
<p>Payment 類變更的觀察窗比一般 config 變更長，原因有兩個。第一，交易確認有延遲 — provider 回傳 settlement 結果可能在數分鐘到數小時後，短觀察窗無法看到完整的交易結果分佈。第二，退款與爭議申請通常在交易後數小時甚至數天才出現，B3 階段需要持續追蹤退款率趨勢，確認新設定沒有引發 provider 層的異常判定。</p>
<h3 id="證據留存格式">證據留存格式</h3>
<p>每批 canary 結束時留存一筆結構化證據，供 <a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23</a> 與 <a href="/blog/backend/08-incident-response/incident-decision-log/" data-link-title="8.19 Incident Decision Log" data-link-desc="把事中假設、決策、證據、回退條件與責任人留下可復盤紀錄">8.19</a> 調用。</p>
<table>
  <thead>
      <tr>
          <th>欄位</th>
          <th>內容</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>batch</td>
          <td>B1 / B2 / B3 / B4</td>
      </tr>
      <tr>
          <td>timestamp</td>
          <td>批次開始與結束時間</td>
      </tr>
      <tr>
          <td>traffic %</td>
          <td>該批實際流量比例</td>
      </tr>
      <tr>
          <td>metrics snapshot</td>
          <td>checkout success rate、latency p99、provider timeout ratio</td>
      </tr>
      <tr>
          <td>decision</td>
          <td>proceed / hold / rollback</td>
      </tr>
      <tr>
          <td>decider</td>
          <td>做出該決策的人與角色</td>
      </tr>
  </tbody>
</table>
<p>這個格式讓事故發生時，<a href="/blog/backend/08-incident-response/incident-decision-log/" data-link-title="8.19 Incident Decision Log" data-link-desc="把事中假設、決策、證據、回退條件與責任人留下可復盤紀錄">8.19 Incident Decision Log</a> 可以直接調用每批的 metrics 與決策紀錄，不需要回溯 dashboard 截圖。</p>
<h2 id="判讀訊號">判讀訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應動作</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>canary 成功率正常但 timeout 升高</td>
          <td>交易完成但成本與延遲風險在累積</td>
          <td>暫停擴批，先調 provider timeout 策略</td>
      </tr>
      <tr>
          <td>error budget 快速消耗</td>
          <td>變更風險超過目前可承受範圍</td>
          <td>觸發 freeze，回到上一批設定</td>
      </tr>
      <tr>
          <td>rollback 成功但客訴仍上升</td>
          <td>影響可能來自非同步補償或下游延遲</td>
          <td>補 replay/對帳證據，再決定是否二次回退</td>
      </tr>
      <tr>
          <td>不同區域結果分歧</td>
          <td>provider 區域品質差異或路由策略不一致</td>
          <td>分區 gate，禁止全域同批放行</td>
      </tr>
      <tr>
          <td>告警只反映症狀無法定位變更關聯</td>
          <td>evidence 與 deploy event 沒對位</td>
          <td>補 deployment event link 與 owner 欄位</td>
      </tr>
  </tbody>
</table>
<h2 id="常見誤區">常見誤區</h2>
<p>把 gate 當成 CI 綠燈會漏掉依賴風險。依賴類變更需要觀測窗與停損條件，單靠測試通過不足以放行。</p>
<p>把 rollback window 寫成「可回退」但沒有時限也會失效。沒有時間邊界的回退通常意味著資料與行為已經漂移。</p>
<h2 id="案例回寫">案例回寫</h2>
<p>這條路徑可用 <a href="/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/" data-link-title="Stripe：Idempotency 與零停機遷移的交易安全設計" data-link-desc="把 API 重試與資料遷移放在同一套安全模型，維持支付交易的一致結果。">Stripe Idempotency and Zero-downtime Migration</a> 回寫。先看交易正確性與變更節奏如何綁定，再回到本章對齊 gate 欄位與停損邏輯。</p>
<p>這個案例主要支撐的是「交易依賴變更放行節奏」判讀，不直接支撐 incident 通訊節奏；對外更新要回到 8.4。</p>
<h2 id="跨模組路由">跨模組路由</h2>
<ol>
<li>與 4.22 的交接：證據來源使用 <a href="/blog/backend/04-observability/checkout-api-evidence-package/" data-link-title="4.22 Checkout API Evidence Package 實作示範" data-link-desc="用 checkout 路徑示範 evidence package 如何交接給 release gate 與 incident decision。">Checkout API Evidence Package</a>。</li>
<li>與 6.8 的交接：策略與制度回到 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">Release Gate 與變更節奏</a>。</li>
<li>與 6.23 的交接：每批驗證證據進 <a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">Verification Evidence Handoff</a>。</li>
<li>與 8.19 的交接：停損與回退決策同步到 incident decision log。</li>
</ol>
<h2 id="下一步路由">下一步路由</h2>
<p>要看控制面事故如何用 decision log 與 write-back 關閉迴圈，接著讀 <a href="/blog/backend/08-incident-response/control-plane-decision-log-write-back/" data-link-title="8.23 Control Plane Decision Log and Write-back 實作示範" data-link-desc="以 rule/config rollout 事故示範 decision log 與 write-back 如何形成可回放閉環。">8.23 Control Plane Decision Log and Write-back 實作示範</a>。</p>
]]></content:encoded></item><item><title>Amazon：Shuffle Sharding 與 Cell 邊界的失效局部化</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/amazon/shuffle-sharding-and-cell-boundary/</link><pubDate>Thu, 07 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/amazon/shuffle-sharding-and-cell-boundary/</guid><description>&lt;p>Amazon 可靠性設計的核心責任是把失效影響限制在局部邊界。當系統採用多租戶與大規模共享資源，隔離策略必須先於恢復策略被定義，否則任何回復動作都會變成全域風險。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>多租戶服務常見的放大路徑是「單租戶異常 → 共享資源飽和 → 全域退化」。若路由與容量都沒有明確邊界，團隊只能在事故後做整體降載，代價高且恢復慢。&lt;/p>
&lt;p>cell-based architecture 與 shuffle sharding 提供的是前置結構：先限制擴散，再談恢復。&lt;/p>
&lt;h2 id="決策機制">決策機制&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>機制&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>交付結果&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Cell 邊界&lt;/td>
 &lt;td>一個失效最多影響到哪裡&lt;/td>
 &lt;td>局部故障域&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Shuffle sharding&lt;/td>
 &lt;td>熱點租戶如何避免重疊影響&lt;/td>
 &lt;td>隨機子集合隔離&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Static stability&lt;/td>
 &lt;td>控制面失效時資料面如何維持&lt;/td>
 &lt;td>降級持續服務&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Constant work&lt;/td>
 &lt;td>失敗模式下是否維持固定工作量&lt;/td>
 &lt;td>防放大設計&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這組機制讓恢復策略從「全域搶救」轉為「分批收斂」。在可用性與成本取捨上，局部隔離通常比全域冗餘更可持續。&lt;/p>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>shard contention&lt;/td>
 &lt;td>熱點是否跨 shard 擴散&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>cell error isolation ratio&lt;/td>
 &lt;td>錯誤是否被限制在局部&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>recovery batch completion&lt;/td>
 &lt;td>分批恢復是否可預測&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/containment-recovery-strategy/" data-link-title="8.3 止血、降級與回復策略" data-link-desc="把短期止血與正式回復拆成可執行步驟">8.3&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>control-plane dependency lag&lt;/td>
 &lt;td>控制面異常是否拖累資料面&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/04-observability/service-topology/" data-link-title="4.13 Service Topology 與 Dependency Map" data-link-desc="把跨服務依賴從文件變成自動發現的觀測訊號">4.13&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="常見陷阱">常見陷阱&lt;/h2>
&lt;p>把 sharding 當成純擴容手段會忽略隔離責任。當分片策略只服務容量，沒有對齊失效邊界，事故時仍會看到跨租戶共振。真正的設計重點是「隔離優先，擴容其次」。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>要把案例轉成可執行設計，先定義 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14&lt;/a> 的依賴預算與共享邊界，再在 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20&lt;/a> 驗證局部化假設。事故時的分批回復流程回到 &lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/multi-incident-coordination/" data-link-title="8.14 Multi-incident Coordination" data-link-desc="把同時多事故的優先序、資源分配與 incident command system pool 協調變成可執行流程">8.14&lt;/a>。&lt;/p></description><content:encoded><![CDATA[<p>Amazon 可靠性設計的核心責任是把失效影響限制在局部邊界。當系統採用多租戶與大規模共享資源，隔離策略必須先於恢復策略被定義，否則任何回復動作都會變成全域風險。</p>
<h2 id="問題場景">問題場景</h2>
<p>多租戶服務常見的放大路徑是「單租戶異常 → 共享資源飽和 → 全域退化」。若路由與容量都沒有明確邊界，團隊只能在事故後做整體降載，代價高且恢復慢。</p>
<p>cell-based architecture 與 shuffle sharding 提供的是前置結構：先限制擴散，再談恢復。</p>
<h2 id="決策機制">決策機制</h2>
<table>
  <thead>
      <tr>
          <th>機制</th>
          <th>核心問題</th>
          <th>交付結果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Cell 邊界</td>
          <td>一個失效最多影響到哪裡</td>
          <td>局部故障域</td>
      </tr>
      <tr>
          <td>Shuffle sharding</td>
          <td>熱點租戶如何避免重疊影響</td>
          <td>隨機子集合隔離</td>
      </tr>
      <tr>
          <td>Static stability</td>
          <td>控制面失效時資料面如何維持</td>
          <td>降級持續服務</td>
      </tr>
      <tr>
          <td>Constant work</td>
          <td>失敗模式下是否維持固定工作量</td>
          <td>防放大設計</td>
      </tr>
  </tbody>
</table>
<p>這組機制讓恢復策略從「全域搶救」轉為「分批收斂」。在可用性與成本取捨上，局部隔離通常比全域冗餘更可持續。</p>
<h2 id="可觀測訊號">可觀測訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>shard contention</td>
          <td>熱點是否跨 shard 擴散</td>
          <td><a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14</a></td>
      </tr>
      <tr>
          <td>cell error isolation ratio</td>
          <td>錯誤是否被限制在局部</td>
          <td><a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20</a></td>
      </tr>
      <tr>
          <td>recovery batch completion</td>
          <td>分批恢復是否可預測</td>
          <td><a href="/blog/backend/08-incident-response/containment-recovery-strategy/" data-link-title="8.3 止血、降級與回復策略" data-link-desc="把短期止血與正式回復拆成可執行步驟">8.3</a></td>
      </tr>
      <tr>
          <td>control-plane dependency lag</td>
          <td>控制面異常是否拖累資料面</td>
          <td><a href="/blog/backend/04-observability/service-topology/" data-link-title="4.13 Service Topology 與 Dependency Map" data-link-desc="把跨服務依賴從文件變成自動發現的觀測訊號">4.13</a></td>
      </tr>
  </tbody>
</table>
<h2 id="常見陷阱">常見陷阱</h2>
<p>把 sharding 當成純擴容手段會忽略隔離責任。當分片策略只服務容量，沒有對齊失效邊界，事故時仍會看到跨租戶共振。真正的設計重點是「隔離優先，擴容其次」。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>要把案例轉成可執行設計，先定義 <a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14</a> 的依賴預算與共享邊界，再在 <a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20</a> 驗證局部化假設。事故時的分批回復流程回到 <a href="/blog/backend/08-incident-response/multi-incident-coordination/" data-link-title="8.14 Multi-incident Coordination" data-link-desc="把同時多事故的優先序、資源分配與 incident command system pool 協調變成可執行流程">8.14</a>。</p>
]]></content:encoded></item><item><title>LinkedIn：Capacity Headroom 與 On-call 分層</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/linkedin/capacity-headroom-and-oncall-tiering/</link><pubDate>Thu, 07 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/linkedin/capacity-headroom-and-oncall-tiering/</guid><description>&lt;p>LinkedIn 案例的核心責任是讓容量治理與 on-call 分工一起運作。高流量服務的穩定性不只靠擴容，還靠清楚的接手邏輯。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>當流量逼近上限時，技術瓶頸與協作瓶頸會同時出現。若只有容量模型，沒有分層值班，恢復節奏仍會失控。&lt;/p>
&lt;h2 id="決策機制">決策機制&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>機制&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>交付結果&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Headroom 預算&lt;/td>
 &lt;td>何時進入風險區&lt;/td>
 &lt;td>擴容與限流門檻&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Primary/Secondary/SME&lt;/td>
 &lt;td>何時由誰接手&lt;/td>
 &lt;td>升級路徑&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>自動化壓測&lt;/td>
 &lt;td>模型是否貼近現況&lt;/td>
 &lt;td>驗證循環&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>replication latency&lt;/td>
 &lt;td>是否接近容量邊界&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/capacity-cost/" data-link-title="6.9 容量與成本邊界" data-link-desc="把容量規劃跟成本約束變成驗證輸入">6.9&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>on-call handoff latency&lt;/td>
 &lt;td>分層交接是否順暢&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/ic-handoff-long-incident/" data-link-title="8.12 IC Handoff 與長事故跨班次協調" data-link-desc="把 24h&amp;#43; / 跨 timezone 事故的接班節奏變成可重複流程">8.12&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>load-test drift&lt;/td>
 &lt;td>模型與真實壓力是否偏移&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>把容量假設寫進 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22&lt;/a>，再把交接規則對齊 &lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/incident-command-roles/" data-link-title="8.2 事故指揮與角色分工" data-link-desc="定義 incident commander 與跨角色協作責任">8.2&lt;/a>。&lt;/p></description><content:encoded><![CDATA[<p>LinkedIn 案例的核心責任是讓容量治理與 on-call 分工一起運作。高流量服務的穩定性不只靠擴容，還靠清楚的接手邏輯。</p>
<h2 id="問題場景">問題場景</h2>
<p>當流量逼近上限時，技術瓶頸與協作瓶頸會同時出現。若只有容量模型，沒有分層值班，恢復節奏仍會失控。</p>
<h2 id="決策機制">決策機制</h2>
<table>
  <thead>
      <tr>
          <th>機制</th>
          <th>核心問題</th>
          <th>交付結果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Headroom 預算</td>
          <td>何時進入風險區</td>
          <td>擴容與限流門檻</td>
      </tr>
      <tr>
          <td>Primary/Secondary/SME</td>
          <td>何時由誰接手</td>
          <td>升級路徑</td>
      </tr>
      <tr>
          <td>自動化壓測</td>
          <td>模型是否貼近現況</td>
          <td>驗證循環</td>
      </tr>
  </tbody>
</table>
<h2 id="可觀測訊號">可觀測訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>replication latency</td>
          <td>是否接近容量邊界</td>
          <td><a href="/blog/backend/06-reliability/capacity-cost/" data-link-title="6.9 容量與成本邊界" data-link-desc="把容量規劃跟成本約束變成驗證輸入">6.9</a></td>
      </tr>
      <tr>
          <td>on-call handoff latency</td>
          <td>分層交接是否順暢</td>
          <td><a href="/blog/backend/08-incident-response/ic-handoff-long-incident/" data-link-title="8.12 IC Handoff 與長事故跨班次協調" data-link-desc="把 24h&#43; / 跨 timezone 事故的接班節奏變成可重複流程">8.12</a></td>
      </tr>
      <tr>
          <td>load-test drift</td>
          <td>模型與真實壓力是否偏移</td>
          <td><a href="/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2</a></td>
      </tr>
  </tbody>
</table>
<h2 id="下一步路由">下一步路由</h2>
<p>把容量假設寫進 <a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22</a>，再把交接規則對齊 <a href="/blog/backend/08-incident-response/incident-command-roles/" data-link-title="8.2 事故指揮與角色分工" data-link-desc="定義 incident commander 與跨角色協作責任">8.2</a>。</p>
]]></content:encoded></item><item><title>Amazon：Static Stability 與 Constant Work Pattern</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/amazon/static-stability-and-constant-work/</link><pubDate>Tue, 23 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/amazon/static-stability-and-constant-work/</guid><description>&lt;p>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/static-stability/" data-link-title="Static Stability" data-link-desc="控制面失效時資料面用快取的已知好配置繼續服務的設計模式">Static stability&lt;/a> 的責任是讓資料面在控制面故障時仍能維持服務。Constant work pattern 的責任是讓系統在失敗模式下的工作量與正常時相同。兩者共同保護系統在最需要穩定時不會因為自救動作而崩潰。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>控制面管理路由、配置推送、服務發現與 auto-scaling。當控制面本身失效，依賴控制面的資料面會同時進入未知狀態。最危險的放大路徑是：控制面掛掉後，資料面節點同時嘗試重新連線或重新取得配置，retry storm 把殘餘容量耗盡，資料面跟著崩潰。&lt;/p>
&lt;p>這個問題在大規模平台上尤其嚴重。節點越多，控制面恢復時的同時 pull 量越大，恢復本身就會變成新的負載來源。&lt;/p>
&lt;h2 id="決策機制">決策機制&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>機制&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>設計約束&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Static stability&lt;/td>
 &lt;td>控制面不可用時資料面能否繼續服務&lt;/td>
 &lt;td>快取的配置必須是完整可用狀態，不能是 partial update&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Constant work&lt;/td>
 &lt;td>失敗模式下的系統工作量是否跟正常時相同&lt;/td>
 &lt;td>push-based 優於 pull-based：定時推全量，不靠拉取&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Pre-computed fallback&lt;/td>
 &lt;td>控制面失效時是否有不需要即時計算的備援路徑&lt;/td>
 &lt;td>fallback 路徑預先建好，切換動作本身不依賴控制面&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Static stability 的實作核心是讓每個資料面節點持有控制面最後已知的好配置。當控制面恢復通訊時，節點用最新配置更新快取；當通訊中斷時，節點用快取繼續服務。這個設計要求配置快取是完整的（能獨立驅動服務），而不是差分的（需要跟控制面合併才能用）。&lt;/p>
&lt;p>Constant work pattern 的核心是讓系統無論在正常或故障狀態下都執行相同的工作量。push-based config distribution 在每個週期推送全量配置給所有節點，不管配置是否有變更。這樣在控制面恢復時，不會因為所有節點同時 pull 而產生 thundering herd。相比之下，pull-based 在正常時流量低，但控制面恢復瞬間流量暴增。&lt;/p>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>control-plane health&lt;/td>
 &lt;td>控制面是否可用、是否在退化中&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/04-observability/service-topology/" data-link-title="4.13 Service Topology 與 Dependency Map" data-link-desc="把跨服務依賴從文件變成自動發現的觀測訊號">4.13&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>cache staleness&lt;/td>
 &lt;td>快取配置距離最後更新多久&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>recovery work amplification&lt;/td>
 &lt;td>恢復過程中負載是否比正常時更高&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>data-plane autonomous duration&lt;/td>
 &lt;td>資料面在無控制面時能獨立運作多久&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/dr-rollback-rehearsal/" data-link-title="6.7 DR 演練與 Rollback Rehearsal" data-link-desc="把回復路徑從紙面計畫變成定期可重播、可量測的驗證流程">6.7&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>cache staleness 是 static stability 最關鍵的健康指標。當快取新鮮度超過預設門檻（取決於配置變更頻率），資料面仍能服務，但服務行為可能與最新意圖不一致。這個門檻決定了 degraded mode 的可接受時間窗。&lt;/p>
&lt;h2 id="常見陷阱">常見陷阱&lt;/h2>
&lt;p>把控制面失效視為低概率事件而不做 static stability 設計，會在真正發生時暴露循環依賴。Meta 2021-10 事故中，BGP 配置變更導致控制面與資料面共用的網路路徑同時失效，而恢復工具本身也依賴這條路徑，恢復動作陷入循環等待。這個案例說明 static stability 的價值在事前設計，而非事後補救。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/dr-rollback-rehearsal/" data-link-title="6.7 DR 演練與 Rollback Rehearsal" data-link-desc="把回復路徑從紙面計畫變成定期可重播、可量測的驗證流程">6.7 DR rollback rehearsal&lt;/a>：static stability 讓資料面在災難期間自主運作，是 DR by design&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14 dependency reliability budget&lt;/a>：控制面是最高風險的內部依賴，budget 設計要先處理控制面失效&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22 steady state definition&lt;/a>：degraded mode 下的穩態需要包含「控制面不可用但資料面仍服務」的定義&lt;/li>
&lt;/ul>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://aws.amazon.com/builders-library/static-stability-using-availability-zones/">Static stability using Availability Zones&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://aws.amazon.com/builders-library/avoiding-insurmountable-queue-backlogs/">Avoiding insurmountable queue backlogs&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p><a href="/blog/backend/knowledge-cards/static-stability/" data-link-title="Static Stability" data-link-desc="控制面失效時資料面用快取的已知好配置繼續服務的設計模式">Static stability</a> 的責任是讓資料面在控制面故障時仍能維持服務。Constant work pattern 的責任是讓系統在失敗模式下的工作量與正常時相同。兩者共同保護系統在最需要穩定時不會因為自救動作而崩潰。</p>
<h2 id="問題場景">問題場景</h2>
<p>控制面管理路由、配置推送、服務發現與 auto-scaling。當控制面本身失效，依賴控制面的資料面會同時進入未知狀態。最危險的放大路徑是：控制面掛掉後，資料面節點同時嘗試重新連線或重新取得配置，retry storm 把殘餘容量耗盡，資料面跟著崩潰。</p>
<p>這個問題在大規模平台上尤其嚴重。節點越多，控制面恢復時的同時 pull 量越大，恢復本身就會變成新的負載來源。</p>
<h2 id="決策機制">決策機制</h2>
<table>
  <thead>
      <tr>
          <th>機制</th>
          <th>核心問題</th>
          <th>設計約束</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Static stability</td>
          <td>控制面不可用時資料面能否繼續服務</td>
          <td>快取的配置必須是完整可用狀態，不能是 partial update</td>
      </tr>
      <tr>
          <td>Constant work</td>
          <td>失敗模式下的系統工作量是否跟正常時相同</td>
          <td>push-based 優於 pull-based：定時推全量，不靠拉取</td>
      </tr>
      <tr>
          <td>Pre-computed fallback</td>
          <td>控制面失效時是否有不需要即時計算的備援路徑</td>
          <td>fallback 路徑預先建好，切換動作本身不依賴控制面</td>
      </tr>
  </tbody>
</table>
<p>Static stability 的實作核心是讓每個資料面節點持有控制面最後已知的好配置。當控制面恢復通訊時，節點用最新配置更新快取；當通訊中斷時，節點用快取繼續服務。這個設計要求配置快取是完整的（能獨立驅動服務），而不是差分的（需要跟控制面合併才能用）。</p>
<p>Constant work pattern 的核心是讓系統無論在正常或故障狀態下都執行相同的工作量。push-based config distribution 在每個週期推送全量配置給所有節點，不管配置是否有變更。這樣在控制面恢復時，不會因為所有節點同時 pull 而產生 thundering herd。相比之下，pull-based 在正常時流量低，但控制面恢復瞬間流量暴增。</p>
<h2 id="可觀測訊號">可觀測訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>control-plane health</td>
          <td>控制面是否可用、是否在退化中</td>
          <td><a href="/blog/backend/04-observability/service-topology/" data-link-title="4.13 Service Topology 與 Dependency Map" data-link-desc="把跨服務依賴從文件變成自動發現的觀測訊號">4.13</a></td>
      </tr>
      <tr>
          <td>cache staleness</td>
          <td>快取配置距離最後更新多久</td>
          <td><a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22</a></td>
      </tr>
      <tr>
          <td>recovery work amplification</td>
          <td>恢復過程中負載是否比正常時更高</td>
          <td><a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14</a></td>
      </tr>
      <tr>
          <td>data-plane autonomous duration</td>
          <td>資料面在無控制面時能獨立運作多久</td>
          <td><a href="/blog/backend/06-reliability/dr-rollback-rehearsal/" data-link-title="6.7 DR 演練與 Rollback Rehearsal" data-link-desc="把回復路徑從紙面計畫變成定期可重播、可量測的驗證流程">6.7</a></td>
      </tr>
  </tbody>
</table>
<p>cache staleness 是 static stability 最關鍵的健康指標。當快取新鮮度超過預設門檻（取決於配置變更頻率），資料面仍能服務，但服務行為可能與最新意圖不一致。這個門檻決定了 degraded mode 的可接受時間窗。</p>
<h2 id="常見陷阱">常見陷阱</h2>
<p>把控制面失效視為低概率事件而不做 static stability 設計，會在真正發生時暴露循環依賴。Meta 2021-10 事故中，BGP 配置變更導致控制面與資料面共用的網路路徑同時失效，而恢復工具本身也依賴這條路徑，恢復動作陷入循環等待。這個案例說明 static stability 的價值在事前設計，而非事後補救。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li><a href="/blog/backend/06-reliability/dr-rollback-rehearsal/" data-link-title="6.7 DR 演練與 Rollback Rehearsal" data-link-desc="把回復路徑從紙面計畫變成定期可重播、可量測的驗證流程">6.7 DR rollback rehearsal</a>：static stability 讓資料面在災難期間自主運作，是 DR by design</li>
<li><a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14 dependency reliability budget</a>：控制面是最高風險的內部依賴，budget 設計要先處理控制面失效</li>
<li><a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22 steady state definition</a>：degraded mode 下的穩態需要包含「控制面不可用但資料面仍服務」的定義</li>
</ul>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://aws.amazon.com/builders-library/static-stability-using-availability-zones/">Static stability using Availability Zones</a></li>
<li><a href="https://aws.amazon.com/builders-library/avoiding-insurmountable-queue-backlogs/">Avoiding insurmountable queue backlogs</a></li>
</ul>
]]></content:encoded></item><item><title>Honeycomb：Production Excellence 與 Test in Production</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/honeycomb/production-excellence-and-test-in-production/</link><pubDate>Tue, 23 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/honeycomb/production-excellence-and-test-in-production/</guid><description>&lt;p>Honeycomb 團隊是 test in production 理念的主要推動者之一。Production excellence 的核心責任是把 production 觀測能力提升到可以安全驗證變更的水準。當觀測能力足夠細緻，團隊可以在真實流量下驗證行為，降低對 staging 環境的依賴。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>Staging 跟 production 之間的差異是結構性的 — 資料量不同、流量模式不同、依賴行為不同、cache 狀態不同。團隊投入大量精力維護 staging parity，但差異仍然存在，staging 通過但 production 失敗的事故反覆出現。&lt;/p>
&lt;p>Honeycomb 提出的替代思路是：與其追求 staging 完美複製 production，不如提升 production 的觀測能力，讓驗證可以安全地在 production 執行。這個思路的前提是三個能力同時到位：high-cardinality observability 能即時看見異常、feature flag 能控制變更的可見範圍、automated rollback 能在問題擴大前收回變更。&lt;/p>
&lt;h2 id="決策機制">決策機制&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>機制&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>交付結果&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Observability readiness&lt;/td>
 &lt;td>觀測能否按 tenant / path / feature 切分&lt;/td>
 &lt;td>high-cardinality trace / structured event&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Feature flag safety&lt;/td>
 &lt;td>變更可見範圍是否可控&lt;/td>
 &lt;td>dark launch + kill switch&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Progressive validation&lt;/td>
 &lt;td>每一步放量是否有即時回饋&lt;/td>
 &lt;td>canary → observe → expand 循環&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Rollback readiness&lt;/td>
 &lt;td>異常出現時能否自動收回&lt;/td>
 &lt;td>automated rollback on anomaly trigger&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Observability readiness 是整個流程的前提。high-cardinality tracing 讓團隊可以按 tenant、feature flag 狀態、request path 等維度切分觀測資料，在問題只影響少量使用者時就被發現。若觀測只有聚合指標（平均 latency、總 error rate），異常會被稀釋到看不見，等到聚合指標也惡化時影響已經擴大。&lt;/p>
&lt;p>Feature flag safety 控制變更的 blast radius。dark launch 讓新邏輯在 production 執行但結果不對外可見，用來驗證效能與正確性。kill switch 讓團隊在異常出現時立即關閉新邏輯，不需要等 redeploy。&lt;/p>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>trace cardinality coverage&lt;/td>
 &lt;td>觀測維度是否足以切分異常&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/04-observability/tracing-context/" data-link-title="4.3 tracing 與 context link" data-link-desc="整理 trace id、span 與跨服務 context propagation">4.3&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>flag rollout anomaly&lt;/td>
 &lt;td>新 flag 開啟後行為是否偏離&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/feature-flag-governance/" data-link-title="6.17 Feature Flag Governance" data-link-desc="把 feature flag 從上線開關升級為有角色分類、lifecycle 管理與 debt 治理的 runtime artifact">6.17&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>production validation pass&lt;/td>
 &lt;td>驗證結果是否支持繼續放量&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>rollback trigger count&lt;/td>
 &lt;td>自動回退是否被觸發&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="常見陷阱">常見陷阱&lt;/h2>
&lt;p>把 test in production 當成「跳過 staging 測試」的簡稱會帶來嚴重風險。test in production 的安全性建立在三個前提上：觀測能力能即時看見異常、feature flag 能控制影響範圍、rollback 能在秒級生效。缺少任何一個前提就直接在 production 測試，只是把風險從 staging 搬到 production，而且 production 的失敗成本更高。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>先回到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/environment-parity/" data-link-title="6.15 Environment Parity 與漂移控制" data-link-desc="把 staging / preprod / prod 之間的差異視為一級風險，按漂移來源分類偵測與治理">6.15 Environment Parity&lt;/a> 評估 staging 差異的實際風險，再到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/feature-flag-governance/" data-link-title="6.17 Feature Flag Governance" data-link-desc="把 feature flag 從上線開關升級為有角色分類、lifecycle 管理與 debt 治理的 runtime artifact">6.17 Feature Flag Governance&lt;/a> 建立 flag safety 機制。production validation 的證據回寫 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate&lt;/a>。&lt;/p></description><content:encoded><![CDATA[<p>Honeycomb 團隊是 test in production 理念的主要推動者之一。Production excellence 的核心責任是把 production 觀測能力提升到可以安全驗證變更的水準。當觀測能力足夠細緻，團隊可以在真實流量下驗證行為，降低對 staging 環境的依賴。</p>
<h2 id="問題場景">問題場景</h2>
<p>Staging 跟 production 之間的差異是結構性的 — 資料量不同、流量模式不同、依賴行為不同、cache 狀態不同。團隊投入大量精力維護 staging parity，但差異仍然存在，staging 通過但 production 失敗的事故反覆出現。</p>
<p>Honeycomb 提出的替代思路是：與其追求 staging 完美複製 production，不如提升 production 的觀測能力，讓驗證可以安全地在 production 執行。這個思路的前提是三個能力同時到位：high-cardinality observability 能即時看見異常、feature flag 能控制變更的可見範圍、automated rollback 能在問題擴大前收回變更。</p>
<h2 id="決策機制">決策機制</h2>
<table>
  <thead>
      <tr>
          <th>機制</th>
          <th>核心問題</th>
          <th>交付結果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Observability readiness</td>
          <td>觀測能否按 tenant / path / feature 切分</td>
          <td>high-cardinality trace / structured event</td>
      </tr>
      <tr>
          <td>Feature flag safety</td>
          <td>變更可見範圍是否可控</td>
          <td>dark launch + kill switch</td>
      </tr>
      <tr>
          <td>Progressive validation</td>
          <td>每一步放量是否有即時回饋</td>
          <td>canary → observe → expand 循環</td>
      </tr>
      <tr>
          <td>Rollback readiness</td>
          <td>異常出現時能否自動收回</td>
          <td>automated rollback on anomaly trigger</td>
      </tr>
  </tbody>
</table>
<p>Observability readiness 是整個流程的前提。high-cardinality tracing 讓團隊可以按 tenant、feature flag 狀態、request path 等維度切分觀測資料，在問題只影響少量使用者時就被發現。若觀測只有聚合指標（平均 latency、總 error rate），異常會被稀釋到看不見，等到聚合指標也惡化時影響已經擴大。</p>
<p>Feature flag safety 控制變更的 blast radius。dark launch 讓新邏輯在 production 執行但結果不對外可見，用來驗證效能與正確性。kill switch 讓團隊在異常出現時立即關閉新邏輯，不需要等 redeploy。</p>
<h2 id="可觀測訊號">可觀測訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>trace cardinality coverage</td>
          <td>觀測維度是否足以切分異常</td>
          <td><a href="/blog/backend/04-observability/tracing-context/" data-link-title="4.3 tracing 與 context link" data-link-desc="整理 trace id、span 與跨服務 context propagation">4.3</a></td>
      </tr>
      <tr>
          <td>flag rollout anomaly</td>
          <td>新 flag 開啟後行為是否偏離</td>
          <td><a href="/blog/backend/06-reliability/feature-flag-governance/" data-link-title="6.17 Feature Flag Governance" data-link-desc="把 feature flag 從上線開關升級為有角色分類、lifecycle 管理與 debt 治理的 runtime artifact">6.17</a></td>
      </tr>
      <tr>
          <td>production validation pass</td>
          <td>驗證結果是否支持繼續放量</td>
          <td><a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8</a></td>
      </tr>
      <tr>
          <td>rollback trigger count</td>
          <td>自動回退是否被觸發</td>
          <td><a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23</a></td>
      </tr>
  </tbody>
</table>
<h2 id="常見陷阱">常見陷阱</h2>
<p>把 test in production 當成「跳過 staging 測試」的簡稱會帶來嚴重風險。test in production 的安全性建立在三個前提上：觀測能力能即時看見異常、feature flag 能控制影響範圍、rollback 能在秒級生效。缺少任何一個前提就直接在 production 測試，只是把風險從 staging 搬到 production，而且 production 的失敗成本更高。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>先回到 <a href="/blog/backend/06-reliability/environment-parity/" data-link-title="6.15 Environment Parity 與漂移控制" data-link-desc="把 staging / preprod / prod 之間的差異視為一級風險，按漂移來源分類偵測與治理">6.15 Environment Parity</a> 評估 staging 差異的實際風險，再到 <a href="/blog/backend/06-reliability/feature-flag-governance/" data-link-title="6.17 Feature Flag Governance" data-link-desc="把 feature flag 從上線開關升級為有角色分類、lifecycle 管理與 debt 治理的 runtime artifact">6.17 Feature Flag Governance</a> 建立 flag safety 機制。production validation 的證據回寫 <a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23</a> 與 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://www.honeycomb.io/blog/observability-every-engineers-job-not-just-ops-problem">Observability: It&rsquo;s Every Engineer&rsquo;s Job, Not Just Ops&rsquo; Problem</a></li>
<li><a href="https://www.honeycomb.io/resources/getting-started/what-is-observability-engineering">What Is Observability Engineering?</a></li>
</ul>
]]></content:encoded></item><item><title>LinkedIn：Automated Load Testing 與 Capacity Forecasting</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/linkedin/automated-load-testing-and-capacity-forecasting/</link><pubDate>Tue, 23 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/linkedin/automated-load-testing-and-capacity-forecasting/</guid><description>&lt;p>Automated load testing 的核心責任是把壓測從一次性活動變成持續回饋的工程流程。Capacity forecasting 的責任是用歷史流量趨勢加上壓測結果，預測什麼時候需要擴容、什麼時候可以縮減。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>大型社交平台的流量增長是漸進的，但容量不足是突然的。超過 saturation point 後 latency 會非線性惡化，從可接受的排隊延遲快速轉成級聯超時。若靠一次性壓測做容量規劃，規劃結論會隨時間漂移：流量結構改變、功能上線帶進新 workload、依賴服務的回應時間波動，都會讓上一次壓測的 saturation point 不再準確。&lt;/p>
&lt;p>LinkedIn 的做法是把壓測自動化並跑在定期排程中，讓容量預測的輸入持續更新。壓測結果直接餵給 forecasting 模型，forecasting 輸出接到 headroom alert，headroom alert 觸發擴容 review。這條鏈路讓容量決策從「每季做一次人工判斷」變成「每週自動更新、異常時才需要人介入」。&lt;/p>
&lt;h2 id="決策機制">決策機制&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>機制&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>交付結果&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Automated load test&lt;/td>
 &lt;td>saturation point 是否仍準確&lt;/td>
 &lt;td>更新後的容量基準&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Traffic forecasting&lt;/td>
 &lt;td>未來 N 天的 peak load 是否會逼近上限&lt;/td>
 &lt;td>擴容時間窗預測&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Headroom alert&lt;/td>
 &lt;td>forecast / ceiling 比值是否超過門檻&lt;/td>
 &lt;td>自動擴容 review&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Capacity budget&lt;/td>
 &lt;td>每個服務的容量開銷是否在預算內&lt;/td>
 &lt;td>超支 justification&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Automated load test 用 production traffic replay 而非固定 scenario，讓壓測的 workload model 跟真實流量保持同步。Traffic forecasting 結合歷史流量趨勢與產品 launch 日曆，把可預期的流量事件（功能上線、促銷、季節性增長）納入預測。Headroom alert 在 forecast peak / capacity ceiling 比值超過 70-80% 時觸發，讓團隊在容量耗盡前有足夠反應窗口。&lt;/p>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>saturation point drift&lt;/td>
 &lt;td>壓測結果是否隨時間漂移&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>headroom ratio&lt;/td>
 &lt;td>peak load 與 capacity ceiling 比值&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/capacity-cost/" data-link-title="6.9 容量與成本邊界" data-link-desc="把容量規劃跟成本約束變成驗證輸入">6.9&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>forecast accuracy&lt;/td>
 &lt;td>預測與實際 peak 的偏差&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>capacity spend trend&lt;/td>
 &lt;td>容量成本是否超出預算&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/capacity-cost/" data-link-title="6.9 容量與成本邊界" data-link-desc="把容量規劃跟成本約束變成驗證輸入">6.9&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="常見陷阱">常見陷阱&lt;/h2>
&lt;p>自動化壓測最常見的失真來源是 workload model 僵化。若自動化跑的是建立時的固定 scenario 而非持續更新的 traffic replay，時間一長模型就跟 production 脫鉤。脫鉤的訊號是壓測結果與 production 同時段的 latency distribution 開始偏離 — p50 / p95 / p99 的比率差異超過 20% 時，模型已需要校準。&lt;/p>
&lt;p>另一個陷阱是把 forecast 當成精確預測。Forecasting 的價值在於提早觸發 review，讓團隊有時間做擴容決策。若團隊把 forecast 當成精確數字做自動擴容，預測偏差會直接變成過度擴容或擴容不足。forecast 輸出應該驅動人工 review，而非直接觸發資源變更。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>先把壓測結果接到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2 load testing&lt;/a> 的 workload model 校準流程，再用 headroom ratio 餵給 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/capacity-cost/" data-link-title="6.9 容量與成本邊界" data-link-desc="把容量規劃跟成本約束變成驗證輸入">6.9 容量與成本邊界&lt;/a> 做容量預算。forecast 準確度的追蹤連到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13 performance regression gate&lt;/a> 的 baseline 校準。&lt;/p></description><content:encoded><![CDATA[<p>Automated load testing 的核心責任是把壓測從一次性活動變成持續回饋的工程流程。Capacity forecasting 的責任是用歷史流量趨勢加上壓測結果，預測什麼時候需要擴容、什麼時候可以縮減。</p>
<h2 id="問題場景">問題場景</h2>
<p>大型社交平台的流量增長是漸進的，但容量不足是突然的。超過 saturation point 後 latency 會非線性惡化，從可接受的排隊延遲快速轉成級聯超時。若靠一次性壓測做容量規劃，規劃結論會隨時間漂移：流量結構改變、功能上線帶進新 workload、依賴服務的回應時間波動，都會讓上一次壓測的 saturation point 不再準確。</p>
<p>LinkedIn 的做法是把壓測自動化並跑在定期排程中，讓容量預測的輸入持續更新。壓測結果直接餵給 forecasting 模型，forecasting 輸出接到 headroom alert，headroom alert 觸發擴容 review。這條鏈路讓容量決策從「每季做一次人工判斷」變成「每週自動更新、異常時才需要人介入」。</p>
<h2 id="決策機制">決策機制</h2>
<table>
  <thead>
      <tr>
          <th>機制</th>
          <th>核心問題</th>
          <th>交付結果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Automated load test</td>
          <td>saturation point 是否仍準確</td>
          <td>更新後的容量基準</td>
      </tr>
      <tr>
          <td>Traffic forecasting</td>
          <td>未來 N 天的 peak load 是否會逼近上限</td>
          <td>擴容時間窗預測</td>
      </tr>
      <tr>
          <td>Headroom alert</td>
          <td>forecast / ceiling 比值是否超過門檻</td>
          <td>自動擴容 review</td>
      </tr>
      <tr>
          <td>Capacity budget</td>
          <td>每個服務的容量開銷是否在預算內</td>
          <td>超支 justification</td>
      </tr>
  </tbody>
</table>
<p>Automated load test 用 production traffic replay 而非固定 scenario，讓壓測的 workload model 跟真實流量保持同步。Traffic forecasting 結合歷史流量趨勢與產品 launch 日曆，把可預期的流量事件（功能上線、促銷、季節性增長）納入預測。Headroom alert 在 forecast peak / capacity ceiling 比值超過 70-80% 時觸發，讓團隊在容量耗盡前有足夠反應窗口。</p>
<h2 id="可觀測訊號">可觀測訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>saturation point drift</td>
          <td>壓測結果是否隨時間漂移</td>
          <td><a href="/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2</a></td>
      </tr>
      <tr>
          <td>headroom ratio</td>
          <td>peak load 與 capacity ceiling 比值</td>
          <td><a href="/blog/backend/06-reliability/capacity-cost/" data-link-title="6.9 容量與成本邊界" data-link-desc="把容量規劃跟成本約束變成驗證輸入">6.9</a></td>
      </tr>
      <tr>
          <td>forecast accuracy</td>
          <td>預測與實際 peak 的偏差</td>
          <td><a href="/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13</a></td>
      </tr>
      <tr>
          <td>capacity spend trend</td>
          <td>容量成本是否超出預算</td>
          <td><a href="/blog/backend/06-reliability/capacity-cost/" data-link-title="6.9 容量與成本邊界" data-link-desc="把容量規劃跟成本約束變成驗證輸入">6.9</a></td>
      </tr>
  </tbody>
</table>
<h2 id="常見陷阱">常見陷阱</h2>
<p>自動化壓測最常見的失真來源是 workload model 僵化。若自動化跑的是建立時的固定 scenario 而非持續更新的 traffic replay，時間一長模型就跟 production 脫鉤。脫鉤的訊號是壓測結果與 production 同時段的 latency distribution 開始偏離 — p50 / p95 / p99 的比率差異超過 20% 時，模型已需要校準。</p>
<p>另一個陷阱是把 forecast 當成精確預測。Forecasting 的價值在於提早觸發 review，讓團隊有時間做擴容決策。若團隊把 forecast 當成精確數字做自動擴容，預測偏差會直接變成過度擴容或擴容不足。forecast 輸出應該驅動人工 review，而非直接觸發資源變更。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>先把壓測結果接到 <a href="/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2 load testing</a> 的 workload model 校準流程，再用 headroom ratio 餵給 <a href="/blog/backend/06-reliability/capacity-cost/" data-link-title="6.9 容量與成本邊界" data-link-desc="把容量規劃跟成本約束變成驗證輸入">6.9 容量與成本邊界</a> 做容量預算。forecast 準確度的追蹤連到 <a href="/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13 performance regression gate</a> 的 baseline 校準。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://engineering.linkedin.com/content/engineering/en-us/blog/2019/eliminating-toil-with-fully-automated-load-testing">Eliminating toil with fully automated load testing</a></li>
<li>（背景脈絡）<a href="https://engineering.linkedin.com/performance/taming-database-replication-latency-capacity-planning">Taming Database Replication Latency by Capacity Planning</a></li>
</ul>
]]></content:encoded></item><item><title>Meta：Region Failover 與可靠性邊界</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/meta/region-failover-and-reliability-boundaries/</link><pubDate>Thu, 07 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/meta/region-failover-and-reliability-boundaries/</guid><description>&lt;p>Meta 案例的核心責任是處理跨區故障時的邊界與回復順序。大規模平台的關鍵風險在跨區相依引發的連鎖退化，單點失效反而是較好處理的情況。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>當核心網路或控制面異常跨越區域邊界，若沒有預先定義故障域與回復順序，恢復動作本身會變成新的放大器。&lt;/p>
&lt;h2 id="決策機制">決策機制&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>機制&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>交付結果&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Region fault domain&lt;/td>
 &lt;td>影響面最多到哪裡&lt;/td>
 &lt;td>故障邊界&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Ordered failover&lt;/td>
 &lt;td>先恢復哪條路徑&lt;/td>
 &lt;td>回復順序&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Dependency isolation&lt;/td>
 &lt;td>共享相依如何降風險&lt;/td>
 &lt;td>局部化策略&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>cross-region error spread&lt;/td>
 &lt;td>擴散是否越界&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/multi-incident-coordination/" data-link-title="8.14 Multi-incident Coordination" data-link-desc="把同時多事故的優先序、資源分配與 incident command system pool 協調變成可執行流程">8.14&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>failover completion lag&lt;/td>
 &lt;td>回復批次是否收斂&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/containment-recovery-strategy/" data-link-title="8.3 止血、降級與回復策略" data-link-desc="把短期止血與正式回復拆成可執行步驟">8.3&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>shared dependency saturation&lt;/td>
 &lt;td>共享依賴是否成瓶頸&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>先定義 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20&lt;/a> 的演練範圍，再回寫 &lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/incident-decision-log/" data-link-title="8.19 Incident Decision Log" data-link-desc="把事中假設、決策、證據、回退條件與責任人留下可復盤紀錄">8.19&lt;/a> 的決策欄位。&lt;/p></description><content:encoded><![CDATA[<p>Meta 案例的核心責任是處理跨區故障時的邊界與回復順序。大規模平台的關鍵風險在跨區相依引發的連鎖退化，單點失效反而是較好處理的情況。</p>
<h2 id="問題場景">問題場景</h2>
<p>當核心網路或控制面異常跨越區域邊界，若沒有預先定義故障域與回復順序，恢復動作本身會變成新的放大器。</p>
<h2 id="決策機制">決策機制</h2>
<table>
  <thead>
      <tr>
          <th>機制</th>
          <th>核心問題</th>
          <th>交付結果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Region fault domain</td>
          <td>影響面最多到哪裡</td>
          <td>故障邊界</td>
      </tr>
      <tr>
          <td>Ordered failover</td>
          <td>先恢復哪條路徑</td>
          <td>回復順序</td>
      </tr>
      <tr>
          <td>Dependency isolation</td>
          <td>共享相依如何降風險</td>
          <td>局部化策略</td>
      </tr>
  </tbody>
</table>
<h2 id="可觀測訊號">可觀測訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>cross-region error spread</td>
          <td>擴散是否越界</td>
          <td><a href="/blog/backend/08-incident-response/multi-incident-coordination/" data-link-title="8.14 Multi-incident Coordination" data-link-desc="把同時多事故的優先序、資源分配與 incident command system pool 協調變成可執行流程">8.14</a></td>
      </tr>
      <tr>
          <td>failover completion lag</td>
          <td>回復批次是否收斂</td>
          <td><a href="/blog/backend/08-incident-response/containment-recovery-strategy/" data-link-title="8.3 止血、降級與回復策略" data-link-desc="把短期止血與正式回復拆成可執行步驟">8.3</a></td>
      </tr>
      <tr>
          <td>shared dependency saturation</td>
          <td>共享依賴是否成瓶頸</td>
          <td><a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14</a></td>
      </tr>
  </tbody>
</table>
<h2 id="下一步路由">下一步路由</h2>
<p>先定義 <a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20</a> 的演練範圍，再回寫 <a href="/blog/backend/08-incident-response/incident-decision-log/" data-link-title="8.19 Incident Decision Log" data-link-desc="把事中假設、決策、證據、回退條件與責任人留下可復盤紀錄">8.19</a> 的決策欄位。</p>
]]></content:encoded></item><item><title>Stripe：Idempotency 與零停機遷移的交易安全設計</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/</link><pubDate>Thu, 07 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/</guid><description>&lt;p>Stripe 案例的核心責任是確保交易語義在重試與變更中保持一致。支付系統的失效成本不只來自停機，還來自錯誤結果；因此可靠性設計要同時守住可用性與正確性。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>交易系統最常見的高風險組合是：客戶端重試、網路抖動、後端部署或資料遷移同時發生。若系統只處理單一失效，結果往往是可用但不一致，或者一致但無法持續交付。&lt;/p>
&lt;p>idempotency key 與 zero-downtime migration 的組合，目標是讓這些變更在同一套邊界下可判讀。&lt;/p>
&lt;h2 id="決策機制">決策機制&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>機制&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>交付結果&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Idempotency key&lt;/td>
 &lt;td>同一交易重送如何得到同一結果&lt;/td>
 &lt;td>重試安全&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Expand/contract migration&lt;/td>
 &lt;td>資料變更如何與新舊版本共存&lt;/td>
 &lt;td>漸進遷移&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Canary + rollback gate&lt;/td>
 &lt;td>發版異常如何快速收斂&lt;/td>
 &lt;td>可回復交付&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Transaction-path observability&lt;/td>
 &lt;td>交易路徑是否可追溯&lt;/td>
 &lt;td>一致性證據&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這組機制把「交易正確性」前移到 API 與遷移設計，而不是事後 reconciliation 才補救。&lt;/p>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>duplicate request collapse ratio&lt;/td>
 &lt;td>重試是否被正確合併&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/idempotency-replay/" data-link-title="6.12 Idempotency 與 Replay 驗證" data-link-desc="把重試、重播與冪等性從口頭約定變成可驗證屬性">6.12&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>migration phase error drift&lt;/td>
 &lt;td>遷移各階段錯誤是否收斂&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/migration-safety/" data-link-title="6.11 Migration Safety 與 DB Rollout" data-link-desc="把 schema migration 從一次性事件變成可逆、可漸進的 rollout 流程">6.11&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>canary transaction anomaly&lt;/td>
 &lt;td>小流量交易是否出現偏差&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>payment trace consistency&lt;/td>
 &lt;td>trace 是否完整覆蓋交易關鍵欄位&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/04-observability/observability-evidence-package/" data-link-title="4.20 Observability Evidence Package" data-link-desc="把 log、metric、trace、audit 與資料品質限制包成可交接證據">4.20&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="常見陷阱">常見陷阱&lt;/h2>
&lt;p>把 idempotency 實作成「只去重請求 ID」會漏掉交易語義。正確做法是讓 key 與業務操作邊界一致，並保留足夠證據以供重放與稽核判讀。另一個常見錯誤是把 migration 視為資料庫任務，沒有與 release gate 共同治理。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>實作層先從 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/idempotency-replay/" data-link-title="6.12 Idempotency 與 Replay 驗證" data-link-desc="把重試、重播與冪等性從口頭約定變成可驗證屬性">6.12&lt;/a> 定義重放語義，再到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/migration-safety/" data-link-title="6.11 Migration Safety 與 DB Rollout" data-link-desc="把 schema migration 從一次性事件變成可逆、可漸進的 rollout 流程">6.11&lt;/a> 建立遷移節奏。發布控制對齊 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8&lt;/a>；事故時的交易影響評估對齊 &lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/customer-impact-assessment/" data-link-title="8.20 Customer Impact Assessment" data-link-desc="把受影響用戶、功能、區域、金額、SLO 與補償判斷串成影響評估模型">8.20&lt;/a>。&lt;/p></description><content:encoded><![CDATA[<p>Stripe 案例的核心責任是確保交易語義在重試與變更中保持一致。支付系統的失效成本不只來自停機，還來自錯誤結果；因此可靠性設計要同時守住可用性與正確性。</p>
<h2 id="問題場景">問題場景</h2>
<p>交易系統最常見的高風險組合是：客戶端重試、網路抖動、後端部署或資料遷移同時發生。若系統只處理單一失效，結果往往是可用但不一致，或者一致但無法持續交付。</p>
<p>idempotency key 與 zero-downtime migration 的組合，目標是讓這些變更在同一套邊界下可判讀。</p>
<h2 id="決策機制">決策機制</h2>
<table>
  <thead>
      <tr>
          <th>機制</th>
          <th>核心問題</th>
          <th>交付結果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Idempotency key</td>
          <td>同一交易重送如何得到同一結果</td>
          <td>重試安全</td>
      </tr>
      <tr>
          <td>Expand/contract migration</td>
          <td>資料變更如何與新舊版本共存</td>
          <td>漸進遷移</td>
      </tr>
      <tr>
          <td>Canary + rollback gate</td>
          <td>發版異常如何快速收斂</td>
          <td>可回復交付</td>
      </tr>
      <tr>
          <td>Transaction-path observability</td>
          <td>交易路徑是否可追溯</td>
          <td>一致性證據</td>
      </tr>
  </tbody>
</table>
<p>這組機制把「交易正確性」前移到 API 與遷移設計，而不是事後 reconciliation 才補救。</p>
<h2 id="可觀測訊號">可觀測訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>duplicate request collapse ratio</td>
          <td>重試是否被正確合併</td>
          <td><a href="/blog/backend/06-reliability/idempotency-replay/" data-link-title="6.12 Idempotency 與 Replay 驗證" data-link-desc="把重試、重播與冪等性從口頭約定變成可驗證屬性">6.12</a></td>
      </tr>
      <tr>
          <td>migration phase error drift</td>
          <td>遷移各階段錯誤是否收斂</td>
          <td><a href="/blog/backend/06-reliability/migration-safety/" data-link-title="6.11 Migration Safety 與 DB Rollout" data-link-desc="把 schema migration 從一次性事件變成可逆、可漸進的 rollout 流程">6.11</a></td>
      </tr>
      <tr>
          <td>canary transaction anomaly</td>
          <td>小流量交易是否出現偏差</td>
          <td><a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8</a></td>
      </tr>
      <tr>
          <td>payment trace consistency</td>
          <td>trace 是否完整覆蓋交易關鍵欄位</td>
          <td><a href="/blog/backend/04-observability/observability-evidence-package/" data-link-title="4.20 Observability Evidence Package" data-link-desc="把 log、metric、trace、audit 與資料品質限制包成可交接證據">4.20</a></td>
      </tr>
  </tbody>
</table>
<h2 id="常見陷阱">常見陷阱</h2>
<p>把 idempotency 實作成「只去重請求 ID」會漏掉交易語義。正確做法是讓 key 與業務操作邊界一致，並保留足夠證據以供重放與稽核判讀。另一個常見錯誤是把 migration 視為資料庫任務，沒有與 release gate 共同治理。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>實作層先從 <a href="/blog/backend/06-reliability/idempotency-replay/" data-link-title="6.12 Idempotency 與 Replay 驗證" data-link-desc="把重試、重播與冪等性從口頭約定變成可驗證屬性">6.12</a> 定義重放語義，再到 <a href="/blog/backend/06-reliability/migration-safety/" data-link-title="6.11 Migration Safety 與 DB Rollout" data-link-desc="把 schema migration 從一次性事件變成可逆、可漸進的 rollout 流程">6.11</a> 建立遷移節奏。發布控制對齊 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8</a>；事故時的交易影響評估對齊 <a href="/blog/backend/08-incident-response/customer-impact-assessment/" data-link-title="8.20 Customer Impact Assessment" data-link-desc="把受影響用戶、功能、區域、金額、SLO 與補償判斷串成影響評估模型">8.20</a>。</p>
]]></content:encoded></item><item><title>Meta：BGP 事故與控制面恢復順序</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/meta/bgp-control-plane-recovery-ordering/</link><pubDate>Tue, 23 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/meta/bgp-control-plane-recovery-ordering/</guid><description>&lt;p>控制面恢復順序的責任是確保回復路徑不依賴已故障的系統。當 DNS、BGP、遠端存取工具與內部通訊都跑在同一個網路上，網路故障會同時切斷服務和回復手段。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>2021-10-04，Meta 的一次 BGP 配置變更導致骨幹網路撤回所有 route announcement。影響的範圍不只是對外服務：DNS 因為無法到達權威 DNS server 而失效，內部工具（包含遠端管理、通訊與身份驗證）也依賴同一個內部網路，因此同步不可用。&lt;/p>
&lt;p>工程師無法透過遠端存取工具連線到設備，必須實體前往資料中心手動恢復 BGP 配置。資料中心的實體存取流程（門禁授權、安全人員協調、設備定位）進一步拉長恢復時間。整個事故從發生到服務恢復超過 6 小時。&lt;/p>
&lt;p>這個事故的核心教訓是恢復工具必須獨立於被恢復的系統。當 out-of-band 路徑在設計上或認證上依賴 production 網路，它就不是真正的 out-of-band。&lt;/p>
&lt;h2 id="決策機制">決策機制&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>機制&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>交付結果&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Out-of-band management&lt;/td>
 &lt;td>恢復路徑是否獨立於 production 網路&lt;/td>
 &lt;td>獨立連線與管理通道&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Recovery dependency mapping&lt;/td>
 &lt;td>每個回復步驟的依賴是否有循環&lt;/td>
 &lt;td>依賴圖與循環偵測&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Staged recovery order&lt;/td>
 &lt;td>恢復順序是否先連通再服務&lt;/td>
 &lt;td>網路 → DNS → 控制面 → 資料面&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Physical access readiness&lt;/td>
 &lt;td>remote 手段失效時實體存取是否可立即啟動&lt;/td>
 &lt;td>授權、存取卡、知識分佈&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Out-of-band management 的設計約束是完全獨立於 production 路徑。這包含網路連線（獨立 ISP 或 cellular）、認證（不依賴 production identity service）與通訊（獨立通訊工具或電話樹）。任何一環依賴 production 系統，就不算真正的 out-of-band。&lt;/p>
&lt;p>Recovery dependency mapping 的責任是在事故前畫出恢復步驟之間的依賴關係，找出循環依賴。Meta 事故中，DNS 恢復依賴網路連通，網路恢復依賴 BGP 設備存取，設備存取依賴 out-of-band 工具，而 out-of-band 工具的認證依賴 production identity service — 形成循環。事前的 dependency mapping 能暴露這類隱性路徑。&lt;/p>
&lt;p>Staged recovery order 把恢復拆成明確的階段：先恢復物理網路連通，再恢復 DNS 與名稱解析，接著恢復控制面服務（監控、部署、配置管理），最後恢復資料面流量。每個階段有明確的完成條件，下一階段才啟動。&lt;/p>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>out-of-band reachability&lt;/td>
 &lt;td>獨立管理通道是否可連線&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/dr-rollback-rehearsal/" data-link-title="6.7 DR 演練與 Rollback Rehearsal" data-link-desc="把回復路徑從紙面計畫變成定期可重播、可量測的驗證流程">6.7&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>recovery dependency cycle count&lt;/td>
 &lt;td>恢復步驟之間是否存在循環依賴&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>DNS propagation lag&lt;/td>
 &lt;td>名稱解析恢復後多久全域生效&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>physical access activation time&lt;/td>
 &lt;td>從決策到實體接觸設備的時間&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/containment-recovery-strategy/" data-link-title="8.3 止血、降級與回復策略" data-link-desc="把短期止血與正式回復拆成可執行步驟">8.3&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="常見陷阱">常見陷阱&lt;/h2>
&lt;p>最常見的錯誤是把 out-of-band 存取當成「有設定就好」而不定期驗證。Meta 事故暴露的問題是 out-of-band 工具的 authentication 依賴 production identity service — 名義上路徑獨立，實際上認證路徑共享。DR rehearsal 必須包含「假設 production 網路完全不可用」的場景，驗證 out-of-band 路徑的每一環（連線、認證、通訊、操作權限）都能獨立運作。&lt;/p>
&lt;p>另一個常見問題是 recovery 知識集中在少數人。當實體恢復需要到場操作時，知識的地理分佈直接影響恢復時間。關鍵設備的恢復程序必須文件化，且分佈在多個地理位置的團隊成員手上。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/dr-rollback-rehearsal/" data-link-title="6.7 DR 演練與 Rollback Rehearsal" data-link-desc="把回復路徑從紙面計畫變成定期可重播、可量測的驗證流程">6.7 DR rollback rehearsal&lt;/a>：out-of-band 路徑的定期驗證&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14 dependency reliability budget&lt;/a>：恢復路徑的隱性依賴治理&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22 steady state definition&lt;/a>：DNS 與控制面恢復完成的判準&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/multi-incident-coordination/" data-link-title="8.14 Multi-incident Coordination" data-link-desc="把同時多事故的優先序、資源分配與 incident command system pool 協調變成可執行流程">8.14 multi-incident coordination&lt;/a>：跨區域恢復的指揮協調&lt;/li>
&lt;/ul>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://engineering.fb.com/2021/10/05/networking-traffic/outage-details/">More details about the October 4 outage&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://engineering.fb.com/2021/10/04/networking-traffic/outage/">Update about the October 4th outage&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>控制面恢復順序的責任是確保回復路徑不依賴已故障的系統。當 DNS、BGP、遠端存取工具與內部通訊都跑在同一個網路上，網路故障會同時切斷服務和回復手段。</p>
<h2 id="問題場景">問題場景</h2>
<p>2021-10-04，Meta 的一次 BGP 配置變更導致骨幹網路撤回所有 route announcement。影響的範圍不只是對外服務：DNS 因為無法到達權威 DNS server 而失效，內部工具（包含遠端管理、通訊與身份驗證）也依賴同一個內部網路，因此同步不可用。</p>
<p>工程師無法透過遠端存取工具連線到設備，必須實體前往資料中心手動恢復 BGP 配置。資料中心的實體存取流程（門禁授權、安全人員協調、設備定位）進一步拉長恢復時間。整個事故從發生到服務恢復超過 6 小時。</p>
<p>這個事故的核心教訓是恢復工具必須獨立於被恢復的系統。當 out-of-band 路徑在設計上或認證上依賴 production 網路，它就不是真正的 out-of-band。</p>
<h2 id="決策機制">決策機制</h2>
<table>
  <thead>
      <tr>
          <th>機制</th>
          <th>核心問題</th>
          <th>交付結果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Out-of-band management</td>
          <td>恢復路徑是否獨立於 production 網路</td>
          <td>獨立連線與管理通道</td>
      </tr>
      <tr>
          <td>Recovery dependency mapping</td>
          <td>每個回復步驟的依賴是否有循環</td>
          <td>依賴圖與循環偵測</td>
      </tr>
      <tr>
          <td>Staged recovery order</td>
          <td>恢復順序是否先連通再服務</td>
          <td>網路 → DNS → 控制面 → 資料面</td>
      </tr>
      <tr>
          <td>Physical access readiness</td>
          <td>remote 手段失效時實體存取是否可立即啟動</td>
          <td>授權、存取卡、知識分佈</td>
      </tr>
  </tbody>
</table>
<p>Out-of-band management 的設計約束是完全獨立於 production 路徑。這包含網路連線（獨立 ISP 或 cellular）、認證（不依賴 production identity service）與通訊（獨立通訊工具或電話樹）。任何一環依賴 production 系統，就不算真正的 out-of-band。</p>
<p>Recovery dependency mapping 的責任是在事故前畫出恢復步驟之間的依賴關係，找出循環依賴。Meta 事故中，DNS 恢復依賴網路連通，網路恢復依賴 BGP 設備存取，設備存取依賴 out-of-band 工具，而 out-of-band 工具的認證依賴 production identity service — 形成循環。事前的 dependency mapping 能暴露這類隱性路徑。</p>
<p>Staged recovery order 把恢復拆成明確的階段：先恢復物理網路連通，再恢復 DNS 與名稱解析，接著恢復控制面服務（監控、部署、配置管理），最後恢復資料面流量。每個階段有明確的完成條件，下一階段才啟動。</p>
<h2 id="可觀測訊號">可觀測訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>out-of-band reachability</td>
          <td>獨立管理通道是否可連線</td>
          <td><a href="/blog/backend/06-reliability/dr-rollback-rehearsal/" data-link-title="6.7 DR 演練與 Rollback Rehearsal" data-link-desc="把回復路徑從紙面計畫變成定期可重播、可量測的驗證流程">6.7</a></td>
      </tr>
      <tr>
          <td>recovery dependency cycle count</td>
          <td>恢復步驟之間是否存在循環依賴</td>
          <td><a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14</a></td>
      </tr>
      <tr>
          <td>DNS propagation lag</td>
          <td>名稱解析恢復後多久全域生效</td>
          <td><a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22</a></td>
      </tr>
      <tr>
          <td>physical access activation time</td>
          <td>從決策到實體接觸設備的時間</td>
          <td><a href="/blog/backend/08-incident-response/containment-recovery-strategy/" data-link-title="8.3 止血、降級與回復策略" data-link-desc="把短期止血與正式回復拆成可執行步驟">8.3</a></td>
      </tr>
  </tbody>
</table>
<h2 id="常見陷阱">常見陷阱</h2>
<p>最常見的錯誤是把 out-of-band 存取當成「有設定就好」而不定期驗證。Meta 事故暴露的問題是 out-of-band 工具的 authentication 依賴 production identity service — 名義上路徑獨立，實際上認證路徑共享。DR rehearsal 必須包含「假設 production 網路完全不可用」的場景，驗證 out-of-band 路徑的每一環（連線、認證、通訊、操作權限）都能獨立運作。</p>
<p>另一個常見問題是 recovery 知識集中在少數人。當實體恢復需要到場操作時，知識的地理分佈直接影響恢復時間。關鍵設備的恢復程序必須文件化，且分佈在多個地理位置的團隊成員手上。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li><a href="/blog/backend/06-reliability/dr-rollback-rehearsal/" data-link-title="6.7 DR 演練與 Rollback Rehearsal" data-link-desc="把回復路徑從紙面計畫變成定期可重播、可量測的驗證流程">6.7 DR rollback rehearsal</a>：out-of-band 路徑的定期驗證</li>
<li><a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14 dependency reliability budget</a>：恢復路徑的隱性依賴治理</li>
<li><a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22 steady state definition</a>：DNS 與控制面恢復完成的判準</li>
<li><a href="/blog/backend/08-incident-response/multi-incident-coordination/" data-link-title="8.14 Multi-incident Coordination" data-link-desc="把同時多事故的優先序、資源分配與 incident command system pool 協調變成可執行流程">8.14 multi-incident coordination</a>：跨區域恢復的指揮協調</li>
</ul>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://engineering.fb.com/2021/10/05/networking-traffic/outage-details/">More details about the October 4 outage</a></li>
<li><a href="https://engineering.fb.com/2021/10/04/networking-traffic/outage/">Update about the October 4th outage</a></li>
</ul>
]]></content:encoded></item><item><title>Pinterest：Storage Migration 與 Data Infrastructure Reliability</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/pinterest/storage-migration-and-data-infrastructure-reliability/</link><pubDate>Tue, 23 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/pinterest/storage-migration-and-data-infrastructure-reliability/</guid><description>&lt;p>Storage migration 的可靠性責任是讓資料基礎設施的變更可漸進、可驗證、可回退。PB 級資料的儲存引擎遷移（如 HBase → TiDB）牽涉 schema mapping、query pattern 差異與 consistency model 變更，任何一處不相容都會在 production 流量下被放大。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>Pinterest 的資料基礎設施服務數十億 pin、推薦系統與搜尋索引。當儲存引擎需要退役或升級時，直接 cutover 的風險在於所有不相容同時暴露 — query 語意差異、pagination 行為、null handling、ordering 規則都可能在切換瞬間衝擊線上流量。&lt;/p>
&lt;p>漸進遷移的設計核心是把一次性 cutover 拆成可觀測的多階段流程，每個階段都有回退路徑。&lt;/p>
&lt;h2 id="決策機制">決策機制&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>機制&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>交付結果&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Dual-write&lt;/td>
 &lt;td>新舊系統的寫入是否同步且完整&lt;/td>
 &lt;td>資料不遺失保證&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Shadow read&lt;/td>
 &lt;td>新舊系統的讀取結果是否一致&lt;/td>
 &lt;td>行為差異清單&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Reconciliation&lt;/td>
 &lt;td>兩套系統的資料是否持續一致&lt;/td>
 &lt;td>一致性報告&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Staged cutover&lt;/td>
 &lt;td>何時可以把流量從舊系統切到新系統&lt;/td>
 &lt;td>漸進切換節奏&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Dual-write 確保遷移期間每筆寫入同時進入新舊系統。寫入失敗的處理策略決定資料完整性 — 若新系統寫入失敗是否 block 舊系統的寫入，取決於遷移階段（早期容許新系統 fail-open、接近 cutover 時需要 fail-close）。&lt;/p>
&lt;p>Shadow read 在真實流量下比對新舊系統的查詢結果。比對維度包含回傳資料的完整性、排序、分頁邊界與 null 值處理。mismatch rate 是 cutover 可行性的核心判準 — rate 趨近零才能進入下一批切換。&lt;/p>
&lt;p>Staged cutover 按 traffic percentage、data partition 或 use case 漸進切換。每一批觀察 mismatch rate、latency overhead 與 error rate，任一指標超門檻即回退到舊系統。&lt;/p>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>shadow read mismatch rate&lt;/td>
 &lt;td>新舊系統行為差異是否收斂&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/migration-safety/" data-link-title="6.11 Migration Safety 與 DB Rollout" data-link-desc="把 schema migration 從一次性事件變成可逆、可漸進的 rollout 流程">6.11&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>dual-write latency overhead&lt;/td>
 &lt;td>同步寫入是否拖累主路徑&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>reconciliation gap&lt;/td>
 &lt;td>兩套系統資料是否持續一致&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>cutover rollback count&lt;/td>
 &lt;td>切換過程是否穩定&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/dr-rollback-rehearsal/" data-link-title="6.7 DR 演練與 Rollback Rehearsal" data-link-desc="把回復路徑從紙面計畫變成定期可重播、可量測的驗證流程">6.7&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="常見陷阱">常見陷阱&lt;/h2>
&lt;p>Shadow read 比對容易只看最終結果是否相同，忽略中間狀態的差異。pagination 的邊界行為、null 欄位的回傳語意、排序在 tie-breaking 時的規則 — 這些差異在主流程不明顯，但在邊界情境會爆發。reconciliation 需要覆蓋 edge case，包含空集合回傳、大量資料分頁與 concurrent write 衝突。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/migration-safety/" data-link-title="6.11 Migration Safety 與 DB Rollout" data-link-desc="把 schema migration 從一次性事件變成可逆、可漸進的 rollout 流程">6.11 migration safety&lt;/a>：storage migration 的 schema 相容與 rollout 策略&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/dr-rollback-rehearsal/" data-link-title="6.7 DR 演練與 Rollback Rehearsal" data-link-desc="把回復路徑從紙面計畫變成定期可重播、可量測的驗證流程">6.7 DR rollback rehearsal&lt;/a>：cutover 失敗時的 rollback 路徑&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13 performance regression gate&lt;/a>：dual-write latency 作為 regression 偵測&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23 verification evidence handoff&lt;/a>：reconciliation 結果作為 cutover 決策證據&lt;/li>
&lt;/ul>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://medium.com/pinterest-engineering/online-data-migration-from-hbase-to-tidb-with-zero-downtime-43f0fb474b84">Online Data Migration from HBase to TiDB with Zero Downtime&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://medium.com/pinterest-engineering/hbase-deprecation-at-pinterest-8a99e6c8e6b7">HBase Deprecation at Pinterest&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Storage migration 的可靠性責任是讓資料基礎設施的變更可漸進、可驗證、可回退。PB 級資料的儲存引擎遷移（如 HBase → TiDB）牽涉 schema mapping、query pattern 差異與 consistency model 變更，任何一處不相容都會在 production 流量下被放大。</p>
<h2 id="問題場景">問題場景</h2>
<p>Pinterest 的資料基礎設施服務數十億 pin、推薦系統與搜尋索引。當儲存引擎需要退役或升級時，直接 cutover 的風險在於所有不相容同時暴露 — query 語意差異、pagination 行為、null handling、ordering 規則都可能在切換瞬間衝擊線上流量。</p>
<p>漸進遷移的設計核心是把一次性 cutover 拆成可觀測的多階段流程，每個階段都有回退路徑。</p>
<h2 id="決策機制">決策機制</h2>
<table>
  <thead>
      <tr>
          <th>機制</th>
          <th>核心問題</th>
          <th>交付結果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Dual-write</td>
          <td>新舊系統的寫入是否同步且完整</td>
          <td>資料不遺失保證</td>
      </tr>
      <tr>
          <td>Shadow read</td>
          <td>新舊系統的讀取結果是否一致</td>
          <td>行為差異清單</td>
      </tr>
      <tr>
          <td>Reconciliation</td>
          <td>兩套系統的資料是否持續一致</td>
          <td>一致性報告</td>
      </tr>
      <tr>
          <td>Staged cutover</td>
          <td>何時可以把流量從舊系統切到新系統</td>
          <td>漸進切換節奏</td>
      </tr>
  </tbody>
</table>
<p>Dual-write 確保遷移期間每筆寫入同時進入新舊系統。寫入失敗的處理策略決定資料完整性 — 若新系統寫入失敗是否 block 舊系統的寫入，取決於遷移階段（早期容許新系統 fail-open、接近 cutover 時需要 fail-close）。</p>
<p>Shadow read 在真實流量下比對新舊系統的查詢結果。比對維度包含回傳資料的完整性、排序、分頁邊界與 null 值處理。mismatch rate 是 cutover 可行性的核心判準 — rate 趨近零才能進入下一批切換。</p>
<p>Staged cutover 按 traffic percentage、data partition 或 use case 漸進切換。每一批觀察 mismatch rate、latency overhead 與 error rate，任一指標超門檻即回退到舊系統。</p>
<h2 id="可觀測訊號">可觀測訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>shadow read mismatch rate</td>
          <td>新舊系統行為差異是否收斂</td>
          <td><a href="/blog/backend/06-reliability/migration-safety/" data-link-title="6.11 Migration Safety 與 DB Rollout" data-link-desc="把 schema migration 從一次性事件變成可逆、可漸進的 rollout 流程">6.11</a></td>
      </tr>
      <tr>
          <td>dual-write latency overhead</td>
          <td>同步寫入是否拖累主路徑</td>
          <td><a href="/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13</a></td>
      </tr>
      <tr>
          <td>reconciliation gap</td>
          <td>兩套系統資料是否持續一致</td>
          <td><a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23</a></td>
      </tr>
      <tr>
          <td>cutover rollback count</td>
          <td>切換過程是否穩定</td>
          <td><a href="/blog/backend/06-reliability/dr-rollback-rehearsal/" data-link-title="6.7 DR 演練與 Rollback Rehearsal" data-link-desc="把回復路徑從紙面計畫變成定期可重播、可量測的驗證流程">6.7</a></td>
      </tr>
  </tbody>
</table>
<h2 id="常見陷阱">常見陷阱</h2>
<p>Shadow read 比對容易只看最終結果是否相同，忽略中間狀態的差異。pagination 的邊界行為、null 欄位的回傳語意、排序在 tie-breaking 時的規則 — 這些差異在主流程不明顯，但在邊界情境會爆發。reconciliation 需要覆蓋 edge case，包含空集合回傳、大量資料分頁與 concurrent write 衝突。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li><a href="/blog/backend/06-reliability/migration-safety/" data-link-title="6.11 Migration Safety 與 DB Rollout" data-link-desc="把 schema migration 從一次性事件變成可逆、可漸進的 rollout 流程">6.11 migration safety</a>：storage migration 的 schema 相容與 rollout 策略</li>
<li><a href="/blog/backend/06-reliability/dr-rollback-rehearsal/" data-link-title="6.7 DR 演練與 Rollback Rehearsal" data-link-desc="把回復路徑從紙面計畫變成定期可重播、可量測的驗證流程">6.7 DR rollback rehearsal</a>：cutover 失敗時的 rollback 路徑</li>
<li><a href="/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13 performance regression gate</a>：dual-write latency 作為 regression 偵測</li>
<li><a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23 verification evidence handoff</a>：reconciliation 結果作為 cutover 決策證據</li>
</ul>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://medium.com/pinterest-engineering/online-data-migration-from-hbase-to-tidb-with-zero-downtime-43f0fb474b84">Online Data Migration from HBase to TiDB with Zero Downtime</a></li>
<li><a href="https://medium.com/pinterest-engineering/hbase-deprecation-at-pinterest-8a99e6c8e6b7">HBase Deprecation at Pinterest</a></li>
</ul>
]]></content:encoded></item><item><title>Spotify：Backstage Service Catalog 與 Reliability Metadata</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/spotify/backstage-service-catalog-and-reliability-metadata/</link><pubDate>Tue, 23 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/spotify/backstage-service-catalog-and-reliability-metadata/</guid><description>&lt;p>Service catalog 在可靠性工程中的責任是讓每個服務的 reliability metadata 有單一查詢入口。事故發生時，團隊能在同一個地方找到 owner、SLO 狀態、依賴圖與 runbook，而不是在 wiki、Slack 與個人筆記之間來回搜尋。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>Squad-based 組織結構讓團隊能獨立交付，但也讓服務數量快速增長。當服務超過數百個，metadata 開始散落在不同系統：ownership 記在 wiki、SLO 記在 monitoring 平台、runbook 記在文件庫、依賴關係靠口頭傳遞。事故時花時間找 owner 和 runbook 的成本直接拉長 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/mttr/" data-link-title="MTTR" data-link-desc="說明平均修復時間如何作為事故處理能力指標">MTTR&lt;/a>。Spotify 用 Backstage 作為 service catalog，把這些 metadata 收攏到同一個入口。&lt;/p>
&lt;h2 id="決策機制">決策機制&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>機制&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>交付結果&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Service ownership&lt;/td>
 &lt;td>這個服務歸誰管&lt;/td>
 &lt;td>強制 owner team&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>SLO metadata&lt;/td>
 &lt;td>這個服務的可靠性承諾是什麼&lt;/td>
 &lt;td>catalog 內嵌 SLO&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Dependency graph&lt;/td>
 &lt;td>這個服務依賴誰、誰依賴它&lt;/td>
 &lt;td>可查詢依賴圖&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Runbook linkage&lt;/td>
 &lt;td>出事時該看哪份 runbook&lt;/td>
 &lt;td>一鍵連結&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Metadata freshness&lt;/td>
 &lt;td>catalog 資料是否仍然準確&lt;/td>
 &lt;td>過期警告機制&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Service ownership 是最基礎的一層。每個服務在 catalog 中必須有明確的 owner team，沒有 owner 的服務標記為 orphan 並進入清理追蹤。ownership 不只是名義歸屬，而是事故時的第一接手責任。&lt;/p>
&lt;p>SLO metadata 讓 catalog 不只是目錄，而是可靠性狀態的即時入口。團隊能在 catalog 頁面直接看到服務目前的 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/error-budget/" data-link-title="Error Budget" data-link-desc="說明 SLO 允許的失敗額度如何影響發版與可靠性投入">error budget&lt;/a> 消耗狀態，判斷該服務的變更風險。&lt;/p>
&lt;p>Dependency graph 的價值在事故時最明顯。當一個服務異常時，catalog 能回答「還有誰會被影響」和「這個問題可能從哪裡傳過來」，讓事故指揮能快速判斷 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/blast-radius/" data-link-title="Blast Radius" data-link-desc="說明事故影響面如何估算與隔離">blast radius&lt;/a>。&lt;/p>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Orphan service count&lt;/td>
 &lt;td>無 owner 服務是否持續增加&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Metadata freshness&lt;/td>
 &lt;td>catalog 資料是否仍然準確&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/reliability-metrics-governance/" data-link-title="6.18 Reliability Metrics Governance" data-link-desc="DORA / SPACE 指標的選用、量測陷阱、anti-gaming 與團隊階段適配">6.18&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Dependency coverage&lt;/td>
 &lt;td>依賴圖是否涵蓋關鍵路徑&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>MTTR vs catalog coverage&lt;/td>
 &lt;td>catalog 覆蓋率是否與恢復速度相關&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/containment-recovery-strategy/" data-link-title="8.3 止血、降級與回復策略" data-link-desc="把短期止血與正式回復拆成可執行步驟">8.3&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="常見陷阱">常見陷阱&lt;/h2>
&lt;p>Catalog 最常見的失效模式是變成靜態文件。若 metadata 靠人工維護但沒有 freshness check，catalog 會隨時間漂移 — owner 換了團隊但 catalog 沒更新、SLO 調整了但 catalog 還是舊值、依賴關係變了但 graph 沒有同步。事故時從 catalog 拿到過期資訊，比沒有 catalog 更危險，因為團隊會信任它。維持 catalog 價值的關鍵是自動化校驗：定期掃描 orphan service、比對 SLO metadata 與 monitoring 平台的實際值、用 runtime trace 驗證依賴圖的準確性。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14 dependency reliability budget&lt;/a>：catalog 的依賴圖是 dependency budget 的資料來源&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/reliability-metrics-governance/" data-link-title="6.18 Reliability Metrics Governance" data-link-desc="DORA / SPACE 指標的選用、量測陷阱、anti-gaming 與團隊階段適配">6.18 reliability metrics governance&lt;/a>：catalog coverage 與 metadata freshness 本身是可靠性指標&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/reliability-readiness-review/" data-link-title="6.19 Reliability Readiness Review" data-link-desc="把上線前、重大變更前與高風險操作前的可靠性準備度變成可檢查門檻">6.19 reliability readiness review&lt;/a>：readiness checklist 可從 catalog 自動拉取&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 reliability debt backlog&lt;/a>：orphan service 與過期 metadata 是 reliability debt&lt;/li>
&lt;/ul>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://backstage.io/">Backstage.io&lt;/a>：Spotify 開源的 developer portal 框架&lt;/li>
&lt;li>&lt;a href="https://backstage.spotify.com/">Spotify Engineering: What is Backstage?&lt;/a>：Backstage 的設計理念與架構&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Service catalog 在可靠性工程中的責任是讓每個服務的 reliability metadata 有單一查詢入口。事故發生時，團隊能在同一個地方找到 owner、SLO 狀態、依賴圖與 runbook，而不是在 wiki、Slack 與個人筆記之間來回搜尋。</p>
<h2 id="問題場景">問題場景</h2>
<p>Squad-based 組織結構讓團隊能獨立交付，但也讓服務數量快速增長。當服務超過數百個，metadata 開始散落在不同系統：ownership 記在 wiki、SLO 記在 monitoring 平台、runbook 記在文件庫、依賴關係靠口頭傳遞。事故時花時間找 owner 和 runbook 的成本直接拉長 <a href="/blog/backend/knowledge-cards/mttr/" data-link-title="MTTR" data-link-desc="說明平均修復時間如何作為事故處理能力指標">MTTR</a>。Spotify 用 Backstage 作為 service catalog，把這些 metadata 收攏到同一個入口。</p>
<h2 id="決策機制">決策機制</h2>
<table>
  <thead>
      <tr>
          <th>機制</th>
          <th>核心問題</th>
          <th>交付結果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Service ownership</td>
          <td>這個服務歸誰管</td>
          <td>強制 owner team</td>
      </tr>
      <tr>
          <td>SLO metadata</td>
          <td>這個服務的可靠性承諾是什麼</td>
          <td>catalog 內嵌 SLO</td>
      </tr>
      <tr>
          <td>Dependency graph</td>
          <td>這個服務依賴誰、誰依賴它</td>
          <td>可查詢依賴圖</td>
      </tr>
      <tr>
          <td>Runbook linkage</td>
          <td>出事時該看哪份 runbook</td>
          <td>一鍵連結</td>
      </tr>
      <tr>
          <td>Metadata freshness</td>
          <td>catalog 資料是否仍然準確</td>
          <td>過期警告機制</td>
      </tr>
  </tbody>
</table>
<p>Service ownership 是最基礎的一層。每個服務在 catalog 中必須有明確的 owner team，沒有 owner 的服務標記為 orphan 並進入清理追蹤。ownership 不只是名義歸屬，而是事故時的第一接手責任。</p>
<p>SLO metadata 讓 catalog 不只是目錄，而是可靠性狀態的即時入口。團隊能在 catalog 頁面直接看到服務目前的 <a href="/blog/backend/knowledge-cards/error-budget/" data-link-title="Error Budget" data-link-desc="說明 SLO 允許的失敗額度如何影響發版與可靠性投入">error budget</a> 消耗狀態，判斷該服務的變更風險。</p>
<p>Dependency graph 的價值在事故時最明顯。當一個服務異常時，catalog 能回答「還有誰會被影響」和「這個問題可能從哪裡傳過來」，讓事故指揮能快速判斷 <a href="/blog/backend/knowledge-cards/blast-radius/" data-link-title="Blast Radius" data-link-desc="說明事故影響面如何估算與隔離">blast radius</a>。</p>
<h2 id="可觀測訊號">可觀測訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Orphan service count</td>
          <td>無 owner 服務是否持續增加</td>
          <td><a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21</a></td>
      </tr>
      <tr>
          <td>Metadata freshness</td>
          <td>catalog 資料是否仍然準確</td>
          <td><a href="/blog/backend/06-reliability/reliability-metrics-governance/" data-link-title="6.18 Reliability Metrics Governance" data-link-desc="DORA / SPACE 指標的選用、量測陷阱、anti-gaming 與團隊階段適配">6.18</a></td>
      </tr>
      <tr>
          <td>Dependency coverage</td>
          <td>依賴圖是否涵蓋關鍵路徑</td>
          <td><a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14</a></td>
      </tr>
      <tr>
          <td>MTTR vs catalog coverage</td>
          <td>catalog 覆蓋率是否與恢復速度相關</td>
          <td><a href="/blog/backend/08-incident-response/containment-recovery-strategy/" data-link-title="8.3 止血、降級與回復策略" data-link-desc="把短期止血與正式回復拆成可執行步驟">8.3</a></td>
      </tr>
  </tbody>
</table>
<h2 id="常見陷阱">常見陷阱</h2>
<p>Catalog 最常見的失效模式是變成靜態文件。若 metadata 靠人工維護但沒有 freshness check，catalog 會隨時間漂移 — owner 換了團隊但 catalog 沒更新、SLO 調整了但 catalog 還是舊值、依賴關係變了但 graph 沒有同步。事故時從 catalog 拿到過期資訊，比沒有 catalog 更危險，因為團隊會信任它。維持 catalog 價值的關鍵是自動化校驗：定期掃描 orphan service、比對 SLO metadata 與 monitoring 平台的實際值、用 runtime trace 驗證依賴圖的準確性。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li><a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14 dependency reliability budget</a>：catalog 的依賴圖是 dependency budget 的資料來源</li>
<li><a href="/blog/backend/06-reliability/reliability-metrics-governance/" data-link-title="6.18 Reliability Metrics Governance" data-link-desc="DORA / SPACE 指標的選用、量測陷阱、anti-gaming 與團隊階段適配">6.18 reliability metrics governance</a>：catalog coverage 與 metadata freshness 本身是可靠性指標</li>
<li><a href="/blog/backend/06-reliability/reliability-readiness-review/" data-link-title="6.19 Reliability Readiness Review" data-link-desc="把上線前、重大變更前與高風險操作前的可靠性準備度變成可檢查門檻">6.19 reliability readiness review</a>：readiness checklist 可從 catalog 自動拉取</li>
<li><a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 reliability debt backlog</a>：orphan service 與過期 metadata 是 reliability debt</li>
</ul>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://backstage.io/">Backstage.io</a>：Spotify 開源的 developer portal 框架</li>
<li><a href="https://backstage.spotify.com/">Spotify Engineering: What is Backstage?</a>：Backstage 的設計理念與架構</li>
</ul>
]]></content:encoded></item><item><title>Stripe：Canary Deploy 與 Progressive Rollout 治理</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/stripe/canary-deploy-and-progressive-rollout/</link><pubDate>Tue, 23 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/stripe/canary-deploy-and-progressive-rollout/</guid><description>&lt;p>金流場景的 canary deploy 核心責任是讓每一批放量都能用交易指標判斷是否安全。progressive rollout 的節奏由交易成功率、duplicate charge 偵測與退款異常等金流特有指標驅動。本文從金流場景的通用壓力推導 progressive rollout 設計，以 Stripe 公開的 deploy 與 idempotency 實踐作為背景脈絡。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>金流變更的風險帶有延遲性。交易失敗可能在結帳時才被發現，退款申請可能在數天後才出現，對帳差異可能在日終結算才暴露。若 canary 只觀察幾分鐘的 error rate，延遲暴露的問題會在全量放行後才浮現。&lt;/p>
&lt;p>這種延遲特性讓金流場景需要比一般功能更長的觀察窗與更多元的判讀指標。放行決策要等交易生命週期的關鍵階段都走過，才能確認變更安全。&lt;/p>
&lt;h2 id="決策機制">決策機制&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>機制&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>控制方式&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Canary traffic control&lt;/td>
 &lt;td>每批流量比例與觀察窗如何設定&lt;/td>
 &lt;td>1% → 5% → 25% → 100%，觀察窗依交易確認延遲調整&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Transaction-specific checks&lt;/td>
 &lt;td>交易指標是否涵蓋結帳到對帳的完整鏈路&lt;/td>
 &lt;td>checkout success rate、capture rate、duplicate、refund anomaly&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Automatic rollback trigger&lt;/td>
 &lt;td>交易異常時是否能即時回退&lt;/td>
 &lt;td>指標超門檻自動回退，不等人工判斷&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Staged config vs code&lt;/td>
 &lt;td>config 變更與 code 變更的風險是否相同&lt;/td>
 &lt;td>timeout / retry 等 config 變更走獨立且更短的 rollout 節奏&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Canary traffic 的觀察窗設計是這個機制的關鍵。1% 階段至少觀察到一個完整的交易確認週期（通常 30 分鐘到數小時），5% 階段需要覆蓋一個對帳週期，25% 階段需要確認退款率無異常。每批之間的 go/no-go 判斷依據是全部交易指標都在 baseline 範圍內，任一指標偏離即暫停擴批。&lt;/p>
&lt;p>Config 變更（如 provider timeout 或 retry 次數）與 code 變更走不同 rollout 路線。config 變更影響面通常更可預測、回退更快（秒級生效），但風險在於小幅調整也可能放大 retry storm 或觸發 cascade timeout。&lt;/p>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>checkout success rate&lt;/td>
 &lt;td>canary 批次是否維持交易承諾&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>canary vs baseline latency&lt;/td>
 &lt;td>延遲偏移是否超過可接受範圍&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>payment duplicate rate&lt;/td>
 &lt;td>重試是否產生重複扣款&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/idempotency-replay/" data-link-title="6.12 Idempotency 與 Replay 驗證" data-link-desc="把重試、重播與冪等性從口頭約定變成可驗證屬性">6.12&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>rollback trigger count&lt;/td>
 &lt;td>自動回退是否頻繁觸發&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>refund anomaly rate&lt;/td>
 &lt;td>退款比率是否偏離歷史 baseline&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/incident-decision-log/" data-link-title="8.19 Incident Decision Log" data-link-desc="把事中假設、決策、證據、回退條件與責任人留下可復盤紀錄">8.19&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="常見陷阱">常見陷阱&lt;/h2>
&lt;p>把金流 canary 跟一般 feature rollout 用同一套觀察窗，會漏掉延遲暴露的問題。金流的 feedback loop 從結帳到退款可能跨越數天，短窗觀察拿到的 pass 訊號只代表即時指標正常，無法涵蓋對帳與退款階段的風險。&lt;/p>
&lt;p>另一個常見問題是 config 變更被視為低風險而跳過 canary。timeout 或 retry 設定的微幅調整看似無害，但在高流量下可能觸發 retry storm 或改變 provider 端的行為，影響幅度可能大於 code 變更。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>先回到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate&lt;/a> 定義金流場景的放行政策，再到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/feature-flag-governance/" data-link-title="6.17 Feature Flag Governance" data-link-desc="把 feature flag 從上線開關升級為有角色分類、lifecycle 管理與 debt 治理的 runtime artifact">6.17 Feature Flag Governance&lt;/a> 設計 progressive rollout 的 flag lifecycle。實作示範見 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/provider-dependency-release-gate/" data-link-title="6.25 Provider Dependency Release Gate 實作示範" data-link-desc="以 payment provider 變更示範 release gate 如何結合 evidence、stop condition 與 rollback window。">6.25 Provider Dependency Release Gate&lt;/a>。&lt;/p></description><content:encoded><![CDATA[<p>金流場景的 canary deploy 核心責任是讓每一批放量都能用交易指標判斷是否安全。progressive rollout 的節奏由交易成功率、duplicate charge 偵測與退款異常等金流特有指標驅動。本文從金流場景的通用壓力推導 progressive rollout 設計，以 Stripe 公開的 deploy 與 idempotency 實踐作為背景脈絡。</p>
<h2 id="問題場景">問題場景</h2>
<p>金流變更的風險帶有延遲性。交易失敗可能在結帳時才被發現，退款申請可能在數天後才出現，對帳差異可能在日終結算才暴露。若 canary 只觀察幾分鐘的 error rate，延遲暴露的問題會在全量放行後才浮現。</p>
<p>這種延遲特性讓金流場景需要比一般功能更長的觀察窗與更多元的判讀指標。放行決策要等交易生命週期的關鍵階段都走過，才能確認變更安全。</p>
<h2 id="決策機制">決策機制</h2>
<table>
  <thead>
      <tr>
          <th>機制</th>
          <th>核心問題</th>
          <th>控制方式</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Canary traffic control</td>
          <td>每批流量比例與觀察窗如何設定</td>
          <td>1% → 5% → 25% → 100%，觀察窗依交易確認延遲調整</td>
      </tr>
      <tr>
          <td>Transaction-specific checks</td>
          <td>交易指標是否涵蓋結帳到對帳的完整鏈路</td>
          <td>checkout success rate、capture rate、duplicate、refund anomaly</td>
      </tr>
      <tr>
          <td>Automatic rollback trigger</td>
          <td>交易異常時是否能即時回退</td>
          <td>指標超門檻自動回退，不等人工判斷</td>
      </tr>
      <tr>
          <td>Staged config vs code</td>
          <td>config 變更與 code 變更的風險是否相同</td>
          <td>timeout / retry 等 config 變更走獨立且更短的 rollout 節奏</td>
      </tr>
  </tbody>
</table>
<p>Canary traffic 的觀察窗設計是這個機制的關鍵。1% 階段至少觀察到一個完整的交易確認週期（通常 30 分鐘到數小時），5% 階段需要覆蓋一個對帳週期，25% 階段需要確認退款率無異常。每批之間的 go/no-go 判斷依據是全部交易指標都在 baseline 範圍內，任一指標偏離即暫停擴批。</p>
<p>Config 變更（如 provider timeout 或 retry 次數）與 code 變更走不同 rollout 路線。config 變更影響面通常更可預測、回退更快（秒級生效），但風險在於小幅調整也可能放大 retry storm 或觸發 cascade timeout。</p>
<h2 id="可觀測訊號">可觀測訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>checkout success rate</td>
          <td>canary 批次是否維持交易承諾</td>
          <td><a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8</a></td>
      </tr>
      <tr>
          <td>canary vs baseline latency</td>
          <td>延遲偏移是否超過可接受範圍</td>
          <td><a href="/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13</a></td>
      </tr>
      <tr>
          <td>payment duplicate rate</td>
          <td>重試是否產生重複扣款</td>
          <td><a href="/blog/backend/06-reliability/idempotency-replay/" data-link-title="6.12 Idempotency 與 Replay 驗證" data-link-desc="把重試、重播與冪等性從口頭約定變成可驗證屬性">6.12</a></td>
      </tr>
      <tr>
          <td>rollback trigger count</td>
          <td>自動回退是否頻繁觸發</td>
          <td><a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23</a></td>
      </tr>
      <tr>
          <td>refund anomaly rate</td>
          <td>退款比率是否偏離歷史 baseline</td>
          <td><a href="/blog/backend/08-incident-response/incident-decision-log/" data-link-title="8.19 Incident Decision Log" data-link-desc="把事中假設、決策、證據、回退條件與責任人留下可復盤紀錄">8.19</a></td>
      </tr>
  </tbody>
</table>
<h2 id="常見陷阱">常見陷阱</h2>
<p>把金流 canary 跟一般 feature rollout 用同一套觀察窗，會漏掉延遲暴露的問題。金流的 feedback loop 從結帳到退款可能跨越數天，短窗觀察拿到的 pass 訊號只代表即時指標正常，無法涵蓋對帳與退款階段的風險。</p>
<p>另一個常見問題是 config 變更被視為低風險而跳過 canary。timeout 或 retry 設定的微幅調整看似無害，但在高流量下可能觸發 retry storm 或改變 provider 端的行為，影響幅度可能大於 code 變更。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>先回到 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate</a> 定義金流場景的放行政策，再到 <a href="/blog/backend/06-reliability/feature-flag-governance/" data-link-title="6.17 Feature Flag Governance" data-link-desc="把 feature flag 從上線開關升級為有角色分類、lifecycle 管理與 debt 治理的 runtime artifact">6.17 Feature Flag Governance</a> 設計 progressive rollout 的 flag lifecycle。實作示範見 <a href="/blog/backend/06-reliability/provider-dependency-release-gate/" data-link-title="6.25 Provider Dependency Release Gate 實作示範" data-link-desc="以 payment provider 變更示範 release gate 如何結合 evidence、stop condition 與 rollback window。">6.25 Provider Dependency Release Gate</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://stripe.com/blog/idempotency">Designing robust and predictable APIs with idempotency</a>：idempotency key 設計，支撐 canary 回退後的重試安全</li>
<li><a href="https://stripe.com/blog/how-stripes-document-databases-supported-99.999-uptime-with-zero-downtime-data-migrations">How Stripe&rsquo;s document databases supported 99.999% uptime with zero-downtime data migrations</a>：zero-downtime migration 的 staged rollout 思路</li>
</ul>
<p>本文的 progressive rollout 機制（觀察窗設計、交易指標門檻、自動回退）從金流場景的通用壓力推導，並非 Stripe 公開的具體 deploy pipeline 描述。</p>
]]></content:encoded></item><item><title>Microsoft：變更治理與可靠性門檻</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/microsoft/change-management-and-reliability-governance/</link><pubDate>Thu, 07 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/microsoft/change-management-and-reliability-governance/</guid><description>&lt;p>Microsoft 案例的核心責任是把變更管理制度化。對大型 SaaS 而言，事故常由多個低風險變更疊加而成，治理重點在於發布節奏與風險分層。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>高頻變更環境中，單一變更看起來都可接受，但累積後會突破可靠性預算。若缺少一致 gate，團隊難以提早收斂。&lt;/p>
&lt;h2 id="決策機制">決策機制&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>機制&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>交付結果&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>變更分層&lt;/td>
 &lt;td>哪些變更需要高門檻&lt;/td>
 &lt;td>風險分級&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>漸進發布&lt;/td>
 &lt;td>何時擴大、何時停止&lt;/td>
 &lt;td>放行節奏&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>復盤回寫&lt;/td>
 &lt;td>事故教訓如何制度化&lt;/td>
 &lt;td>持續改善&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>release rollback frequency&lt;/td>
 &lt;td>變更品質是否退化&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>freeze trigger count&lt;/td>
 &lt;td>凍結是否過晚&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>incident recurrence&lt;/td>
 &lt;td>同型事件是否重複&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/repeated-incident-toil/" data-link-title="8.13 Repeated Incident 與 Toil 治理" data-link-desc="把同型事故反覆發生與重複手動修復作為工程化治理對象">8.13&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>把風險分層寫進 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/reliability-readiness-review/" data-link-title="6.19 Reliability Readiness Review" data-link-desc="把上線前、重大變更前與高風險操作前的可靠性準備度變成可檢查門檻">6.19&lt;/a>，並將復盤項目回寫 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21&lt;/a>。&lt;/p></description><content:encoded><![CDATA[<p>Microsoft 案例的核心責任是把變更管理制度化。對大型 SaaS 而言，事故常由多個低風險變更疊加而成，治理重點在於發布節奏與風險分層。</p>
<h2 id="問題場景">問題場景</h2>
<p>高頻變更環境中，單一變更看起來都可接受，但累積後會突破可靠性預算。若缺少一致 gate，團隊難以提早收斂。</p>
<h2 id="決策機制">決策機制</h2>
<table>
  <thead>
      <tr>
          <th>機制</th>
          <th>核心問題</th>
          <th>交付結果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>變更分層</td>
          <td>哪些變更需要高門檻</td>
          <td>風險分級</td>
      </tr>
      <tr>
          <td>漸進發布</td>
          <td>何時擴大、何時停止</td>
          <td>放行節奏</td>
      </tr>
      <tr>
          <td>復盤回寫</td>
          <td>事故教訓如何制度化</td>
          <td>持續改善</td>
      </tr>
  </tbody>
</table>
<h2 id="可觀測訊號">可觀測訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>release rollback frequency</td>
          <td>變更品質是否退化</td>
          <td><a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8</a></td>
      </tr>
      <tr>
          <td>freeze trigger count</td>
          <td>凍結是否過晚</td>
          <td><a href="/blog/backend/06-reliability/slo-error-budget/" data-link-title="6.6 SLO 與 Error Budget 政策" data-link-desc="把可靠性目標轉成可驗證量測與凍結條件">6.6</a></td>
      </tr>
      <tr>
          <td>incident recurrence</td>
          <td>同型事件是否重複</td>
          <td><a href="/blog/backend/08-incident-response/repeated-incident-toil/" data-link-title="8.13 Repeated Incident 與 Toil 治理" data-link-desc="把同型事故反覆發生與重複手動修復作為工程化治理對象">8.13</a></td>
      </tr>
  </tbody>
</table>
<h2 id="下一步路由">下一步路由</h2>
<p>把風險分層寫進 <a href="/blog/backend/06-reliability/reliability-readiness-review/" data-link-title="6.19 Reliability Readiness Review" data-link-desc="把上線前、重大變更前與高風險操作前的可靠性準備度變成可檢查門檻">6.19</a>，並將復盤項目回寫 <a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21</a>。</p>
]]></content:encoded></item><item><title>Shopify：BFCM 容量治理與 Game Day 驗證節奏</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/shopify/bfcm-capacity-and-game-day/</link><pubDate>Thu, 07 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/shopify/bfcm-capacity-and-game-day/</guid><description>&lt;p>Shopify 案例的核心責任是把可預期峰值轉成可預演流程。當流量高峰是年度固定事件，可靠性工作重點是提前把容量與失效路徑變成可驗證資產，臨場救火代表準備不足。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>BFCM 類型高峰會同時放大三種壓力：流量突增、資料層寫入壓力、跨服務依賴抖動。若只在活動前做單次壓測，團隊通常只能看到系統上限，無法看到恢復節奏與指揮負載。&lt;/p>
&lt;p>Shopify 的做法是把容量規劃、隔離邊界與演練節奏綁成同一條年度路線。&lt;/p>
&lt;h2 id="決策機制">決策機制&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>機制&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>交付結果&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Capacity planning baseline&lt;/td>
 &lt;td>高峰前可承受上限是多少&lt;/td>
 &lt;td>容量預算&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Pod/isolation boundary&lt;/td>
 &lt;td>故障影響如何限制在局部&lt;/td>
 &lt;td>擴散邊界&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Game Day&lt;/td>
 &lt;td>高峰前如何驗證假設&lt;/td>
 &lt;td>演練證據&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Resiliency matrix&lt;/td>
 &lt;td>服務與失效模式如何對齊&lt;/td>
 &lt;td>控制面清單&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這個機制的價值是讓高峰風險在活動前被分批消化，而不是在活動中一次承擔。&lt;/p>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>peak-load headroom&lt;/td>
 &lt;td>高峰前安全緩衝是否充足&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/capacity-cost/" data-link-title="6.9 容量與成本邊界" data-link-desc="把容量規劃跟成本約束變成驗證輸入">6.9&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>game-day action closure&lt;/td>
 &lt;td>演練缺口是否完成回寫&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>pod-level degradation&lt;/td>
 &lt;td>退化是否被限制在局部&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>command handoff latency&lt;/td>
 &lt;td>高峰日交接節奏是否穩定&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/ic-handoff-long-incident/" data-link-title="8.12 IC Handoff 與長事故跨班次協調" data-link-desc="把 24h&amp;#43; / 跨 timezone 事故的接班節奏變成可重複流程">8.12&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="常見陷阱">常見陷阱&lt;/h2>
&lt;p>把高峰準備當成一次性專案會讓知識斷層快速累積。可靠做法是把每輪活動輸出的缺口回寫成固定資產：runbook、matrix、驗證腳本與放行門檻。這讓下一輪準備從更高基準開始，而不是重來。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>若要落地本案例，先從 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/capacity-cost/" data-link-title="6.9 容量與成本邊界" data-link-desc="把容量規劃跟成本約束變成驗證輸入">6.9&lt;/a> 建容量模型，再在 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22&lt;/a> 定義高峰穩態。演練證據回寫 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/drills-and-oncall-readiness/" data-link-title="8.6 演練與值班能力建設" data-link-desc="用演練與值班訓練提升事故反應品質">8.6&lt;/a>。&lt;/p></description><content:encoded><![CDATA[<p>Shopify 案例的核心責任是把可預期峰值轉成可預演流程。當流量高峰是年度固定事件，可靠性工作重點是提前把容量與失效路徑變成可驗證資產，臨場救火代表準備不足。</p>
<h2 id="問題場景">問題場景</h2>
<p>BFCM 類型高峰會同時放大三種壓力：流量突增、資料層寫入壓力、跨服務依賴抖動。若只在活動前做單次壓測，團隊通常只能看到系統上限，無法看到恢復節奏與指揮負載。</p>
<p>Shopify 的做法是把容量規劃、隔離邊界與演練節奏綁成同一條年度路線。</p>
<h2 id="決策機制">決策機制</h2>
<table>
  <thead>
      <tr>
          <th>機制</th>
          <th>核心問題</th>
          <th>交付結果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Capacity planning baseline</td>
          <td>高峰前可承受上限是多少</td>
          <td>容量預算</td>
      </tr>
      <tr>
          <td>Pod/isolation boundary</td>
          <td>故障影響如何限制在局部</td>
          <td>擴散邊界</td>
      </tr>
      <tr>
          <td>Game Day</td>
          <td>高峰前如何驗證假設</td>
          <td>演練證據</td>
      </tr>
      <tr>
          <td>Resiliency matrix</td>
          <td>服務與失效模式如何對齊</td>
          <td>控制面清單</td>
      </tr>
  </tbody>
</table>
<p>這個機制的價值是讓高峰風險在活動前被分批消化，而不是在活動中一次承擔。</p>
<h2 id="可觀測訊號">可觀測訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>peak-load headroom</td>
          <td>高峰前安全緩衝是否充足</td>
          <td><a href="/blog/backend/06-reliability/capacity-cost/" data-link-title="6.9 容量與成本邊界" data-link-desc="把容量規劃跟成本約束變成驗證輸入">6.9</a></td>
      </tr>
      <tr>
          <td>game-day action closure</td>
          <td>演練缺口是否完成回寫</td>
          <td><a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21</a></td>
      </tr>
      <tr>
          <td>pod-level degradation</td>
          <td>退化是否被限制在局部</td>
          <td><a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22</a></td>
      </tr>
      <tr>
          <td>command handoff latency</td>
          <td>高峰日交接節奏是否穩定</td>
          <td><a href="/blog/backend/08-incident-response/ic-handoff-long-incident/" data-link-title="8.12 IC Handoff 與長事故跨班次協調" data-link-desc="把 24h&#43; / 跨 timezone 事故的接班節奏變成可重複流程">8.12</a></td>
      </tr>
  </tbody>
</table>
<h2 id="常見陷阱">常見陷阱</h2>
<p>把高峰準備當成一次性專案會讓知識斷層快速累積。可靠做法是把每輪活動輸出的缺口回寫成固定資產：runbook、matrix、驗證腳本與放行門檻。這讓下一輪準備從更高基準開始，而不是重來。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>若要落地本案例，先從 <a href="/blog/backend/06-reliability/capacity-cost/" data-link-title="6.9 容量與成本邊界" data-link-desc="把容量規劃跟成本約束變成驗證輸入">6.9</a> 建容量模型，再在 <a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22</a> 定義高峰穩態。演練證據回寫 <a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23</a> 與 <a href="/blog/backend/08-incident-response/drills-and-oncall-readiness/" data-link-title="8.6 演練與值班能力建設" data-link-desc="用演練與值班訓練提升事故反應品質">8.6</a>。</p>
]]></content:encoded></item><item><title>Microsoft：Safe Deployment Practices 與 Resilience Patterns</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/microsoft/safe-deployment-practices-and-resilience-patterns/</link><pubDate>Tue, 23 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/microsoft/safe-deployment-practices-and-resilience-patterns/</guid><description>&lt;p>Safe deployment practices 的核心責任是讓大規模服務的每次變更都經過漸進驗證。ring-based deployment 把影響面從小到大排列，每一層是下一層的安全網。resilience patterns 的責任是讓服務在依賴失效時有標準化的降級行為，降低臨場判斷的成本。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>Azure 與 M365 等大型 SaaS 每天部署數千次變更，單靠人工審核不可擴展。當部署速度超過人工審查能力，需要一套自動化的漸進驗證流程來控制每次變更的風險。同時，服務間的依賴關係複雜，任何一個依賴的劣化都可能影響多個下游服務，需要標準化的降級行為避免連鎖失效。&lt;/p>
&lt;h2 id="決策機制">決策機制&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>機制&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>交付結果&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Ring-based deployment&lt;/td>
 &lt;td>變更如何從小範圍漸進到全量&lt;/td>
 &lt;td>分層放行節奏&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Automatic rollback&lt;/td>
 &lt;td>health signal 異常時如何自動退回&lt;/td>
 &lt;td>自動化回退條件&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Resilience patterns&lt;/td>
 &lt;td>依賴失效時服務如何標準化降級&lt;/td>
 &lt;td>retry / breaker / bulkhead&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Blast radius control&lt;/td>
 &lt;td>ring boundary 如何限制影響範圍&lt;/td>
 &lt;td>每層的最大影響面&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Ring-based deployment 的標準路徑是 Ring 0（internal dogfood）→ Ring 1（canary）→ Ring 2（early adopters）→ Ring 3（broad）。每一層的 go/no-go 條件包含 health signal delta（跟前一版 baseline 比較）、error rate、latency percentile 與 customer impact signal。只有當前層的所有指標都在可接受範圍內，才進入下一層。&lt;/p>
&lt;p>Automatic rollback 是 ring progression 的安全網。當 health signal 超過預設門檻時，系統自動回退到前一版，不需要等人工判斷。自動回退的觸發條件要嚴格定義 — 過於敏感會造成頻繁 false positive rollback，過於寬鬆會讓問題擴散到下一個 ring。&lt;/p>
&lt;p>Resilience patterns 讓依賴失效時的行為可預測。retry with &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/jitter/" data-link-title="Jitter" data-link-desc="說明重試或排程加入隨機偏移如何降低同步尖峰">jitter&lt;/a> 避免重試風暴、circuit breaker 在依賴持續失效時停止發送請求、bulkhead isolation 把不同依賴的資源池隔開。這些 patterns 的價值在於標準化 — 團隊不需要每次都從頭設計降級邏輯，而是從已驗證的 pattern 庫中選擇。&lt;/p>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>ring health delta&lt;/td>
 &lt;td>每層的品質是否維持&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>automatic rollback frequency&lt;/td>
 &lt;td>自動回退是否過於頻繁或過少&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/reliability-metrics-governance/" data-link-title="6.18 Reliability Metrics Governance" data-link-desc="DORA / SPACE 指標的選用、量測陷阱、anti-gaming 與團隊階段適配">6.18&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>circuit breaker trip rate&lt;/td>
 &lt;td>依賴失效是否被及時隔離&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>deployment velocity&lt;/td>
 &lt;td>漸進部署是否拖慢交付速度&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/ci-pipeline/" data-link-title="6.1 CI pipeline" data-link-desc="CI pipeline 的分層策略、artifact 管理、flaky 治理與 release gate 輸入">6.1&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="常見陷阱">常見陷阱&lt;/h2>
&lt;p>Ring progression 的觀察窗長度需要跟服務的 feedback loop 對齊。通用服務可能幾分鐘內就能看到異常，但有延遲確認的服務（結算、對帳、非同步補償）可能需要數小時甚至數天才暴露問題。觀察窗太短會漏掉延遲暴露的問題；太長會拖慢所有變更的交付速度。分服務類型設定不同觀察窗，比用統一時長更有效。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>先把 ring-based deployment 的 go/no-go 條件寫進 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate&lt;/a>，再把 resilience patterns 的 circuit breaker 與 retry 設計接到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14 Dependency Reliability Budget&lt;/a>。deployment velocity 的量測回到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/reliability-metrics-governance/" data-link-title="6.18 Reliability Metrics Governance" data-link-desc="DORA / SPACE 指標的選用、量測陷阱、anti-gaming 與團隊階段適配">6.18 Reliability Metrics&lt;/a>，CI 整合回到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/ci-pipeline/" data-link-title="6.1 CI pipeline" data-link-desc="CI pipeline 的分層策略、artifact 管理、flaky 治理與 release gate 輸入">6.1 CI Pipeline&lt;/a>。&lt;/p></description><content:encoded><![CDATA[<p>Safe deployment practices 的核心責任是讓大規模服務的每次變更都經過漸進驗證。ring-based deployment 把影響面從小到大排列，每一層是下一層的安全網。resilience patterns 的責任是讓服務在依賴失效時有標準化的降級行為，降低臨場判斷的成本。</p>
<h2 id="問題場景">問題場景</h2>
<p>Azure 與 M365 等大型 SaaS 每天部署數千次變更，單靠人工審核不可擴展。當部署速度超過人工審查能力，需要一套自動化的漸進驗證流程來控制每次變更的風險。同時，服務間的依賴關係複雜，任何一個依賴的劣化都可能影響多個下游服務，需要標準化的降級行為避免連鎖失效。</p>
<h2 id="決策機制">決策機制</h2>
<table>
  <thead>
      <tr>
          <th>機制</th>
          <th>核心問題</th>
          <th>交付結果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Ring-based deployment</td>
          <td>變更如何從小範圍漸進到全量</td>
          <td>分層放行節奏</td>
      </tr>
      <tr>
          <td>Automatic rollback</td>
          <td>health signal 異常時如何自動退回</td>
          <td>自動化回退條件</td>
      </tr>
      <tr>
          <td>Resilience patterns</td>
          <td>依賴失效時服務如何標準化降級</td>
          <td>retry / breaker / bulkhead</td>
      </tr>
      <tr>
          <td>Blast radius control</td>
          <td>ring boundary 如何限制影響範圍</td>
          <td>每層的最大影響面</td>
      </tr>
  </tbody>
</table>
<p>Ring-based deployment 的標準路徑是 Ring 0（internal dogfood）→ Ring 1（canary）→ Ring 2（early adopters）→ Ring 3（broad）。每一層的 go/no-go 條件包含 health signal delta（跟前一版 baseline 比較）、error rate、latency percentile 與 customer impact signal。只有當前層的所有指標都在可接受範圍內，才進入下一層。</p>
<p>Automatic rollback 是 ring progression 的安全網。當 health signal 超過預設門檻時，系統自動回退到前一版，不需要等人工判斷。自動回退的觸發條件要嚴格定義 — 過於敏感會造成頻繁 false positive rollback，過於寬鬆會讓問題擴散到下一個 ring。</p>
<p>Resilience patterns 讓依賴失效時的行為可預測。retry with <a href="/blog/backend/knowledge-cards/jitter/" data-link-title="Jitter" data-link-desc="說明重試或排程加入隨機偏移如何降低同步尖峰">jitter</a> 避免重試風暴、circuit breaker 在依賴持續失效時停止發送請求、bulkhead isolation 把不同依賴的資源池隔開。這些 patterns 的價值在於標準化 — 團隊不需要每次都從頭設計降級邏輯，而是從已驗證的 pattern 庫中選擇。</p>
<h2 id="可觀測訊號">可觀測訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>ring health delta</td>
          <td>每層的品質是否維持</td>
          <td><a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8</a></td>
      </tr>
      <tr>
          <td>automatic rollback frequency</td>
          <td>自動回退是否過於頻繁或過少</td>
          <td><a href="/blog/backend/06-reliability/reliability-metrics-governance/" data-link-title="6.18 Reliability Metrics Governance" data-link-desc="DORA / SPACE 指標的選用、量測陷阱、anti-gaming 與團隊階段適配">6.18</a></td>
      </tr>
      <tr>
          <td>circuit breaker trip rate</td>
          <td>依賴失效是否被及時隔離</td>
          <td><a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14</a></td>
      </tr>
      <tr>
          <td>deployment velocity</td>
          <td>漸進部署是否拖慢交付速度</td>
          <td><a href="/blog/backend/06-reliability/ci-pipeline/" data-link-title="6.1 CI pipeline" data-link-desc="CI pipeline 的分層策略、artifact 管理、flaky 治理與 release gate 輸入">6.1</a></td>
      </tr>
  </tbody>
</table>
<h2 id="常見陷阱">常見陷阱</h2>
<p>Ring progression 的觀察窗長度需要跟服務的 feedback loop 對齊。通用服務可能幾分鐘內就能看到異常，但有延遲確認的服務（結算、對帳、非同步補償）可能需要數小時甚至數天才暴露問題。觀察窗太短會漏掉延遲暴露的問題；太長會拖慢所有變更的交付速度。分服務類型設定不同觀察窗，比用統一時長更有效。</p>
<h2 id="下一步路由">下一步路由</h2>
<p>先把 ring-based deployment 的 go/no-go 條件寫進 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate</a>，再把 resilience patterns 的 circuit breaker 與 retry 設計接到 <a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14 Dependency Reliability Budget</a>。deployment velocity 的量測回到 <a href="/blog/backend/06-reliability/reliability-metrics-governance/" data-link-title="6.18 Reliability Metrics Governance" data-link-desc="DORA / SPACE 指標的選用、量測陷阱、anti-gaming 與團隊階段適配">6.18 Reliability Metrics</a>，CI 整合回到 <a href="/blog/backend/06-reliability/ci-pipeline/" data-link-title="6.1 CI pipeline" data-link-desc="CI pipeline 的分層策略、artifact 管理、flaky 治理與 release gate 輸入">6.1 CI Pipeline</a>。</p>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://learn.microsoft.com/en-us/devops/operate/safe-deployment-practices">Safe deployment practices</a></li>
<li><a href="https://learn.microsoft.com/en-gb/azure/well-architected/reliability/design-patterns">Architecture design patterns that support reliability</a></li>
</ul>
]]></content:encoded></item><item><title>Shopify：Pod Architecture 與 Resiliency Matrix</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/shopify/pod-architecture-and-resiliency-matrix/</link><pubDate>Tue, 23 Jun 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/shopify/pod-architecture-and-resiliency-matrix/</guid><description>&lt;p>Shopify pod architecture 的核心責任是把多租戶流量限制在獨立的 pod 內，讓一個 pod 的故障不影響其他 pod 的商店。&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/resiliency-matrix/" data-link-title="Resiliency Matrix" data-link-desc="服務與失敗模式的交叉矩陣，標記每個交叉點的防護狀態與驗證覆蓋">resiliency matrix&lt;/a> 的核心責任是把每個服務的失敗模式與防護狀態列成可檢查的矩陣，讓 game day 有結構化的驗證清單。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>多租戶電商平台的流量分佈高度不均。大商店的促銷活動可能在短時間內吃掉共享資源的大部分容量，若缺少隔離機制，一個商店的流量爆增會拖垮同一基礎設施上的其他商店。&lt;/p>
&lt;p>隔離解決的是擴散問題，但隔離本身不回答「哪些失敗模式已經有防護、哪些還是缺口」。resiliency matrix 把這個問題結構化：每個服務列出已知的失敗模式，每種模式標註防護狀態，缺口直接成為下一輪演練的輸入。&lt;/p>
&lt;h2 id="決策機制">決策機制&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>機制&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>交付結果&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Pod boundary&lt;/td>
 &lt;td>一個商店的故障最多影響到哪裡&lt;/td>
 &lt;td>獨立隔離單位&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Tenant routing&lt;/td>
 &lt;td>商店按什麼規則分配到 pod&lt;/td>
 &lt;td>映射策略&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Resiliency matrix&lt;/td>
 &lt;td>每個服務的失敗模式是否都有對應防護&lt;/td>
 &lt;td>防護覆蓋狀態&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Game Day 整合&lt;/td>
 &lt;td>matrix 的缺口如何轉成演練題目&lt;/td>
 &lt;td>演練驗證清單&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Pod boundary 的設計是每個 pod 擁有獨立的 DB、cache 與 compute 資源。這讓 pod 之間在資源層完全隔離 — 一個 pod 的 DB 連線耗盡不會影響其他 pod 的查詢能力。隔離的代價是資源利用率降低，但在峰值場景下，隔離帶來的故障局部化價值遠高於利用率損失。&lt;/p>
&lt;p>Tenant routing 決定商店到 pod 的映射。映射規則通常考慮商店規模（大商店獨立 pod 或少量共用）、地理區域、與風險等級（新商店 vs 穩定商店）。映射一旦建立，變更需要 migration — 這是隔離架構的操作成本之一。&lt;/p>
&lt;p>Resiliency matrix 是 service × failure mode 的二維矩陣。每格填入三種狀態之一：covered（有防護且已驗證）、gap（已知缺口、尚未補齊）、in-progress（正在建設）。matrix 的維護責任跟服務 owner 綁定，每輪 game day 前 review 一次。&lt;/p>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>pod-level error isolation&lt;/td>
 &lt;td>故障是否被限制在單一 pod 內&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>matrix gap count trend&lt;/td>
 &lt;td>缺口是否在收斂&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>cross-pod contamination&lt;/td>
 &lt;td>是否有故障穿越 pod 邊界&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>game-day action closure&lt;/td>
 &lt;td>演練暴露的缺口是否被關閉&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/failure-mode-pre-mortem/" data-link-title="6.5 失敗模式預判（Pre-mortem 與 FMEA）" data-link-desc="用 pre-mortem 反向推導失敗路徑、用 FMEA 分類軸評估驗證缺口，把可靠性盲區變成可排序的改善輸入">6.5&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="常見陷阱">常見陷阱&lt;/h2>
&lt;p>resiliency matrix 最大的風險是退化為文件。若 matrix 只在年度 review 更新一次、gap 沒有 owner、action item 沒有 deadline，它就失去了驅動演練的功能。有效的 matrix 跟 game day 節奏綁定：每輪演練前 review gap、演練後更新狀態、新服務上線時補齊對應行列。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/failure-mode-pre-mortem/" data-link-title="6.5 失敗模式預判（Pre-mortem 與 FMEA）" data-link-desc="用 pre-mortem 反向推導失敗路徑、用 FMEA 分類軸評估驗證缺口，把可靠性盲區變成可排序的改善輸入">6.5 失敗模式預判&lt;/a>：resiliency matrix 是 FMEA 的落地工具&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14 dependency budget&lt;/a>：pod 隔離是依賴預算的實作手段&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 experiment safety&lt;/a>：跨 pod 實驗的 blast radius 控制&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 reliability debt&lt;/a>：matrix gap 回寫成 reliability debt&lt;/li>
&lt;/ul>
&lt;h2 id="引用源">引用源&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://shopify.engineering/four-steps-creating-effective-game-day-tests">Four Steps to Creating Effective Game Day Tests&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://shopify.engineering/resiliency-planning-for-high-traffic-events">Resiliency Planning for High-Traffic Events&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://shopify.engineering/a-pods-architecture-to-allow-shopify-to-scale">A Pods Architecture To Allow Shopify To Scale&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Shopify pod architecture 的核心責任是把多租戶流量限制在獨立的 pod 內，讓一個 pod 的故障不影響其他 pod 的商店。<a href="/blog/backend/knowledge-cards/resiliency-matrix/" data-link-title="Resiliency Matrix" data-link-desc="服務與失敗模式的交叉矩陣，標記每個交叉點的防護狀態與驗證覆蓋">resiliency matrix</a> 的核心責任是把每個服務的失敗模式與防護狀態列成可檢查的矩陣，讓 game day 有結構化的驗證清單。</p>
<h2 id="問題場景">問題場景</h2>
<p>多租戶電商平台的流量分佈高度不均。大商店的促銷活動可能在短時間內吃掉共享資源的大部分容量，若缺少隔離機制，一個商店的流量爆增會拖垮同一基礎設施上的其他商店。</p>
<p>隔離解決的是擴散問題，但隔離本身不回答「哪些失敗模式已經有防護、哪些還是缺口」。resiliency matrix 把這個問題結構化：每個服務列出已知的失敗模式，每種模式標註防護狀態，缺口直接成為下一輪演練的輸入。</p>
<h2 id="決策機制">決策機制</h2>
<table>
  <thead>
      <tr>
          <th>機制</th>
          <th>核心問題</th>
          <th>交付結果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Pod boundary</td>
          <td>一個商店的故障最多影響到哪裡</td>
          <td>獨立隔離單位</td>
      </tr>
      <tr>
          <td>Tenant routing</td>
          <td>商店按什麼規則分配到 pod</td>
          <td>映射策略</td>
      </tr>
      <tr>
          <td>Resiliency matrix</td>
          <td>每個服務的失敗模式是否都有對應防護</td>
          <td>防護覆蓋狀態</td>
      </tr>
      <tr>
          <td>Game Day 整合</td>
          <td>matrix 的缺口如何轉成演練題目</td>
          <td>演練驗證清單</td>
      </tr>
  </tbody>
</table>
<p>Pod boundary 的設計是每個 pod 擁有獨立的 DB、cache 與 compute 資源。這讓 pod 之間在資源層完全隔離 — 一個 pod 的 DB 連線耗盡不會影響其他 pod 的查詢能力。隔離的代價是資源利用率降低，但在峰值場景下，隔離帶來的故障局部化價值遠高於利用率損失。</p>
<p>Tenant routing 決定商店到 pod 的映射。映射規則通常考慮商店規模（大商店獨立 pod 或少量共用）、地理區域、與風險等級（新商店 vs 穩定商店）。映射一旦建立，變更需要 migration — 這是隔離架構的操作成本之一。</p>
<p>Resiliency matrix 是 service × failure mode 的二維矩陣。每格填入三種狀態之一：covered（有防護且已驗證）、gap（已知缺口、尚未補齊）、in-progress（正在建設）。matrix 的維護責任跟服務 owner 綁定，每輪 game day 前 review 一次。</p>
<h2 id="可觀測訊號">可觀測訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>pod-level error isolation</td>
          <td>故障是否被限制在單一 pod 內</td>
          <td><a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14</a></td>
      </tr>
      <tr>
          <td>matrix gap count trend</td>
          <td>缺口是否在收斂</td>
          <td><a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21</a></td>
      </tr>
      <tr>
          <td>cross-pod contamination</td>
          <td>是否有故障穿越 pod 邊界</td>
          <td><a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20</a></td>
      </tr>
      <tr>
          <td>game-day action closure</td>
          <td>演練暴露的缺口是否被關閉</td>
          <td><a href="/blog/backend/06-reliability/failure-mode-pre-mortem/" data-link-title="6.5 失敗模式預判（Pre-mortem 與 FMEA）" data-link-desc="用 pre-mortem 反向推導失敗路徑、用 FMEA 分類軸評估驗證缺口，把可靠性盲區變成可排序的改善輸入">6.5</a></td>
      </tr>
  </tbody>
</table>
<h2 id="常見陷阱">常見陷阱</h2>
<p>resiliency matrix 最大的風險是退化為文件。若 matrix 只在年度 review 更新一次、gap 沒有 owner、action item 沒有 deadline，它就失去了驅動演練的功能。有效的 matrix 跟 game day 節奏綁定：每輪演練前 review gap、演練後更新狀態、新服務上線時補齊對應行列。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li><a href="/blog/backend/06-reliability/failure-mode-pre-mortem/" data-link-title="6.5 失敗模式預判（Pre-mortem 與 FMEA）" data-link-desc="用 pre-mortem 反向推導失敗路徑、用 FMEA 分類軸評估驗證缺口，把可靠性盲區變成可排序的改善輸入">6.5 失敗模式預判</a>：resiliency matrix 是 FMEA 的落地工具</li>
<li><a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14 dependency budget</a>：pod 隔離是依賴預算的實作手段</li>
<li><a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 experiment safety</a>：跨 pod 實驗的 blast radius 控制</li>
<li><a href="/blog/backend/06-reliability/reliability-debt-backlog/" data-link-title="6.21 Reliability Debt Backlog" data-link-desc="把反覆事故、演練缺口與手動修復累積成可排序、可關閉的 reliability debt">6.21 reliability debt</a>：matrix gap 回寫成 reliability debt</li>
</ul>
<h2 id="引用源">引用源</h2>
<ul>
<li><a href="https://shopify.engineering/four-steps-creating-effective-game-day-tests">Four Steps to Creating Effective Game Day Tests</a></li>
<li><a href="https://shopify.engineering/resiliency-planning-for-high-traffic-events">Resiliency Planning for High-Traffic Events</a></li>
<li><a href="https://shopify.engineering/a-pods-architecture-to-allow-shopify-to-scale">A Pods Architecture To Allow Shopify To Scale</a></li>
</ul>
]]></content:encoded></item><item><title>Pinterest：快取可靠性與容量驚奇治理</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/pinterest/cache-reliability-and-capacity-surprises/</link><pubDate>Thu, 07 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/pinterest/cache-reliability-and-capacity-surprises/</guid><description>&lt;p>Pinterest 案例的核心責任是處理快取層造成的容量驚奇。快取命中率下滑會在短時間放大到資料層與下游依賴，因此需要預先設計退化與重建節奏。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>流量高峰或快取失溫時，回源壓力會瞬間上升。若沒有緩衝機制與重建策略，系統容易進入連鎖退化。&lt;/p>
&lt;h2 id="決策機制">決策機制&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>機制&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>交付結果&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Cache headroom&lt;/td>
 &lt;td>命中率下滑能承受多久&lt;/td>
 &lt;td>容量緩衝&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Graceful degradation&lt;/td>
 &lt;td>快取失效時如何降級&lt;/td>
 &lt;td>服務連續性&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Rewarm strategy&lt;/td>
 &lt;td>熱資料如何有序回填&lt;/td>
 &lt;td>恢復節奏&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>cache hit ratio drop&lt;/td>
 &lt;td>是否進入危險區&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/capacity-cost/" data-link-title="6.9 容量與成本邊界" data-link-desc="把容量規劃跟成本約束變成驗證輸入">6.9&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>fallback latency&lt;/td>
 &lt;td>降級路徑是否可接受&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>rewarm backlog&lt;/td>
 &lt;td>回填是否可收斂&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/containment-recovery-strategy/" data-link-title="8.3 止血、降級與回復策略" data-link-desc="把短期止血與正式回復拆成可執行步驟">8.3&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>先在 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2&lt;/a> 模擬命中率崩落，再把恢復證據寫入 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23&lt;/a>。&lt;/p></description><content:encoded><![CDATA[<p>Pinterest 案例的核心責任是處理快取層造成的容量驚奇。快取命中率下滑會在短時間放大到資料層與下游依賴，因此需要預先設計退化與重建節奏。</p>
<h2 id="問題場景">問題場景</h2>
<p>流量高峰或快取失溫時，回源壓力會瞬間上升。若沒有緩衝機制與重建策略，系統容易進入連鎖退化。</p>
<h2 id="決策機制">決策機制</h2>
<table>
  <thead>
      <tr>
          <th>機制</th>
          <th>核心問題</th>
          <th>交付結果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Cache headroom</td>
          <td>命中率下滑能承受多久</td>
          <td>容量緩衝</td>
      </tr>
      <tr>
          <td>Graceful degradation</td>
          <td>快取失效時如何降級</td>
          <td>服務連續性</td>
      </tr>
      <tr>
          <td>Rewarm strategy</td>
          <td>熱資料如何有序回填</td>
          <td>恢復節奏</td>
      </tr>
  </tbody>
</table>
<h2 id="可觀測訊號">可觀測訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>cache hit ratio drop</td>
          <td>是否進入危險區</td>
          <td><a href="/blog/backend/06-reliability/capacity-cost/" data-link-title="6.9 容量與成本邊界" data-link-desc="把容量規劃跟成本約束變成驗證輸入">6.9</a></td>
      </tr>
      <tr>
          <td>fallback latency</td>
          <td>降級路徑是否可接受</td>
          <td><a href="/blog/backend/06-reliability/steady-state-definition/" data-link-title="6.22 Steady State Definition" data-link-desc="在 chaos 與 failover 前先定義系統應維持的穩定狀態與可接受退化">6.22</a></td>
      </tr>
      <tr>
          <td>rewarm backlog</td>
          <td>回填是否可收斂</td>
          <td><a href="/blog/backend/08-incident-response/containment-recovery-strategy/" data-link-title="8.3 止血、降級與回復策略" data-link-desc="把短期止血與正式回復拆成可執行步驟">8.3</a></td>
      </tr>
  </tbody>
</table>
<h2 id="下一步路由">下一步路由</h2>
<p>先在 <a href="/blog/backend/06-reliability/load-testing/" data-link-title="6.2 load test" data-link-desc="把 production 流量結構轉成可重播壓力情境，定位 saturation 轉折與容量邊界">6.2</a> 模擬命中率崩落，再把恢復證據寫入 <a href="/blog/backend/06-reliability/verification-evidence-handoff/" data-link-title="6.23 Verification Evidence Handoff" data-link-desc="把 SLO、load、chaos、DR 與 readiness 結果包成 release / incident 可用證據">6.23</a>。</p>
]]></content:encoded></item><item><title>Spotify：平台工程與可靠性契約</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/spotify/platform-engineering-and-reliability-contracts/</link><pubDate>Thu, 07 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/spotify/platform-engineering-and-reliability-contracts/</guid><description>&lt;p>Spotify 案例的核心責任是把可靠性標準平台化。當團隊自治程度高，若沒有共同契約，跨服務風險會在整合時爆發。&lt;/p>
&lt;h2 id="問題場景">問題場景&lt;/h2>
&lt;p>不同團隊採用不同部署與觀測習慣，單隊看似穩定，但跨服務路徑會出現隱性斷點，導致事故時難以協同定位。&lt;/p>
&lt;h2 id="決策機制">決策機制&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>機制&lt;/th>
 &lt;th>核心問題&lt;/th>
 &lt;th>交付結果&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Reliability contract&lt;/td>
 &lt;td>每個服務最低要提供什麼&lt;/td>
 &lt;td>基線能力&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Platform self-service&lt;/td>
 &lt;td>標準如何降低導入成本&lt;/td>
 &lt;td>擴散能力&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Cross-team evidence&lt;/td>
 &lt;td>證據如何跨團隊共享&lt;/td>
 &lt;td>協作效率&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="可觀測訊號">可觀測訊號&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>訊號&lt;/th>
 &lt;th>判讀重點&lt;/th>
 &lt;th>對應章節&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>contract compliance rate&lt;/td>
 &lt;td>契約覆蓋是否足夠&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/contract-testing/" data-link-title="6.10 Contract Testing 與 Schema 演進" data-link-desc="把跨服務 / API / event schema 的隱性期待變成可驗證契約，控制演進相容性">6.10&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>release dependency failures&lt;/td>
 &lt;td>依賴變更是否常破壞發布&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>cross-team incident handoff latency&lt;/td>
 &lt;td>交接是否有共同語言&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/incident-command-roles/" data-link-title="8.2 事故指揮與角色分工" data-link-desc="定義 incident commander 與跨角色協作責任">8.2&lt;/a>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;p>先補 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/contract-testing/" data-link-title="6.10 Contract Testing 與 Schema 演進" data-link-desc="把跨服務 / API / event schema 的隱性期待變成可驗證契約，控制演進相容性">6.10&lt;/a> 的契約欄位，再以 &lt;a href="https://tarrragon.github.io/blog/backend/04-observability/observability-operating-model/" data-link-title="4.18 Observability Operating Model" data-link-desc="定義 platform / service team / on-call 對訊號、dashboard、alert 與成本的 ownership">4.18&lt;/a> 對齊 owner 與責任邊界。&lt;/p></description><content:encoded><![CDATA[<p>Spotify 案例的核心責任是把可靠性標準平台化。當團隊自治程度高，若沒有共同契約，跨服務風險會在整合時爆發。</p>
<h2 id="問題場景">問題場景</h2>
<p>不同團隊採用不同部署與觀測習慣，單隊看似穩定，但跨服務路徑會出現隱性斷點，導致事故時難以協同定位。</p>
<h2 id="決策機制">決策機制</h2>
<table>
  <thead>
      <tr>
          <th>機制</th>
          <th>核心問題</th>
          <th>交付結果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Reliability contract</td>
          <td>每個服務最低要提供什麼</td>
          <td>基線能力</td>
      </tr>
      <tr>
          <td>Platform self-service</td>
          <td>標準如何降低導入成本</td>
          <td>擴散能力</td>
      </tr>
      <tr>
          <td>Cross-team evidence</td>
          <td>證據如何跨團隊共享</td>
          <td>協作效率</td>
      </tr>
  </tbody>
</table>
<h2 id="可觀測訊號">可觀測訊號</h2>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀重點</th>
          <th>對應章節</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>contract compliance rate</td>
          <td>契約覆蓋是否足夠</td>
          <td><a href="/blog/backend/06-reliability/contract-testing/" data-link-title="6.10 Contract Testing 與 Schema 演進" data-link-desc="把跨服務 / API / event schema 的隱性期待變成可驗證契約，控制演進相容性">6.10</a></td>
      </tr>
      <tr>
          <td>release dependency failures</td>
          <td>依賴變更是否常破壞發布</td>
          <td><a href="/blog/backend/06-reliability/dependency-reliability-budget/" data-link-title="6.14 Dependency Reliability Budget" data-link-desc="把內外依賴的可靠性納入 SLO 計算與設計約束">6.14</a></td>
      </tr>
      <tr>
          <td>cross-team incident handoff latency</td>
          <td>交接是否有共同語言</td>
          <td><a href="/blog/backend/08-incident-response/incident-command-roles/" data-link-title="8.2 事故指揮與角色分工" data-link-desc="定義 incident commander 與跨角色協作責任">8.2</a></td>
      </tr>
  </tbody>
</table>
<h2 id="下一步路由">下一步路由</h2>
<p>先補 <a href="/blog/backend/06-reliability/contract-testing/" data-link-title="6.10 Contract Testing 與 Schema 演進" data-link-desc="把跨服務 / API / event schema 的隱性期待變成可驗證契約，控制演進相容性">6.10</a> 的契約欄位，再以 <a href="/blog/backend/04-observability/observability-operating-model/" data-link-title="4.18 Observability Operating Model" data-link-desc="定義 platform / service team / on-call 對訊號、dashboard、alert 與成本的 ownership">4.18</a> 對齊 owner 與責任邊界。</p>
]]></content:encoded></item><item><title>可靠性服務案例庫</title><link>https://tarrragon.github.io/blog/backend/06-reliability/cases/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/cases/</guid><description>&lt;p>本案例庫以服務為單位、收錄公開 SRE 實踐（SRE Book / 工程部落格 / 演講 / paper）。每個服務一個資料夾，累積該服務的可靠性工程文化、failure mode 與 chaos / DR 案例。&lt;/p>
&lt;p>服務分層依 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/" data-link-title="模組六：可靠性驗證流程" data-link-desc="用 SRE 領域詞彙建問題節點、以服務級案例庫累積驗證脈絡，先建概念與案例庫再進實作交接">模組六 _index&lt;/a> 的 T1 / T2 / T3 規劃。重複出現於 06 / 08 的服務（stripe / cloudflare / linkedin）資料夾住在主要教學模組、跨模組以連結互通。&lt;/p>
&lt;h2 id="t1-服務">T1 服務&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/google/" data-link-title="Google" data-link-desc="Google SRE 實踐原典：SLI / SLO / Error Budget / Postmortem 文化">google&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/netflix/" data-link-title="Netflix" data-link-desc="Netflix Chaos Engineering 起源：Simian Army / FIT / 規模化故障注入">netflix&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/amazon/" data-link-title="Amazon" data-link-desc="Amazon Cell-based Architecture / Shuffle Sharding / Blast Radius 設計">amazon&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/stripe/" data-link-title="Stripe" data-link-desc="Stripe Deploy Strategy / Game Day / Idempotency 實踐">stripe&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/shopify/" data-link-title="Shopify" data-link-desc="Shopify BFCM Scaling / Pod-based Isolation / Capacity Planning">shopify&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="t1-第一批正文已完成">T1 第一批正文（已完成）&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>服務&lt;/th>
 &lt;th>正文入口&lt;/th>
 &lt;th>主題重點&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Google&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/google/error-budget-policy-and-release-gating/" data-link-title="Google：Error Budget 政策如何決定發布節奏" data-link-desc="把 SLO 消耗量轉成 release gate，讓可靠性與交付速度共用同一套決策語言。">G1 Error Budget 與 Release Gating&lt;/a>&lt;/td>
 &lt;td>可靠性消耗如何直接決定發布節奏&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Netflix&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/netflix/steady-state-chaos-and-fit/" data-link-title="Netflix：Steady State、Chaos 與 FIT 的驗證路徑" data-link-desc="把故障注入從工具操作升級成可驗證流程：先定義穩態，再設計注入與回復條件。">N1 Steady State、Chaos 與 FIT&lt;/a>&lt;/td>
 &lt;td>故障注入如何變成可證偽流程&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Amazon&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/amazon/shuffle-sharding-and-cell-boundary/" data-link-title="Amazon：Shuffle Sharding 與 Cell 邊界的失效局部化" data-link-desc="用 cell 與 shuffle sharding 將多租戶故障限制在局部，讓恢復策略可分批執行。">A1 Shuffle Sharding 與 Cell 邊界&lt;/a>&lt;/td>
 &lt;td>多租戶故障如何被局部化&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Stripe&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/" data-link-title="Stripe：Idempotency 與零停機遷移的交易安全設計" data-link-desc="把 API 重試與資料遷移放在同一套安全模型，維持支付交易的一致結果。">S1 Idempotency 與零停機遷移&lt;/a>&lt;/td>
 &lt;td>交易重試與遷移如何共用一致性模型&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Shopify&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/shopify/bfcm-capacity-and-game-day/" data-link-title="Shopify：BFCM 容量治理與 Game Day 驗證節奏" data-link-desc="把季節性流量峰值轉成年度可靠性流程，透過容量模型、演練與隔離策略提前吸收風險。">H1 BFCM 容量治理與 Game Day&lt;/a>&lt;/td>
 &lt;td>峰值風險如何在活動前被消化&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="t1-第二批正文已完成">T1 第二批正文（已完成）&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>服務&lt;/th>
 &lt;th>正文入口&lt;/th>
 &lt;th>主題重點&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Amazon&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/amazon/static-stability-and-constant-work/" data-link-title="Amazon：Static Stability 與 Constant Work Pattern" data-link-desc="控制面失效時資料面如何維持服務：用快取、預計算與固定工作量避免恢復放大。">A2 Static Stability 與 Constant Work&lt;/a>&lt;/td>
 &lt;td>控制面失效時資料面如何維持服務&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Stripe&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/stripe/canary-deploy-and-progressive-rollout/" data-link-title="Stripe：Canary Deploy 與 Progressive Rollout 治理" data-link-desc="金流場景如何用交易指標驅動放行節奏：延遲確認、duplicate 偵測與自動回退。">S2 Canary Deploy 與 Progressive Rollout&lt;/a>&lt;/td>
 &lt;td>金流場景的放行節奏與交易指標驅動&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Shopify&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/shopify/pod-architecture-and-resiliency-matrix/" data-link-title="Shopify：Pod Architecture 與 Resiliency Matrix" data-link-desc="多租戶隔離與系統化失敗模式盤點：pod 邊界控制擴散、resiliency matrix 驅動演練。">H2 Pod Architecture 與 Resiliency Matrix&lt;/a>&lt;/td>
 &lt;td>多租戶隔離與系統化失敗模式盤點&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="t1-深挖批次已完成">T1 深挖批次（已完成）&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>服務&lt;/th>
 &lt;th>正文入口&lt;/th>
 &lt;th>主題重點&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Google&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/google/postmortem-action-item-closure-governance/" data-link-title="Google：Postmortem Action Item Closure 治理" data-link-desc="把 blameless postmortem 從會議文件變成可追蹤的可靠性治理機制：action item 分級、完成條件與回寫節奏。">G2 Postmortem Action Item Closure 治理&lt;/a>&lt;/td>
 &lt;td>事故教訓如何轉成有 owner 的改進項&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Google&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/google/toil-budget-and-automation-investment-policy/" data-link-title="Google：Toil Budget 與 Automation 投資政策" data-link-desc="把 toil 從感受問題轉成預算問題：用時間配比與自動化回報機制，避免 on-call 壓力長期侵蝕可靠性工程。">G3 Toil Budget 與 Automation 投資政策&lt;/a>&lt;/td>
 &lt;td>值班壓力如何轉成工程投資決策&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Netflix&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/netflix/chaos-monkey-business-hours-guardrails/" data-link-title="Netflix：Business-Hours Chaos 與 Guardrails" data-link-desc="Chaos Monkey 為何刻意在 business hours 執行：把即時應變能力納入驗證，並用 guardrails 限制實驗風險。">N2 Business-Hours Chaos Guardrails&lt;/a>&lt;/td>
 &lt;td>business hours 故障注入的安全邊界設計&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Netflix&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/netflix/fit-failure-injection-evidence-handoff/" data-link-title="Netflix：FIT 證據交接與 Release Gate 回寫" data-link-desc="用 Failure Injection Testing 產出的證據直接驅動 release gate：把實驗結果轉成可放行、可凍結、可回退的決策欄位。">N3 FIT 證據交接與 Release Gate 回寫&lt;/a>&lt;/td>
 &lt;td>故障注入結果如何結構化驅動放行決策&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="t2-服務">T2 服務&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/linkedin/" data-link-title="LinkedIn" data-link-desc="LinkedIn Capacity Planning 與 On-call 結構">linkedin&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/honeycomb/" data-link-title="Honeycomb" data-link-desc="Honeycomb Observability-driven SRE 與 SLO 實作">honeycomb&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/cases/cloudflare/" data-link-title="Cloudflare" data-link-desc="Cloudflare 全球 edge 事故時間線與架構脈絡">cloudflare（住於 08）&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/microsoft/" data-link-title="Microsoft / Azure SRE" data-link-desc="Microsoft Azure SRE Practices 與 Resilience Patterns">microsoft&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="t2t3-第一批正文已完成">T2/T3 第一批正文（已完成）&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>服務&lt;/th>
 &lt;th>正文入口&lt;/th>
 &lt;th>主題重點&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>LinkedIn&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/linkedin/capacity-headroom-and-oncall-tiering/" data-link-title="LinkedIn：Capacity Headroom 與 On-call 分層" data-link-desc="把容量預測與值班分層綁在一起，降低高峰時段的升級混亂與恢復延遲。">L1 Capacity 與 On-call 分層&lt;/a>&lt;/td>
 &lt;td>容量邊界與值班交接協同&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Honeycomb&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/honeycomb/burn-rate-driven-reliability-operations/" data-link-title="Honeycomb：以 Burn Rate 驅動的可靠性操作" data-link-desc="把 SLO burn rate 直接連到值班決策與改善優先序，降低高噪音告警造成的判讀失真。">HC1 Burn Rate 驅動可靠性&lt;/a>&lt;/td>
 &lt;td>用 SLO 消耗速度驅動行動&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Microsoft&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/microsoft/change-management-and-reliability-governance/" data-link-title="Microsoft：變更治理與可靠性門檻" data-link-desc="透過分層變更管理與發布閘門，降低大型 SaaS 平台的系統性回歸風險。">MS1 變更治理與可靠性門檻&lt;/a>&lt;/td>
 &lt;td>變更分層與 release gate&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Spotify&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/spotify/platform-engineering-and-reliability-contracts/" data-link-title="Spotify：平台工程與可靠性契約" data-link-desc="用平台契約統一服務團隊的可靠性最低標準，降低跨團隊變更造成的隱性風險。">SP1 平台工程與可靠性契約&lt;/a>&lt;/td>
 &lt;td>分散團隊共用可靠性基線&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Pinterest&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/pinterest/cache-reliability-and-capacity-surprises/" data-link-title="Pinterest：快取可靠性與容量驚奇治理" data-link-desc="針對快取層失效與流量突增，建立容量緩衝、退化路徑與重建節奏。">P1 快取可靠性與容量驚奇&lt;/a>&lt;/td>
 &lt;td>命中率崩落時的恢復節奏&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Meta&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/meta/region-failover-and-reliability-boundaries/" data-link-title="Meta：Region Failover 與可靠性邊界" data-link-desc="把跨區故障視為邊界治理問題，透過分區隔離與回復順序控制失效擴散。">M1 Region Failover 邊界治理&lt;/a>&lt;/td>
 &lt;td>跨區擴散與回復順序治理&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="t2t3-第二批正文已完成">T2/T3 第二批正文（已完成）&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>服務&lt;/th>
 &lt;th>正文入口&lt;/th>
 &lt;th>主題重點&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>LinkedIn&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/linkedin/automated-load-testing-and-capacity-forecasting/" data-link-title="LinkedIn：Automated Load Testing 與 Capacity Forecasting" data-link-desc="持續壓測驅動容量預測：用自動化回饋取代一次性壓測的容量規劃。">L2 Automated Load Testing 與 Capacity Forecasting&lt;/a>&lt;/td>
 &lt;td>持續壓測驅動容量預測&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Meta&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/meta/bgp-control-plane-recovery-ordering/" data-link-title="Meta：BGP 事故與控制面恢復順序" data-link-desc="當回復工具依賴已故障的系統：2021-10 事故揭露控制面恢復順序與 out-of-band 存取的設計約束。">M2 BGP 事故與控制面恢復順序&lt;/a>&lt;/td>
 &lt;td>回復工具依賴已故障系統的恢復困境&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Honeycomb&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/honeycomb/production-excellence-and-test-in-production/" data-link-title="Honeycomb：Production Excellence 與 Test in Production" data-link-desc="用 high-cardinality observability 把 production 變成安全的驗證環境：feature flag、progressive rollout 與即時回饋的配合。">HC2 Production Excellence 與 Test in Production&lt;/a>&lt;/td>
 &lt;td>observability-driven 生產驗證文化&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Microsoft&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/microsoft/safe-deployment-practices-and-resilience-patterns/" data-link-title="Microsoft：Safe Deployment Practices 與 Resilience Patterns" data-link-desc="大型 SaaS 用 ring-based deployment 控制變更擴散，用標準化 resilience patterns 讓依賴失效時的降級行為可預測。">MS2 Safe Deployment Practices 與 Resilience Patterns&lt;/a>&lt;/td>
 &lt;td>ring-based deployment 與韌性設計模式&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Spotify&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/spotify/backstage-service-catalog-and-reliability-metadata/" data-link-title="Spotify：Backstage Service Catalog 與 Reliability Metadata" data-link-desc="用 service catalog 治理分散團隊的可靠性資訊：ownership、SLO 狀態、依賴圖與 runbook 的單一入口。">SP2 Backstage Service Catalog 與 Reliability Metadata&lt;/a>&lt;/td>
 &lt;td>service catalog 治理可靠性資訊&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Pinterest&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/pinterest/storage-migration-and-data-infrastructure-reliability/" data-link-title="Pinterest：Storage Migration 與 Data Infrastructure Reliability" data-link-desc="大規模儲存遷移的可靠性設計：用 dual-write、shadow read 與 staged cutover 讓 PB 級資料基礎設施變更可漸進、可驗證、可回退。">P2 Storage Migration 與 Data Infrastructure Reliability&lt;/a>&lt;/td>
 &lt;td>大規模儲存遷移的驗證流程&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="t3-服務">T3 服務&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/spotify/" data-link-title="Spotify" data-link-desc="Spotify Chaos Engineering 與 Squad-based SRE">spotify&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/pinterest/" data-link-title="Pinterest" data-link-desc="Pinterest Capacity Planning 與儲存架構可靠性">pinterest&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/meta/" data-link-title="Meta / Facebook" data-link-desc="Meta Reliability Engineering 與超大規模事故學習">meta&lt;/a>&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>本案例庫以服務為單位、收錄公開 SRE 實踐（SRE Book / 工程部落格 / 演講 / paper）。每個服務一個資料夾，累積該服務的可靠性工程文化、failure mode 與 chaos / DR 案例。</p>
<p>服務分層依 <a href="/blog/backend/06-reliability/" data-link-title="模組六：可靠性驗證流程" data-link-desc="用 SRE 領域詞彙建問題節點、以服務級案例庫累積驗證脈絡，先建概念與案例庫再進實作交接">模組六 _index</a> 的 T1 / T2 / T3 規劃。重複出現於 06 / 08 的服務（stripe / cloudflare / linkedin）資料夾住在主要教學模組、跨模組以連結互通。</p>
<h2 id="t1-服務">T1 服務</h2>
<ul>
<li><a href="/blog/backend/06-reliability/cases/google/" data-link-title="Google" data-link-desc="Google SRE 實踐原典：SLI / SLO / Error Budget / Postmortem 文化">google</a></li>
<li><a href="/blog/backend/06-reliability/cases/netflix/" data-link-title="Netflix" data-link-desc="Netflix Chaos Engineering 起源：Simian Army / FIT / 規模化故障注入">netflix</a></li>
<li><a href="/blog/backend/06-reliability/cases/amazon/" data-link-title="Amazon" data-link-desc="Amazon Cell-based Architecture / Shuffle Sharding / Blast Radius 設計">amazon</a></li>
<li><a href="/blog/backend/06-reliability/cases/stripe/" data-link-title="Stripe" data-link-desc="Stripe Deploy Strategy / Game Day / Idempotency 實踐">stripe</a></li>
<li><a href="/blog/backend/06-reliability/cases/shopify/" data-link-title="Shopify" data-link-desc="Shopify BFCM Scaling / Pod-based Isolation / Capacity Planning">shopify</a></li>
</ul>
<h2 id="t1-第一批正文已完成">T1 第一批正文（已完成）</h2>
<table>
  <thead>
      <tr>
          <th>服務</th>
          <th>正文入口</th>
          <th>主題重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Google</td>
          <td><a href="/blog/backend/06-reliability/cases/google/error-budget-policy-and-release-gating/" data-link-title="Google：Error Budget 政策如何決定發布節奏" data-link-desc="把 SLO 消耗量轉成 release gate，讓可靠性與交付速度共用同一套決策語言。">G1 Error Budget 與 Release Gating</a></td>
          <td>可靠性消耗如何直接決定發布節奏</td>
      </tr>
      <tr>
          <td>Netflix</td>
          <td><a href="/blog/backend/06-reliability/cases/netflix/steady-state-chaos-and-fit/" data-link-title="Netflix：Steady State、Chaos 與 FIT 的驗證路徑" data-link-desc="把故障注入從工具操作升級成可驗證流程：先定義穩態，再設計注入與回復條件。">N1 Steady State、Chaos 與 FIT</a></td>
          <td>故障注入如何變成可證偽流程</td>
      </tr>
      <tr>
          <td>Amazon</td>
          <td><a href="/blog/backend/06-reliability/cases/amazon/shuffle-sharding-and-cell-boundary/" data-link-title="Amazon：Shuffle Sharding 與 Cell 邊界的失效局部化" data-link-desc="用 cell 與 shuffle sharding 將多租戶故障限制在局部，讓恢復策略可分批執行。">A1 Shuffle Sharding 與 Cell 邊界</a></td>
          <td>多租戶故障如何被局部化</td>
      </tr>
      <tr>
          <td>Stripe</td>
          <td><a href="/blog/backend/06-reliability/cases/stripe/idempotency-and-zero-downtime-migration/" data-link-title="Stripe：Idempotency 與零停機遷移的交易安全設計" data-link-desc="把 API 重試與資料遷移放在同一套安全模型，維持支付交易的一致結果。">S1 Idempotency 與零停機遷移</a></td>
          <td>交易重試與遷移如何共用一致性模型</td>
      </tr>
      <tr>
          <td>Shopify</td>
          <td><a href="/blog/backend/06-reliability/cases/shopify/bfcm-capacity-and-game-day/" data-link-title="Shopify：BFCM 容量治理與 Game Day 驗證節奏" data-link-desc="把季節性流量峰值轉成年度可靠性流程，透過容量模型、演練與隔離策略提前吸收風險。">H1 BFCM 容量治理與 Game Day</a></td>
          <td>峰值風險如何在活動前被消化</td>
      </tr>
  </tbody>
</table>
<h2 id="t1-第二批正文已完成">T1 第二批正文（已完成）</h2>
<table>
  <thead>
      <tr>
          <th>服務</th>
          <th>正文入口</th>
          <th>主題重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Amazon</td>
          <td><a href="/blog/backend/06-reliability/cases/amazon/static-stability-and-constant-work/" data-link-title="Amazon：Static Stability 與 Constant Work Pattern" data-link-desc="控制面失效時資料面如何維持服務：用快取、預計算與固定工作量避免恢復放大。">A2 Static Stability 與 Constant Work</a></td>
          <td>控制面失效時資料面如何維持服務</td>
      </tr>
      <tr>
          <td>Stripe</td>
          <td><a href="/blog/backend/06-reliability/cases/stripe/canary-deploy-and-progressive-rollout/" data-link-title="Stripe：Canary Deploy 與 Progressive Rollout 治理" data-link-desc="金流場景如何用交易指標驅動放行節奏：延遲確認、duplicate 偵測與自動回退。">S2 Canary Deploy 與 Progressive Rollout</a></td>
          <td>金流場景的放行節奏與交易指標驅動</td>
      </tr>
      <tr>
          <td>Shopify</td>
          <td><a href="/blog/backend/06-reliability/cases/shopify/pod-architecture-and-resiliency-matrix/" data-link-title="Shopify：Pod Architecture 與 Resiliency Matrix" data-link-desc="多租戶隔離與系統化失敗模式盤點：pod 邊界控制擴散、resiliency matrix 驅動演練。">H2 Pod Architecture 與 Resiliency Matrix</a></td>
          <td>多租戶隔離與系統化失敗模式盤點</td>
      </tr>
  </tbody>
</table>
<h2 id="t1-深挖批次已完成">T1 深挖批次（已完成）</h2>
<table>
  <thead>
      <tr>
          <th>服務</th>
          <th>正文入口</th>
          <th>主題重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Google</td>
          <td><a href="/blog/backend/06-reliability/cases/google/postmortem-action-item-closure-governance/" data-link-title="Google：Postmortem Action Item Closure 治理" data-link-desc="把 blameless postmortem 從會議文件變成可追蹤的可靠性治理機制：action item 分級、完成條件與回寫節奏。">G2 Postmortem Action Item Closure 治理</a></td>
          <td>事故教訓如何轉成有 owner 的改進項</td>
      </tr>
      <tr>
          <td>Google</td>
          <td><a href="/blog/backend/06-reliability/cases/google/toil-budget-and-automation-investment-policy/" data-link-title="Google：Toil Budget 與 Automation 投資政策" data-link-desc="把 toil 從感受問題轉成預算問題：用時間配比與自動化回報機制，避免 on-call 壓力長期侵蝕可靠性工程。">G3 Toil Budget 與 Automation 投資政策</a></td>
          <td>值班壓力如何轉成工程投資決策</td>
      </tr>
      <tr>
          <td>Netflix</td>
          <td><a href="/blog/backend/06-reliability/cases/netflix/chaos-monkey-business-hours-guardrails/" data-link-title="Netflix：Business-Hours Chaos 與 Guardrails" data-link-desc="Chaos Monkey 為何刻意在 business hours 執行：把即時應變能力納入驗證，並用 guardrails 限制實驗風險。">N2 Business-Hours Chaos Guardrails</a></td>
          <td>business hours 故障注入的安全邊界設計</td>
      </tr>
      <tr>
          <td>Netflix</td>
          <td><a href="/blog/backend/06-reliability/cases/netflix/fit-failure-injection-evidence-handoff/" data-link-title="Netflix：FIT 證據交接與 Release Gate 回寫" data-link-desc="用 Failure Injection Testing 產出的證據直接驅動 release gate：把實驗結果轉成可放行、可凍結、可回退的決策欄位。">N3 FIT 證據交接與 Release Gate 回寫</a></td>
          <td>故障注入結果如何結構化驅動放行決策</td>
      </tr>
  </tbody>
</table>
<h2 id="t2-服務">T2 服務</h2>
<ul>
<li><a href="/blog/backend/06-reliability/cases/linkedin/" data-link-title="LinkedIn" data-link-desc="LinkedIn Capacity Planning 與 On-call 結構">linkedin</a></li>
<li><a href="/blog/backend/06-reliability/cases/honeycomb/" data-link-title="Honeycomb" data-link-desc="Honeycomb Observability-driven SRE 與 SLO 實作">honeycomb</a></li>
<li><a href="/blog/backend/08-incident-response/cases/cloudflare/" data-link-title="Cloudflare" data-link-desc="Cloudflare 全球 edge 事故時間線與架構脈絡">cloudflare（住於 08）</a></li>
<li><a href="/blog/backend/06-reliability/cases/microsoft/" data-link-title="Microsoft / Azure SRE" data-link-desc="Microsoft Azure SRE Practices 與 Resilience Patterns">microsoft</a></li>
</ul>
<h2 id="t2t3-第一批正文已完成">T2/T3 第一批正文（已完成）</h2>
<table>
  <thead>
      <tr>
          <th>服務</th>
          <th>正文入口</th>
          <th>主題重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>LinkedIn</td>
          <td><a href="/blog/backend/06-reliability/cases/linkedin/capacity-headroom-and-oncall-tiering/" data-link-title="LinkedIn：Capacity Headroom 與 On-call 分層" data-link-desc="把容量預測與值班分層綁在一起，降低高峰時段的升級混亂與恢復延遲。">L1 Capacity 與 On-call 分層</a></td>
          <td>容量邊界與值班交接協同</td>
      </tr>
      <tr>
          <td>Honeycomb</td>
          <td><a href="/blog/backend/06-reliability/cases/honeycomb/burn-rate-driven-reliability-operations/" data-link-title="Honeycomb：以 Burn Rate 驅動的可靠性操作" data-link-desc="把 SLO burn rate 直接連到值班決策與改善優先序，降低高噪音告警造成的判讀失真。">HC1 Burn Rate 驅動可靠性</a></td>
          <td>用 SLO 消耗速度驅動行動</td>
      </tr>
      <tr>
          <td>Microsoft</td>
          <td><a href="/blog/backend/06-reliability/cases/microsoft/change-management-and-reliability-governance/" data-link-title="Microsoft：變更治理與可靠性門檻" data-link-desc="透過分層變更管理與發布閘門，降低大型 SaaS 平台的系統性回歸風險。">MS1 變更治理與可靠性門檻</a></td>
          <td>變更分層與 release gate</td>
      </tr>
      <tr>
          <td>Spotify</td>
          <td><a href="/blog/backend/06-reliability/cases/spotify/platform-engineering-and-reliability-contracts/" data-link-title="Spotify：平台工程與可靠性契約" data-link-desc="用平台契約統一服務團隊的可靠性最低標準，降低跨團隊變更造成的隱性風險。">SP1 平台工程與可靠性契約</a></td>
          <td>分散團隊共用可靠性基線</td>
      </tr>
      <tr>
          <td>Pinterest</td>
          <td><a href="/blog/backend/06-reliability/cases/pinterest/cache-reliability-and-capacity-surprises/" data-link-title="Pinterest：快取可靠性與容量驚奇治理" data-link-desc="針對快取層失效與流量突增，建立容量緩衝、退化路徑與重建節奏。">P1 快取可靠性與容量驚奇</a></td>
          <td>命中率崩落時的恢復節奏</td>
      </tr>
      <tr>
          <td>Meta</td>
          <td><a href="/blog/backend/06-reliability/cases/meta/region-failover-and-reliability-boundaries/" data-link-title="Meta：Region Failover 與可靠性邊界" data-link-desc="把跨區故障視為邊界治理問題，透過分區隔離與回復順序控制失效擴散。">M1 Region Failover 邊界治理</a></td>
          <td>跨區擴散與回復順序治理</td>
      </tr>
  </tbody>
</table>
<h2 id="t2t3-第二批正文已完成">T2/T3 第二批正文（已完成）</h2>
<table>
  <thead>
      <tr>
          <th>服務</th>
          <th>正文入口</th>
          <th>主題重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>LinkedIn</td>
          <td><a href="/blog/backend/06-reliability/cases/linkedin/automated-load-testing-and-capacity-forecasting/" data-link-title="LinkedIn：Automated Load Testing 與 Capacity Forecasting" data-link-desc="持續壓測驅動容量預測：用自動化回饋取代一次性壓測的容量規劃。">L2 Automated Load Testing 與 Capacity Forecasting</a></td>
          <td>持續壓測驅動容量預測</td>
      </tr>
      <tr>
          <td>Meta</td>
          <td><a href="/blog/backend/06-reliability/cases/meta/bgp-control-plane-recovery-ordering/" data-link-title="Meta：BGP 事故與控制面恢復順序" data-link-desc="當回復工具依賴已故障的系統：2021-10 事故揭露控制面恢復順序與 out-of-band 存取的設計約束。">M2 BGP 事故與控制面恢復順序</a></td>
          <td>回復工具依賴已故障系統的恢復困境</td>
      </tr>
      <tr>
          <td>Honeycomb</td>
          <td><a href="/blog/backend/06-reliability/cases/honeycomb/production-excellence-and-test-in-production/" data-link-title="Honeycomb：Production Excellence 與 Test in Production" data-link-desc="用 high-cardinality observability 把 production 變成安全的驗證環境：feature flag、progressive rollout 與即時回饋的配合。">HC2 Production Excellence 與 Test in Production</a></td>
          <td>observability-driven 生產驗證文化</td>
      </tr>
      <tr>
          <td>Microsoft</td>
          <td><a href="/blog/backend/06-reliability/cases/microsoft/safe-deployment-practices-and-resilience-patterns/" data-link-title="Microsoft：Safe Deployment Practices 與 Resilience Patterns" data-link-desc="大型 SaaS 用 ring-based deployment 控制變更擴散，用標準化 resilience patterns 讓依賴失效時的降級行為可預測。">MS2 Safe Deployment Practices 與 Resilience Patterns</a></td>
          <td>ring-based deployment 與韌性設計模式</td>
      </tr>
      <tr>
          <td>Spotify</td>
          <td><a href="/blog/backend/06-reliability/cases/spotify/backstage-service-catalog-and-reliability-metadata/" data-link-title="Spotify：Backstage Service Catalog 與 Reliability Metadata" data-link-desc="用 service catalog 治理分散團隊的可靠性資訊：ownership、SLO 狀態、依賴圖與 runbook 的單一入口。">SP2 Backstage Service Catalog 與 Reliability Metadata</a></td>
          <td>service catalog 治理可靠性資訊</td>
      </tr>
      <tr>
          <td>Pinterest</td>
          <td><a href="/blog/backend/06-reliability/cases/pinterest/storage-migration-and-data-infrastructure-reliability/" data-link-title="Pinterest：Storage Migration 與 Data Infrastructure Reliability" data-link-desc="大規模儲存遷移的可靠性設計：用 dual-write、shadow read 與 staged cutover 讓 PB 級資料基礎設施變更可漸進、可驗證、可回退。">P2 Storage Migration 與 Data Infrastructure Reliability</a></td>
          <td>大規模儲存遷移的驗證流程</td>
      </tr>
  </tbody>
</table>
<h2 id="t3-服務">T3 服務</h2>
<ul>
<li><a href="/blog/backend/06-reliability/cases/spotify/" data-link-title="Spotify" data-link-desc="Spotify Chaos Engineering 與 Squad-based SRE">spotify</a></li>
<li><a href="/blog/backend/06-reliability/cases/pinterest/" data-link-title="Pinterest" data-link-desc="Pinterest Capacity Planning 與儲存架構可靠性">pinterest</a></li>
<li><a href="/blog/backend/06-reliability/cases/meta/" data-link-title="Meta / Facebook" data-link-desc="Meta Reliability Engineering 與超大規模事故學習">meta</a></li>
</ul>
]]></content:encoded></item><item><title>可靠性 Vendor 清單</title><link>https://tarrragon.github.io/blog/backend/06-reliability/vendors/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/06-reliability/vendors/</guid><description>&lt;p>可靠性 Vendor 清單的核心責任是把工具名稱放回 verification loop、release gate、fault injection、SLO governance 與 evidence handoff 的判斷。每個服務頁先回答它承擔哪一種可靠性驗證責任，再討論整合成本、風險控制、artifact 與案例回寫。&lt;/p>
&lt;p>跟 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/cases/" data-link-title="可靠性服務案例庫" data-link-desc="按服務組織的 SRE 實踐案例庫，累積架構脈絡與工程文化">cases/&lt;/a> 是不同維度。Cases 是教學案例來源，vendors 是把驗證流程落地的工具入口。&lt;/p>
&lt;h2 id="讀法">讀法&lt;/h2>
&lt;p>可靠性工具要從驗證流程進入。讀者如果要處理 release gate，先回到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate&lt;/a>；如果要處理 load test 與 regression，先回到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13 Performance Regression Gate&lt;/a>；如果要處理 chaos，先回到 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 Experiment Safety Boundary&lt;/a>。&lt;/p>
&lt;h2 id="教學順序同步">教學順序同步&lt;/h2>
&lt;p>可靠性工具頁的教學順序是先建立 CI / release gate，再進入 load test、chaos / fault injection 與 SLO governance。這個順序對齊 checkout E5：讀者先理解變更如何被放行與停止，再比較哪些工具產生 regression evidence、experiment evidence 與 error budget evidence。&lt;/p>
&lt;h2 id="t1-服務頁大綱">T1 服務頁大綱&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>服務&lt;/th>
 &lt;th>類型&lt;/th>
 &lt;th>頁面要回答的核心問題&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/vendors/github-actions/" data-link-title="GitHub Actions" data-link-desc="GitHub 原生 CI/CD、PR check、deploy gate">GitHub Actions&lt;/a>&lt;/td>
 &lt;td>CI/CD&lt;/td>
 &lt;td>workflow、environment、artifact 與 approval gate 如何支援 release evidence&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/vendors/circleci/" data-link-title="CircleCI" data-link-desc="CI/CD 平台、強 cache 與 parallelism">CircleCI&lt;/a>&lt;/td>
 &lt;td>CI/CD&lt;/td>
 &lt;td>pipeline、orb、parallelism 與 context 權限如何取捨&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/vendors/k6/" data-link-title="k6" data-link-desc="現代 load test、JS scripting、Grafana Labs">k6&lt;/a>&lt;/td>
 &lt;td>Load test&lt;/td>
 &lt;td>scenario、threshold 與 CI gate 如何支援可靠性驗證&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/vendors/gatling/" data-link-title="Gatling" data-link-desc="JVM-based load test、Scala / Java / Kotlin DSL、強型別 scenario、HAR-driven recording">Gatling&lt;/a>&lt;/td>
 &lt;td>Load test&lt;/td>
 &lt;td>JVM simulation、injection profile 與 report 如何支援 regression gate&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/vendors/jmeter/" data-link-title="Apache JMeter" data-link-desc="老牌 load test 工具、GUI &amp;#43; plugins">JMeter&lt;/a>&lt;/td>
 &lt;td>Load test&lt;/td>
 &lt;td>GUI plan、protocol sampler 與既有測試資產如何治理&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/vendors/locust/" data-link-title="Locust" data-link-desc="Python-based load test、distributed、易擴展">Locust&lt;/a>&lt;/td>
 &lt;td>Load test&lt;/td>
 &lt;td>Python user behavior 與 distributed worker 如何支援自訂 workload&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/vendors/chaos-mesh/" data-link-title="Chaos Mesh" data-link-desc="Kubernetes-native chaos engineering（CNCF incubating）">Chaos Mesh&lt;/a>&lt;/td>
 &lt;td>Chaos engineering&lt;/td>
 &lt;td>Kubernetes-native fault injection 與 experiment scope 如何控制&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/vendors/litmuschaos/" data-link-title="LitmusChaos" data-link-desc="Kubernetes chaos engineering 平台（CNCF graduated）">LitmusChaos&lt;/a>&lt;/td>
 &lt;td>Chaos engineering&lt;/td>
 &lt;td>chaos workflow、hub 與 Kubernetes 實驗治理如何取捨&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/vendors/gremlin/" data-link-title="Gremlin" data-link-desc="商業 chaos engineering 平台、跨平台與 GameDay">Gremlin&lt;/a>&lt;/td>
 &lt;td>Chaos platform&lt;/td>
 &lt;td>商業 chaos 平台、blast radius guardrail 與審計如何支援成熟團隊&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/vendors/toxiproxy/" data-link-title="Toxiproxy" data-link-desc="TCP-level fault injection proxy（Shopify 開源）">Toxiproxy&lt;/a>&lt;/td>
 &lt;td>Fault injection&lt;/td>
 &lt;td>TCP fault、local integration test 與 dependency failure 如何模擬&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/vendors/nobl9/" data-link-title="Nobl9" data-link-desc="SLO platform、跨 data source、企業 SLO 治理">Nobl9&lt;/a>&lt;/td>
 &lt;td>SLO platform&lt;/td>
 &lt;td>SLO、error budget、alerting 與 governance 如何整合&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/vendors/sloth/" data-link-title="Sloth" data-link-desc="OSS SLO generator for Prometheus">Sloth&lt;/a>&lt;/td>
 &lt;td>SLO generator&lt;/td>
 &lt;td>OpenSLO / Prometheus rule 生成如何降低 SLO 維護成本&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="內容覆蓋進度">內容覆蓋進度&lt;/h2>
&lt;p>每個 vendor 服務頁下會擴充兩類文章：deep article（vendor 自身的配置、故障、容量、走 &lt;a href="https://tarrragon.github.io/blog/posts/vendor-%E6%B7%B1%E5%BA%A6%E6%8A%80%E8%A1%93%E6%96%87%E7%AB%A0%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84%E5%90%8C-vendor-%E7%B3%BB%E5%88%97%E7%9A%84%E9%96%8B%E5%A0%B4%E8%BC%AA%E6%9B%BF%E9%A9%97%E8%AD%89/" data-link-title="Vendor 深度技術文章方法論的演化紀錄：同 vendor 系列的開場輪替驗證" data-link-desc="vendor overview 飽和後要寫單一功能深度文章、需要選題與結構依據時回來。這套方法論的驗證來源與 cadence variant 在高風險場景（同 vendor sub-tool 系列）的實證。">6-section 模板&lt;/a>）跟 migration playbook（跨 vendor 遷移流程、走 &lt;a href="https://tarrragon.github.io/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">6-type 結構&lt;/a>）。「← X」代表從 X 遷入。&lt;/p></description><content:encoded><![CDATA[<p>可靠性 Vendor 清單的核心責任是把工具名稱放回 verification loop、release gate、fault injection、SLO governance 與 evidence handoff 的判斷。每個服務頁先回答它承擔哪一種可靠性驗證責任，再討論整合成本、風險控制、artifact 與案例回寫。</p>
<p>跟 <a href="/blog/backend/06-reliability/cases/" data-link-title="可靠性服務案例庫" data-link-desc="按服務組織的 SRE 實踐案例庫，累積架構脈絡與工程文化">cases/</a> 是不同維度。Cases 是教學案例來源，vendors 是把驗證流程落地的工具入口。</p>
<h2 id="讀法">讀法</h2>
<p>可靠性工具要從驗證流程進入。讀者如果要處理 release gate，先回到 <a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate</a>；如果要處理 load test 與 regression，先回到 <a href="/blog/backend/06-reliability/performance-regression-gate/" data-link-title="6.13 Performance Regression Gate" data-link-desc="把效能 baseline 從一次性壓測變成持續對齊的 release gate，涵蓋 baseline 設定、判讀方法、variance 控制與退化定位">6.13 Performance Regression Gate</a>；如果要處理 chaos，先回到 <a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 Experiment Safety Boundary</a>。</p>
<h2 id="教學順序同步">教學順序同步</h2>
<p>可靠性工具頁的教學順序是先建立 CI / release gate，再進入 load test、chaos / fault injection 與 SLO governance。這個順序對齊 checkout E5：讀者先理解變更如何被放行與停止，再比較哪些工具產生 regression evidence、experiment evidence 與 error budget evidence。</p>
<h2 id="t1-服務頁大綱">T1 服務頁大綱</h2>
<table>
  <thead>
      <tr>
          <th>服務</th>
          <th>類型</th>
          <th>頁面要回答的核心問題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/backend/06-reliability/vendors/github-actions/" data-link-title="GitHub Actions" data-link-desc="GitHub 原生 CI/CD、PR check、deploy gate">GitHub Actions</a></td>
          <td>CI/CD</td>
          <td>workflow、environment、artifact 與 approval gate 如何支援 release evidence</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/vendors/circleci/" data-link-title="CircleCI" data-link-desc="CI/CD 平台、強 cache 與 parallelism">CircleCI</a></td>
          <td>CI/CD</td>
          <td>pipeline、orb、parallelism 與 context 權限如何取捨</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/vendors/k6/" data-link-title="k6" data-link-desc="現代 load test、JS scripting、Grafana Labs">k6</a></td>
          <td>Load test</td>
          <td>scenario、threshold 與 CI gate 如何支援可靠性驗證</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/vendors/gatling/" data-link-title="Gatling" data-link-desc="JVM-based load test、Scala / Java / Kotlin DSL、強型別 scenario、HAR-driven recording">Gatling</a></td>
          <td>Load test</td>
          <td>JVM simulation、injection profile 與 report 如何支援 regression gate</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/vendors/jmeter/" data-link-title="Apache JMeter" data-link-desc="老牌 load test 工具、GUI &#43; plugins">JMeter</a></td>
          <td>Load test</td>
          <td>GUI plan、protocol sampler 與既有測試資產如何治理</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/vendors/locust/" data-link-title="Locust" data-link-desc="Python-based load test、distributed、易擴展">Locust</a></td>
          <td>Load test</td>
          <td>Python user behavior 與 distributed worker 如何支援自訂 workload</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/vendors/chaos-mesh/" data-link-title="Chaos Mesh" data-link-desc="Kubernetes-native chaos engineering（CNCF incubating）">Chaos Mesh</a></td>
          <td>Chaos engineering</td>
          <td>Kubernetes-native fault injection 與 experiment scope 如何控制</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/vendors/litmuschaos/" data-link-title="LitmusChaos" data-link-desc="Kubernetes chaos engineering 平台（CNCF graduated）">LitmusChaos</a></td>
          <td>Chaos engineering</td>
          <td>chaos workflow、hub 與 Kubernetes 實驗治理如何取捨</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/vendors/gremlin/" data-link-title="Gremlin" data-link-desc="商業 chaos engineering 平台、跨平台與 GameDay">Gremlin</a></td>
          <td>Chaos platform</td>
          <td>商業 chaos 平台、blast radius guardrail 與審計如何支援成熟團隊</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/vendors/toxiproxy/" data-link-title="Toxiproxy" data-link-desc="TCP-level fault injection proxy（Shopify 開源）">Toxiproxy</a></td>
          <td>Fault injection</td>
          <td>TCP fault、local integration test 與 dependency failure 如何模擬</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/vendors/nobl9/" data-link-title="Nobl9" data-link-desc="SLO platform、跨 data source、企業 SLO 治理">Nobl9</a></td>
          <td>SLO platform</td>
          <td>SLO、error budget、alerting 與 governance 如何整合</td>
      </tr>
      <tr>
          <td><a href="/blog/backend/06-reliability/vendors/sloth/" data-link-title="Sloth" data-link-desc="OSS SLO generator for Prometheus">Sloth</a></td>
          <td>SLO generator</td>
          <td>OpenSLO / Prometheus rule 生成如何降低 SLO 維護成本</td>
      </tr>
  </tbody>
</table>
<h2 id="內容覆蓋進度">內容覆蓋進度</h2>
<p>每個 vendor 服務頁下會擴充兩類文章：deep article（vendor 自身的配置、故障、容量、走 <a href="/blog/posts/vendor-%E6%B7%B1%E5%BA%A6%E6%8A%80%E8%A1%93%E6%96%87%E7%AB%A0%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84%E5%90%8C-vendor-%E7%B3%BB%E5%88%97%E7%9A%84%E9%96%8B%E5%A0%B4%E8%BC%AA%E6%9B%BF%E9%A9%97%E8%AD%89/" data-link-title="Vendor 深度技術文章方法論的演化紀錄：同 vendor 系列的開場輪替驗證" data-link-desc="vendor overview 飽和後要寫單一功能深度文章、需要選題與結構依據時回來。這套方法論的驗證來源與 cadence variant 在高風險場景（同 vendor sub-tool 系列）的實證。">6-section 模板</a>）跟 migration playbook（跨 vendor 遷移流程、走 <a href="/blog/posts/migration-playbook-%E6%96%B9%E6%B3%95%E8%AB%96%E7%9A%84%E6%BC%94%E5%8C%96%E7%B4%80%E9%8C%84stage-0-variant-%E8%A6%8F%E5%8A%83%E6%8A%8A-collapse-%E7%8E%87%E5%BE%9E-60-%E9%99%8D%E5%88%B0-0/" data-link-title="Migration Playbook 方法論的演化紀錄：Stage 0 variant 規劃把 collapse 率從 60% 降到 0%" data-link-desc="跨 vendor migration playbook 需要獨立寫作方法論的依據，以及這套方法論從三輪 batch dogfood 中演化出來的驗證證據。">6-type 結構</a>）。「← X」代表從 X 遷入。</p>
<table>
  <thead>
      <tr>
          <th>Vendor</th>
          <th>Deep article</th>
          <th>Migration playbook</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="github-actions/">GitHub Actions</a></td>
          <td><a href="github-actions/environment-protection-and-oidc-cloud-auth/">Environment Protection + OIDC</a></td>
          <td><a href="github-actions/migrate-from-jenkins/">← Jenkins</a></td>
      </tr>
      <tr>
          <td><a href="k6/">k6</a></td>
          <td><a href="k6/threshold-ci-gate-and-scenario-design/">Threshold CI Gate + Scenario</a></td>
          <td>—</td>
      </tr>
      <tr>
          <td><a href="chaos-mesh/">Chaos Mesh</a></td>
          <td><a href="chaos-mesh/workflow-experiment-scope-and-steady-state-probe/">Workflow + Scope + Steady State Probe</a></td>
          <td>—</td>
      </tr>
      <tr>
          <td><a href="sloth/">Sloth</a></td>
          <td><a href="sloth/slo-yaml-and-multi-burn-rate-alert-generation/">SLO YAML + Multi-burn-rate Alert</a></td>
          <td>—</td>
      </tr>
  </tbody>
</table>
<p>其他 T1 vendor（CircleCI / Gatling / JMeter / Locust / LitmusChaos / Gremlin / Toxiproxy / Nobl9）的 deep article 尚未開始。對應的 backlog 議題見上方「T1 服務頁大綱」段每個服務頁要回答的核心問題、跟各 vendor <code>_index.md</code> 的「預計實作話題」段。</p>
<h2 id="服務頁撰寫欄位">服務頁撰寫欄位</h2>
<table>
  <thead>
      <tr>
          <th>欄位</th>
          <th>可靠性服務頁要保留的問題</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>服務責任</td>
          <td>它承擔 CI gate、load test、chaos、fault injection 還是 SLO governance</td>
      </tr>
      <tr>
          <td>適用壓力</td>
          <td>release frequency、failure mode、experiment safety、SLO maturity 哪個壓力最明顯</td>
      </tr>
      <tr>
          <td>替代邊界</td>
          <td>CI 平台、09 壓測工具、chaos 平台、SLO tool 的機會成本</td>
      </tr>
      <tr>
          <td>操作成本</td>
          <td>runner、secret、artifact、test data、blast radius、experiment approval</td>
      </tr>
      <tr>
          <td>Evidence</td>
          <td>workflow run、test report、experiment result、SLO burn、gate decision</td>
      </tr>
      <tr>
          <td>案例回寫</td>
          <td>Google SRE、Netflix chaos、release gate 與 replay 案例如何提供判準</td>
      </tr>
  </tbody>
</table>
<h2 id="服務頁標準章節">服務頁標準章節</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>可靠性工具頁要補的內容</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>工具定位</td>
          <td>它是 CI gate、load test、chaos platform、fault injection 還是 SLO governance</td>
      </tr>
      <tr>
          <td>本章目標</td>
          <td>讀者能判斷該工具能產生哪種 verification evidence 與 gate decision</td>
      </tr>
      <tr>
          <td>最短判讀路徑</td>
          <td>用「要擋 release、驗證負載、注入失敗、追 SLO」快速定位工具類型</td>
      </tr>
      <tr>
          <td>日常操作與決策形狀</td>
          <td>workflow、runner、secret、artifact、approval、experiment scope、SLO rule</td>
      </tr>
      <tr>
          <td>核心取捨表</td>
          <td>CI 平台、09 壓測工具、chaos 平台、SLO 平台的機會成本</td>
      </tr>
      <tr>
          <td>進階主題</td>
          <td>self-hosted runner、blast radius guardrail、error budget policy、audit</td>
      </tr>
      <tr>
          <td>排錯與失敗快速判讀</td>
          <td>flaky job、missing artifact、unsafe experiment、false SLO alert、runner bottleneck</td>
      </tr>
      <tr>
          <td>何時改走其他服務</td>
          <td>容量模型回 09、觀測資料回 04、事故協作回 08、部署控制回 05</td>
      </tr>
      <tr>
          <td>不在本頁內的主題</td>
          <td>完整 pipeline cookbook、每個 test framework、所有 chaos experiment 範本</td>
      </tr>
      <tr>
          <td>案例回寫與下一步路由</td>
          <td>回到 06 cases、6.8 release gate、6.20 experiment safety boundary</td>
      </tr>
  </tbody>
</table>
<h2 id="跨-vendor-議題對照">跨 vendor 議題對照</h2>
<p>本模組 12 個 vendor 跨 4 個 sub-category（CI/CD / load test / chaos / SLO）、不是同類選一。對照表用「橫向 reliability gate 議題」標明每個議題在哪個 sub-category 落地。</p>
<table>
  <thead>
      <tr>
          <th>議題</th>
          <th>GH Actions</th>
          <th>CircleCI</th>
          <th>k6</th>
          <th>Gatling</th>
          <th>JMeter</th>
          <th>Locust</th>
          <th>Chaos Mesh</th>
          <th>Litmus</th>
          <th>Gremlin</th>
          <th>Toxiproxy</th>
          <th>Nobl9</th>
          <th>Sloth</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>主責任</td>
          <td>CI gate</td>
          <td>CI gate</td>
          <td>Load test</td>
          <td>Load test</td>
          <td>Load test</td>
          <td>Load test</td>
          <td>K8s chaos</td>
          <td>K8s chaos</td>
          <td>跨平台 chaos</td>
          <td>TCP fault</td>
          <td>SLO governance</td>
          <td>SLO generator</td>
      </tr>
      <tr>
          <td>整合 CI gate</td>
          <td>原生</td>
          <td>原生</td>
          <td>threshold</td>
          <td>assertion</td>
          <td>non-GUI mode</td>
          <td>headless</td>
          <td>workflow</td>
          <td>workflow</td>
          <td>scenario</td>
          <td>client SDK</td>
          <td>error budget</td>
          <td>rule gen</td>
      </tr>
      <tr>
          <td>配置模式</td>
          <td>YAML</td>
          <td>YAML</td>
          <td>JS</td>
          <td>Scala / Java</td>
          <td>XML GUI</td>
          <td>Python</td>
          <td>CRD</td>
          <td>CRD</td>
          <td>UI / API</td>
          <td>API</td>
          <td>YAML / UI</td>
          <td>YAML</td>
      </tr>
      <tr>
          <td>環境支援</td>
          <td>GitHub-hosted</td>
          <td>cross-VCS</td>
          <td>OSS / Cloud</td>
          <td>OSS / Enterprise</td>
          <td>OSS</td>
          <td>OSS</td>
          <td>K8s only</td>
          <td>K8s only</td>
          <td>跨平台</td>
          <td>TCP layer</td>
          <td>multi-source</td>
          <td>Prometheus</td>
      </tr>
      <tr>
          <td>進階產出</td>
          <td>matrix / OIDC</td>
          <td>parallelism</td>
          <td>extension</td>
          <td>feeder</td>
          <td>plugins</td>
          <td>distributed</td>
          <td>scope control</td>
          <td>ChaosHub</td>
          <td>GameDay</td>
          <td>toxic types</td>
          <td>composite SLO</td>
          <td>multi-burn</td>
      </tr>
      <tr>
          <td>商業 / 開源</td>
          <td>商業 + SaaS</td>
          <td>商業 + SaaS</td>
          <td>OSS + Cloud</td>
          <td>OSS + Enterprise</td>
          <td>OSS</td>
          <td>OSS</td>
          <td>OSS</td>
          <td>OSS + 商業</td>
          <td>商業 SaaS</td>
          <td>OSS</td>
          <td>商業 SaaS</td>
          <td>OSS</td>
      </tr>
      <tr>
          <td>主討論案例</td>
          <td>待補</td>
          <td>待補</td>
          <td>待補</td>
          <td>待補</td>
          <td>待補</td>
          <td>待補</td>
          <td>Netflix/Google</td>
          <td>待補</td>
          <td>待補</td>
          <td>Shopify</td>
          <td>Google SRE</td>
          <td>待補</td>
      </tr>
  </tbody>
</table>
<p>對照表的用途有三：</p>
<ul>
<li>寫某 vendor 頁時、看相同 sub-category 對手如何處理同一議題</li>
<li>讀者組 reliability stack：CI gate + load test + chaos + SLO 各選 1</li>
<li>評估 OSS vs 商業 trade-off</li>
</ul>
<p>下面 4 段把對照表的 sub-category 展開、不是每行都展開。</p>
<h3 id="ci-gategithub-actions--circleci">CI gate（GitHub Actions / CircleCI）</h3>
<p>CI gate 是 release 前最後一道驗證、決定哪些工件可發。<strong>GitHub Actions</strong> 跟 GitHub 深度整合（PR check / environment protection / OIDC cloud auth）、marketplace action 生態最廣；<strong>CircleCI</strong> 強進階 cache + parallelism + macOS / GPU resource class、cross-VCS（GitHub / Bitbucket / GitLab）。</p>
<p>選型判讀：GitHub-hosted + 普通用 → GitHub Actions；極致 build speed / macOS / 跨 VCS → CircleCI；複雜 DAG → Tekton / Argo。</p>
<h3 id="load-testk6--gatling--jmeter--locust">Load test（k6 / Gatling / JMeter / Locust）</h3>
<p>Load test 提供 performance regression evidence。差異主要在語言生態：<strong>k6</strong> JS / CLI-first / Grafana 生態；<strong>Gatling</strong> Scala / Java / 強型別 / 複雜 scenario；<strong>JMeter</strong> GUI / 老牌 / 多 protocol；<strong>Locust</strong> Python / 自訂邏輯極彈性。</p>
<p>選型判讀：CI-first JS → k6；JVM 生態 → Gatling；既有 .jmx 資產 → JMeter；Python 團隊 / 複雜邏輯 → Locust。詳見 <a href="/blog/backend/09-performance-capacity/" data-link-title="模組九：效能工程與容量規劃" data-link-desc="把『目前配置能撐多少、要加多少機器』變成可量化、可驗證、可改進的工程流程">9 performance capacity 模組</a> 的 capacity planning 角度。</p>
<h3 id="chaos-engineeringchaos-mesh--litmuschaos--gremlin--toxiproxy">Chaos engineering（Chaos Mesh / LitmusChaos / Gremlin / Toxiproxy）</h3>
<p>Chaos 工具按 scope 跟運維模式分四類：<strong>Chaos Mesh</strong> K8s-native CRD-driven 多 fault types；<strong>LitmusChaos</strong> K8s + ChaosHub experiment 庫；<strong>Gremlin</strong> 商業 SaaS / 跨平台 / GameDay；<strong>Toxiproxy</strong> TCP-level / integration test 用。</p>
<p>選型判讀：K8s production + OSS → Chaos Mesh / Litmus；跨平台 + 商業 → Gremlin；CI integration test → Toxiproxy。對應 <a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 Experiment Safety Boundary</a> 的 blast radius 設計。</p>
<h3 id="slo-governancenobl9--sloth">SLO governance（Nobl9 / Sloth）</h3>
<p>SLO 工具按 source 跟運維模式分兩類：<strong>Nobl9</strong> 商業 SaaS / multi-source / OpenSLO 主導 / 企業 governance；<strong>Sloth</strong> OSS / Prometheus-only / 產生 Prometheus rules。</p>
<p>選型判讀：multi-source / SaaS / governance → Nobl9；Prometheus-only / OSS → Sloth / Pyrra；vendor 內建夠 → Datadog SLO / Grafana SLO / Honeycomb SLO。對應 <a href="/blog/backend/knowledge-cards/burn-rate/" data-link-title="Burn Rate" data-link-desc="說明 error budget 消耗速度如何支援告警與事故分級">knowledge cards burn-rate</a>。</p>
<h2 id="撰寫批次">撰寫批次</h2>
<table>
  <thead>
      <tr>
          <th>批次</th>
          <th>服務頁</th>
          <th>撰寫目的</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>R1</td>
          <td>GitHub Actions / CircleCI</td>
          <td>建立 CI gate、artifact 與 approval baseline</td>
      </tr>
      <tr>
          <td>R2</td>
          <td>k6 / Gatling / JMeter / Locust</td>
          <td>建立 release gate 視角的 load test 與 regression evidence</td>
      </tr>
      <tr>
          <td>R3</td>
          <td>Chaos Mesh / LitmusChaos / Gremlin / Toxiproxy</td>
          <td>建立 fault injection 與 experiment safety 對照</td>
      </tr>
      <tr>
          <td>R4</td>
          <td>Nobl9 / Sloth</td>
          <td>建立 SLO governance、error budget 與 rule generation 判準</td>
      </tr>
  </tbody>
</table>
<h2 id="後續候選">後續候選</h2>
<table>
  <thead>
      <tr>
          <th>類型</th>
          <th>候選服務</th>
          <th>寫作重點</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>CI/CD</td>
          <td>GitLab CI、Jenkins、Buildkite、Tekton、Harness、Azure Pipelines</td>
          <td>self-hosted runner、enterprise workflow、pipeline governance</td>
      </tr>
      <tr>
          <td>Load / browser gate</td>
          <td>Artillery、Grafana k6 Cloud、BlazeMeter、Playwright、Cypress</td>
          <td>managed runner、browser flow、release gate、cost</td>
      </tr>
      <tr>
          <td>Chaos / fault</td>
          <td>AWS Fault Injection Service、Azure Chaos Studio、Pumba</td>
          <td>cloud-native fault、container fault、blast radius</td>
      </tr>
      <tr>
          <td>SLO</td>
          <td>Pyrra、OpenSLO、Keptn</td>
          <td>Prometheus-native SLO、portable SLO spec、quality gate</td>
      </tr>
      <tr>
          <td>Policy / audit</td>
          <td>Steampipe、Conftest</td>
          <td>compliance query、control evidence、change review</td>
      </tr>
  </tbody>
</table>
<p>主流覆蓋檢查的重點是分開 CI gate、performance gate、chaos gate、SLO gate 與 policy gate。CI 工具負責 release artifact 與 approval；load / browser 工具負責 regression evidence；chaos 工具負責 failure mode evidence；SLO 工具負責 error budget governance；policy 工具負責控制證據。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>上游：<a href="/blog/backend/06-reliability/release-gate/" data-link-title="6.8 Release Gate 與變更節奏" data-link-desc="把驗證、migration、相容性納入放行判準">6.8 Release Gate</a></li>
<li>上游：<a href="/blog/backend/06-reliability/experiment-safety-boundary/" data-link-title="6.20 Experiment Safety Boundary" data-link-desc="定義 chaos、load test、DR drill 的 [blast radius](/backend/knowledge-cards/blast-radius/)、停止條件與權限約束">6.20 Experiment Safety Boundary</a></li>
<li>服務路徑：<a href="/blog/backend/06-reliability/provider-dependency-release-gate/" data-link-title="6.25 Provider Dependency Release Gate 實作示範" data-link-desc="以 payment provider 變更示範 release gate 如何結合 evidence、stop condition 與 rollback window。">6.25 Provider Dependency Release Gate 實作示範</a></li>
<li>平行：<a href="/blog/backend/09-performance-capacity/vendors/" data-link-title="效能與容量工具清單" data-link-desc="整理效能工程、容量規劃、壓測、production replay 與 profiling 工具的服務責任與選型路由">09 效能與容量工具清單</a></li>
</ul>
]]></content:encoded></item><item><title>Cutover Window</title><link>https://tarrragon.github.io/blog/backend/knowledge-cards/cutover-window/</link><pubDate>Mon, 11 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/knowledge-cards/cutover-window/</guid><description>&lt;p>Cutover window 的核心概念是「正式切換發生並被密集觀察的時間與條件範圍」。它連接 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/cutover-switchover/" data-link-title="Cutover / Switchover" data-link-desc="說明遷移期間如何把正式流量切到新路徑">cutover / switchover&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/migration-gate/" data-link-title="Migration Gate" data-link-desc="說明遷移流程何時可以進入下一階段或正式切換">migration gate&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/rollback-window/" data-link-title="Rollback Window" data-link-desc="說明變更進入 production 後還能用哪種方式回退或改路線的時間與條件">rollback-window&lt;/a>，讓切換成為一段可停止、可判讀的窗口，脫離瞬間按鈕的思維。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Cutover window 位在 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">release gate&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/steady-state/" data-link-title="Steady State" data-link-desc="說明可靠性實驗與事故恢復如何定義系統應維持的可接受狀態">steady state&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/evidence-package/" data-link-title="Evidence Package" data-link-desc="說明觀測、驗證與事故流程如何把證據包成可交接、可回放的 artifact">evidence package&lt;/a> 之間。Release gate 決定能否開始切換，cutover window 定義切換後多久內要看哪些訊號、達到什麼條件才算穩定。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;p>系統需要 cutover window 的訊號是：&lt;/p>
&lt;ul>
&lt;li>新路徑開始承接正式讀取或寫入&lt;/li>
&lt;li>切換後需要觀察 mismatch、latency、error rate 或 lag&lt;/li>
&lt;li>回退條件只在切換初期仍然低成本&lt;/li>
&lt;li>多個入口會分批切換，需要分別記錄時間窗&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實網路服務的例子">接近真實網路服務的例子&lt;/h2>
&lt;p>客服後台先切到新 &lt;code>payment_state&lt;/code> 讀取後，前 30 分鐘是 cutover window。這段期間要看 mismatch sample、客服查詢慢查詢、對帳補償量與 rollback window；穩定後才放行使用者可見讀取。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Cutover window 要定義開始時間、觀察長度、通過條件、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/stop-condition/" data-link-title="Stop Condition" data-link-desc="說明變更、實驗或事故處理何時必須暫停、回退或改路線">stop condition&lt;/a> 與 owner。它應進入 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/evidence-package/" data-link-title="Evidence Package" data-link-desc="說明觀測、驗證與事故流程如何把證據包成可交接、可回放的 artifact">evidence package&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/incident-decision-log/" data-link-title="Incident Decision Log" data-link-desc="說明事故期間如何保留決策、證據、owner 與回退條件">incident decision log&lt;/a>，讓事後能回放切換當時的訊號。&lt;/p></description><content:encoded><![CDATA[<p>Cutover window 的核心概念是「正式切換發生並被密集觀察的時間與條件範圍」。它連接 <a href="/blog/backend/knowledge-cards/cutover-switchover/" data-link-title="Cutover / Switchover" data-link-desc="說明遷移期間如何把正式流量切到新路徑">cutover / switchover</a>、<a href="/blog/backend/knowledge-cards/migration-gate/" data-link-title="Migration Gate" data-link-desc="說明遷移流程何時可以進入下一階段或正式切換">migration gate</a> 與 <a href="/blog/backend/knowledge-cards/rollback-window/" data-link-title="Rollback Window" data-link-desc="說明變更進入 production 後還能用哪種方式回退或改路線的時間與條件">rollback-window</a>，讓切換成為一段可停止、可判讀的窗口，脫離瞬間按鈕的思維。</p>
<h2 id="概念位置">概念位置</h2>
<p>Cutover window 位在 <a href="/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">release gate</a>、<a href="/blog/backend/knowledge-cards/steady-state/" data-link-title="Steady State" data-link-desc="說明可靠性實驗與事故恢復如何定義系統應維持的可接受狀態">steady state</a> 與 <a href="/blog/backend/knowledge-cards/evidence-package/" data-link-title="Evidence Package" data-link-desc="說明觀測、驗證與事故流程如何把證據包成可交接、可回放的 artifact">evidence package</a> 之間。Release gate 決定能否開始切換，cutover window 定義切換後多久內要看哪些訊號、達到什麼條件才算穩定。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<p>系統需要 cutover window 的訊號是：</p>
<ul>
<li>新路徑開始承接正式讀取或寫入</li>
<li>切換後需要觀察 mismatch、latency、error rate 或 lag</li>
<li>回退條件只在切換初期仍然低成本</li>
<li>多個入口會分批切換，需要分別記錄時間窗</li>
</ul>
<h2 id="接近真實網路服務的例子">接近真實網路服務的例子</h2>
<p>客服後台先切到新 <code>payment_state</code> 讀取後，前 30 分鐘是 cutover window。這段期間要看 mismatch sample、客服查詢慢查詢、對帳補償量與 rollback window；穩定後才放行使用者可見讀取。</p>
<h2 id="設計責任">設計責任</h2>
<p>Cutover window 要定義開始時間、觀察長度、通過條件、<a href="/blog/backend/knowledge-cards/stop-condition/" data-link-title="Stop Condition" data-link-desc="說明變更、實驗或事故處理何時必須暫停、回退或改路線">stop condition</a> 與 owner。它應進入 <a href="/blog/backend/knowledge-cards/evidence-package/" data-link-title="Evidence Package" data-link-desc="說明觀測、驗證與事故流程如何把證據包成可交接、可回放的 artifact">evidence package</a> 與 <a href="/blog/backend/knowledge-cards/incident-decision-log/" data-link-title="Incident Decision Log" data-link-desc="說明事故期間如何保留決策、證據、owner 與回退條件">incident decision log</a>，讓事後能回放切換當時的訊號。</p>
]]></content:encoded></item><item><title>Rollback Window</title><link>https://tarrragon.github.io/blog/backend/knowledge-cards/rollback-window/</link><pubDate>Mon, 11 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/knowledge-cards/rollback-window/</guid><description>&lt;p>Rollback window 的核心概念是「變更進入 production 後，仍能用特定方式回退或改路線的有效窗口」。它連接 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明事故期間如何判斷回滾、回切與暫停變更">rollback strategy&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">release gate&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/migration-gate/" data-link-title="Migration Gate" data-link-desc="說明遷移流程何時可以進入下一階段或正式切換">migration gate&lt;/a>，讓 gate 能判斷目前還剩哪種退路。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Rollback window 位在 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/cutover-switchover/" data-link-title="Cutover / Switchover" data-link-desc="說明遷移期間如何把正式流量切到新路徑">cutover / switchover&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/fallback-plan/" data-link-title="Fallback Plan" data-link-desc="說明變更失敗時如何回到可接受狀態">fallback plan&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/incident-decision-log/" data-link-title="Incident Decision Log" data-link-desc="說明事故期間如何保留決策、證據、owner 與回退條件">incident decision log&lt;/a> 之間。Rollback strategy 說明回退決策，rollback window 說明這個決策在目前階段是否仍可執行。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;p>系統需要 rollback window 的訊號是：&lt;/p>
&lt;ul>
&lt;li>expand、backfill、cutover、contract 每一階段的回退方式不同&lt;/li>
&lt;li>舊版本或舊資料語意只能支撐一段時間&lt;/li>
&lt;li>cutover 後仍可 fallback read，但 contract 後只能資料修復或 fail-forward&lt;/li>
&lt;li>release gate 要判斷是否還能安全暫停或回退&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實網路服務的例子">接近真實網路服務的例子&lt;/h2>
&lt;p>資料庫 migration 在 expand 階段通常能回到舊讀取；backfill 階段可以暫停與重跑；cutover 後可回到 fallback read；contract 移除舊欄位後，回退會轉成資料修補或 fail-forward。這些差異都屬於 rollback window。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Rollback window 要寫清楚目前階段、可用回退方式、最後可回退時間、資料相容性限制與 owner。它要進入 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">release gate&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/incident-decision-log/" data-link-title="Incident Decision Log" data-link-desc="說明事故期間如何保留決策、證據、owner 與回退條件">incident decision log&lt;/a>，避免事故期間把已經關閉的退路當成可用選項。&lt;/p></description><content:encoded><![CDATA[<p>Rollback window 的核心概念是「變更進入 production 後，仍能用特定方式回退或改路線的有效窗口」。它連接 <a href="/blog/backend/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明事故期間如何判斷回滾、回切與暫停變更">rollback strategy</a>、<a href="/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">release gate</a> 與 <a href="/blog/backend/knowledge-cards/migration-gate/" data-link-title="Migration Gate" data-link-desc="說明遷移流程何時可以進入下一階段或正式切換">migration gate</a>，讓 gate 能判斷目前還剩哪種退路。</p>
<h2 id="概念位置">概念位置</h2>
<p>Rollback window 位在 <a href="/blog/backend/knowledge-cards/cutover-switchover/" data-link-title="Cutover / Switchover" data-link-desc="說明遷移期間如何把正式流量切到新路徑">cutover / switchover</a>、<a href="/blog/backend/knowledge-cards/fallback-plan/" data-link-title="Fallback Plan" data-link-desc="說明變更失敗時如何回到可接受狀態">fallback plan</a> 與 <a href="/blog/backend/knowledge-cards/incident-decision-log/" data-link-title="Incident Decision Log" data-link-desc="說明事故期間如何保留決策、證據、owner 與回退條件">incident decision log</a> 之間。Rollback strategy 說明回退決策，rollback window 說明這個決策在目前階段是否仍可執行。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<p>系統需要 rollback window 的訊號是：</p>
<ul>
<li>expand、backfill、cutover、contract 每一階段的回退方式不同</li>
<li>舊版本或舊資料語意只能支撐一段時間</li>
<li>cutover 後仍可 fallback read，但 contract 後只能資料修復或 fail-forward</li>
<li>release gate 要判斷是否還能安全暫停或回退</li>
</ul>
<h2 id="接近真實網路服務的例子">接近真實網路服務的例子</h2>
<p>資料庫 migration 在 expand 階段通常能回到舊讀取；backfill 階段可以暫停與重跑；cutover 後可回到 fallback read；contract 移除舊欄位後，回退會轉成資料修補或 fail-forward。這些差異都屬於 rollback window。</p>
<h2 id="設計責任">設計責任</h2>
<p>Rollback window 要寫清楚目前階段、可用回退方式、最後可回退時間、資料相容性限制與 owner。它要進入 <a href="/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">release gate</a> 與 <a href="/blog/backend/knowledge-cards/incident-decision-log/" data-link-title="Incident Decision Log" data-link-desc="說明事故期間如何保留決策、證據、owner 與回退條件">incident decision log</a>，避免事故期間把已經關閉的退路當成可用選項。</p>
]]></content:encoded></item><item><title>Fail-forward</title><link>https://tarrragon.github.io/blog/backend/knowledge-cards/fail-forward/</link><pubDate>Mon, 11 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/knowledge-cards/fail-forward/</guid><description>&lt;p>Fail-forward 的核心概念是「當回退代價高於前進修復時，用受控方式往新狀態完成修復」。它連接 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明事故期間如何判斷回滾、回切與暫停變更">rollback strategy&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/fallback-plan/" data-link-title="Fallback Plan" data-link-desc="說明變更失敗時如何回到可接受狀態">fallback plan&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/incident-decision-log/" data-link-title="Incident Decision Log" data-link-desc="說明事故期間如何保留決策、證據、owner 與回退條件">incident decision log&lt;/a>，不是忽略失敗繼續推進。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Fail-forward 位在 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/rollback-window/" data-link-title="Rollback Window" data-link-desc="說明變更進入 production 後還能用哪種方式回退或改路線的時間與條件">rollback window&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/containment/" data-link-title="Containment" data-link-desc="說明事故處理中如何限制擴散面，為回復與驗證爭取時間">containment&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/post-incident-review/" data-link-title="Post-Incident Review" data-link-desc="說明事故後如何完成復盤、學習與改進閉環">post-incident review&lt;/a> 之間。Rollback window 關閉後，團隊仍需要一條能限制影響、補資料、完成相容收斂的前進路線。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;p>系統需要 fail-forward 的訊號是：&lt;/p>
&lt;ul>
&lt;li>舊資料語意已被 contract 或不可逆寫入移除&lt;/li>
&lt;li>回退會造成更大的資料不一致或客戶影響&lt;/li>
&lt;li>新路徑有明確修補方案、停損條件與 owner&lt;/li>
&lt;li>事故 decision log 需要記錄為何不回滾&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實網路服務的例子">接近真實網路服務的例子&lt;/h2>
&lt;p>資料庫 migration 已完成 contract 後，舊欄位被移除，回到舊版本會讓讀取路徑失效。此時比較可控的做法可能是暫停部分寫入、修補 mismatch、補 validation query，再讓新路徑收斂到可用狀態。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Fail-forward 要定義 containment、修補步驟、預期效果、停止條件與回寫項目。它要搭配 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/evidence-package/" data-link-title="Evidence Package" data-link-desc="說明觀測、驗證與事故流程如何把證據包成可交接、可回放的 artifact">evidence package&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/action-item-closure/" data-link-title="Action Item Closure" data-link-desc="說明事故行動項如何被驗證完成，而不是只停留在待辦清單">action item closure&lt;/a>，避免「不能回滾」被誤用成沒有證據的硬推。&lt;/p></description><content:encoded><![CDATA[<p>Fail-forward 的核心概念是「當回退代價高於前進修復時，用受控方式往新狀態完成修復」。它連接 <a href="/blog/backend/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明事故期間如何判斷回滾、回切與暫停變更">rollback strategy</a>、<a href="/blog/backend/knowledge-cards/fallback-plan/" data-link-title="Fallback Plan" data-link-desc="說明變更失敗時如何回到可接受狀態">fallback plan</a> 與 <a href="/blog/backend/knowledge-cards/incident-decision-log/" data-link-title="Incident Decision Log" data-link-desc="說明事故期間如何保留決策、證據、owner 與回退條件">incident decision log</a>，不是忽略失敗繼續推進。</p>
<h2 id="概念位置">概念位置</h2>
<p>Fail-forward 位在 <a href="/blog/backend/knowledge-cards/rollback-window/" data-link-title="Rollback Window" data-link-desc="說明變更進入 production 後還能用哪種方式回退或改路線的時間與條件">rollback window</a>、<a href="/blog/backend/knowledge-cards/containment/" data-link-title="Containment" data-link-desc="說明事故處理中如何限制擴散面，為回復與驗證爭取時間">containment</a> 與 <a href="/blog/backend/knowledge-cards/post-incident-review/" data-link-title="Post-Incident Review" data-link-desc="說明事故後如何完成復盤、學習與改進閉環">post-incident review</a> 之間。Rollback window 關閉後，團隊仍需要一條能限制影響、補資料、完成相容收斂的前進路線。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<p>系統需要 fail-forward 的訊號是：</p>
<ul>
<li>舊資料語意已被 contract 或不可逆寫入移除</li>
<li>回退會造成更大的資料不一致或客戶影響</li>
<li>新路徑有明確修補方案、停損條件與 owner</li>
<li>事故 decision log 需要記錄為何不回滾</li>
</ul>
<h2 id="接近真實網路服務的例子">接近真實網路服務的例子</h2>
<p>資料庫 migration 已完成 contract 後，舊欄位被移除，回到舊版本會讓讀取路徑失效。此時比較可控的做法可能是暫停部分寫入、修補 mismatch、補 validation query，再讓新路徑收斂到可用狀態。</p>
<h2 id="設計責任">設計責任</h2>
<p>Fail-forward 要定義 containment、修補步驟、預期效果、停止條件與回寫項目。它要搭配 <a href="/blog/backend/knowledge-cards/evidence-package/" data-link-title="Evidence Package" data-link-desc="說明觀測、驗證與事故流程如何把證據包成可交接、可回放的 artifact">evidence package</a> 與 <a href="/blog/backend/knowledge-cards/action-item-closure/" data-link-title="Action Item Closure" data-link-desc="說明事故行動項如何被驗證完成，而不是只停留在待辦清單">action item closure</a>，避免「不能回滾」被誤用成沒有證據的硬推。</p>
]]></content:encoded></item><item><title>Stop Condition</title><link>https://tarrragon.github.io/blog/backend/knowledge-cards/stop-condition/</link><pubDate>Mon, 11 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/knowledge-cards/stop-condition/</guid><description>&lt;p>Stop condition 的核心概念是「事前定義何時必須暫停、回退或改路線」。它連接 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">release gate&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明事故期間如何判斷回滾、回切與暫停變更">rollback strategy&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/incident-decision-log/" data-link-title="Incident Decision Log" data-link-desc="說明事故期間如何保留決策、證據、owner 與回退條件">incident decision log&lt;/a>，避免團隊在壓力下用感覺決定是否繼續。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Stop condition 位在 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/migration-gate/" data-link-title="Migration Gate" data-link-desc="說明遷移流程何時可以進入下一階段或正式切換">migration gate&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/cutover-window/" data-link-title="Cutover Window" data-link-desc="說明正式切換發生的觀察窗口、停止條件與回退判讀範圍">cutover-window&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/steady-state/" data-link-title="Steady State" data-link-desc="說明可靠性實驗與事故恢復如何定義系統應維持的可接受狀態">steady state&lt;/a> 之間。Gate 說明能否開始，stop condition 說明開始後看到哪些訊號必須停。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;p>系統需要 stop condition 的訊號是：&lt;/p>
&lt;ul>
&lt;li>rollout、backfill、replay 或 experiment 會逐批擴大影響&lt;/li>
&lt;li>指標短暫變壞時需要知道是觀察、暫停還是回退&lt;/li>
&lt;li>owner 需要在事故現場快速做一致決策&lt;/li>
&lt;li>post-incident review 要檢查當時是否該更早停下來&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實網路服務的例子">接近真實網路服務的例子&lt;/h2>
&lt;p>資料庫 migration 可以定義 &lt;code>mismatch_rate &amp;gt;= 0.1% for two consecutive batches&lt;/code> 或 &lt;code>replication_lag &amp;gt;= 30s for 10 minutes&lt;/code> 作為 stop condition。達到條件時，團隊先暫停下一批 backfill 或回到 fallback read，而不是等使用者回報。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Stop condition 要包含訊號、門檻、觀察窗口、對應動作與 owner。它要進入 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">release gate&lt;/a> 和 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/incident-decision-log/" data-link-title="Incident Decision Log" data-link-desc="說明事故期間如何保留決策、證據、owner 與回退條件">incident decision log&lt;/a>，並且要能被 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/evidence-package/" data-link-title="Evidence Package" data-link-desc="說明觀測、驗證與事故流程如何把證據包成可交接、可回放的 artifact">evidence package&lt;/a> 支撐。&lt;/p></description><content:encoded><![CDATA[<p>Stop condition 的核心概念是「事前定義何時必須暫停、回退或改路線」。它連接 <a href="/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">release gate</a>、<a href="/blog/backend/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明事故期間如何判斷回滾、回切與暫停變更">rollback strategy</a> 與 <a href="/blog/backend/knowledge-cards/incident-decision-log/" data-link-title="Incident Decision Log" data-link-desc="說明事故期間如何保留決策、證據、owner 與回退條件">incident decision log</a>，避免團隊在壓力下用感覺決定是否繼續。</p>
<h2 id="概念位置">概念位置</h2>
<p>Stop condition 位在 <a href="/blog/backend/knowledge-cards/migration-gate/" data-link-title="Migration Gate" data-link-desc="說明遷移流程何時可以進入下一階段或正式切換">migration gate</a>、<a href="/blog/backend/knowledge-cards/cutover-window/" data-link-title="Cutover Window" data-link-desc="說明正式切換發生的觀察窗口、停止條件與回退判讀範圍">cutover-window</a> 與 <a href="/blog/backend/knowledge-cards/steady-state/" data-link-title="Steady State" data-link-desc="說明可靠性實驗與事故恢復如何定義系統應維持的可接受狀態">steady state</a> 之間。Gate 說明能否開始，stop condition 說明開始後看到哪些訊號必須停。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<p>系統需要 stop condition 的訊號是：</p>
<ul>
<li>rollout、backfill、replay 或 experiment 會逐批擴大影響</li>
<li>指標短暫變壞時需要知道是觀察、暫停還是回退</li>
<li>owner 需要在事故現場快速做一致決策</li>
<li>post-incident review 要檢查當時是否該更早停下來</li>
</ul>
<h2 id="接近真實網路服務的例子">接近真實網路服務的例子</h2>
<p>資料庫 migration 可以定義 <code>mismatch_rate &gt;= 0.1% for two consecutive batches</code> 或 <code>replication_lag &gt;= 30s for 10 minutes</code> 作為 stop condition。達到條件時，團隊先暫停下一批 backfill 或回到 fallback read，而不是等使用者回報。</p>
<h2 id="設計責任">設計責任</h2>
<p>Stop condition 要包含訊號、門檻、觀察窗口、對應動作與 owner。它要進入 <a href="/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">release gate</a> 和 <a href="/blog/backend/knowledge-cards/incident-decision-log/" data-link-title="Incident Decision Log" data-link-desc="說明事故期間如何保留決策、證據、owner 與回退條件">incident decision log</a>，並且要能被 <a href="/blog/backend/knowledge-cards/evidence-package/" data-link-title="Evidence Package" data-link-desc="說明觀測、驗證與事故流程如何把證據包成可交接、可回放的 artifact">evidence package</a> 支撐。</p>
]]></content:encoded></item><item><title>Gate Decision</title><link>https://tarrragon.github.io/blog/backend/knowledge-cards/gate-decision/</link><pubDate>Mon, 11 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/knowledge-cards/gate-decision/</guid><description>&lt;p>Gate decision 的核心概念是「release gate 根據證據做出的明確下一步」。它連接 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">release gate&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/evidence-package/" data-link-title="Evidence Package" data-link-desc="說明觀測、驗證與事故流程如何把證據包成可交接、可回放的 artifact">evidence package&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/stop-condition/" data-link-title="Stop Condition" data-link-desc="說明變更、實驗或事故處理何時必須暫停、回退或改路線">stop condition&lt;/a>，讓 gate 不只寫檢查結果，也寫出能不能前進。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Gate decision 位在 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/confidence/" data-link-title="Confidence" data-link-desc="說明證據包如何標示 confirmed、suspected 或 needs follow-up 的判讀信心">confidence&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/rollback-window/" data-link-title="Rollback Window" data-link-desc="說明變更進入 production 後還能用哪種方式回退或改路線的時間與條件">rollback window&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/incident-decision-log/" data-link-title="Incident Decision Log" data-link-desc="說明事故期間如何保留決策、證據、owner 與回退條件">incident decision log&lt;/a> 之間。Checks 描述檢查結果，gate decision 把結果轉成放行、暫停、回退、fail-forward 或補證據。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;p>系統需要 gate decision 的訊號是：&lt;/p>
&lt;ul>
&lt;li>CI、SLO、validation query 都有結果，但沒人知道下一步&lt;/li>
&lt;li>evidence 足以支持部分放行，但不足以支持完整 cutover&lt;/li>
&lt;li>變更需要逐批 rollout、backfill、warmup 或 replay&lt;/li>
&lt;li>gate 要保留 owner 與 rollback window&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實網路服務的例子">接近真實網路服務的例子&lt;/h2>
&lt;p>資料庫 migration 的 gate decision 可以寫成 &lt;code>allow next 10% backfill; block customer-visible read cutover&lt;/code>。這句話比 &lt;code>migration pass&lt;/code> 更可操作，因為它同時說明允許前進的範圍與被擋住的風險面。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Gate decision 要包含決策內容、支撐 checks、stop condition、rollback window 與 owner。它要能被 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/incident-decision-log/" data-link-title="Incident Decision Log" data-link-desc="說明事故期間如何保留決策、證據、owner 與回退條件">incident decision log&lt;/a> 承接，讓放行後出現異常時能回放當時依據。&lt;/p></description><content:encoded><![CDATA[<p>Gate decision 的核心概念是「release gate 根據證據做出的明確下一步」。它連接 <a href="/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">release gate</a>、<a href="/blog/backend/knowledge-cards/evidence-package/" data-link-title="Evidence Package" data-link-desc="說明觀測、驗證與事故流程如何把證據包成可交接、可回放的 artifact">evidence package</a> 與 <a href="/blog/backend/knowledge-cards/stop-condition/" data-link-title="Stop Condition" data-link-desc="說明變更、實驗或事故處理何時必須暫停、回退或改路線">stop condition</a>，讓 gate 不只寫檢查結果，也寫出能不能前進。</p>
<h2 id="概念位置">概念位置</h2>
<p>Gate decision 位在 <a href="/blog/backend/knowledge-cards/confidence/" data-link-title="Confidence" data-link-desc="說明證據包如何標示 confirmed、suspected 或 needs follow-up 的判讀信心">confidence</a>、<a href="/blog/backend/knowledge-cards/rollback-window/" data-link-title="Rollback Window" data-link-desc="說明變更進入 production 後還能用哪種方式回退或改路線的時間與條件">rollback window</a> 與 <a href="/blog/backend/knowledge-cards/incident-decision-log/" data-link-title="Incident Decision Log" data-link-desc="說明事故期間如何保留決策、證據、owner 與回退條件">incident decision log</a> 之間。Checks 描述檢查結果，gate decision 把結果轉成放行、暫停、回退、fail-forward 或補證據。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<p>系統需要 gate decision 的訊號是：</p>
<ul>
<li>CI、SLO、validation query 都有結果，但沒人知道下一步</li>
<li>evidence 足以支持部分放行，但不足以支持完整 cutover</li>
<li>變更需要逐批 rollout、backfill、warmup 或 replay</li>
<li>gate 要保留 owner 與 rollback window</li>
</ul>
<h2 id="接近真實網路服務的例子">接近真實網路服務的例子</h2>
<p>資料庫 migration 的 gate decision 可以寫成 <code>allow next 10% backfill; block customer-visible read cutover</code>。這句話比 <code>migration pass</code> 更可操作，因為它同時說明允許前進的範圍與被擋住的風險面。</p>
<h2 id="設計責任">設計責任</h2>
<p>Gate decision 要包含決策內容、支撐 checks、stop condition、rollback window 與 owner。它要能被 <a href="/blog/backend/knowledge-cards/incident-decision-log/" data-link-title="Incident Decision Log" data-link-desc="說明事故期間如何保留決策、證據、owner 與回退條件">incident decision log</a> 承接，讓放行後出現異常時能回放當時依據。</p>
]]></content:encoded></item><item><title>Rollback Condition</title><link>https://tarrragon.github.io/blog/backend/knowledge-cards/rollback-condition/</link><pubDate>Mon, 11 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/knowledge-cards/rollback-condition/</guid><description>&lt;p>Rollback condition 的核心概念是「某個決策執行後，看到哪些訊號時要撤回、回退或改路線」。它連接 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/incident-decision-log/" data-link-title="Incident Decision Log" data-link-desc="說明事故期間如何保留決策、證據、owner 與回退條件">incident decision log&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明事故期間如何判斷回滾、回切與暫停變更">rollback strategy&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/stop-condition/" data-link-title="Stop Condition" data-link-desc="說明變更、實驗或事故處理何時必須暫停、回退或改路線">stop condition&lt;/a>，讓事故現場能控制次生風險。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Rollback condition 位在 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/gate-decision/" data-link-title="Gate Decision" data-link-desc="說明 release gate 如何把證據轉成放行、暫停、回退或補證據的決策">gate decision&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/rollback-window/" data-link-title="Rollback Window" data-link-desc="說明變更進入 production 後還能用哪種方式回退或改路線的時間與條件">rollback window&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/time-range/" data-link-title="Time Range" data-link-desc="說明證據、查詢與事故判讀如何用時間窗保留可回放上下文">time range&lt;/a> 之間。Stop condition 常用於流程何時停，rollback condition 則跟某筆已做出的 decision 綁在一起。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;p>系統需要 rollback condition 的訊號是：&lt;/p>
&lt;ul>
&lt;li>rollback、fallback、degradation 或 fail-forward 本身也可能造成風險&lt;/li>
&lt;li>IC handoff 後，新 IC 需要知道什麼條件下要改路線&lt;/li>
&lt;li>stakeholder update 需要說明目前決策如何被監控&lt;/li>
&lt;li>PIR 需要檢查當時是否有明確撤回條件&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實網路服務的例子">接近真實網路服務的例子&lt;/h2>
&lt;p>客服後台切回 legacy status fallback 後，rollback condition 可以寫成 &lt;code>mismatch remains above threshold after 15 minutes&lt;/code>。這表示 fallback 沒有降低錯誤時，團隊要改成資料修補或暫停相關入口，而不是繼續等待。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Rollback condition 要包含訊號、門檻、觀察窗口、對應動作與 owner。它要連到 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/query-link/" data-link-title="Query Link" data-link-desc="說明證據包如何保存可重跑查詢入口，而不是只保留截圖或口頭結論">query link&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/time-range/" data-link-title="Time Range" data-link-desc="說明證據、查詢與事故判讀如何用時間窗保留可回放上下文">time range&lt;/a>，讓決策撤回成為可回放的證據判讀，口頭判斷的準確度和可追溯性都不足。&lt;/p></description><content:encoded><![CDATA[<p>Rollback condition 的核心概念是「某個決策執行後，看到哪些訊號時要撤回、回退或改路線」。它連接 <a href="/blog/backend/knowledge-cards/incident-decision-log/" data-link-title="Incident Decision Log" data-link-desc="說明事故期間如何保留決策、證據、owner 與回退條件">incident decision log</a>、<a href="/blog/backend/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明事故期間如何判斷回滾、回切與暫停變更">rollback strategy</a> 與 <a href="/blog/backend/knowledge-cards/stop-condition/" data-link-title="Stop Condition" data-link-desc="說明變更、實驗或事故處理何時必須暫停、回退或改路線">stop condition</a>，讓事故現場能控制次生風險。</p>
<h2 id="概念位置">概念位置</h2>
<p>Rollback condition 位在 <a href="/blog/backend/knowledge-cards/gate-decision/" data-link-title="Gate Decision" data-link-desc="說明 release gate 如何把證據轉成放行、暫停、回退或補證據的決策">gate decision</a>、<a href="/blog/backend/knowledge-cards/rollback-window/" data-link-title="Rollback Window" data-link-desc="說明變更進入 production 後還能用哪種方式回退或改路線的時間與條件">rollback window</a> 與 <a href="/blog/backend/knowledge-cards/time-range/" data-link-title="Time Range" data-link-desc="說明證據、查詢與事故判讀如何用時間窗保留可回放上下文">time range</a> 之間。Stop condition 常用於流程何時停，rollback condition 則跟某筆已做出的 decision 綁在一起。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<p>系統需要 rollback condition 的訊號是：</p>
<ul>
<li>rollback、fallback、degradation 或 fail-forward 本身也可能造成風險</li>
<li>IC handoff 後，新 IC 需要知道什麼條件下要改路線</li>
<li>stakeholder update 需要說明目前決策如何被監控</li>
<li>PIR 需要檢查當時是否有明確撤回條件</li>
</ul>
<h2 id="接近真實網路服務的例子">接近真實網路服務的例子</h2>
<p>客服後台切回 legacy status fallback 後，rollback condition 可以寫成 <code>mismatch remains above threshold after 15 minutes</code>。這表示 fallback 沒有降低錯誤時，團隊要改成資料修補或暫停相關入口，而不是繼續等待。</p>
<h2 id="設計責任">設計責任</h2>
<p>Rollback condition 要包含訊號、門檻、觀察窗口、對應動作與 owner。它要連到 <a href="/blog/backend/knowledge-cards/query-link/" data-link-title="Query Link" data-link-desc="說明證據包如何保存可重跑查詢入口，而不是只保留截圖或口頭結論">query link</a> 與 <a href="/blog/backend/knowledge-cards/time-range/" data-link-title="Time Range" data-link-desc="說明證據、查詢與事故判讀如何用時間窗保留可回放上下文">time range</a>，讓決策撤回成為可回放的證據判讀，口頭判斷的準確度和可追溯性都不足。</p>
]]></content:encoded></item><item><title>7.23 資安與可靠性的共同控制面</title><link>https://tarrragon.github.io/blog/backend/07-security-data-protection/security-and-reliability-shared-controls/</link><pubDate>Thu, 30 Apr 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/backend/07-security-data-protection/security-and-reliability-shared-controls/</guid><description>&lt;p>本篇的責任是建立資安與可靠性的共同控制面。讀者讀完後，能用同一組控制語言處理風險收斂與服務穩定。&lt;/p>
&lt;h2 id="核心論點">核心論點&lt;/h2>
&lt;p>共同控制面的核心概念是同一項能力同時承擔安全與穩定責任。共同控制面明確後，團隊能避免重複建設與交接斷層。&lt;/p>
&lt;h2 id="共同控制項">共同控制項&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>控制項&lt;/th>
 &lt;th>資安責任&lt;/th>
 &lt;th>可靠性責任&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Containment&lt;/td>
 &lt;td>收斂攻擊或曝險擴散&lt;/td>
 &lt;td>限制故障擴散範圍&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Rollback&lt;/td>
 &lt;td>回退高風險變更&lt;/td>
 &lt;td>恢復服務穩定狀態&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Degradation&lt;/td>
 &lt;td>保留核心服務能力&lt;/td>
 &lt;td>降低系統壓力與損耗&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Evidence chain&lt;/td>
 &lt;td>保留回查與審計資料&lt;/td>
 &lt;td>保留故障與修復證據&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Runbook&lt;/td>
 &lt;td>固定安全處置步驟&lt;/td>
 &lt;td>固定運維處置步驟&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="控制欄位對齊">控制欄位對齊&lt;/h2>
&lt;p>控制欄位對齊的責任是讓兩個模組共享決策資料。共同欄位可包含 trigger、owner、action、validation、rollback condition 與 write-back target。&lt;/p>
&lt;h2 id="演練與驗證">演練與驗證&lt;/h2>
&lt;p>演練與驗證的責任是讓控制在壓力情境保持可用。共同演練可同時驗證安全處置與可靠性恢復，並記錄雙方指標。&lt;/p>
&lt;h2 id="交接路由">交接路由&lt;/h2>
&lt;p>交接路由的責任是把控制決策推進到 06 模組。交接資料可包含風險分級、處置結果、回退證據與後續改善任務。&lt;/p>
&lt;h2 id="與-04--06--08-的組合路由">與 04 / 06 / 08 的組合路由&lt;/h2>
&lt;p>組合路由的責任是讓共同控制面同時接上訊號、驗證與事故流程。7.23 不只把資安控制交給可靠性驗證，也把證據需求交給 04、把處置節奏交給 08。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>組合點&lt;/th>
 &lt;th>04 可觀測性承接&lt;/th>
 &lt;th>06 可靠性承接&lt;/th>
 &lt;th>08 事故處理承接&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Evidence chain&lt;/td>
 &lt;td>audit log、trace、證據保留&lt;/td>
 &lt;td>evidence replay、演練驗證&lt;/td>
 &lt;td>事故 timeline 與復盤證據&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Detection gap&lt;/td>
 &lt;td>alert rule、dashboard、SLO&lt;/td>
 &lt;td>chaos hypothesis、SLO gate&lt;/td>
 &lt;td>severity trigger、runbook&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Containment&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/blast-radius/" data-link-title="Blast Radius" data-link-desc="說明事故影響面如何估算與隔離">blast radius&lt;/a> 訊號與拓撲關係&lt;/td>
 &lt;td>隔離演練、降級驗證&lt;/td>
 &lt;td>指揮、隔離與恢復排序&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Rollback&lt;/td>
 &lt;td>rollback 前後健康訊號&lt;/td>
 &lt;td>rollback rehearsal、DR drill&lt;/td>
 &lt;td>rollback decision log&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Degradation&lt;/td>
 &lt;td>容量、latency、queue 指標&lt;/td>
 &lt;td>load test、capacity rehearsal&lt;/td>
 &lt;td>降級公告與恢復節點&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Evidence chain 在真實服務中會落到誰在什麼時間看過什麼資料、哪個 token 被使用、哪個服務產生異常輸出。04 承接資料可觀測性，06 驗證 evidence replay 是否可重播，08 在事故 timeline 中使用同一組證據做決策與復盤。&lt;/p>
&lt;p>Detection gap 在真實服務中通常表現為資安事件被客訴、成本異常或下游故障先發現。04 補 alert 與 dashboard，06 把缺口轉成 chaos hypothesis 或 release gate，08 把觸發條件寫進 severity 與 runbook。&lt;/p>
&lt;p>Containment 在真實服務中同時是資安隔離與可靠性限縮。04 提供 blast radius 與 service topology，06 驗證隔離後核心服務是否維持，08 決定封鎖、切流、降級與恢復順序。&lt;/p>
&lt;p>Rollback 在真實服務中需要把風險變更退回到穩定狀態。04 提供 rollback 前後的健康訊號，06 定期演練回退路徑，08 記錄誰在什麼條件下做出 rollback 決策。&lt;/p>
&lt;p>Degradation 在真實服務中是保留核心功能、放棄次要能力。04 觀察容量與延遲訊號，06 驗證 degraded mode 的承載能力，08 負責對內外說明目前服務狀態與恢復節點。&lt;/p>
&lt;h2 id="判讀訊號與路由">判讀訊號與路由&lt;/h2>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>判讀訊號&lt;/th>
 &lt;th>代表需求&lt;/th>
 &lt;th>下一步路由&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>安全處置造成服務不穩定&lt;/td>
 &lt;td>需要補 shared rollback 策略&lt;/td>
 &lt;td>7.23 → 06&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>可靠性演練未覆蓋安全情境&lt;/td>
 &lt;td>需要補共同 scenario&lt;/td>
 &lt;td>7.23 → 7.B9&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>事件復盤只記錄單一面向&lt;/td>
 &lt;td>需要補 shared evidence&lt;/td>
 &lt;td>7.23 → 7.24&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>控制 owner 在兩模組不一致&lt;/td>
 &lt;td>需要補共同控制欄位&lt;/td>
 &lt;td>7.23 → 7.B1&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>偵測訊號不足以支持資安判讀&lt;/td>
 &lt;td>需要補 observability 訊號&lt;/td>
 &lt;td>7.23 → 04&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>處置決策沒有事故節奏&lt;/td>
 &lt;td>需要補 incident route&lt;/td>
 &lt;td>7.23 → 08&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="必連章節">必連章節&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/07-security-data-protection/blue-team/security-control-validation/" data-link-title="7.B3 資安控制驗證" data-link-desc="建立資安控制面如何用證據、演練與 release gate 驗證的大綱">7.B3 資安控制驗證&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/07-security-data-protection/blue-team/incident-triage-loop/" data-link-title="7.B6 Incident Triage Loop" data-link-desc="把資安訊號轉成 triage、severity、owner、containment 與 evidence 的回應循環">7.B6 Incident Triage Loop&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/07-security-data-protection/security-control-handoff-to-delivery-and-incident/" data-link-title="7.18 資安控制面如何交接到部署與事故流程" data-link-desc="建立資安控制面交接到部署、可靠性與事故流程的大綱">7.18 資安控制面如何交接到部署與事故流程&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">04 模組：可觀測性&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/" data-link-title="模組六：可靠性驗證流程" data-link-desc="用 SRE 領域詞彙建問題節點、以服務級案例庫累積驗證脈絡，先建概念與案例庫再進實作交接">06 模組：可靠性&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/backend/08-incident-response/" data-link-title="模組八：事故處理與復盤" data-link-desc="用 IR 領域詞彙建問題節點、以服務級案例庫累積事故脈絡，先建概念與案例庫再進實作交接">08 模組：事故處理&lt;/a>&lt;/li>
&lt;/ul>
&lt;h2 id="完稿判準">完稿判準&lt;/h2>
&lt;p>完稿時要讓讀者能列出共同控制面與交接欄位。輸出至少包含控制項、雙責任、驗證方式與交接路由。&lt;/p></description><content:encoded><![CDATA[<p>本篇的責任是建立資安與可靠性的共同控制面。讀者讀完後，能用同一組控制語言處理風險收斂與服務穩定。</p>
<h2 id="核心論點">核心論點</h2>
<p>共同控制面的核心概念是同一項能力同時承擔安全與穩定責任。共同控制面明確後，團隊能避免重複建設與交接斷層。</p>
<h2 id="共同控制項">共同控制項</h2>
<table>
  <thead>
      <tr>
          <th>控制項</th>
          <th>資安責任</th>
          <th>可靠性責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Containment</td>
          <td>收斂攻擊或曝險擴散</td>
          <td>限制故障擴散範圍</td>
      </tr>
      <tr>
          <td>Rollback</td>
          <td>回退高風險變更</td>
          <td>恢復服務穩定狀態</td>
      </tr>
      <tr>
          <td>Degradation</td>
          <td>保留核心服務能力</td>
          <td>降低系統壓力與損耗</td>
      </tr>
      <tr>
          <td>Evidence chain</td>
          <td>保留回查與審計資料</td>
          <td>保留故障與修復證據</td>
      </tr>
      <tr>
          <td>Runbook</td>
          <td>固定安全處置步驟</td>
          <td>固定運維處置步驟</td>
      </tr>
  </tbody>
</table>
<h2 id="控制欄位對齊">控制欄位對齊</h2>
<p>控制欄位對齊的責任是讓兩個模組共享決策資料。共同欄位可包含 trigger、owner、action、validation、rollback condition 與 write-back target。</p>
<h2 id="演練與驗證">演練與驗證</h2>
<p>演練與驗證的責任是讓控制在壓力情境保持可用。共同演練可同時驗證安全處置與可靠性恢復，並記錄雙方指標。</p>
<h2 id="交接路由">交接路由</h2>
<p>交接路由的責任是把控制決策推進到 06 模組。交接資料可包含風險分級、處置結果、回退證據與後續改善任務。</p>
<h2 id="與-04--06--08-的組合路由">與 04 / 06 / 08 的組合路由</h2>
<p>組合路由的責任是讓共同控制面同時接上訊號、驗證與事故流程。7.23 不只把資安控制交給可靠性驗證，也把證據需求交給 04、把處置節奏交給 08。</p>
<table>
  <thead>
      <tr>
          <th>組合點</th>
          <th>04 可觀測性承接</th>
          <th>06 可靠性承接</th>
          <th>08 事故處理承接</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Evidence chain</td>
          <td>audit log、trace、證據保留</td>
          <td>evidence replay、演練驗證</td>
          <td>事故 timeline 與復盤證據</td>
      </tr>
      <tr>
          <td>Detection gap</td>
          <td>alert rule、dashboard、SLO</td>
          <td>chaos hypothesis、SLO gate</td>
          <td>severity trigger、runbook</td>
      </tr>
      <tr>
          <td>Containment</td>
          <td><a href="/blog/backend/knowledge-cards/blast-radius/" data-link-title="Blast Radius" data-link-desc="說明事故影響面如何估算與隔離">blast radius</a> 訊號與拓撲關係</td>
          <td>隔離演練、降級驗證</td>
          <td>指揮、隔離與恢復排序</td>
      </tr>
      <tr>
          <td>Rollback</td>
          <td>rollback 前後健康訊號</td>
          <td>rollback rehearsal、DR drill</td>
          <td>rollback decision log</td>
      </tr>
      <tr>
          <td>Degradation</td>
          <td>容量、latency、queue 指標</td>
          <td>load test、capacity rehearsal</td>
          <td>降級公告與恢復節點</td>
      </tr>
  </tbody>
</table>
<p>Evidence chain 在真實服務中會落到誰在什麼時間看過什麼資料、哪個 token 被使用、哪個服務產生異常輸出。04 承接資料可觀測性，06 驗證 evidence replay 是否可重播，08 在事故 timeline 中使用同一組證據做決策與復盤。</p>
<p>Detection gap 在真實服務中通常表現為資安事件被客訴、成本異常或下游故障先發現。04 補 alert 與 dashboard，06 把缺口轉成 chaos hypothesis 或 release gate，08 把觸發條件寫進 severity 與 runbook。</p>
<p>Containment 在真實服務中同時是資安隔離與可靠性限縮。04 提供 blast radius 與 service topology，06 驗證隔離後核心服務是否維持，08 決定封鎖、切流、降級與恢復順序。</p>
<p>Rollback 在真實服務中需要把風險變更退回到穩定狀態。04 提供 rollback 前後的健康訊號，06 定期演練回退路徑，08 記錄誰在什麼條件下做出 rollback 決策。</p>
<p>Degradation 在真實服務中是保留核心功能、放棄次要能力。04 觀察容量與延遲訊號，06 驗證 degraded mode 的承載能力，08 負責對內外說明目前服務狀態與恢復節點。</p>
<h2 id="判讀訊號與路由">判讀訊號與路由</h2>
<table>
  <thead>
      <tr>
          <th>判讀訊號</th>
          <th>代表需求</th>
          <th>下一步路由</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>安全處置造成服務不穩定</td>
          <td>需要補 shared rollback 策略</td>
          <td>7.23 → 06</td>
      </tr>
      <tr>
          <td>可靠性演練未覆蓋安全情境</td>
          <td>需要補共同 scenario</td>
          <td>7.23 → 7.B9</td>
      </tr>
      <tr>
          <td>事件復盤只記錄單一面向</td>
          <td>需要補 shared evidence</td>
          <td>7.23 → 7.24</td>
      </tr>
      <tr>
          <td>控制 owner 在兩模組不一致</td>
          <td>需要補共同控制欄位</td>
          <td>7.23 → 7.B1</td>
      </tr>
      <tr>
          <td>偵測訊號不足以支持資安判讀</td>
          <td>需要補 observability 訊號</td>
          <td>7.23 → 04</td>
      </tr>
      <tr>
          <td>處置決策沒有事故節奏</td>
          <td>需要補 incident route</td>
          <td>7.23 → 08</td>
      </tr>
  </tbody>
</table>
<h2 id="必連章節">必連章節</h2>
<ul>
<li><a href="/blog/backend/07-security-data-protection/blue-team/security-control-validation/" data-link-title="7.B3 資安控制驗證" data-link-desc="建立資安控制面如何用證據、演練與 release gate 驗證的大綱">7.B3 資安控制驗證</a></li>
<li><a href="/blog/backend/07-security-data-protection/blue-team/incident-triage-loop/" data-link-title="7.B6 Incident Triage Loop" data-link-desc="把資安訊號轉成 triage、severity、owner、containment 與 evidence 的回應循環">7.B6 Incident Triage Loop</a></li>
<li><a href="/blog/backend/07-security-data-protection/security-control-handoff-to-delivery-and-incident/" data-link-title="7.18 資安控制面如何交接到部署與事故流程" data-link-desc="建立資安控制面交接到部署、可靠性與事故流程的大綱">7.18 資安控制面如何交接到部署與事故流程</a></li>
<li><a href="/blog/backend/04-observability/" data-link-title="模組四：可觀測性平台" data-link-desc="整理 log、metric、trace、dashboard 與 alert 的後端操作實務">04 模組：可觀測性</a></li>
<li><a href="/blog/backend/06-reliability/" data-link-title="模組六：可靠性驗證流程" data-link-desc="用 SRE 領域詞彙建問題節點、以服務級案例庫累積驗證脈絡，先建概念與案例庫再進實作交接">06 模組：可靠性</a></li>
<li><a href="/blog/backend/08-incident-response/" data-link-title="模組八：事故處理與復盤" data-link-desc="用 IR 領域詞彙建問題節點、以服務級案例庫累積事故脈絡，先建概念與案例庫再進實作交接">08 模組：事故處理</a></li>
</ul>
<h2 id="完稿判準">完稿判準</h2>
<p>完稿時要讓讀者能列出共同控制面與交接欄位。輸出至少包含控制項、雙責任、驗證方式與交接路由。</p>
]]></content:encoded></item></channel></rss>