<?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>CD on Tarragon</title><link>https://tarrragon.github.io/blog/tags/cd/</link><description>Recent content in CD on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Thu, 21 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/cd/index.xml" rel="self" type="application/rss+xml"/><item><title>App 簽章、商店審核與分批發布流程</title><link>https://tarrragon.github.io/blog/ci/app-deploy/signing-store-rollout-flow/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/app-deploy/signing-store-rollout-flow/</guid><description>&lt;p>App 發布流程的核心責任是把可安裝 artifact 送進受控發行通道。App 與 web 最大差異是使用者裝置會長期保留舊版本；CI/CD 需要把 build number、簽章、審核、分批發布與服務端相容性一起管理。&lt;/p>
&lt;h2 id="流程定位">流程定位&lt;/h2>
&lt;p>App 部署的風險集中在不可變 artifact 與外部 gate。IPA、APK、AAB 或桌面安裝包一旦被使用者安裝，團隊需要靠 hotfix、remote config、kill switch 或服務端相容性止血；store review、簽章憑證與 phased rollout 會決定錯誤版本能否快速收斂。&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>Version&lt;/td>
 &lt;td>管理 version 與 build number&lt;/td>
 &lt;td>每次上傳是否可唯一追溯&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/app-signing/" data-link-title="App Signing" data-link-desc="說明行動與桌面應用的簽章憑證如何影響發布能力">App signing&lt;/a>&lt;/td>
 &lt;td>產生可信 artifact&lt;/td>
 &lt;td>certificate / keystore 是否安全&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Test&lt;/td>
 &lt;td>驗證裝置與 OS matrix&lt;/td>
 &lt;td>高風險裝置、權限與離線情境&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Store review&lt;/td>
 &lt;td>通過商店或企業發行 gate&lt;/td>
 &lt;td>審核時間、拒審理由、metadata&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">Rollout strategy&lt;/a>&lt;/td>
 &lt;td>控制使用者取得比例&lt;/td>
 &lt;td>crash-free rate、conversion、回報&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Recovery&lt;/td>
 &lt;td>hotfix、remote config、kill switch&lt;/td>
 &lt;td>是否能處理已安裝版本&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Version 階段負責讓 artifact 可追溯。App crash report、客服回報與 store console 都依賴 version / build number；版本號對應 commit 與 workflow run 時，事故定位可以直接回到發布紀錄。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/app-signing/" data-link-title="App Signing" data-link-desc="說明行動與桌面應用的簽章憑證如何影響發布能力">App signing&lt;/a> 階段負責維持發布信任鏈。簽章憑證、provisioning profile、keystore 與 notarization credential 都是發布能力；它們要用 secret 管理、權限隔離、輪替與備援流程保護。&lt;/p>
&lt;p>Test 階段負責覆蓋目標裝置條件。App 測試要依實際使用者分佈選擇 OS、裝置、權限狀態、網路條件與升級路徑；只跑 emulator smoke test，通常抓不到真機權限、背景限制或升級資料遷移問題。&lt;/p>
&lt;p>Store review 階段負責處理外部 gate。審核可能因 metadata、隱私揭露、權限使用、付款政策或 crash 被拒；CI/CD 文件要記錄誰能處理審核回覆、哪些變更需要重新提交。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">Rollout strategy&lt;/a> 階段負責控制新版本擴散速度。分批發布的觀察指標包含 crash rate、登入、購買、同步、推播與核心流程完成率；達到停損條件時應暫停 rollout，先讓已受影響範圍維持可控。&lt;/p>
&lt;p>Recovery 階段負責處理已安裝版本。App 常見止血工具是 remote config、feature flag、kill switch、server-side compatibility、hotfix build 與要求使用者升級；每個工具都要在事故前實作，事故時才有路可走。&lt;/p>
&lt;h2 id="多版本共存契約">多版本共存契約&lt;/h2>
&lt;p>多版本共存是 App 發布的基本前提。後端 API、資料格式、推播 payload 與 remote config 都要支援一段時間的新舊 client，因為使用者更新節奏不受團隊完全控制。&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>API response&lt;/td>
 &lt;td>舊 app 看到新增欄位是否能正常處理&lt;/td>
 &lt;td>刪欄位或改語意造成舊版 crash&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Auth / session&lt;/td>
 &lt;td>更新前後 token 是否仍可使用&lt;/td>
 &lt;td>強制登出或登入狀態破壞&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Local storage&lt;/td>
 &lt;td>app upgrade 是否能遷移本機資料&lt;/td>
 &lt;td>新版寫入後舊版讀取契約失效&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Push payload&lt;/td>
 &lt;td>舊版是否能忽略未知 action&lt;/td>
 &lt;td>推播點擊進入不存在頁面&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Remote config&lt;/td>
 &lt;td>config key 是否有預設值與版本條件&lt;/td>
 &lt;td>未支援版本收到新功能開關&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這些契約要在 CI 或 release checklist 裡被驗證。若只靠後端「盡量相容」，App 發布失敗會在使用者更新後才暴露，回復成本會比 web 或後端高。&lt;/p>
&lt;h2 id="release-checklist">Release checklist&lt;/h2>
&lt;p>Release checklist 的責任是把外部 gate 與內部 gate 接起來。App 發布牽涉商店、憑證、客服、行銷與後端相容，因此 checklist 應該是流程契約，不只是提醒清單。&lt;/p></description><content:encoded><![CDATA[<p>App 發布流程的核心責任是把可安裝 artifact 送進受控發行通道。App 與 web 最大差異是使用者裝置會長期保留舊版本；CI/CD 需要把 build number、簽章、審核、分批發布與服務端相容性一起管理。</p>
<h2 id="流程定位">流程定位</h2>
<p>App 部署的風險集中在不可變 artifact 與外部 gate。IPA、APK、AAB 或桌面安裝包一旦被使用者安裝，團隊需要靠 hotfix、remote config、kill switch 或服務端相容性止血；store review、簽章憑證與 phased rollout 會決定錯誤版本能否快速收斂。</p>
<table>
  <thead>
      <tr>
          <th>階段</th>
          <th>責任</th>
          <th>判讀訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Version</td>
          <td>管理 version 與 build number</td>
          <td>每次上傳是否可唯一追溯</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/app-signing/" data-link-title="App Signing" data-link-desc="說明行動與桌面應用的簽章憑證如何影響發布能力">App signing</a></td>
          <td>產生可信 artifact</td>
          <td>certificate / keystore 是否安全</td>
      </tr>
      <tr>
          <td>Test</td>
          <td>驗證裝置與 OS matrix</td>
          <td>高風險裝置、權限與離線情境</td>
      </tr>
      <tr>
          <td>Store review</td>
          <td>通過商店或企業發行 gate</td>
          <td>審核時間、拒審理由、metadata</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">Rollout strategy</a></td>
          <td>控制使用者取得比例</td>
          <td>crash-free rate、conversion、回報</td>
      </tr>
      <tr>
          <td>Recovery</td>
          <td>hotfix、remote config、kill switch</td>
          <td>是否能處理已安裝版本</td>
      </tr>
  </tbody>
</table>
<p>Version 階段負責讓 artifact 可追溯。App crash report、客服回報與 store console 都依賴 version / build number；版本號對應 commit 與 workflow run 時，事故定位可以直接回到發布紀錄。</p>
<p><a href="/blog/ci/knowledge-cards/app-signing/" data-link-title="App Signing" data-link-desc="說明行動與桌面應用的簽章憑證如何影響發布能力">App signing</a> 階段負責維持發布信任鏈。簽章憑證、provisioning profile、keystore 與 notarization credential 都是發布能力；它們要用 secret 管理、權限隔離、輪替與備援流程保護。</p>
<p>Test 階段負責覆蓋目標裝置條件。App 測試要依實際使用者分佈選擇 OS、裝置、權限狀態、網路條件與升級路徑；只跑 emulator smoke test，通常抓不到真機權限、背景限制或升級資料遷移問題。</p>
<p>Store review 階段負責處理外部 gate。審核可能因 metadata、隱私揭露、權限使用、付款政策或 crash 被拒；CI/CD 文件要記錄誰能處理審核回覆、哪些變更需要重新提交。</p>
<p><a href="/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">Rollout strategy</a> 階段負責控制新版本擴散速度。分批發布的觀察指標包含 crash rate、登入、購買、同步、推播與核心流程完成率；達到停損條件時應暫停 rollout，先讓已受影響範圍維持可控。</p>
<p>Recovery 階段負責處理已安裝版本。App 常見止血工具是 remote config、feature flag、kill switch、server-side compatibility、hotfix build 與要求使用者升級；每個工具都要在事故前實作，事故時才有路可走。</p>
<h2 id="多版本共存契約">多版本共存契約</h2>
<p>多版本共存是 App 發布的基本前提。後端 API、資料格式、推播 payload 與 remote config 都要支援一段時間的新舊 client，因為使用者更新節奏不受團隊完全控制。</p>
<table>
  <thead>
      <tr>
          <th>契約</th>
          <th>判讀問題</th>
          <th>常見風險</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>API response</td>
          <td>舊 app 看到新增欄位是否能正常處理</td>
          <td>刪欄位或改語意造成舊版 crash</td>
      </tr>
      <tr>
          <td>Auth / session</td>
          <td>更新前後 token 是否仍可使用</td>
          <td>強制登出或登入狀態破壞</td>
      </tr>
      <tr>
          <td>Local storage</td>
          <td>app upgrade 是否能遷移本機資料</td>
          <td>新版寫入後舊版讀取契約失效</td>
      </tr>
      <tr>
          <td>Push payload</td>
          <td>舊版是否能忽略未知 action</td>
          <td>推播點擊進入不存在頁面</td>
      </tr>
      <tr>
          <td>Remote config</td>
          <td>config key 是否有預設值與版本條件</td>
          <td>未支援版本收到新功能開關</td>
      </tr>
  </tbody>
</table>
<p>這些契約要在 CI 或 release checklist 裡被驗證。若只靠後端「盡量相容」，App 發布失敗會在使用者更新後才暴露，回復成本會比 web 或後端高。</p>
<h2 id="release-checklist">Release checklist</h2>
<p>Release checklist 的責任是把外部 gate 與內部 gate 接起來。App 發布牽涉商店、憑證、客服、行銷與後端相容，因此 checklist 應該是流程契約，不只是提醒清單。</p>
<ol>
<li>確認 version、build number、commit 與 artifact 對應。</li>
<li>確認 signing secret、profile 或 keystore 仍有效。</li>
<li>跑 unit、UI、device matrix 與 upgrade test。</li>
<li>檢查 API / remote config / push payload 多版本相容。</li>
<li>上傳 internal / beta track，跑 smoke test。</li>
<li>提交 store review，記錄審核狀態。</li>
<li>用 phased rollout 推進，觀察 crash-free rate 與核心指標。</li>
<li>觸發停損條件時暫停 rollout、關閉功能或準備 hotfix。</li>
</ol>
<p>這個順序讓 App 發布從「把包丟上去」變成可觀測流程。每一步都對應一個失敗路由，事故時能知道下一個可執行動作。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>App 部署總覽：回 <a href="../">App 部署 CI/CD</a>。</li>
<li>簽章概念：讀 <a href="/blog/ci/knowledge-cards/app-signing/" data-link-title="App Signing" data-link-desc="說明行動與桌面應用的簽章憑證如何影響發布能力">App Signing</a>。</li>
<li>Gate 原理：讀 <a href="../../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a>。</li>
</ul>
]]></content:encoded></item><item><title>Data pipeline backfill、checkpoint 與 rerun 流程</title><link>https://tarrragon.github.io/blog/ci/data-pipeline-deploy/backfill-checkpoint-rerun-flow/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/data-pipeline-deploy/backfill-checkpoint-rerun-flow/</guid><description>&lt;p>Data pipeline 發布流程的核心責任是讓資料處理邏輯變更可驗證、可重跑、可修補。資料任務部署成功不等於資料正確；CI/CD 要同時檢查輸入 schema、輸出契約、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/backfill/" data-link-title="Backfill" data-link-desc="說明資料處理與 migration 中如何受控補算歷史資料">Backfill&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/checkpoint/" data-link-title="Checkpoint" data-link-desc="說明長時間任務如何記錄進度以支援接續、重跑與事故修復">Checkpoint&lt;/a> 與異常資料修復路徑。&lt;/p>
&lt;h2 id="流程定位">流程定位&lt;/h2>
&lt;p>Data pipeline 的風險集中在資料副作用。API 發布錯誤通常會表現成 request failure；資料任務錯誤可能把錯誤結果寫進 warehouse、feature store、報表或下游模型，並在很久之後才被看見。發布流程要把 correctness check 放到 deploy 前後。&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>Build&lt;/td>
 &lt;td>產生 transform code、DAG、query&lt;/td>
 &lt;td>版本是否可重現&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Validation&lt;/td>
 &lt;td>驗證 input / output schema&lt;/td>
 &lt;td>新舊欄位、型別、nullability 是否相容&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Deploy&lt;/td>
 &lt;td>推進 job、DAG、schedule、trigger&lt;/td>
 &lt;td>新版本是否正確接管&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/backfill/" data-link-title="Backfill" data-link-desc="說明資料處理與 migration 中如何受控補算歷史資料">Backfill&lt;/a>&lt;/td>
 &lt;td>受控補算歷史資料&lt;/td>
 &lt;td>範圍、節流、checkpoint 是否明確&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rerun/" data-link-title="Rerun" data-link-desc="說明 CI/CD 與 data pipeline 中重跑任務前需要判斷的輸出語意與副作用">Rerun&lt;/a>&lt;/td>
 &lt;td>修復失敗區間或錯誤輸出&lt;/td>
 &lt;td>idempotency、覆寫規則、對帳是否存在&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Recovery&lt;/td>
 &lt;td>rollback、forward fix、資料修補&lt;/td>
 &lt;td>下游是否已消費錯誤資料&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Build 階段負責固定執行邏輯。dbt model、Spark job、Flink processor、Airflow DAG 或 SQL transform 都需要能追到 commit 與 dependency，讓歷史資料重跑時能確認使用哪一版邏輯。&lt;/p>
&lt;p>Validation 階段負責檢查資料契約。Schema check、sample run、contract test、row count、null ratio、distinct count 與 business invariant 都可以作為 gate；重點是讓輸出變更在下游消費前被看見。&lt;/p>
&lt;p>Deploy 階段負責切換任務版本。Scheduler、trigger、checkpoint location 與 credential 都會影響新版本是否真正接管；部署後要確認下一次 run 用的是新版本，並保留舊版本停止或恢復路徑。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/backfill/" data-link-title="Backfill" data-link-desc="說明資料處理與 migration 中如何受控補算歷史資料">Backfill&lt;/a> 階段負責補算歷史資料。Backfill 應有時間範圍、節流、checkpoint、停損條件與對帳策略，避免一次掃完整個歷史區間壓垮上游或把錯誤大規模寫入下游。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rerun/" data-link-title="Rerun" data-link-desc="說明 CI/CD 與 data pipeline 中重跑任務前需要判斷的輸出語意與副作用">Rerun&lt;/a> 階段負責修復失敗 run 或錯誤區間。Rerun 要定義輸出覆寫、去重、idempotency 與下游通知；同一段資料被跑兩次時，結果應可預期。&lt;/p>
&lt;p>Recovery 階段負責處理錯誤資料已被消費的情況。資料 pipeline 的 rollback 常常採用 forward fix、重新計算、標記污染區間與通知下游重新讀取。&lt;/p>
&lt;h2 id="backfill-控制面">Backfill 控制面&lt;/h2>
&lt;p>Backfill 控制面的責任是限制歷史補算的影響範圍。歷史資料量通常遠大於日常增量；沒有控制面的 backfill 會同時衝擊計算成本、上游讀取、下游寫入與資料正確性。&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>Range&lt;/td>
 &lt;td>補算哪個時間或 partition 區間&lt;/td>
 &lt;td>先小範圍驗證，再擴大區間&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Throttle&lt;/td>
 &lt;td>每批處理多少資料&lt;/td>
 &lt;td>限制 concurrency、batch size&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/checkpoint/" data-link-title="Checkpoint" data-link-desc="說明長時間任務如何記錄進度以支援接續、重跑與事故修復">Checkpoint&lt;/a>&lt;/td>
 &lt;td>失敗後從哪裡接續&lt;/td>
 &lt;td>記錄 partition、offset、run id&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Stop loss&lt;/td>
 &lt;td>哪些訊號要暫停&lt;/td>
 &lt;td>error rate、成本、row count 異常&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Reconcile&lt;/td>
 &lt;td>補算結果如何確認&lt;/td>
 &lt;td>新舊輸出比對、抽樣、business check&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這些控制項要寫進 workflow 或 runbook。若 backfill 只能靠工程師現場下 SQL，事故時很難保證每次操作都有相同邏輯。&lt;/p>
&lt;h2 id="rerun-判讀">Rerun 判讀&lt;/h2>
&lt;p>Rerun 判讀的責任是確認重跑是否會造成二次傷害。資料任務失敗後，最危險的動作是未確認輸出語意就直接重跑。&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>部分 partition 已寫入&lt;/td>
 &lt;td>需要去重或覆寫策略&lt;/td>
 &lt;td>檢查 output 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>input schema 已改&lt;/td>
 &lt;td>舊版本重跑條件可能失效&lt;/td>
 &lt;td>用相容版本或轉換層&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>streaming checkpoint 壞&lt;/td>
 &lt;td>重跑可能重複消費或漏資料&lt;/td>
 &lt;td>評估 checkpoint repair / replay&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這張表讓 rerun 從「再跑一次」變成有條件的恢復策略。資料正確性比任務綠燈更重要；綠燈只代表 job 完成，不代表輸出可信。&lt;/p></description><content:encoded><![CDATA[<p>Data pipeline 發布流程的核心責任是讓資料處理邏輯變更可驗證、可重跑、可修補。資料任務部署成功不等於資料正確；CI/CD 要同時檢查輸入 schema、輸出契約、<a href="/blog/ci/knowledge-cards/backfill/" data-link-title="Backfill" data-link-desc="說明資料處理與 migration 中如何受控補算歷史資料">Backfill</a>、<a href="/blog/ci/knowledge-cards/checkpoint/" data-link-title="Checkpoint" data-link-desc="說明長時間任務如何記錄進度以支援接續、重跑與事故修復">Checkpoint</a> 與異常資料修復路徑。</p>
<h2 id="流程定位">流程定位</h2>
<p>Data pipeline 的風險集中在資料副作用。API 發布錯誤通常會表現成 request failure；資料任務錯誤可能把錯誤結果寫進 warehouse、feature store、報表或下游模型，並在很久之後才被看見。發布流程要把 correctness check 放到 deploy 前後。</p>
<table>
  <thead>
      <tr>
          <th>階段</th>
          <th>責任</th>
          <th>判讀訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Build</td>
          <td>產生 transform code、DAG、query</td>
          <td>版本是否可重現</td>
      </tr>
      <tr>
          <td>Validation</td>
          <td>驗證 input / output schema</td>
          <td>新舊欄位、型別、nullability 是否相容</td>
      </tr>
      <tr>
          <td>Deploy</td>
          <td>推進 job、DAG、schedule、trigger</td>
          <td>新版本是否正確接管</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/backfill/" data-link-title="Backfill" data-link-desc="說明資料處理與 migration 中如何受控補算歷史資料">Backfill</a></td>
          <td>受控補算歷史資料</td>
          <td>範圍、節流、checkpoint 是否明確</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/rerun/" data-link-title="Rerun" data-link-desc="說明 CI/CD 與 data pipeline 中重跑任務前需要判斷的輸出語意與副作用">Rerun</a></td>
          <td>修復失敗區間或錯誤輸出</td>
          <td>idempotency、覆寫規則、對帳是否存在</td>
      </tr>
      <tr>
          <td>Recovery</td>
          <td>rollback、forward fix、資料修補</td>
          <td>下游是否已消費錯誤資料</td>
      </tr>
  </tbody>
</table>
<p>Build 階段負責固定執行邏輯。dbt model、Spark job、Flink processor、Airflow DAG 或 SQL transform 都需要能追到 commit 與 dependency，讓歷史資料重跑時能確認使用哪一版邏輯。</p>
<p>Validation 階段負責檢查資料契約。Schema check、sample run、contract test、row count、null ratio、distinct count 與 business invariant 都可以作為 gate；重點是讓輸出變更在下游消費前被看見。</p>
<p>Deploy 階段負責切換任務版本。Scheduler、trigger、checkpoint location 與 credential 都會影響新版本是否真正接管；部署後要確認下一次 run 用的是新版本，並保留舊版本停止或恢復路徑。</p>
<p><a href="/blog/ci/knowledge-cards/backfill/" data-link-title="Backfill" data-link-desc="說明資料處理與 migration 中如何受控補算歷史資料">Backfill</a> 階段負責補算歷史資料。Backfill 應有時間範圍、節流、checkpoint、停損條件與對帳策略，避免一次掃完整個歷史區間壓垮上游或把錯誤大規模寫入下游。</p>
<p><a href="/blog/ci/knowledge-cards/rerun/" data-link-title="Rerun" data-link-desc="說明 CI/CD 與 data pipeline 中重跑任務前需要判斷的輸出語意與副作用">Rerun</a> 階段負責修復失敗 run 或錯誤區間。Rerun 要定義輸出覆寫、去重、idempotency 與下游通知；同一段資料被跑兩次時，結果應可預期。</p>
<p>Recovery 階段負責處理錯誤資料已被消費的情況。資料 pipeline 的 rollback 常常採用 forward fix、重新計算、標記污染區間與通知下游重新讀取。</p>
<h2 id="backfill-控制面">Backfill 控制面</h2>
<p>Backfill 控制面的責任是限制歷史補算的影響範圍。歷史資料量通常遠大於日常增量；沒有控制面的 backfill 會同時衝擊計算成本、上游讀取、下游寫入與資料正確性。</p>
<table>
  <thead>
      <tr>
          <th>控制項</th>
          <th>判讀問題</th>
          <th>常見做法</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Range</td>
          <td>補算哪個時間或 partition 區間</td>
          <td>先小範圍驗證，再擴大區間</td>
      </tr>
      <tr>
          <td>Throttle</td>
          <td>每批處理多少資料</td>
          <td>限制 concurrency、batch size</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/checkpoint/" data-link-title="Checkpoint" data-link-desc="說明長時間任務如何記錄進度以支援接續、重跑與事故修復">Checkpoint</a></td>
          <td>失敗後從哪裡接續</td>
          <td>記錄 partition、offset、run id</td>
      </tr>
      <tr>
          <td>Stop loss</td>
          <td>哪些訊號要暫停</td>
          <td>error rate、成本、row count 異常</td>
      </tr>
      <tr>
          <td>Reconcile</td>
          <td>補算結果如何確認</td>
          <td>新舊輸出比對、抽樣、business check</td>
      </tr>
  </tbody>
</table>
<p>這些控制項要寫進 workflow 或 runbook。若 backfill 只能靠工程師現場下 SQL，事故時很難保證每次操作都有相同邏輯。</p>
<h2 id="rerun-判讀">Rerun 判讀</h2>
<p>Rerun 判讀的責任是確認重跑是否會造成二次傷害。資料任務失敗後，最危險的動作是未確認輸出語意就直接重跑。</p>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀</th>
          <th>下一步</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>任務失敗但沒有輸出</td>
          <td>可用同版本重跑</td>
          <td>確認輸入仍可取得</td>
      </tr>
      <tr>
          <td>部分 partition 已寫入</td>
          <td>需要去重或覆寫策略</td>
          <td>檢查 output mode</td>
      </tr>
      <tr>
          <td>下游已消費錯誤輸出</td>
          <td>需要通知下游或重算衍生資料</td>
          <td>標記污染區間</td>
      </tr>
      <tr>
          <td>input schema 已改</td>
          <td>舊版本重跑條件可能失效</td>
          <td>用相容版本或轉換層</td>
      </tr>
      <tr>
          <td>streaming checkpoint 壞</td>
          <td>重跑可能重複消費或漏資料</td>
          <td>評估 checkpoint repair / replay</td>
      </tr>
  </tbody>
</table>
<p>這張表讓 rerun 從「再跑一次」變成有條件的恢復策略。資料正確性比任務綠燈更重要；綠燈只代表 job 完成，不代表輸出可信。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>Data pipeline 部署總覽：回 <a href="../">Data Pipeline 部署 CI/CD</a>。</li>
<li>Migration 概念：讀 <a href="/blog/ci/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明資料或結構變更如何在服務不中斷前提下受控推進">Migration</a>。</li>
<li>Gate 原理：讀 <a href="../../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a>。</li>
</ul>
]]></content:encoded></item><item><title>Desktop client 簽章、公證與自動更新流程</title><link>https://tarrragon.github.io/blog/ci/desktop-client-deploy/signing-notarization-update-flow/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/desktop-client-deploy/signing-notarization-update-flow/</guid><description>&lt;p>Desktop client 發布流程的核心責任是讓多平台安裝包可信、可更新、可回復。桌面應用和 web 不同，使用者會下載 installer 或 package 到本機；CI/CD 需要處理平台差異、code signing、notarization、auto-update feed、delta package 與多版本共存。&lt;/p>
&lt;h2 id="流程定位">流程定位&lt;/h2>
&lt;p>Desktop client 的風險集中在作業系統信任鏈與更新通道。macOS、Windows、Linux 對簽章、安裝包格式與安全提示的要求不同；同一份 source 通常會產生多個平台 artifact，因此 workflow 要把平台 matrix、簽章 secret 與 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/release-channel/" data-link-title="Release Channel" data-link-desc="說明 stable、beta、internal 等發行通道如何控制 artifact 接觸到的使用者範圍">Release Channel&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>Build&lt;/td>
 &lt;td>產生 &lt;code>.dmg&lt;/code>、&lt;code>.pkg&lt;/code>、&lt;code>.msi&lt;/code>、AppImage 等&lt;/td>
 &lt;td>平台 matrix 是否完整&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Signing&lt;/td>
 &lt;td>建立 OS 信任&lt;/td>
 &lt;td>certificate、timestamp、keychain&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Notarize&lt;/td>
 &lt;td>通過 macOS 公證或平台審查&lt;/td>
 &lt;td>staple、gatekeeper 是否通過&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Release&lt;/td>
 &lt;td>發布到 channel 或 download page&lt;/td>
 &lt;td>stable / beta / internal 分流&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Update&lt;/td>
 &lt;td>推送 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/update-feed/" data-link-title="Update Feed" data-link-desc="說明桌面與客戶端應用如何透過更新來源取得已簽章版本與回復路徑">Update Feed&lt;/a> 或 delta package&lt;/td>
 &lt;td>feed 簽章、版本相容、回退策略&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Recovery&lt;/td>
 &lt;td>hotfix、rollback channel、停用更新&lt;/td>
 &lt;td>是否能阻止錯誤版本擴散&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Build 階段負責產生平台專屬 artifact。Flutter Desktop、Electron 與 Tauri 的輸出格式不同，但共同要求是每個 artifact 都能追到 commit、workflow run 與 dependency lock。&lt;/p>
&lt;p>Signing 階段負責讓 OS 信任安裝包。Windows code signing certificate、macOS Developer ID、timestamp server 與 Linux package signing key 都是發布能力；secret 應放在受控環境，並限制能觸發 signing job 的分支與 reviewer。&lt;/p>
&lt;p>Notarize 階段負責處理 macOS 信任 gate。macOS app 即使完成簽章，也常需要 notarization 與 stapling；CI 要把 notarization log 保存下來，否則使用者看到 Gatekeeper 警告時很難回溯。&lt;/p>
&lt;p>Release 階段負責把 artifact 放到正確 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/release-channel/" data-link-title="Release Channel" data-link-desc="說明 stable、beta、internal 等發行通道如何控制 artifact 接觸到的使用者範圍">Release Channel&lt;/a>。Internal、beta、stable 與 enterprise channel 的 gate 不同；CI/CD 要避免未審核的 beta artifact 被 stable feed 取用。&lt;/p>
&lt;p>Update 階段負責維持升級路徑。&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/update-feed/" data-link-title="Update Feed" data-link-desc="說明桌面與客戶端應用如何透過更新來源取得已簽章版本與回復路徑">Update Feed&lt;/a>、delta package、signature、minimum supported version 與 rollback channel 要一起設計；更新壞掉時，使用者可能卡在需要人工修復的版本。&lt;/p>
&lt;p>Recovery 階段負責止血。桌面客戶端常用方式是撤下 update feed、發布 hotfix、切換 rollback channel、停用 remote feature 或要求最低版本；每種方式都依賴 app 內建相容支援。&lt;/p>
&lt;h2 id="平台差異判讀">平台差異判讀&lt;/h2>
&lt;p>平台差異判讀的責任是讓 CI matrix 對應真實發布風險。桌面發布除了確認「三平台都 build 成功」，還要確認每個平台的安裝、啟動、更新與卸載行為。&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>macOS&lt;/td>
 &lt;td>Developer ID、notarization、universal binary&lt;/td>
 &lt;td>Gatekeeper、arm64 / x64 啟動&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Windows&lt;/td>
 &lt;td>Authenticode、SmartScreen、installer 權限&lt;/td>
 &lt;td>安裝、更新、卸載、權限提示&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Linux&lt;/td>
 &lt;td>AppImage、deb、rpm、repository key&lt;/td>
 &lt;td>dependency、desktop entry、sandbox&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這張表的用途是避免平台細節被單一「desktop build」欄位抹平。每個 OS 的失敗代價不同，CI 應保留平台專屬 gate。&lt;/p></description><content:encoded><![CDATA[<p>Desktop client 發布流程的核心責任是讓多平台安裝包可信、可更新、可回復。桌面應用和 web 不同，使用者會下載 installer 或 package 到本機；CI/CD 需要處理平台差異、code signing、notarization、auto-update feed、delta package 與多版本共存。</p>
<h2 id="流程定位">流程定位</h2>
<p>Desktop client 的風險集中在作業系統信任鏈與更新通道。macOS、Windows、Linux 對簽章、安裝包格式與安全提示的要求不同；同一份 source 通常會產生多個平台 artifact，因此 workflow 要把平台 matrix、簽章 secret 與 <a href="/blog/ci/knowledge-cards/release-channel/" data-link-title="Release Channel" data-link-desc="說明 stable、beta、internal 等發行通道如何控制 artifact 接觸到的使用者範圍">Release Channel</a> 拆清楚。</p>
<table>
  <thead>
      <tr>
          <th>階段</th>
          <th>責任</th>
          <th>判讀訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Build</td>
          <td>產生 <code>.dmg</code>、<code>.pkg</code>、<code>.msi</code>、AppImage 等</td>
          <td>平台 matrix 是否完整</td>
      </tr>
      <tr>
          <td>Signing</td>
          <td>建立 OS 信任</td>
          <td>certificate、timestamp、keychain</td>
      </tr>
      <tr>
          <td>Notarize</td>
          <td>通過 macOS 公證或平台審查</td>
          <td>staple、gatekeeper 是否通過</td>
      </tr>
      <tr>
          <td>Release</td>
          <td>發布到 channel 或 download page</td>
          <td>stable / beta / internal 分流</td>
      </tr>
      <tr>
          <td>Update</td>
          <td>推送 <a href="/blog/ci/knowledge-cards/update-feed/" data-link-title="Update Feed" data-link-desc="說明桌面與客戶端應用如何透過更新來源取得已簽章版本與回復路徑">Update Feed</a> 或 delta package</td>
          <td>feed 簽章、版本相容、回退策略</td>
      </tr>
      <tr>
          <td>Recovery</td>
          <td>hotfix、rollback channel、停用更新</td>
          <td>是否能阻止錯誤版本擴散</td>
      </tr>
  </tbody>
</table>
<p>Build 階段負責產生平台專屬 artifact。Flutter Desktop、Electron 與 Tauri 的輸出格式不同，但共同要求是每個 artifact 都能追到 commit、workflow run 與 dependency lock。</p>
<p>Signing 階段負責讓 OS 信任安裝包。Windows code signing certificate、macOS Developer ID、timestamp server 與 Linux package signing key 都是發布能力；secret 應放在受控環境，並限制能觸發 signing job 的分支與 reviewer。</p>
<p>Notarize 階段負責處理 macOS 信任 gate。macOS app 即使完成簽章，也常需要 notarization 與 stapling；CI 要把 notarization log 保存下來，否則使用者看到 Gatekeeper 警告時很難回溯。</p>
<p>Release 階段負責把 artifact 放到正確 <a href="/blog/ci/knowledge-cards/release-channel/" data-link-title="Release Channel" data-link-desc="說明 stable、beta、internal 等發行通道如何控制 artifact 接觸到的使用者範圍">Release Channel</a>。Internal、beta、stable 與 enterprise channel 的 gate 不同；CI/CD 要避免未審核的 beta artifact 被 stable feed 取用。</p>
<p>Update 階段負責維持升級路徑。<a href="/blog/ci/knowledge-cards/update-feed/" data-link-title="Update Feed" data-link-desc="說明桌面與客戶端應用如何透過更新來源取得已簽章版本與回復路徑">Update Feed</a>、delta package、signature、minimum supported version 與 rollback channel 要一起設計；更新壞掉時，使用者可能卡在需要人工修復的版本。</p>
<p>Recovery 階段負責止血。桌面客戶端常用方式是撤下 update feed、發布 hotfix、切換 rollback channel、停用 remote feature 或要求最低版本；每種方式都依賴 app 內建相容支援。</p>
<h2 id="平台差異判讀">平台差異判讀</h2>
<p>平台差異判讀的責任是讓 CI matrix 對應真實發布風險。桌面發布除了確認「三平台都 build 成功」，還要確認每個平台的安裝、啟動、更新與卸載行為。</p>
<table>
  <thead>
      <tr>
          <th>平台</th>
          <th>高風險點</th>
          <th>驗證方向</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>macOS</td>
          <td>Developer ID、notarization、universal binary</td>
          <td>Gatekeeper、arm64 / x64 啟動</td>
      </tr>
      <tr>
          <td>Windows</td>
          <td>Authenticode、SmartScreen、installer 權限</td>
          <td>安裝、更新、卸載、權限提示</td>
      </tr>
      <tr>
          <td>Linux</td>
          <td>AppImage、deb、rpm、repository key</td>
          <td>dependency、desktop entry、sandbox</td>
      </tr>
  </tbody>
</table>
<p>這張表的用途是避免平台細節被單一「desktop build」欄位抹平。每個 OS 的失敗代價不同，CI 應保留平台專屬 gate。</p>
<h2 id="update-feed-契約">Update feed 契約</h2>
<p><a href="/blog/ci/knowledge-cards/update-feed/" data-link-title="Update Feed" data-link-desc="說明桌面與客戶端應用如何透過更新來源取得已簽章版本與回復路徑">Update Feed</a> 契約的責任是讓已安裝使用者安全升級。Auto-update 需要簽章、版本比較、channel、最低版本與回退策略共同成立，才能讓新版本 URL 進入 feed。</p>
<ol>
<li>Feed 只指向已簽章且已驗證的 artifact。</li>
<li>Stable feed 只接收 stable release，beta feed 只接收 beta release。</li>
<li>App 啟動時能處理 feed 暫時不可用。</li>
<li>Delta update 失敗時能 fallback 到 full installer。</li>
<li>錯誤版本要能從 feed 撤下，並讓未更新使用者停止取得。</li>
<li>已更新使用者要有 hotfix 或 rollback channel。</li>
</ol>
<p>這些條件讓更新通道具備操作性。若 app 只知道「看到新版就下載」，錯誤 feed 會把事故放大到所有啟動中的使用者。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>Desktop 部署總覽：回 <a href="../">Desktop Client 部署 CI/CD</a>。</li>
<li>App 發布通用觀念：讀 <a href="../../app-deploy/signing-store-rollout-flow/">App 簽章、商店審核與分批發布流程</a>。</li>
<li>簽章術語：讀 <a href="/blog/ci/knowledge-cards/app-signing/" data-link-title="App Signing" data-link-desc="說明行動與桌面應用的簽章憑證如何影響發布能力">App Signing</a>。</li>
</ul>
]]></content:encoded></item><item><title>IaC plan、apply、drift 與 recovery 流程</title><link>https://tarrragon.github.io/blog/ci/iac-platform-deploy/plan-apply-drift-recovery-flow/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/iac-platform-deploy/plan-apply-drift-recovery-flow/</guid><description>&lt;p>IaC 發布流程的核心責任是把基礎設施變更變成可審查、可套用、可追溯的狀態轉移。Terraform、Pulumi、Helm 或平台自動化會改變網路、權限、資料庫、節點、DNS 與部署平台，因此 CI/CD 要把 plan、review、apply、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/infrastructure-drift/" data-link-title="Infrastructure Drift" data-link-desc="說明真實基礎設施狀態與 IaC 宣告分叉時的偵測、判讀與修復責任">Infrastructure Drift&lt;/a> 與 recovery 分成明確 gate。&lt;/p>
&lt;h2 id="流程定位">流程定位&lt;/h2>
&lt;p>IaC 的風險集中在共享狀態與不可逆資源。應用部署失敗常可回退 artifact；基礎設施變更可能刪除資料、替換節點、改掉 IAM 權限或讓 state 與真實環境分叉。發布流程應讓 reviewer 在 apply 前看到「將要改什麼」，並讓 apply 後能確認「環境是否真的符合宣告」。&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>Plan&lt;/td>
 &lt;td>預覽資源差異與風險&lt;/td>
 &lt;td>create / update / replace / destroy&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Review&lt;/td>
 &lt;td>審核變更意圖、權限與影響面&lt;/td>
 &lt;td>高風險資源、跨環境、資料資源&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Apply&lt;/td>
 &lt;td>在鎖定狀態下套用變更&lt;/td>
 &lt;td>state lock、timeout、partial apply&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Verify&lt;/td>
 &lt;td>確認環境符合預期&lt;/td>
 &lt;td>health、policy、smoke、connectivity&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/infrastructure-drift/" data-link-title="Infrastructure Drift" data-link-desc="說明真實基礎設施狀態與 IaC 宣告分叉時的偵測、判讀與修復責任">Infrastructure Drift&lt;/a>&lt;/td>
 &lt;td>偵測真實環境與宣告分叉&lt;/td>
 &lt;td>手動 hotfix、console edit、外部系統&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Recovery&lt;/td>
 &lt;td>回退、補正或 state repair&lt;/td>
 &lt;td>是否能安全恢復服務與 state&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Plan 階段負責產生可審查差異。Plan 是 reviewer 判斷資源替換、權限擴大、資料刪除與網路暴露的主要材料。CI 應保留 plan artifact，讓 apply 使用同一份輸入與版本。&lt;/p>
&lt;p>Review 階段負責把風險放到正確 owner。平台、資安、資料庫或服務 owner 應依資源類型參與審核；高風險變更需要額外 gate，例如 maintenance window、人工 approval 或雙人審核。&lt;/p>
&lt;p>Apply 階段負責把宣告狀態寫入環境。&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/state-lock/" data-link-title="State Lock" data-link-desc="說明 IaC apply 如何用狀態鎖避免併發變更覆寫基礎設施狀態">State Lock&lt;/a>、credential、workspace 與環境變數都要固定；partial apply 或 timeout 後，要先判斷 state 與真實資源是否一致，再決定下一步。&lt;/p>
&lt;p>Verify 階段負責確認平台可用。Apply 成功只代表 provider API 接受變更；仍需要 connectivity test、policy check、service smoke test、DNS / certificate check 或 cluster health，確認服務真的能跑。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/infrastructure-drift/" data-link-title="Infrastructure Drift" data-link-desc="說明真實基礎設施狀態與 IaC 宣告分叉時的偵測、判讀與修復責任">Infrastructure Drift&lt;/a> 階段負責發現宣告與現況分叉。手動 hotfix、雲端 console 調整、外部 controller 或 provider 預設值都可能造成 drift；drift detection 要定期執行，並把修復責任導回宣告檔。&lt;/p>
&lt;p>Recovery 階段負責處理失敗套用。IaC 回復不一定是 &lt;code>git revert&lt;/code> 後 apply；可能需要 import、state mv、taint / untaint、手動修復資料資源或 forward fix。流程要先保護資料與服務，再修正宣告與 state。&lt;/p>
&lt;h2 id="plan-review-判讀">Plan review 判讀&lt;/h2>
&lt;p>Plan review 的責任是讓變更影響在 apply 前被看見。Reviewer 應依資源語意判斷，讓 diff 行數退居輔助訊號。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Plan 訊號&lt;/th>
 &lt;th>判讀&lt;/th>
 &lt;th>下一步&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>destroy&lt;/code>&lt;/td>
 &lt;td>資源將被刪除&lt;/td>
 &lt;td>確認資料、依賴與備份&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>replace&lt;/code>&lt;/td>
 &lt;td>先刪後建或重建資源&lt;/td>
 &lt;td>檢查 downtime、IP、DNS、資料&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>IAM 權限擴大&lt;/td>
 &lt;td>blast radius 增加&lt;/td>
 &lt;td>資安或平台 owner 審核&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Network 開放&lt;/td>
 &lt;td>暴露面增加&lt;/td>
 &lt;td>檢查 security group / firewall&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>State 大量漂移&lt;/td>
 &lt;td>宣告與現況長期分叉&lt;/td>
 &lt;td>先處理 drift，再進 feature change&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這張表讓 review 從「有人按 approve」變成風險判讀。IaC review 的價值在於提前看見不可逆或高代價變更。&lt;/p></description><content:encoded><![CDATA[<p>IaC 發布流程的核心責任是把基礎設施變更變成可審查、可套用、可追溯的狀態轉移。Terraform、Pulumi、Helm 或平台自動化會改變網路、權限、資料庫、節點、DNS 與部署平台，因此 CI/CD 要把 plan、review、apply、<a href="/blog/ci/knowledge-cards/infrastructure-drift/" data-link-title="Infrastructure Drift" data-link-desc="說明真實基礎設施狀態與 IaC 宣告分叉時的偵測、判讀與修復責任">Infrastructure Drift</a> 與 recovery 分成明確 gate。</p>
<h2 id="流程定位">流程定位</h2>
<p>IaC 的風險集中在共享狀態與不可逆資源。應用部署失敗常可回退 artifact；基礎設施變更可能刪除資料、替換節點、改掉 IAM 權限或讓 state 與真實環境分叉。發布流程應讓 reviewer 在 apply 前看到「將要改什麼」，並讓 apply 後能確認「環境是否真的符合宣告」。</p>
<table>
  <thead>
      <tr>
          <th>階段</th>
          <th>責任</th>
          <th>判讀訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Plan</td>
          <td>預覽資源差異與風險</td>
          <td>create / update / replace / destroy</td>
      </tr>
      <tr>
          <td>Review</td>
          <td>審核變更意圖、權限與影響面</td>
          <td>高風險資源、跨環境、資料資源</td>
      </tr>
      <tr>
          <td>Apply</td>
          <td>在鎖定狀態下套用變更</td>
          <td>state lock、timeout、partial apply</td>
      </tr>
      <tr>
          <td>Verify</td>
          <td>確認環境符合預期</td>
          <td>health、policy、smoke、connectivity</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/infrastructure-drift/" data-link-title="Infrastructure Drift" data-link-desc="說明真實基礎設施狀態與 IaC 宣告分叉時的偵測、判讀與修復責任">Infrastructure Drift</a></td>
          <td>偵測真實環境與宣告分叉</td>
          <td>手動 hotfix、console edit、外部系統</td>
      </tr>
      <tr>
          <td>Recovery</td>
          <td>回退、補正或 state repair</td>
          <td>是否能安全恢復服務與 state</td>
      </tr>
  </tbody>
</table>
<p>Plan 階段負責產生可審查差異。Plan 是 reviewer 判斷資源替換、權限擴大、資料刪除與網路暴露的主要材料。CI 應保留 plan artifact，讓 apply 使用同一份輸入與版本。</p>
<p>Review 階段負責把風險放到正確 owner。平台、資安、資料庫或服務 owner 應依資源類型參與審核；高風險變更需要額外 gate，例如 maintenance window、人工 approval 或雙人審核。</p>
<p>Apply 階段負責把宣告狀態寫入環境。<a href="/blog/ci/knowledge-cards/state-lock/" data-link-title="State Lock" data-link-desc="說明 IaC apply 如何用狀態鎖避免併發變更覆寫基礎設施狀態">State Lock</a>、credential、workspace 與環境變數都要固定；partial apply 或 timeout 後，要先判斷 state 與真實資源是否一致，再決定下一步。</p>
<p>Verify 階段負責確認平台可用。Apply 成功只代表 provider API 接受變更；仍需要 connectivity test、policy check、service smoke test、DNS / certificate check 或 cluster health，確認服務真的能跑。</p>
<p><a href="/blog/ci/knowledge-cards/infrastructure-drift/" data-link-title="Infrastructure Drift" data-link-desc="說明真實基礎設施狀態與 IaC 宣告分叉時的偵測、判讀與修復責任">Infrastructure Drift</a> 階段負責發現宣告與現況分叉。手動 hotfix、雲端 console 調整、外部 controller 或 provider 預設值都可能造成 drift；drift detection 要定期執行，並把修復責任導回宣告檔。</p>
<p>Recovery 階段負責處理失敗套用。IaC 回復不一定是 <code>git revert</code> 後 apply；可能需要 import、state mv、taint / untaint、手動修復資料資源或 forward fix。流程要先保護資料與服務，再修正宣告與 state。</p>
<h2 id="plan-review-判讀">Plan review 判讀</h2>
<p>Plan review 的責任是讓變更影響在 apply 前被看見。Reviewer 應依資源語意判斷，讓 diff 行數退居輔助訊號。</p>
<table>
  <thead>
      <tr>
          <th>Plan 訊號</th>
          <th>判讀</th>
          <th>下一步</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>destroy</code></td>
          <td>資源將被刪除</td>
          <td>確認資料、依賴與備份</td>
      </tr>
      <tr>
          <td><code>replace</code></td>
          <td>先刪後建或重建資源</td>
          <td>檢查 downtime、IP、DNS、資料</td>
      </tr>
      <tr>
          <td>IAM 權限擴大</td>
          <td>blast radius 增加</td>
          <td>資安或平台 owner 審核</td>
      </tr>
      <tr>
          <td>Network 開放</td>
          <td>暴露面增加</td>
          <td>檢查 security group / firewall</td>
      </tr>
      <tr>
          <td>State 大量漂移</td>
          <td>宣告與現況長期分叉</td>
          <td>先處理 drift，再進 feature change</td>
      </tr>
  </tbody>
</table>
<p>這張表讓 review 從「有人按 approve」變成風險判讀。IaC review 的價值在於提前看見不可逆或高代價變更。</p>
<h2 id="drift-處理路由">Drift 處理路由</h2>
<p>Drift 處理的責任是把現況重新帶回可管理狀態。Drift 發現後不應直接 apply 覆蓋，因為 drift 可能是事故 hotfix、外部系統自動調整或宣告檔過期。</p>
<ol>
<li>確認 drift 來源：人工 hotfix、provider 預設、外部 controller 或宣告過期。</li>
<li>判斷 drift 是否仍需要保留：若是真實修復，應回寫到 IaC。</li>
<li>判斷 apply 是否會破壞服務：特別看 replacement、destroy、權限與 network。</li>
<li>修正宣告或 state：必要時使用 import、state mv 或 provider-specific repair。</li>
<li>重新 plan，確認差異收斂到預期。</li>
</ol>
<p>這個路由讓 drift 修復具備審查性。直接在 console 裡補到看起來正常，會讓下一次 CI apply 把修復覆蓋掉。</p>
<h2 id="常見反模式">常見反模式</h2>
<p>反模式的共同問題是把 IaC 降成指令自動化，忽略它承擔的狀態治理責任。</p>
<table>
  <thead>
      <tr>
          <th>反模式</th>
          <th>風險</th>
          <th>替代做法</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>plan 與 apply 使用不同輸入</td>
          <td>review 內容與實際套用內容分叉</td>
          <td>保存 plan artifact 或鎖定版本</td>
      </tr>
      <tr>
          <td>沒有 <a href="/blog/ci/knowledge-cards/state-lock/" data-link-title="State Lock" data-link-desc="說明 IaC apply 如何用狀態鎖避免併發變更覆寫基礎設施狀態">State Lock</a></td>
          <td>併發 apply 覆寫狀態</td>
          <td>使用 remote backend 與 locking</td>
      </tr>
      <tr>
          <td>drift 長期忽略</td>
          <td>宣告失去可信度</td>
          <td>定期 drift detection 與 owner 路由</td>
      </tr>
      <tr>
          <td>高風險資源無額外 gate</td>
          <td>資料或網路變更直接進環境</td>
          <td>environment protection / approval</td>
      </tr>
  </tbody>
</table>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>IaC 部署總覽：回 <a href="../">IaC / Platform 部署 CI/CD</a>。</li>
<li>環境保護：讀 <a href="/blog/ci/knowledge-cards/environment-protection/" data-link-title="Environment Protection" data-link-desc="說明目標環境的審核、權限與放行條件如何保護發布">Environment Protection</a>。</li>
<li>Gate 原理：讀 <a href="../../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a>。</li>
</ul>
]]></content:encoded></item><item><title>Image build、scan、registry 與 promotion 流程</title><link>https://tarrragon.github.io/blog/ci/docker-deploy/image-supply-chain-flow/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/docker-deploy/image-supply-chain-flow/</guid><description>&lt;p>Image 供應鏈流程的核心責任是讓 container image 從 build 到 runtime 都可追溯。Image 同時包含 application、runtime、OS package 與 dependency；CI/CD 需要把 Dockerfile、base image、tag、scan、registry 與 deployment manifest 串成同一條供應鏈。&lt;/p>
&lt;h2 id="流程定位">流程定位&lt;/h2>
&lt;p>Image deployment 的風險集中在「看似同名、實際不同」的產物漂移。&lt;code>latest&lt;/code>、mutable tag、重新 build 與跨 registry promotion 都可能讓 staging 測過的 image 不等於 production 跑的 image。嚴謹流程應以 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/image-digest/" data-link-title="Image Digest" data-link-desc="說明 container image digest 如何作為不可變產物身分，支撐掃描、推進與 runtime 追溯">Image Digest&lt;/a> 或 immutable tag 作為 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>Build&lt;/td>
 &lt;td>從 Dockerfile 產生 image&lt;/td>
 &lt;td>base image、lockfile、build arg 是否固定&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Tag&lt;/td>
 &lt;td>建立查詢與推進入口&lt;/td>
 &lt;td>commit SHA、semver、digest 是否可追&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Scan&lt;/td>
 &lt;td>顯性化漏洞、secret、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/sbom/" data-link-title="SBOM" data-link-desc="說明 Software Bill of Materials 如何揭露 artifact 內含元件，支撐供應鏈掃描與例外治理">SBOM&lt;/a> 風險&lt;/td>
 &lt;td>阻擋門檻與例外流程是否存在&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/container-registry/" data-link-title="Container Registry" data-link-desc="說明容器產物儲存、權限與推進流程在 CD 中的責任">Container registry&lt;/a>&lt;/td>
 &lt;td>保存 image 並控制 promotion&lt;/td>
 &lt;td>immutable、retention、權限&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Runtime handoff&lt;/td>
 &lt;td>讓 deployment 使用已驗證 image&lt;/td>
 &lt;td>manifest 是否指向已掃描 digest&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Build 階段負責封裝 runtime。Multi-stage build、dependency cache、base image pinning 與 build secret 處理會直接影響安全性；CI 應能在乾淨 runner 上重建 image，避免開發機狀態被帶入。&lt;/p>
&lt;p>Tag 階段負責支援不同查詢情境。Commit SHA 適合事故追溯，semver 適合 release 溝通，&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/image-digest/" data-link-title="Image Digest" data-link-desc="說明 container image digest 如何作為不可變產物身分，支撐掃描、推進與 runtime 追溯">Image Digest&lt;/a> 適合 runtime 精準鎖定；production 判讀應以 digest 為準，tag 只作為人類入口。&lt;/p>
&lt;p>Scan 階段負責把風險分流。Vulnerability scan、secret scan、license scan 與 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/sbom/" data-link-title="SBOM" data-link-desc="說明 Software Bill of Materials 如何揭露 artifact 內含元件，支撐供應鏈掃描與例外治理">SBOM&lt;/a> 不應只是報表；流程要定義哪些風險阻擋發布、哪些風險允許例外、例外誰審核、何時重新評估。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/container-registry/" data-link-title="Container Registry" data-link-desc="說明容器產物儲存、權限與推進流程在 CD 中的責任">Container registry&lt;/a> 階段負責保存與推進 image。Registry 要處理權限、retention、immutability、promotion 與垃圾回收；若 production 直接從 feature branch push 的 tag 拉 image，供應鏈邊界就失去治理。&lt;/p>
&lt;p>Runtime handoff 階段負責把已驗證 image 交給部署平台。Kubernetes、ECS、Compose 或其他 runtime 都應指向已驗證 digest 或 immutable tag，並把 health、readiness、resource limit 與 rollback 連到同一次 release。&lt;/p>
&lt;h2 id="tag-與-digest-策略">Tag 與 digest 策略&lt;/h2>
&lt;p>Tag 策略的責任是讓人查得到、機器鎖得住。單一 tag 很難同時滿足可讀性、可追溯與不可變三個需求，因此實務上常搭配多個 tag 與 digest。&lt;/p></description><content:encoded><![CDATA[<p>Image 供應鏈流程的核心責任是讓 container image 從 build 到 runtime 都可追溯。Image 同時包含 application、runtime、OS package 與 dependency；CI/CD 需要把 Dockerfile、base image、tag、scan、registry 與 deployment manifest 串成同一條供應鏈。</p>
<h2 id="流程定位">流程定位</h2>
<p>Image deployment 的風險集中在「看似同名、實際不同」的產物漂移。<code>latest</code>、mutable tag、重新 build 與跨 registry promotion 都可能讓 staging 測過的 image 不等於 production 跑的 image。嚴謹流程應以 <a href="/blog/ci/knowledge-cards/image-digest/" data-link-title="Image Digest" data-link-desc="說明 container image digest 如何作為不可變產物身分，支撐掃描、推進與 runtime 追溯">Image Digest</a> 或 immutable tag 作為 artifact 身分。</p>
<table>
  <thead>
      <tr>
          <th>階段</th>
          <th>責任</th>
          <th>判讀訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Build</td>
          <td>從 Dockerfile 產生 image</td>
          <td>base image、lockfile、build arg 是否固定</td>
      </tr>
      <tr>
          <td>Tag</td>
          <td>建立查詢與推進入口</td>
          <td>commit SHA、semver、digest 是否可追</td>
      </tr>
      <tr>
          <td>Scan</td>
          <td>顯性化漏洞、secret、<a href="/blog/ci/knowledge-cards/sbom/" data-link-title="SBOM" data-link-desc="說明 Software Bill of Materials 如何揭露 artifact 內含元件，支撐供應鏈掃描與例外治理">SBOM</a> 風險</td>
          <td>阻擋門檻與例外流程是否存在</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/container-registry/" data-link-title="Container Registry" data-link-desc="說明容器產物儲存、權限與推進流程在 CD 中的責任">Container registry</a></td>
          <td>保存 image 並控制 promotion</td>
          <td>immutable、retention、權限</td>
      </tr>
      <tr>
          <td>Runtime handoff</td>
          <td>讓 deployment 使用已驗證 image</td>
          <td>manifest 是否指向已掃描 digest</td>
      </tr>
  </tbody>
</table>
<p>Build 階段負責封裝 runtime。Multi-stage build、dependency cache、base image pinning 與 build secret 處理會直接影響安全性；CI 應能在乾淨 runner 上重建 image，避免開發機狀態被帶入。</p>
<p>Tag 階段負責支援不同查詢情境。Commit SHA 適合事故追溯，semver 適合 release 溝通，<a href="/blog/ci/knowledge-cards/image-digest/" data-link-title="Image Digest" data-link-desc="說明 container image digest 如何作為不可變產物身分，支撐掃描、推進與 runtime 追溯">Image Digest</a> 適合 runtime 精準鎖定；production 判讀應以 digest 為準，tag 只作為人類入口。</p>
<p>Scan 階段負責把風險分流。Vulnerability scan、secret scan、license scan 與 <a href="/blog/ci/knowledge-cards/sbom/" data-link-title="SBOM" data-link-desc="說明 Software Bill of Materials 如何揭露 artifact 內含元件，支撐供應鏈掃描與例外治理">SBOM</a> 不應只是報表；流程要定義哪些風險阻擋發布、哪些風險允許例外、例外誰審核、何時重新評估。</p>
<p><a href="/blog/ci/knowledge-cards/container-registry/" data-link-title="Container Registry" data-link-desc="說明容器產物儲存、權限與推進流程在 CD 中的責任">Container registry</a> 階段負責保存與推進 image。Registry 要處理權限、retention、immutability、promotion 與垃圾回收；若 production 直接從 feature branch push 的 tag 拉 image，供應鏈邊界就失去治理。</p>
<p>Runtime handoff 階段負責把已驗證 image 交給部署平台。Kubernetes、ECS、Compose 或其他 runtime 都應指向已驗證 digest 或 immutable tag，並把 health、readiness、resource limit 與 rollback 連到同一次 release。</p>
<h2 id="tag-與-digest-策略">Tag 與 digest 策略</h2>
<p>Tag 策略的責任是讓人查得到、機器鎖得住。單一 tag 很難同時滿足可讀性、可追溯與不可變三個需求，因此實務上常搭配多個 tag 與 digest。</p>
<table>
  <thead>
      <tr>
          <th>標識</th>
          <th>適合用途</th>
          <th>風險</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Commit SHA</td>
          <td>從 runtime 回查 source</td>
          <td>對使用者不友善</td>
      </tr>
      <tr>
          <td>Semver</td>
          <td>對外 release 溝通</td>
          <td>tag 可能被覆寫，需搭配 immutability</td>
      </tr>
      <tr>
          <td>Branch tag</td>
          <td>preview / staging 快速迭代</td>
          <td>不適合作為 production 依據</td>
      </tr>
      <tr>
          <td>Digest</td>
          <td>runtime 精準鎖定</td>
          <td>人類閱讀成本高</td>
      </tr>
  </tbody>
</table>
<p>Production deployment 應能從 running pod 或 task 反查 image digest，再反查 registry metadata、scan report、workflow run 與 source commit。這條查詢路徑是 incident response 的基本能力。</p>
<h2 id="scan-gate-分流">Scan gate 分流</h2>
<p>Scan gate 的責任是讓安全訊號變成可操作路由。掃描工具會產生大量結果，沒有分流規則時，團隊會在兩種壞狀態間搖擺：全部阻擋導致發不出去，全部忽略導致掃描失去信任。</p>
<table>
  <thead>
      <tr>
          <th>結果類型</th>
          <th>策略</th>
          <th>下一步</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Critical exploitable</td>
          <td>阻擋 production promotion</td>
          <td>升級 dependency / base image</td>
      </tr>
      <tr>
          <td>High with mitigation</td>
          <td>需要審核例外與到期日</td>
          <td>記錄風險、設定重新掃描</td>
      </tr>
      <tr>
          <td>Base image aging</td>
          <td>排入 base image refresh</td>
          <td>建立定期更新節奏</td>
      </tr>
      <tr>
          <td>Secret in layer</td>
          <td>阻擋並輪替 secret</td>
          <td>重建 image、撤銷已暴露 credential</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/sbom/" data-link-title="SBOM" data-link-desc="說明 Software Bill of Materials 如何揭露 artifact 內含元件，支撐供應鏈掃描與例外治理">SBOM</a> missing</td>
          <td>阻擋高治理環境，低風險環境警告</td>
          <td>補 provenance / SBOM 產出</td>
      </tr>
  </tbody>
</table>
<p>這個分流讓 scan 成為 gate。例外流程要有 owner 與到期日，讓例外維持可追蹤、可重新評估。</p>
<h2 id="常見反模式">常見反模式</h2>
<p>反模式的共同問題是讓 image 身分失去穩定錨點。當 image 身分漂移，測試結果、掃描結果與 runtime 狀態會彼此分叉。</p>
<table>
  <thead>
      <tr>
          <th>反模式</th>
          <th>風險</th>
          <th>替代做法</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>production 使用 <code>latest</code></td>
          <td>running image 缺少精準身分</td>
          <td>使用 <a href="/blog/ci/knowledge-cards/image-digest/" data-link-title="Image Digest" data-link-desc="說明 container image digest 如何作為不可變產物身分，支撐掃描、推進與 runtime 追溯">Image Digest</a> 或 immutable tag</td>
      </tr>
      <tr>
          <td>staging 與 production 各自 build</td>
          <td>測試產物與上線產物分叉</td>
          <td>build once，promote same image</td>
      </tr>
      <tr>
          <td>build secret 留在 layer</td>
          <td>secret 進入 registry 與節點</td>
          <td>使用 BuildKit secret mount</td>
      </tr>
      <tr>
          <td>scan 只報告不阻擋</td>
          <td>高風險漏洞仍進 production</td>
          <td>定義阻擋門檻與例外流程</td>
      </tr>
  </tbody>
</table>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>Image 部署總覽：回 <a href="../">Docker / Image 部署 CI/CD</a>。</li>
<li>Registry 術語：讀 <a href="/blog/ci/knowledge-cards/container-registry/" data-link-title="Container Registry" data-link-desc="說明容器產物儲存、權限與推進流程在 CD 中的責任">Container Registry</a>。</li>
<li>後端 runtime 部署：讀 <a href="../../backend-deploy/">後端部署 CI/CD</a>。</li>
</ul>
]]></content:encoded></item><item><title>Serverless function 版本、事件來源與回復流程</title><link>https://tarrragon.github.io/blog/ci/serverless-deploy/function-version-event-flow/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/serverless-deploy/function-version-event-flow/</guid><description>&lt;p>Serverless 發布流程的核心責任是把函式 artifact、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/function-alias/" data-link-title="Function Alias" data-link-desc="說明 serverless function alias 如何把穩定入口指向特定版本並支援流量切換與回復">Function Alias&lt;/a>、權限與 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/event-source/" data-link-title="Event Source" data-link-desc="說明 serverless 與事件驅動流程中觸發來源如何影響 retry、dead-letter 與回復策略">Event Source&lt;/a> 一起推進。Serverless 部署看起來比長駐服務短，但每次 invocation 都依賴 runtime、IAM、event source、retry policy 與 observability；CI/CD 需要把這些條件視為發布契約。&lt;/p>
&lt;h2 id="流程定位">流程定位&lt;/h2>
&lt;p>Serverless 的風險集中在觸發條件。函式部署成功只代表新版本存在，實際風險會在 HTTP request、queue message、topic event、scheduled job 或 edge request 觸發時出現。發布流程要能區分「版本建立成功」「alias 切流量成功」「事件來源行為正確」三件事。&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>Package&lt;/td>
 &lt;td>產生 function bundle / layer&lt;/td>
 &lt;td>dependency、runtime target 是否固定&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Version&lt;/td>
 &lt;td>發布 immutable function version&lt;/td>
 &lt;td>version 是否可追到 commit&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Alias / traffic&lt;/td>
 &lt;td>控制新舊版本流量&lt;/td>
 &lt;td>alias 權重、錯誤率、冷啟動&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Permission&lt;/td>
 &lt;td>限制 IAM、secret、resource policy&lt;/td>
 &lt;td>最小權限與環境隔離&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/event-source/" data-link-title="Event Source" data-link-desc="說明 serverless 與事件驅動流程中觸發來源如何影響 retry、dead-letter 與回復策略">Event Source&lt;/a>&lt;/td>
 &lt;td>管理 trigger、retry、dead-letter&lt;/td>
 &lt;td>重試與毒訊息處理是否明確&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Recovery&lt;/td>
 &lt;td>alias rollback、disable trigger、replay&lt;/td>
 &lt;td>是否能止血與修補資料&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Package 階段負責產生可執行 bundle。Serverless 常見失敗是本機 dependency 可用，但打包後缺檔、runtime target 不符、native extension 不相容或 layer 版本漂移；CI 應在接近目標 runtime 的環境做 smoke test。&lt;/p>
&lt;p>Version 階段負責建立不可變版本。直接覆蓋 &lt;code>$LATEST&lt;/code> 會讓事故追溯困難；正式流量應指向 version 或 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/function-alias/" data-link-title="Function Alias" data-link-desc="說明 serverless function alias 如何把穩定入口指向特定版本並支援流量切換與回復">Function Alias&lt;/a>，讓 rollback 能把 alias 切回前一個已知版本。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/function-alias/" data-link-title="Function Alias" data-link-desc="說明 serverless function alias 如何把穩定入口指向特定版本並支援流量切換與回復">Function Alias&lt;/a> / traffic 階段負責控制流量切換。HTTP function 可以用少量權重 canary；queue trigger 則要觀察 batch failure、retry、dead-letter 與 downstream side effect，因為同一個錯誤 event 可能被重試多次。&lt;/p>
&lt;p>Permission 階段負責限制 blast radius。Serverless 函式容易因部署方便而累積過大 IAM 權限；每個 function 應只拿到必要 resource、secret 與 network access，並把 production secret 與 preview / staging 隔離。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/event-source/" data-link-title="Event Source" data-link-desc="說明 serverless 與事件驅動流程中觸發來源如何影響 retry、dead-letter 與回復策略">Event Source&lt;/a> 階段負責定義失敗重送語意。Queue、topic、object storage、HTTP 與 scheduler 的錯誤行為不同；CI/CD 文件要記錄 retry 次數、dead-letter destination、batch size、concurrency limit 與 replay 條件。&lt;/p>
&lt;p>Recovery 階段負責止血。Serverless 常見止血方式是 alias rollback、停用 trigger、降低 concurrency、清理毒訊息、重放事件或 forward fix；只回退 code 版本不一定能處理已經排入 queue 的事件。&lt;/p></description><content:encoded><![CDATA[<p>Serverless 發布流程的核心責任是把函式 artifact、<a href="/blog/ci/knowledge-cards/function-alias/" data-link-title="Function Alias" data-link-desc="說明 serverless function alias 如何把穩定入口指向特定版本並支援流量切換與回復">Function Alias</a>、權限與 <a href="/blog/ci/knowledge-cards/event-source/" data-link-title="Event Source" data-link-desc="說明 serverless 與事件驅動流程中觸發來源如何影響 retry、dead-letter 與回復策略">Event Source</a> 一起推進。Serverless 部署看起來比長駐服務短，但每次 invocation 都依賴 runtime、IAM、event source、retry policy 與 observability；CI/CD 需要把這些條件視為發布契約。</p>
<h2 id="流程定位">流程定位</h2>
<p>Serverless 的風險集中在觸發條件。函式部署成功只代表新版本存在，實際風險會在 HTTP request、queue message、topic event、scheduled job 或 edge request 觸發時出現。發布流程要能區分「版本建立成功」「alias 切流量成功」「事件來源行為正確」三件事。</p>
<table>
  <thead>
      <tr>
          <th>階段</th>
          <th>責任</th>
          <th>判讀訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Package</td>
          <td>產生 function bundle / layer</td>
          <td>dependency、runtime target 是否固定</td>
      </tr>
      <tr>
          <td>Version</td>
          <td>發布 immutable function version</td>
          <td>version 是否可追到 commit</td>
      </tr>
      <tr>
          <td>Alias / traffic</td>
          <td>控制新舊版本流量</td>
          <td>alias 權重、錯誤率、冷啟動</td>
      </tr>
      <tr>
          <td>Permission</td>
          <td>限制 IAM、secret、resource policy</td>
          <td>最小權限與環境隔離</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/event-source/" data-link-title="Event Source" data-link-desc="說明 serverless 與事件驅動流程中觸發來源如何影響 retry、dead-letter 與回復策略">Event Source</a></td>
          <td>管理 trigger、retry、dead-letter</td>
          <td>重試與毒訊息處理是否明確</td>
      </tr>
      <tr>
          <td>Recovery</td>
          <td>alias rollback、disable trigger、replay</td>
          <td>是否能止血與修補資料</td>
      </tr>
  </tbody>
</table>
<p>Package 階段負責產生可執行 bundle。Serverless 常見失敗是本機 dependency 可用，但打包後缺檔、runtime target 不符、native extension 不相容或 layer 版本漂移；CI 應在接近目標 runtime 的環境做 smoke test。</p>
<p>Version 階段負責建立不可變版本。直接覆蓋 <code>$LATEST</code> 會讓事故追溯困難；正式流量應指向 version 或 <a href="/blog/ci/knowledge-cards/function-alias/" data-link-title="Function Alias" data-link-desc="說明 serverless function alias 如何把穩定入口指向特定版本並支援流量切換與回復">Function Alias</a>，讓 rollback 能把 alias 切回前一個已知版本。</p>
<p><a href="/blog/ci/knowledge-cards/function-alias/" data-link-title="Function Alias" data-link-desc="說明 serverless function alias 如何把穩定入口指向特定版本並支援流量切換與回復">Function Alias</a> / traffic 階段負責控制流量切換。HTTP function 可以用少量權重 canary；queue trigger 則要觀察 batch failure、retry、dead-letter 與 downstream side effect，因為同一個錯誤 event 可能被重試多次。</p>
<p>Permission 階段負責限制 blast radius。Serverless 函式容易因部署方便而累積過大 IAM 權限；每個 function 應只拿到必要 resource、secret 與 network access，並把 production secret 與 preview / staging 隔離。</p>
<p><a href="/blog/ci/knowledge-cards/event-source/" data-link-title="Event Source" data-link-desc="說明 serverless 與事件驅動流程中觸發來源如何影響 retry、dead-letter 與回復策略">Event Source</a> 階段負責定義失敗重送語意。Queue、topic、object storage、HTTP 與 scheduler 的錯誤行為不同；CI/CD 文件要記錄 retry 次數、dead-letter destination、batch size、concurrency limit 與 replay 條件。</p>
<p>Recovery 階段負責止血。Serverless 常見止血方式是 alias rollback、停用 trigger、降低 concurrency、清理毒訊息、重放事件或 forward fix；只回退 code 版本不一定能處理已經排入 queue 的事件。</p>
<h2 id="事件來源判讀">事件來源判讀</h2>
<p>事件來源判讀的責任是找出失敗是否可重試。Serverless 常被誤判為「函式自己失敗」，但實際根因可能是 event schema、權限、上游重試或下游限流。</p>
<table>
  <thead>
      <tr>
          <th>Event source</th>
          <th>常見失敗</th>
          <th>下一步</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>HTTP / API</td>
          <td>status code、timeout、冷啟動</td>
          <td>看 latency、concurrency、alias</td>
      </tr>
      <tr>
          <td>Queue</td>
          <td>batch failure、毒訊息、重試風暴</td>
          <td>看 DLQ、batch size、visibility timeout</td>
      </tr>
      <tr>
          <td>Topic</td>
          <td>event schema 漂移</td>
          <td>驗證 publisher / subscriber 契約</td>
      </tr>
      <tr>
          <td>Object store</td>
          <td>權限或路徑 pattern 錯誤</td>
          <td>檢查 resource policy 與 filter</td>
      </tr>
      <tr>
          <td>Scheduler</td>
          <td>timezone、重入、上次執行未完成</td>
          <td>檢查 idempotency 與 lock</td>
      </tr>
  </tbody>
</table>
<p>這張表讓 release failure 能被導向正確 owner。若 event schema 變了，修 function 可能只是表面補丁；真正的 gate 要加在 publisher contract 或 sample event validation。</p>
<h2 id="最小發布-gate">最小發布 gate</h2>
<p>Serverless workflow 的最小 gate 應覆蓋 package、permission、event 與 alias。缺其中一段，部署成功就可能只是建立了一個尚未被驗證的函式版本。</p>
<ol>
<li>Package bundle，固定 runtime target 與 dependency。</li>
<li>對 bundle 執行 unit / contract / sample event test。</li>
<li>用 least privilege policy 做 deploy dry run 或 policy diff。</li>
<li>發布 immutable function version。</li>
<li>用 alias 將少量流量導向新版本。</li>
<li>觀察 error、latency、retry、DLQ 與 downstream 指標。</li>
<li>指標穩定後提高 alias 權重或完成切換。</li>
<li>指標觸發 tripwire 時切回 alias、停用 trigger 或啟動 repair。</li>
</ol>
<p>這個流程把 Serverless 發布從「上傳函式」提升成可回復流程。對事件驅動函式而言，trigger 與 retry policy 是發布契約的一部分。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>Serverless 部署總覽：回 <a href="../">Serverless 部署 CI/CD</a>。</li>
<li>Rollout 概念：讀 <a href="/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">Rollout Strategy</a>。</li>
<li>失敗處理：讀 <a href="../../github-actions-failure-flow/">CI 失敗到修復發布流程</a>。</li>
</ul>
]]></content:encoded></item><item><title>前端 artifact 與 preview deployment 流程</title><link>https://tarrragon.github.io/blog/ci/frontend-deploy/static-artifact-preview-flow/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/frontend-deploy/static-artifact-preview-flow/</guid><description>&lt;p>前端 artifact 流程的核心責任是讓測試、預覽與正式發布使用同一份靜態產物。前端部署常見輸出是 HTML、CSS、JavaScript、圖片、sourcemap 與搜尋索引；這些產物一旦被重新 build，就可能受到環境變數、依賴版本、base URL 或 framework 設定影響，因此 CI/CD 需要把「產生一次、驗證一次、推進同一份」當成主線。&lt;/p>
&lt;h2 id="流程定位">流程定位&lt;/h2>
&lt;p>前端部署的風險集中在 build time。後端服務可以在 runtime 讀取設定、檢查資料庫與逐步接流量；前端靜態產物多半在 build 階段就把 route、asset path、環境變數與 feature flag 預先寫入 bundle。CI/CD 的判讀重點因此是「被部署的 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>Build&lt;/td>
 &lt;td>產生 production-like static artifact&lt;/td>
 &lt;td>lockfile、Node 版本、base URL 是否固定&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Browser test&lt;/td>
 &lt;td>驗證使用者可見行為&lt;/td>
 &lt;td>測試是否跑在 build 後 artifact&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/preview-environment/" data-link-title="Preview Environment" data-link-desc="說明 pull request 變更如何在隔離部署環境中被驗證">Preview environment&lt;/a>&lt;/td>
 &lt;td>讓 PR 變更可被 reviewer 實際操作&lt;/td>
 &lt;td>preview URL 是否對應 commit / PR&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Deploy&lt;/td>
 &lt;td>推進到 hosting、Pages 或 CDN&lt;/td>
 &lt;td>HTML cache、asset cache、SPA fallback&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback strategy&lt;/a>&lt;/td>
 &lt;td>重新服務上一份已知可用 artifact&lt;/td>
 &lt;td>舊 artifact、cache purge 與 API 相容性&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Build 階段負責建立可驗證產物。真實服務裡，&lt;code>npm run dev&lt;/code> 成功不代表 production build 能成功；CI 應固定 Node 版本、package manager、lockfile、build command 與必要環境變數，讓 artifact 可以從乾淨環境重建。&lt;/p>
&lt;p>Browser test 階段負責驗證使用者實際會看到的頁面。Playwright、visual diff、a11y check 或 smoke test 應盡量對 build 後的靜態站執行，避免 dev server 的 fallback、熱更新或寬鬆路由遮蔽 production 問題。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/preview-environment/" data-link-title="Preview Environment" data-link-desc="說明 pull request 變更如何在隔離部署環境中被驗證">Preview environment&lt;/a> 階段負責把 PR 變成可操作畫面。Preview URL 要能追到 PR、commit 與 workflow run，reviewer 才能把畫面問題回報到正確版本；preview 也要隔離 production 資料與 credential，避免預覽環境變成未受控入口。&lt;/p>
&lt;p>Deploy 階段負責把 artifact 放到 hosting 或 CDN。前端部署失敗常出現在 cache policy、SPA fallback、base URL、static route 與 sourcemap 權限；deploy 成功只代表檔案上傳完成，仍需要檢查入口頁、核心路由與 asset 是否能從公開網址載入。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback strategy&lt;/a> 階段負責恢復上一份可用靜態產物。前端 rollback 表面上只是切回舊檔案，但若 API schema、build time config 或 CDN cache 已經變動，舊頁面仍可能呼叫不相容的後端，因此 rollback 要搭配 smoke test 與 cache purge。&lt;/p>
&lt;h2 id="常見失敗路由">常見失敗路由&lt;/h2>
&lt;p>前端 CI 紅燈要先判斷失敗在 build、browser test、preview 還是 production deploy。不同層的修復入口不同；把所有紅燈都當成「重跑 workflow」會掩蓋 artifact 漂移與 cache 問題。&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>本機 dev 正常、CI build 失敗&lt;/td>
 &lt;td>production build 條件與本機不同&lt;/td>
 &lt;td>回本機跑 CI 同一條 build command&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>測試通過、上線後空白頁&lt;/td>
 &lt;td>測試沒有覆蓋 production artifact / URL&lt;/td>
 &lt;td>對已部署 artifact 跑 smoke test&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Preview URL 顯示舊畫面&lt;/td>
 &lt;td>preview cache 或 commit 對應錯位&lt;/td>
 &lt;td>檢查 preview artifact 與 workflow run&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>只有深層路由 404&lt;/td>
 &lt;td>SPA fallback 或 static route 設定錯誤&lt;/td>
 &lt;td>檢查 hosting rewrite / base URL&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>rollback 後仍看到新版&lt;/td>
 &lt;td>CDN / browser cache 尚未失效&lt;/td>
 &lt;td>檢查 cache invalidation 與 HTML cache policy&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這張表的用途是縮短定位時間。前端部署問題常被誤判成「CDN 壞掉」或「瀏覽器快取」，但更常見的根因是 build artifact、route 與 cache policy 的契約沒有明確寫進 pipeline。&lt;/p></description><content:encoded><![CDATA[<p>前端 artifact 流程的核心責任是讓測試、預覽與正式發布使用同一份靜態產物。前端部署常見輸出是 HTML、CSS、JavaScript、圖片、sourcemap 與搜尋索引；這些產物一旦被重新 build，就可能受到環境變數、依賴版本、base URL 或 framework 設定影響，因此 CI/CD 需要把「產生一次、驗證一次、推進同一份」當成主線。</p>
<h2 id="流程定位">流程定位</h2>
<p>前端部署的風險集中在 build time。後端服務可以在 runtime 讀取設定、檢查資料庫與逐步接流量；前端靜態產物多半在 build 階段就把 route、asset path、環境變數與 feature flag 預先寫入 bundle。CI/CD 的判讀重點因此是「被部署的 artifact 是否就是已驗證的那一份」。</p>
<table>
  <thead>
      <tr>
          <th>階段</th>
          <th>責任</th>
          <th>判讀訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Build</td>
          <td>產生 production-like static artifact</td>
          <td>lockfile、Node 版本、base URL 是否固定</td>
      </tr>
      <tr>
          <td>Browser test</td>
          <td>驗證使用者可見行為</td>
          <td>測試是否跑在 build 後 artifact</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/preview-environment/" data-link-title="Preview Environment" data-link-desc="說明 pull request 變更如何在隔離部署環境中被驗證">Preview environment</a></td>
          <td>讓 PR 變更可被 reviewer 實際操作</td>
          <td>preview URL 是否對應 commit / PR</td>
      </tr>
      <tr>
          <td>Deploy</td>
          <td>推進到 hosting、Pages 或 CDN</td>
          <td>HTML cache、asset cache、SPA fallback</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback strategy</a></td>
          <td>重新服務上一份已知可用 artifact</td>
          <td>舊 artifact、cache purge 與 API 相容性</td>
      </tr>
  </tbody>
</table>
<p>Build 階段負責建立可驗證產物。真實服務裡，<code>npm run dev</code> 成功不代表 production build 能成功；CI 應固定 Node 版本、package manager、lockfile、build command 與必要環境變數，讓 artifact 可以從乾淨環境重建。</p>
<p>Browser test 階段負責驗證使用者實際會看到的頁面。Playwright、visual diff、a11y check 或 smoke test 應盡量對 build 後的靜態站執行，避免 dev server 的 fallback、熱更新或寬鬆路由遮蔽 production 問題。</p>
<p><a href="/blog/ci/knowledge-cards/preview-environment/" data-link-title="Preview Environment" data-link-desc="說明 pull request 變更如何在隔離部署環境中被驗證">Preview environment</a> 階段負責把 PR 變成可操作畫面。Preview URL 要能追到 PR、commit 與 workflow run，reviewer 才能把畫面問題回報到正確版本；preview 也要隔離 production 資料與 credential，避免預覽環境變成未受控入口。</p>
<p>Deploy 階段負責把 artifact 放到 hosting 或 CDN。前端部署失敗常出現在 cache policy、SPA fallback、base URL、static route 與 sourcemap 權限；deploy 成功只代表檔案上傳完成，仍需要檢查入口頁、核心路由與 asset 是否能從公開網址載入。</p>
<p><a href="/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback strategy</a> 階段負責恢復上一份可用靜態產物。前端 rollback 表面上只是切回舊檔案，但若 API schema、build time config 或 CDN cache 已經變動，舊頁面仍可能呼叫不相容的後端，因此 rollback 要搭配 smoke test 與 cache purge。</p>
<h2 id="常見失敗路由">常見失敗路由</h2>
<p>前端 CI 紅燈要先判斷失敗在 build、browser test、preview 還是 production deploy。不同層的修復入口不同；把所有紅燈都當成「重跑 workflow」會掩蓋 artifact 漂移與 cache 問題。</p>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀</th>
          <th>下一步</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>本機 dev 正常、CI build 失敗</td>
          <td>production build 條件與本機不同</td>
          <td>回本機跑 CI 同一條 build command</td>
      </tr>
      <tr>
          <td>測試通過、上線後空白頁</td>
          <td>測試沒有覆蓋 production artifact / URL</td>
          <td>對已部署 artifact 跑 smoke test</td>
      </tr>
      <tr>
          <td>Preview URL 顯示舊畫面</td>
          <td>preview cache 或 commit 對應錯位</td>
          <td>檢查 preview artifact 與 workflow run</td>
      </tr>
      <tr>
          <td>只有深層路由 404</td>
          <td>SPA fallback 或 static route 設定錯誤</td>
          <td>檢查 hosting rewrite / base URL</td>
      </tr>
      <tr>
          <td>rollback 後仍看到新版</td>
          <td>CDN / browser cache 尚未失效</td>
          <td>檢查 cache invalidation 與 HTML cache policy</td>
      </tr>
  </tbody>
</table>
<p>這張表的用途是縮短定位時間。前端部署問題常被誤判成「CDN 壞掉」或「瀏覽器快取」，但更常見的根因是 build artifact、route 與 cache policy 的契約沒有明確寫進 pipeline。</p>
<h2 id="最小-workflow-骨架">最小 workflow 骨架</h2>
<p>前端 workflow 應把 build、test、preview 與 deploy 的資料流顯性化。下面是概念骨架，重點在 artifact handoff 的方向，特定平台語法是次要的。</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">jobs</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">build</span><span class="p">:</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">steps</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">run</span><span class="p">:</span><span class="w"> </span><span class="l">npm ci</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">run</span><span class="p">:</span><span class="w"> </span><span class="l">npm run build</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">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/upload-artifact</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">with</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">name</span><span class="p">:</span><span class="w"> </span><span class="l">web-dist</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">path</span><span class="p">:</span><span class="w"> </span><span class="l">dist</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">test</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">needs</span><span class="p">:</span><span class="w"> </span><span class="l">build</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">steps</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">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/download-artifact</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">with</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">name</span><span class="p">:</span><span class="w"> </span><span class="l">web-dist</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">run</span><span class="p">:</span><span class="w"> </span><span class="l">npm run test:e2e:static</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">preview</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">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">21</span><span class="cl"><span class="w">    </span><span class="nt">if</span><span class="p">:</span><span class="w"> </span><span class="l">github.event_name == &#39;pull_request&#39;</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">actions/download-artifact</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">name</span><span class="p">:</span><span class="w"> </span><span class="l">web-dist</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">run</span><span class="p">:</span><span class="w"> </span><span class="l">npm run deploy:preview</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">  </span><span class="nt">deploy</span><span class="p">:</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">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">30</span><span class="cl"><span class="w">    </span><span class="nt">if</span><span class="p">:</span><span class="w"> </span><span class="l">github.ref == &#39;refs/heads/main&#39;</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">steps</span><span class="p">:</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">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/download-artifact</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">with</span><span class="p">:</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">name</span><span class="p">:</span><span class="w"> </span><span class="l">web-dist</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">run</span><span class="p">:</span><span class="w"> </span><span class="l">npm run deploy:production</span></span></span></code></pre></div><p>這個骨架讓 deploy 依賴 test，也讓 test 與 deploy 使用 build job 產生的同一份產物。若專案需要在不同環境注入設定，要明確區分 build time config 與 runtime config，避免同一份 artifact 被重新 build 成另一份內容。</p>
<h2 id="tripwire">Tripwire</h2>
<p>Tripwire 的責任是提醒前端 workflow 需要重切。當同一類問題反覆出現，局部補命令通常只能暫時遮住資料流錯位。</p>
<ul>
<li>Preview 常和 production 不一致：把 preview 改成部署 build artifact，讓 preview job 沿用同一份產物。</li>
<li>E2E 測試通過但 production 壞：把 E2E 改到 static artifact 或 production-like server 上執行。</li>
<li>rollback 依賴人工找舊 commit：保留 release artifact 與版本索引，讓回退指向明確產物。</li>
<li>CDN cache 問題反覆出現：把 HTML cache、asset cache 與 purge 策略寫進 deploy checklist。</li>
</ul>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>前端部署總覽：回 <a href="../">前端部署 CI/CD</a>。</li>
<li>Gate 原理：讀 <a href="../../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a>。</li>
<li>本 blog 靜態站案例：讀 <a href="../../blog-project-deploy/">本 blog 專案部署</a>。</li>
</ul>
]]></content:encoded></item><item><title>後端 migration、rollout 與 rollback 流程</title><link>https://tarrragon.github.io/blog/ci/backend-deploy/migration-rollout-rollback-flow/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/backend-deploy/migration-rollout-rollback-flow/</guid><description>&lt;p>後端部署流程的核心責任是讓程式、資料與流量在相容窗口內推進。後端服務通常會同時依賴 database、queue、cache、外部 API 與 runtime config；CI/CD 需要把 build 成功、migration 安全、readiness 可信、rollback 可執行分成不同 gate。&lt;/p>
&lt;h2 id="流程定位">流程定位&lt;/h2>
&lt;p>後端部署的主要風險是有狀態依賴。前端 artifact 可以直接回退上一份靜態檔，後端服務一旦寫入新資料、消費 queue message 或呼叫外部 side effect，rollback 就不再只是換回舊 image。發布流程要先定義新舊版本如何短暫共存，再決定 migration 與流量切換順序。&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>Build&lt;/td>
 &lt;td>產生 binary、package 或 image&lt;/td>
 &lt;td>版本是否可追到 commit&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Contract test&lt;/td>
 &lt;td>驗證 API、queue、DB 相容性&lt;/td>
 &lt;td>新舊 schema / message 是否可共存&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明資料或結構變更如何在服務不中斷前提下受控推進">Migration&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/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">Rollout strategy&lt;/a>&lt;/td>
 &lt;td>分批接流量&lt;/td>
 &lt;td>readiness、error rate、latency 是否可信&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback strategy&lt;/a>&lt;/td>
 &lt;td>縮小錯誤版本影響&lt;/td>
 &lt;td>程式、資料、queue 與 config 是否可回復&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Build 階段負責產生可部署服務。服務版本要能從 runtime 反查 commit、workflow run、image digest 與 migration 版本，讓事故時能快速定位哪一次變更進入環境。&lt;/p>
&lt;p>Contract test 階段負責驗證跨邊界相容。API response、database schema、queue message 與 config key 都是契約；只測 service 內部函式，通常抓不到新舊版本並存時的破壞性變更。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明資料或結構變更如何在服務不中斷前提下受控推進">Migration&lt;/a> 階段負責推進資料狀態。安全 migration 通常採 expand-and-contract：先加相容欄位或表、部署可讀新舊格式的程式、回填資料，最後移除舊格式。直接在同一次 release 刪欄位與切程式，會讓 rollback 失去空間。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">Rollout strategy&lt;/a> 階段負責控制新版本接到的流量。Rolling、canary 與 blue-green 都需要可信 readiness；readiness 應檢查服務能否接流量，而不只是 process alive。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback strategy&lt;/a> 階段負責定義失敗時的處理路由。後端 rollback 常見做法是 app rollback、config rollback、traffic rollback 或 forward fix；資料已被新程式寫入時，forward fix 往往比直接資料回滾安全。&lt;/p>
&lt;h2 id="migration-順序">Migration 順序&lt;/h2>
&lt;p>Migration 順序的責任是保留相容窗口。資料結構變更應讓至少兩個相鄰程式版本能共存，避免部署中途任何一端先完成都造成服務不可用。&lt;/p>
&lt;ol>
&lt;li>新增向前相容 schema，例如新增 nullable column 或新表。&lt;/li>
&lt;li>部署可同時讀舊欄位與新欄位的程式。&lt;/li>
&lt;li>執行 backfill 或 background migration。&lt;/li>
&lt;li>切換讀取來源或寫入路徑。&lt;/li>
&lt;li>觀察穩定後移除舊欄位、舊 index 或舊 message 格式。&lt;/li>
&lt;/ol>
&lt;p>這個順序的價值是可停止。若第 3 步回填異常，可以暫停 backfill，不必立即回退 app；若第 4 步切換後錯誤率升高，可以先切回舊讀取路徑，再評估資料修補。&lt;/p>
&lt;h2 id="rollout-判讀">Rollout 判讀&lt;/h2>
&lt;p>Rollout 判讀要同時看技術指標與業務副作用。服務能啟動不代表能安全接流量；API error、queue lag、database lock、第三方 API 錯誤與核心業務漏斗都可能是發布問題。&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>readiness 未通過&lt;/td>
 &lt;td>新版本尚未能接流量&lt;/td>
 &lt;td>暫停 rollout，查 config / 依賴&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>error rate 上升&lt;/td>
 &lt;td>新版本或相依服務契約出錯&lt;/td>
 &lt;td>降低流量或切回舊版本&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>migration lock 久&lt;/td>
 &lt;td>schema 變更影響正常查詢&lt;/td>
 &lt;td>停止 migration，改成分段方案&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>consumer lag 上升&lt;/td>
 &lt;td>worker 消費速度或 message 壞&lt;/td>
 &lt;td>暫停新版 worker 或降速&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>rollback 後仍錯&lt;/td>
 &lt;td>資料或外部 side effect 已變動&lt;/td>
 &lt;td>進入 forward fix / repair 流程&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這些訊號要先接到發布流程。若指標只存在 dashboard 裡、workflow 不知道如何判讀，團隊仍會在事故當下靠人工臨場決策。&lt;/p></description><content:encoded><![CDATA[<p>後端部署流程的核心責任是讓程式、資料與流量在相容窗口內推進。後端服務通常會同時依賴 database、queue、cache、外部 API 與 runtime config；CI/CD 需要把 build 成功、migration 安全、readiness 可信、rollback 可執行分成不同 gate。</p>
<h2 id="流程定位">流程定位</h2>
<p>後端部署的主要風險是有狀態依賴。前端 artifact 可以直接回退上一份靜態檔，後端服務一旦寫入新資料、消費 queue message 或呼叫外部 side effect，rollback 就不再只是換回舊 image。發布流程要先定義新舊版本如何短暫共存，再決定 migration 與流量切換順序。</p>
<table>
  <thead>
      <tr>
          <th>階段</th>
          <th>責任</th>
          <th>判讀訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Build</td>
          <td>產生 binary、package 或 image</td>
          <td>版本是否可追到 commit</td>
      </tr>
      <tr>
          <td>Contract test</td>
          <td>驗證 API、queue、DB 相容性</td>
          <td>新舊 schema / message 是否可共存</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明資料或結構變更如何在服務不中斷前提下受控推進">Migration</a></td>
          <td>推進資料結構與資料狀態</td>
          <td>是否可漸進、可重試、可停止</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">Rollout strategy</a></td>
          <td>分批接流量</td>
          <td>readiness、error rate、latency 是否可信</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback strategy</a></td>
          <td>縮小錯誤版本影響</td>
          <td>程式、資料、queue 與 config 是否可回復</td>
      </tr>
  </tbody>
</table>
<p>Build 階段負責產生可部署服務。服務版本要能從 runtime 反查 commit、workflow run、image digest 與 migration 版本，讓事故時能快速定位哪一次變更進入環境。</p>
<p>Contract test 階段負責驗證跨邊界相容。API response、database schema、queue message 與 config key 都是契約；只測 service 內部函式，通常抓不到新舊版本並存時的破壞性變更。</p>
<p><a href="/blog/ci/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明資料或結構變更如何在服務不中斷前提下受控推進">Migration</a> 階段負責推進資料狀態。安全 migration 通常採 expand-and-contract：先加相容欄位或表、部署可讀新舊格式的程式、回填資料，最後移除舊格式。直接在同一次 release 刪欄位與切程式，會讓 rollback 失去空間。</p>
<p><a href="/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">Rollout strategy</a> 階段負責控制新版本接到的流量。Rolling、canary 與 blue-green 都需要可信 readiness；readiness 應檢查服務能否接流量，而不只是 process alive。</p>
<p><a href="/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback strategy</a> 階段負責定義失敗時的處理路由。後端 rollback 常見做法是 app rollback、config rollback、traffic rollback 或 forward fix；資料已被新程式寫入時，forward fix 往往比直接資料回滾安全。</p>
<h2 id="migration-順序">Migration 順序</h2>
<p>Migration 順序的責任是保留相容窗口。資料結構變更應讓至少兩個相鄰程式版本能共存，避免部署中途任何一端先完成都造成服務不可用。</p>
<ol>
<li>新增向前相容 schema，例如新增 nullable column 或新表。</li>
<li>部署可同時讀舊欄位與新欄位的程式。</li>
<li>執行 backfill 或 background migration。</li>
<li>切換讀取來源或寫入路徑。</li>
<li>觀察穩定後移除舊欄位、舊 index 或舊 message 格式。</li>
</ol>
<p>這個順序的價值是可停止。若第 3 步回填異常，可以暫停 backfill，不必立即回退 app；若第 4 步切換後錯誤率升高，可以先切回舊讀取路徑，再評估資料修補。</p>
<h2 id="rollout-判讀">Rollout 判讀</h2>
<p>Rollout 判讀要同時看技術指標與業務副作用。服務能啟動不代表能安全接流量；API error、queue lag、database lock、第三方 API 錯誤與核心業務漏斗都可能是發布問題。</p>
<table>
  <thead>
      <tr>
          <th>訊號</th>
          <th>判讀</th>
          <th>下一步</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>readiness 未通過</td>
          <td>新版本尚未能接流量</td>
          <td>暫停 rollout，查 config / 依賴</td>
      </tr>
      <tr>
          <td>error rate 上升</td>
          <td>新版本或相依服務契約出錯</td>
          <td>降低流量或切回舊版本</td>
      </tr>
      <tr>
          <td>migration lock 久</td>
          <td>schema 變更影響正常查詢</td>
          <td>停止 migration，改成分段方案</td>
      </tr>
      <tr>
          <td>consumer lag 上升</td>
          <td>worker 消費速度或 message 壞</td>
          <td>暫停新版 worker 或降速</td>
      </tr>
      <tr>
          <td>rollback 後仍錯</td>
          <td>資料或外部 side effect 已變動</td>
          <td>進入 forward fix / repair 流程</td>
      </tr>
  </tbody>
</table>
<p>這些訊號要先接到發布流程。若指標只存在 dashboard 裡、workflow 不知道如何判讀，團隊仍會在事故當下靠人工臨場決策。</p>
<h2 id="常見反模式">常見反模式</h2>
<p>反模式的共同問題是把後端部署當成單一 deploy 動作。後端發布的本質是多個相依狀態的協調流程。</p>
<table>
  <thead>
      <tr>
          <th>反模式</th>
          <th>風險</th>
          <th>替代做法</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>app 與 destructive migration 同步</td>
          <td>rollback 後舊程式失去讀取契約</td>
          <td>expand-and-contract</td>
      </tr>
      <tr>
          <td>readiness 只檢查 process alive</td>
          <td>流量進入尚未準備好的服務</td>
          <td>檢查依賴、config 與初始化狀態</td>
      </tr>
      <tr>
          <td>rollback 只切 image tag</td>
          <td>資料與 queue side effect 留下</td>
          <td>定義 app / data / config 路由</td>
      </tr>
      <tr>
          <td>migration 沒有 dry run</td>
          <td>發布時才發現權限或鎖表問題</td>
          <td>staging 或 shadow 環境先跑驗證</td>
      </tr>
  </tbody>
</table>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>後端部署總覽：回 <a href="../">後端部署 CI/CD</a>。</li>
<li>Migration 術語：讀 <a href="/blog/ci/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明資料或結構變更如何在服務不中斷前提下受控推進">Migration</a>。</li>
<li>Gate 原理：讀 <a href="../../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a>。</li>
</ul>
]]></content:encoded></item><item><title>Binary release 與 installer 模式</title><link>https://tarrragon.github.io/blog/ci/package-library-release/binary-release-and-installer/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/package-library-release/binary-release-and-installer/</guid><description>&lt;p>Binary release 是一條直接把預編譯執行檔掛在 GitHub Release 下供使用者下載的發版通道，跳過 package registry。它解決的問題是：當套件不是函式庫而是 CLI binary，下游不需要重新編譯、也不一定有對應語言的 toolchain 時，需要一條「平台無關、即拿即用」的安裝路線。本篇用 &lt;a href="https://github.com/sysprog21/zhtw-mcp">&lt;code>zhtw-mcp&lt;/code>&lt;/a> 為陪跑案例，公開協作軌跡可直接對照 &lt;a href="https://github.com/sysprog21/zhtw-mcp/issues/35">issue #35&lt;/a> 與 &lt;a href="https://github.com/sysprog21/zhtw-mcp/pull/40">PR #40&lt;/a>。&lt;/p>
&lt;h2 id="為什麼需要這條通道">為什麼需要這條通道&lt;/h2>
&lt;p>CLI binary 跟函式庫的下游使用脈絡不同。函式庫需要被同語言專案 import，自然走 registry（&lt;code>npm install&lt;/code>、&lt;code>pip install&lt;/code>、&lt;code>cargo add&lt;/code>）。CLI binary 的目標讀者是「只想跑這個工具」的人，他們不一定有對應 toolchain、不想花時間編譯，也不會接受「先裝開發環境才能用」的入場門檻。&lt;/p>
&lt;p>Binary release 的契約是：&lt;strong>上游負責編譯、下游負責下載&lt;/strong>。這條契約成立需要三個前提同時滿足：&lt;/p>
&lt;ol>
&lt;li>CI 能在多平台 cross-compile 出可執行檔（macOS x64/arm64、Linux x64/arm64、Windows x64）&lt;/li>
&lt;li>編譯產物有穩定 URL，下游可以用一行 shell 命令取得&lt;/li>
&lt;li>安裝過程不依賴開發環境（不需要 git clone、不需要 build toolchain）&lt;/li>
&lt;/ol>
&lt;p>達成這三點需要一個 release 工具鏈把 build matrix、artifact 上傳、installer script 產生包成一個 tag-driven 的 workflow。Rust 生態用 &lt;a href="https://opensource.axo.dev/cargo-dist/">cargo-dist&lt;/a>、Go 生態用 &lt;a href="https://goreleaser.com/">goreleaser&lt;/a>、語言中性的方案則是手刻 GitHub Actions matrix。三者觸發條件相同（push semver tag）、產物落點相同（GitHub Release assets），只在 build pipeline 細節有差。&lt;/p>
&lt;h2 id="tag-driven-release-的鏈路">Tag-driven release 的鏈路&lt;/h2>
&lt;p>Tag-driven 的核心設計：&lt;strong>push tag 是發版意圖的唯一訊號&lt;/strong>。這條因果鏈每一環都要實作起來才會通：&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">維護者 push tag vX.Y.Z ↓
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl"> → release.yml workflow 觸發（tag pattern 匹配）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl"> → cross-compile to N platforms（GitHub Actions matrix）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl"> → 打包成 &amp;lt;pkg&amp;gt;-x86_64-apple-darwin.tar.xz 等 N 個 archive
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">5&lt;/span>&lt;span class="cl"> → 產生 &amp;lt;pkg&amp;gt;-installer.sh / .ps1（內嵌指向上述 archive 的 download URL）
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">6&lt;/span>&lt;span class="cl"> → 建立 GitHub Release vX.Y.Z
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">7&lt;/span>&lt;span class="cl"> → 上傳所有 archive + installer 為 release assets
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">8&lt;/span>&lt;span class="cl"> → GitHub 自動把 vX.Y.Z 的 assets 也鏡射到 /releases/latest/download/&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>這條鏈路上每個節點都是一塊要設定的工作：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Tag pattern&lt;/strong>：cargo-dist 預設匹配 &lt;code>**[0-9]+.[0-9]+.[0-9]+*&lt;/code>，符合 semver 才會觸發&lt;/li>
&lt;li>&lt;strong>Build matrix&lt;/strong>：在 &lt;code>Cargo.toml&lt;/code> 的 &lt;code>[workspace.metadata.dist]&lt;/code> 宣告 &lt;code>targets = [...]&lt;/code>，cargo-dist 會展開成對應的 GitHub Actions runners&lt;/li>
&lt;li>&lt;strong>Pre-build hooks&lt;/strong>：如果編譯前需要產生程式碼或下載資料，要透過 &lt;code>github-build-setup&lt;/code> 注入（zhtw-mcp 的案例就是要先跑 &lt;code>gen-s2t-tables.py&lt;/code> 產生 &lt;code>s2t_data.rs&lt;/code>）&lt;/li>
&lt;li>&lt;strong>Installer 範本&lt;/strong>：cargo-dist 內建 &lt;code>shell&lt;/code> / &lt;code>powershell&lt;/code> / &lt;code>homebrew&lt;/code> / &lt;code>npm&lt;/code> 等多種 installer 產生器，在 &lt;code>installers = [...]&lt;/code> 設定&lt;/li>
&lt;li>&lt;strong>&lt;code>/releases/latest/download/&lt;/code> alias&lt;/strong>：GitHub 自動提供，指向 latest non-prerelease release 的 asset；prerelease 不會更新這個 alias&lt;/li>
&lt;/ul>
&lt;p>這也解釋了為什麼 &lt;code>git tag dev&lt;/code> 或單純 commit 到 main 都不會發版 — 那不符合 tag pattern、不是發版意圖。&lt;/p></description><content:encoded><![CDATA[<p>Binary release 是一條直接把預編譯執行檔掛在 GitHub Release 下供使用者下載的發版通道，跳過 package registry。它解決的問題是：當套件不是函式庫而是 CLI binary，下游不需要重新編譯、也不一定有對應語言的 toolchain 時，需要一條「平台無關、即拿即用」的安裝路線。本篇用 <a href="https://github.com/sysprog21/zhtw-mcp"><code>zhtw-mcp</code></a> 為陪跑案例，公開協作軌跡可直接對照 <a href="https://github.com/sysprog21/zhtw-mcp/issues/35">issue #35</a> 與 <a href="https://github.com/sysprog21/zhtw-mcp/pull/40">PR #40</a>。</p>
<h2 id="為什麼需要這條通道">為什麼需要這條通道</h2>
<p>CLI binary 跟函式庫的下游使用脈絡不同。函式庫需要被同語言專案 import，自然走 registry（<code>npm install</code>、<code>pip install</code>、<code>cargo add</code>）。CLI binary 的目標讀者是「只想跑這個工具」的人，他們不一定有對應 toolchain、不想花時間編譯，也不會接受「先裝開發環境才能用」的入場門檻。</p>
<p>Binary release 的契約是：<strong>上游負責編譯、下游負責下載</strong>。這條契約成立需要三個前提同時滿足：</p>
<ol>
<li>CI 能在多平台 cross-compile 出可執行檔（macOS x64/arm64、Linux x64/arm64、Windows x64）</li>
<li>編譯產物有穩定 URL，下游可以用一行 shell 命令取得</li>
<li>安裝過程不依賴開發環境（不需要 git clone、不需要 build toolchain）</li>
</ol>
<p>達成這三點需要一個 release 工具鏈把 build matrix、artifact 上傳、installer script 產生包成一個 tag-driven 的 workflow。Rust 生態用 <a href="https://opensource.axo.dev/cargo-dist/">cargo-dist</a>、Go 生態用 <a href="https://goreleaser.com/">goreleaser</a>、語言中性的方案則是手刻 GitHub Actions matrix。三者觸發條件相同（push semver tag）、產物落點相同（GitHub Release assets），只在 build pipeline 細節有差。</p>
<h2 id="tag-driven-release-的鏈路">Tag-driven release 的鏈路</h2>
<p>Tag-driven 的核心設計：<strong>push tag 是發版意圖的唯一訊號</strong>。這條因果鏈每一環都要實作起來才會通：</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">維護者 push tag vX.Y.Z         ↓
</span></span><span class="line"><span class="ln">2</span><span class="cl">                                →  release.yml workflow 觸發（tag pattern 匹配）
</span></span><span class="line"><span class="ln">3</span><span class="cl">                                →  cross-compile to N platforms（GitHub Actions matrix）
</span></span><span class="line"><span class="ln">4</span><span class="cl">                                →  打包成 &lt;pkg&gt;-x86_64-apple-darwin.tar.xz 等 N 個 archive
</span></span><span class="line"><span class="ln">5</span><span class="cl">                                →  產生 &lt;pkg&gt;-installer.sh / .ps1（內嵌指向上述 archive 的 download URL）
</span></span><span class="line"><span class="ln">6</span><span class="cl">                                →  建立 GitHub Release vX.Y.Z
</span></span><span class="line"><span class="ln">7</span><span class="cl">                                →  上傳所有 archive + installer 為 release assets
</span></span><span class="line"><span class="ln">8</span><span class="cl">                                →  GitHub 自動把 vX.Y.Z 的 assets 也鏡射到 /releases/latest/download/</span></span></code></pre></div><p>這條鏈路上每個節點都是一塊要設定的工作：</p>
<ul>
<li><strong>Tag pattern</strong>：cargo-dist 預設匹配 <code>**[0-9]+.[0-9]+.[0-9]+*</code>，符合 semver 才會觸發</li>
<li><strong>Build matrix</strong>：在 <code>Cargo.toml</code> 的 <code>[workspace.metadata.dist]</code> 宣告 <code>targets = [...]</code>，cargo-dist 會展開成對應的 GitHub Actions runners</li>
<li><strong>Pre-build hooks</strong>：如果編譯前需要產生程式碼或下載資料，要透過 <code>github-build-setup</code> 注入（zhtw-mcp 的案例就是要先跑 <code>gen-s2t-tables.py</code> 產生 <code>s2t_data.rs</code>）</li>
<li><strong>Installer 範本</strong>：cargo-dist 內建 <code>shell</code> / <code>powershell</code> / <code>homebrew</code> / <code>npm</code> 等多種 installer 產生器，在 <code>installers = [...]</code> 設定</li>
<li><strong><code>/releases/latest/download/</code> alias</strong>：GitHub 自動提供，指向 latest non-prerelease release 的 asset；prerelease 不會更新這個 alias</li>
</ul>
<p>這也解釋了為什麼 <code>git tag dev</code> 或單純 commit 到 main 都不會發版 — 那不符合 tag pattern、不是發版意圖。</p>
<h2 id="第一次搭-cargo-dist-的實作步驟">第一次搭 cargo-dist 的實作步驟</h2>
<p>從零開始的維護者視角，Rust binary 專案要搭 cargo-dist 大致是這幾步：</p>
<ol>
<li><strong>裝 cargo-dist CLI</strong>：<code>cargo install cargo-dist</code>（或從它自家的 installer 裝）</li>
<li><strong>跑 <code>dist init</code></strong>：互動式問答，選 targets、installers、CI provider（GitHub Actions），它會在 <code>Cargo.toml</code> 寫入 <code>[workspace.metadata.dist]</code> 並產生 <code>.github/workflows/release.yml</code></li>
<li><strong>檢查產出</strong>：<code>release.yml</code> 是 auto-generated、開頭會標 <code># This file was autogenerated by dist</code>，<strong>不要手改</strong>，下次 <code>dist generate</code> 會被覆蓋</li>
<li><strong>設定 pre-build hook（如果需要）</strong>：在 <code>Cargo.toml</code> 加 <code>github-build-setup = &quot;build-setup.yml&quot;</code>，把編譯前要跑的步驟寫在 <code>.github/build-setup.yml</code>（這個檔不會被 <code>dist generate</code> 覆蓋）</li>
<li><strong>設定 preflight gate（重要）</strong>：把現有的 main CI workflow 加上 <code>workflow_call</code> trigger，在 <code>Cargo.toml</code> 設 <code>plan-jobs = [&quot;./.github/workflows/main.yml&quot;]</code>，讓 release pipeline 在 cross-compile 前先確認測試全綠</li>
<li><strong>推第一個 prerelease tag 試水溫</strong>：<code>git tag v0.1.0-alpha.1 &amp;&amp; git push origin v0.1.0-alpha.1</code>，看 release.yml 跑出來的 matrix 是不是全綠</li>
<li><strong>確認 installer script 可用</strong>：在乾淨機器上跑 <code>curl ... /releases/download/v0.1.0-alpha.1/&lt;pkg&gt;-installer.sh | sh</code>（注意 prerelease 要用完整 tag URL、不是 <code>latest</code>）</li>
<li><strong>推第一個正式 tag</strong>：跑 <code>v0.1.0</code>，這時 <code>/releases/latest/download/</code> alias 才會生效</li>
<li><strong>更新 README</strong>：把 installer 安裝命令寫上去；正式版發出後就能用 <code>latest</code> URL，prerelease 階段要寫完整 tag URL</li>
<li><strong>後續維護</strong>：bump version → tag → push，cargo-dist 自動處理；只有改 <code>[workspace.metadata.dist]</code> 時才需要重跑 <code>dist generate</code></li>
</ol>
<p>第 5 步的 preflight gate 是新手最容易漏的關。沒有它的話、main 紅燈時你還是能 push tag、cargo-dist 還是會跑 cross-compile、爛 binary 還是會推到所有人。<code>workflow_call</code> 反向 reuse 這個 pattern 在 <a href="../../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a> 有更完整討論。</p>
<h2 id="installer-script-模式的契約">Installer script 模式的契約</h2>
<p><code>curl ... | sh</code> 是這條通道的常見下游入口。這個入口要成立，前提是上游提供可驗證產物、下游執行前有最小安全檢查。</p>
<p>cargo-dist 產生的 installer 命令長這樣：</p>





<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">curl --proto <span class="s1">&#39;=https&#39;</span> --tlsv1.2 -LsSf <span class="se">\
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="se"></span>  https://github.com/&lt;owner&gt;/&lt;repo&gt;/releases/latest/download/&lt;pkg&gt;-installer.sh <span class="p">|</span> sh</span></span></code></pre></div><p>逐項拆解 curl 的 flag：</p>
<table>
  <thead>
      <tr>
          <th>片段</th>
          <th>用途</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>--proto '=https'</code></td>
          <td>限制只走 HTTPS，避免被中間人 downgrade 到 HTTP</td>
      </tr>
      <tr>
          <td><code>--tlsv1.2</code></td>
          <td>拒絕舊版 TLS</td>
      </tr>
      <tr>
          <td><code>-L</code></td>
          <td>跟隨 redirect（GitHub 的 latest alias 是 302）</td>
      </tr>
      <tr>
          <td><code>-sS</code></td>
          <td>安靜但保留錯誤訊息</td>
      </tr>
      <tr>
          <td><code>-f</code></td>
          <td>HTTP 錯誤時 curl 自己 exit non-zero（不把 404 HTML 當內容 pipe 進 sh）</td>
      </tr>
      <tr>
          <td><code>| sh</code></td>
          <td>把腳本內容餵給 shell 執行</td>
      </tr>
  </tbody>
</table>
<p><code>-f</code> 那個 flag 是這條鏈路的安全點：沒有它的話、如果 release URL 暫時 404，GitHub 的 404 HTML 會被 pipe 到 sh 然後爆出一堆語法錯誤；有 <code>-f</code> 時 curl 會直接 exit 22、<code>sh</code> 不會被呼叫，使用者看到的是清楚的錯誤碼。這就是為什麼 cargo-dist 產生的範本預設帶 <code>-f</code>、不能省。</p>
<p>PowerShell 版本（<code>irm | iex</code>）的等價契約相同 — <code>Invoke-RestMethod</code> 對 404 也會丟 exception、不會把 HTML 餵給 <code>Invoke-Expression</code>。</p>
<p>Installer script 自己的內部行為：偵測平台、下載對應 archive、解壓、放到 <code>~/.local/bin</code> 或 <code>~/.cargo/bin</code>、視需要更新 PATH。這部分由 cargo-dist 範本生成、跨專案幾乎一致、維護者不需要手寫。</p>
<h2 id="最小安全基線教學案例版">最小安全基線（教學案例版）</h2>
<p>教學案例可以示範 <code>curl | sh</code>，但可維護版本要同時提供「下載、驗證、執行」路徑，讓使用者在高風險環境可切換到可審計流程。</p>





<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) 下載 installer 與 checksum</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">curl --proto <span class="s1">&#39;=https&#39;</span> --tlsv1.2 -LsSf <span class="se">\
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="se"></span>  -o /tmp/&lt;pkg&gt;-installer.sh <span class="se">\
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="se"></span>  https://github.com/&lt;owner&gt;/&lt;repo&gt;/releases/download/vX.Y.Z/&lt;pkg&gt;-installer.sh
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">curl --proto <span class="s1">&#39;=https&#39;</span> --tlsv1.2 -LsSf <span class="se">\
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="se"></span>  -o /tmp/&lt;pkg&gt;-checksums.txt <span class="se">\
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="se"></span>  https://github.com/&lt;owner&gt;/&lt;repo&gt;/releases/download/vX.Y.Z/&lt;pkg&gt;-checksums.txt
</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"># 2) 驗證 checksum（sha256sum 或 shasum 擇一）</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">sha256sum -c /tmp/&lt;pkg&gt;-checksums.txt --ignore-missing
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="c1"># shasum -a 256 -c /tmp/&lt;pkg&gt;-checksums.txt</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="c1"># 3) 執行 installer</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">sh /tmp/&lt;pkg&gt;-installer.sh</span></span></code></pre></div><p>這條路徑的責任分工是：</p>
<ol>
<li>上游：發布 installer 與對應 checksum（或 provenance）。</li>
<li>下游：先驗證再執行。</li>
<li>文件：同時提供快速路徑與可審計路徑，並標明適用情境。</li>
</ol>
<h2 id="pre-releaseearly-adopter通道">Pre-release（early adopter）通道</h2>
<p>第一個正式 release 之前，pipeline 本身需要先被驗證。這時 prerelease tag（<code>v0.1.0-alpha.1</code>、<code>v0.1.0-rc1</code> 之類）就派上用場：</p>
<ul>
<li><strong>作為 pipeline 自身的測試</strong>：tag 推下去能跑出多平台 binary，代表 cargo-dist 設定正確</li>
<li><strong>給 early adopter 試用</strong>：願意當先驅者的使用者可以用完整 tag URL 取得 binary</li>
<li><strong>不污染 latest alias</strong>：GitHub 的 <code>releases/latest/download/</code> 只指向 non-prerelease，所以 prerelease 不會「假發版」</li>
</ul>
<p>代價是 prerelease 沒有 stable URL — 每個版本要寫完整 tag、不能用 <code>latest</code>。所以 README 安裝段落在 v0.1.0 出來之前要寫：</p>





<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"># Pre-release example（給 early adopter）</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">curl --proto <span class="s1">&#39;=https&#39;</span> --tlsv1.2 -LsSf <span class="se">\
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="se"></span>  https://github.com/&lt;owner&gt;/&lt;repo&gt;/releases/download/v0.1.0-alpha.1/&lt;pkg&gt;-installer.sh <span class="p">|</span> sh</span></span></code></pre></div><p>正式 v0.1.0 出來之後再切回 <code>latest</code> URL。這是 zhtw-mcp issue #35 討論裡 hydai 提的折衷方案，能讓社群在 pipeline 完備前先試用、又不誤導不明就裡的使用者以為正式版已就位。</p>
<h2 id="zhtw-mcp-案例社群協作把-release-pipeline-搭起來">zhtw-mcp 案例：社群協作把 release pipeline 搭起來</h2>
<p>zhtw-mcp 的 <a href="https://github.com/sysprog21/zhtw-mcp/issues/35">issue #35</a> 跟 <a href="https://github.com/sysprog21/zhtw-mcp/pull/40">PR #40</a> 是這條搭建過程的活案例。整個討論的時間軸：</p>
<ol>
<li><strong>dlackty 提 issue #35</strong>：建議導入 cargo-dist + Homebrew、列出建議 targets、指出 <code>s2t_data.rs</code> 需要 pre-build hook</li>
<li><strong>作者 jserv 回應</strong>：認同方向，但坦承自己 Rust 經驗有限、這個專案部分目的就是為了學 Rust 生態，邀請社群提 PR 推進</li>
<li><strong>hydai 開 PR #40</strong>：第一次用 cargo-dist，自己也在學，誠實表示「想知道方向對不對，希望熟手能接手」，並引用自己之前用 knope 手刻 release 的另一個 repo 作為對照</li>
<li><strong>jserv 提到 installer URL 失效</strong>：README 已經寫了 <code>releases/latest/download/...</code>，但還沒有正式 release，建議用 pre-release 給 early adopter</li>
<li><strong>hydai 提議 <code>v0.1.0-alpha.1</code></strong>：作為 early adopter 通道、提醒 prerelease 沒有 latest alias、要用完整 tag URL</li>
</ol>
<p>這個討論留下幾個值得學的點：</p>
<ul>
<li><strong>公開承認還在學是好事</strong>：jserv 直接說「我 Rust 經驗有限、我也在學」、hydai 說「我第一次用 cargo-dist」，這比假裝專家有效率多了。社群協作的核心是大家都看到同一個未完成狀態、一起補。</li>
<li><strong>README 先寫安裝命令再補 release 是常見順序</strong>：把 release 路線當作目標釘出來、再倒推實作，是刻意的設計。先寫文件再補 pipeline 的順序也讓 issue #35 / PR #40 更容易聚焦。</li>
<li><strong>特殊 build hook 是 cargo-dist 的明確支援點</strong>：zhtw-mcp 需要在編譯前跑 <code>gen-s2t-tables.py</code> 產生 <code>s2t_data.rs</code>，這正好是 <code>github-build-setup</code> 設計給的場景。如果你的 repo 有類似「編譯前要產生程式碼／下載資料」的需求、不必為此放棄 cargo-dist。</li>
<li><strong>Pre-release 是 pipeline 學習期的合理工具</strong>：先用 <code>v0.1.0-alpha.1</code> 把 pipeline 跑通、把問題暴露出來，比等到一切完美才發版更有效率。</li>
</ul>
<p>跟著這個 issue 串看完一輪、可以得到一個從零搭 cargo-dist 的真實參照框架，比官方文件更貼近實際遇到的問題。</p>
<h2 id="homebrew-通道cargo-dist-怎麼幫你出-formula">Homebrew 通道：cargo-dist 怎麼幫你出 formula</h2>
<p><code>brew install</code> 是 macOS 使用者最熟的安裝路線，但 Homebrew 有兩種發版形式：</p>
<table>
  <thead>
      <tr>
          <th>形式</th>
          <th>怎麼裝</th>
          <th>維護成本</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Homebrew core</td>
          <td><code>brew install &lt;pkg&gt;</code></td>
          <td>高 — 要過 homebrew-core 的 PR review，門檻嚴</td>
      </tr>
      <tr>
          <td>Homebrew tap</td>
          <td><code>brew install &lt;user&gt;/&lt;tap&gt;/&lt;pkg&gt;</code></td>
          <td>低 — 在自己的 GitHub repo <code>homebrew-&lt;tap&gt;</code> 放 formula</td>
      </tr>
  </tbody>
</table>
<p>cargo-dist 預設支援的是後者（tap）。設定方式是在 <code>[workspace.metadata.dist]</code> 加：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="ln">1</span><span class="cl"><span class="nx">installers</span> <span class="p">=</span> <span class="p">[</span><span class="s2">&#34;shell&#34;</span><span class="p">,</span> <span class="s2">&#34;powershell&#34;</span><span class="p">,</span> <span class="s2">&#34;homebrew&#34;</span><span class="p">]</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="nx">tap</span> <span class="p">=</span> <span class="s2">&#34;&lt;your-github-username&gt;/homebrew-&lt;tap-name&gt;&#34;</span></span></span></code></pre></div><p>然後在 GitHub 開一個叫 <code>homebrew-&lt;tap-name&gt;</code> 的 repo（命名規則是 Homebrew 強制的），cargo-dist 會在每次 release 自動 push 一個更新過的 formula 到那個 repo。下游使用者只要：</p>





<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">brew tap &lt;your-github-username&gt;/&lt;tap-name&gt;
</span></span><span class="line"><span class="ln">2</span><span class="cl">brew install &lt;pkg&gt;</span></span></code></pre></div><p>要走 homebrew-core 是另一個層級的事 — 需要套件夠成熟、有穩定使用者基數、有清楚的 license、過 homebrew-core maintainer 的 review。多數新專案先做 tap、累積使用者跟成熟度後再考慮 core。</p>
<h2 id="上線前的最後檢查">上線前的最後檢查</h2>
<p>第一個正式 v0.1.0 推出去之前最後跑一遍：</p>
<ul>
<li><input disabled="" type="checkbox"> Prerelease tag（<code>v0.1.0-alpha.1</code> 之類）跑過 release.yml、cross-compile matrix 全綠</li>
<li><input disabled="" type="checkbox"> 從乾淨機器跑 README 寫的 installer 命令、從下載到執行整條順</li>
<li><input disabled="" type="checkbox"> Pre-build hook（如果有）在所有 platform 都能跑、不依賴特定 OS</li>
<li><input disabled="" type="checkbox"> Preflight gate 的 <code>workflow_call</code> reuse 確實 block 住紅燈 main</li>
<li><input disabled="" type="checkbox"> README 的 installer URL 跟實際 asset 命名規則一致（cargo-dist 會用 <code>&lt;pkg&gt;-installer.sh</code>、不要寫成 <code>install.sh</code>）</li>
<li><input disabled="" type="checkbox"> Changelog 跟 tag 對齊（cargo-dist 會把 changelog 抓進 release notes）</li>
<li><input disabled="" type="checkbox"> 有提供可審計安裝路徑（下載 + checksum/provenance 驗證 + 執行）</li>
</ul>
<p>第一條 v0.1.0 推出去後 <code>releases/latest/download/...</code> alias 才會生效、那時就能把 README 改成 <code>latest</code> URL、徹底完成這條通道的搭建。</p>
<h2 id="來源與規格">來源與規格</h2>
<ul>
<li>cargo-dist 官方文件：<a href="https://opensource.axo.dev/cargo-dist/">https://opensource.axo.dev/cargo-dist/</a></li>
<li>cargo-dist GitHub Action / 生成流程：<a href="https://github.com/axodotdev/cargo-dist">https://github.com/axodotdev/cargo-dist</a></li>
<li>GitHub Releases 與 latest 行為：<a href="https://docs.github.com/en/repositories/releasing-projects-on-github/about-releases">https://docs.github.com/en/repositories/releasing-projects-on-github/about-releases</a></li>
<li>zhtw-mcp 案例 issue：<a href="https://github.com/sysprog21/zhtw-mcp/issues/35">https://github.com/sysprog21/zhtw-mcp/issues/35</a></li>
<li>zhtw-mcp 案例 PR：<a href="https://github.com/sysprog21/zhtw-mcp/pull/40">https://github.com/sysprog21/zhtw-mcp/pull/40</a></li>
</ul>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>想理解整體 release 類型分類：回 <a href="../">Package / Library Release CI/CD</a>。</li>
<li>想理解 workflow_call 的反向 reuse：讀 <a href="../../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a>。</li>
<li>想理解 release workflow 紅燈時的處理：讀 <a href="../../github-actions-failure-flow/">CI 失敗到修復發布流程</a>。</li>
<li>想理解 artifact 可信度：讀 <a href="/blog/backend/knowledge-cards/artifact-provenance/" data-link-title="Artifact Provenance" data-link-desc="說明交付物的來源、完整性與簽章關聯如何建立信任">Artifact Provenance</a>。</li>
</ul>
]]></content:encoded></item><item><title>CD Pipeline</title><link>https://tarrragon.github.io/blog/ci/knowledge-cards/cd-pipeline/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/knowledge-cards/cd-pipeline/</guid><description>&lt;p>CD Pipeline 的核心概念是「把已驗證產物安全交付到目標環境」。它把 build、artifact、deploy 與 release gate 串成可重播流程。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>CD Pipeline 位在 CI 驗證之後，負責 artifact promotion、部署執行、環境保護與回復路徑。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;ul>
&lt;li>同一份 artifact 需要在多個環境推進。&lt;/li>
&lt;li>發布步驟需要審核、權限或時間窗控制。&lt;/li>
&lt;li>發布失敗時需要可回退或可修復路徑。&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實服務的例子">接近真實服務的例子&lt;/h2>
&lt;p>靜態站會在 CI 成功後上傳 artifact 到 hosting。後端服務會推進同一個 image tag 到 staging 與 production，並以 rollout strategy 控制風險。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>CD Pipeline 要明確定義放行條件、部署順序、例外流程與回復策略，確保發布節奏與風險控制一致。&lt;/p></description><content:encoded><![CDATA[<p>CD Pipeline 的核心概念是「把已驗證產物安全交付到目標環境」。它把 build、artifact、deploy 與 release gate 串成可重播流程。</p>
<h2 id="概念位置">概念位置</h2>
<p>CD Pipeline 位在 CI 驗證之後，負責 artifact promotion、部署執行、環境保護與回復路徑。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<ul>
<li>同一份 artifact 需要在多個環境推進。</li>
<li>發布步驟需要審核、權限或時間窗控制。</li>
<li>發布失敗時需要可回退或可修復路徑。</li>
</ul>
<h2 id="接近真實服務的例子">接近真實服務的例子</h2>
<p>靜態站會在 CI 成功後上傳 artifact 到 hosting。後端服務會推進同一個 image tag 到 staging 與 production，並以 rollout strategy 控制風險。</p>
<h2 id="設計責任">設計責任</h2>
<p>CD Pipeline 要明確定義放行條件、部署順序、例外流程與回復策略，確保發布節奏與風險控制一致。</p>
]]></content:encoded></item><item><title>Environment Protection</title><link>https://tarrragon.github.io/blog/ci/knowledge-cards/environment-protection/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/knowledge-cards/environment-protection/</guid><description>&lt;p>Environment Protection 的核心概念是「用環境層 gate 控制正式發布」。它把環境風險從 workflow 腳本外顯成可檢查規則。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Environment Protection 位在部署 job 與目標環境之間，包含 reviewer、wait timer、branch policy 與 secret scope。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;ul>
&lt;li>測試綠燈後仍需要人工批准才能進 production。&lt;/li>
&lt;li>不同環境需要不同發布權限與審核規則。&lt;/li>
&lt;li>發布失誤常來自權限配置或保護規則缺失。&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實服務的例子">接近真實服務的例子&lt;/h2>
&lt;p>GitHub Actions deploy job 指向 &lt;code>production&lt;/code> environment，需指定 reviewer 批准後才可部署。staging 則採自動放行。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Environment Protection 要定義環境分層、審核責任、發布時窗與例外流程，讓高風險發布有明確控制面。&lt;/p></description><content:encoded><![CDATA[<p>Environment Protection 的核心概念是「用環境層 gate 控制正式發布」。它把環境風險從 workflow 腳本外顯成可檢查規則。</p>
<h2 id="概念位置">概念位置</h2>
<p>Environment Protection 位在部署 job 與目標環境之間，包含 reviewer、wait timer、branch policy 與 secret scope。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<ul>
<li>測試綠燈後仍需要人工批准才能進 production。</li>
<li>不同環境需要不同發布權限與審核規則。</li>
<li>發布失誤常來自權限配置或保護規則缺失。</li>
</ul>
<h2 id="接近真實服務的例子">接近真實服務的例子</h2>
<p>GitHub Actions deploy job 指向 <code>production</code> environment，需指定 reviewer 批准後才可部署。staging 則採自動放行。</p>
<h2 id="設計責任">設計責任</h2>
<p>Environment Protection 要定義環境分層、審核責任、發布時窗與例外流程，讓高風險發布有明確控制面。</p>
]]></content:encoded></item><item><title>Rollout Strategy</title><link>https://tarrragon.github.io/blog/ci/knowledge-cards/rollout-strategy/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/knowledge-cards/rollout-strategy/</guid><description>&lt;p>Rollout Strategy 的核心概念是「用分批推進控制發布風險」。它讓變更從小範圍驗證逐步擴大到全量。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Rollout Strategy 位在部署執行與正式流量切換之間，常見型態包含 rolling、canary、blue-green 與 phased rollout。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;ul>
&lt;li>發布後需要觀察一段時間再擴大流量。&lt;/li>
&lt;li>高風險變更不適合一次全量切換。&lt;/li>
&lt;li>團隊需要把監控訊號綁定到擴大量決策。&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實服務的例子">接近真實服務的例子&lt;/h2>
&lt;p>後端 API 先以 10% canary 流量觀察錯誤率與延遲，再逐步推進。App 發布以 phased rollout 控制商店推送比例。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Rollout Strategy 要定義推進節點、觀察指標、阻擋條件與升降級節奏，讓部署風險可被量化管理。&lt;/p></description><content:encoded><![CDATA[<p>Rollout Strategy 的核心概念是「用分批推進控制發布風險」。它讓變更從小範圍驗證逐步擴大到全量。</p>
<h2 id="概念位置">概念位置</h2>
<p>Rollout Strategy 位在部署執行與正式流量切換之間，常見型態包含 rolling、canary、blue-green 與 phased rollout。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<ul>
<li>發布後需要觀察一段時間再擴大流量。</li>
<li>高風險變更不適合一次全量切換。</li>
<li>團隊需要把監控訊號綁定到擴大量決策。</li>
</ul>
<h2 id="接近真實服務的例子">接近真實服務的例子</h2>
<p>後端 API 先以 10% canary 流量觀察錯誤率與延遲，再逐步推進。App 發布以 phased rollout 控制商店推送比例。</p>
<h2 id="設計責任">設計責任</h2>
<p>Rollout Strategy 要定義推進節點、觀察指標、阻擋條件與升降級節奏，讓部署風險可被量化管理。</p>
]]></content:encoded></item><item><title>Rollback Strategy</title><link>https://tarrragon.github.io/blog/ci/knowledge-cards/rollback-strategy/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/knowledge-cards/rollback-strategy/</guid><description>&lt;p>Rollback Strategy 的核心概念是「在異常發布後縮小影響範圍並回到可用狀態」。它屬於部署設計的一部分，需要在事故前完成。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Rollback Strategy 位在 deploy、rollout 與 incident handling 之間，通常要和資料遷移、feature flag 與流量切換一起設計。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;ul>
&lt;li>發布後錯誤率或延遲快速升高。&lt;/li>
&lt;li>新舊版本存在相容性風險。&lt;/li>
&lt;li>團隊需要在分鐘級別恢復核心功能。&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實服務的例子">接近真實服務的例子&lt;/h2>
&lt;p>靜態站可回退前一版 artifact。後端服務可回退 image tag 並暫停新 migration。App 場域可先用 remote config 關閉新功能，再走 hotfix 發版。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Rollback Strategy 要定義觸發條件、責任人、回退動作與回復後驗證，並定期演練。&lt;/p></description><content:encoded><![CDATA[<p>Rollback Strategy 的核心概念是「在異常發布後縮小影響範圍並回到可用狀態」。它屬於部署設計的一部分，需要在事故前完成。</p>
<h2 id="概念位置">概念位置</h2>
<p>Rollback Strategy 位在 deploy、rollout 與 incident handling 之間，通常要和資料遷移、feature flag 與流量切換一起設計。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<ul>
<li>發布後錯誤率或延遲快速升高。</li>
<li>新舊版本存在相容性風險。</li>
<li>團隊需要在分鐘級別恢復核心功能。</li>
</ul>
<h2 id="接近真實服務的例子">接近真實服務的例子</h2>
<p>靜態站可回退前一版 artifact。後端服務可回退 image tag 並暫停新 migration。App 場域可先用 remote config 關閉新功能，再走 hotfix 發版。</p>
<h2 id="設計責任">設計責任</h2>
<p>Rollback Strategy 要定義觸發條件、責任人、回退動作與回復後驗證，並定期演練。</p>
]]></content:encoded></item><item><title>Deployment Dry Run</title><link>https://tarrragon.github.io/blog/ci/knowledge-cards/deployment-dry-run/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/knowledge-cards/deployment-dry-run/</guid><description>&lt;p>Deployment Dry Run 的核心概念是「在正式部署前預演關鍵步驟」。它讓流程在低風險條件下先驗證 artifact、權限與目標環境配置。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Deployment Dry Run 位在 build / test 完成後、production deploy 之前，通常以 preflight check、模擬發布或目標環境校驗實作。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;ul>
&lt;li>正式部署常失敗於權限、路徑或配置差異。&lt;/li>
&lt;li>團隊需要在不影響使用者前提下驗證部署條件。&lt;/li>
&lt;li>發布流程包含高成本動作或不可逆步驟。&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實服務的例子">接近真實服務的例子&lt;/h2>
&lt;p>部署腳本先驗證 artifact 存在、環境密鑰可讀、目標 bucket 或 registry 可寫，再進入正式 deploy。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Deployment Dry Run 要定義檢查範圍、成功條件、失敗回饋與執行時機，並和正式部署命令保持一致語意。&lt;/p></description><content:encoded><![CDATA[<p>Deployment Dry Run 的核心概念是「在正式部署前預演關鍵步驟」。它讓流程在低風險條件下先驗證 artifact、權限與目標環境配置。</p>
<h2 id="概念位置">概念位置</h2>
<p>Deployment Dry Run 位在 build / test 完成後、production deploy 之前，通常以 preflight check、模擬發布或目標環境校驗實作。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<ul>
<li>正式部署常失敗於權限、路徑或配置差異。</li>
<li>團隊需要在不影響使用者前提下驗證部署條件。</li>
<li>發布流程包含高成本動作或不可逆步驟。</li>
</ul>
<h2 id="接近真實服務的例子">接近真實服務的例子</h2>
<p>部署腳本先驗證 artifact 存在、環境密鑰可讀、目標 bucket 或 registry 可寫，再進入正式 deploy。</p>
<h2 id="設計責任">設計責任</h2>
<p>Deployment Dry Run 要定義檢查範圍、成功條件、失敗回饋與執行時機，並和正式部署命令保持一致語意。</p>
]]></content:encoded></item><item><title>Migration</title><link>https://tarrragon.github.io/blog/ci/knowledge-cards/migration/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/knowledge-cards/migration/</guid><description>&lt;p>Migration 的核心概念是「把舊狀態受控推進到新狀態」。它不只涉及資料庫 schema，也包含資料回填、相容窗口與發布順序。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Migration 位在 build 之後、deploy 與 rollout 之前後的關鍵路徑，常與 release gate、rollback strategy 一起設計。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;ul>
&lt;li>新舊版本需要共存一段時間。&lt;/li>
&lt;li>發布步驟包含 schema 或資料形狀變更。&lt;/li>
&lt;li>部署失敗時要判斷是否可回退或需要 forward fix。&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實服務的例子">接近真實服務的例子&lt;/h2>
&lt;p>後端服務先擴充 schema，再讓新版本寫入新欄位，最後收斂舊欄位讀取；整個過程需要 migration gate 與回退方案。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Migration 要定義相容策略、執行順序、觀測指標與異常回復路由，避免部署成功但資料邏輯失效。&lt;/p></description><content:encoded><![CDATA[<p>Migration 的核心概念是「把舊狀態受控推進到新狀態」。它不只涉及資料庫 schema，也包含資料回填、相容窗口與發布順序。</p>
<h2 id="概念位置">概念位置</h2>
<p>Migration 位在 build 之後、deploy 與 rollout 之前後的關鍵路徑，常與 release gate、rollback strategy 一起設計。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<ul>
<li>新舊版本需要共存一段時間。</li>
<li>發布步驟包含 schema 或資料形狀變更。</li>
<li>部署失敗時要判斷是否可回退或需要 forward fix。</li>
</ul>
<h2 id="接近真實服務的例子">接近真實服務的例子</h2>
<p>後端服務先擴充 schema，再讓新版本寫入新欄位，最後收斂舊欄位讀取；整個過程需要 migration gate 與回退方案。</p>
<h2 id="設計責任">設計責任</h2>
<p>Migration 要定義相容策略、執行順序、觀測指標與異常回復路由，避免部署成功但資料邏輯失效。</p>
]]></content:encoded></item><item><title>前端部署 CI/CD</title><link>https://tarrragon.github.io/blog/ci/frontend-deploy/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/frontend-deploy/</guid><description>&lt;p>前端部署 CI/CD 的核心責任是把瀏覽器可執行的靜態產物安全交付到 hosting、CDN 或 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/preview-environment/" data-link-title="Preview Environment" data-link-desc="說明 pull request 變更如何在隔離部署環境中被驗證">preview environment&lt;/a>。前端部署常見輸出是 HTML、CSS、JavaScript、圖片與搜尋索引；它的風險集中在 build &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">artifact&lt;/a>、路由、cache、環境變數與使用者可見回歸。&lt;/p>
&lt;h2 id="場域定位">場域定位&lt;/h2>
&lt;p>前端部署和後端部署的差異在於 runtime 責任位置。前端產物通常在 build time 完成大部分工作，發布後由 browser、CDN 或 static hosting 提供服務；後端服務則要在 runtime 處理連線、資料庫、migration、狀態與 rollback。&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>Build&lt;/td>
 &lt;td>bundle、static site、asset hashing&lt;/td>
 &lt;td>build 是否可重現&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Test&lt;/td>
 &lt;td>browser regression、a11y、layout&lt;/td>
 &lt;td>Playwright / visual diff 是否通過&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">Artifact&lt;/a>&lt;/td>
 &lt;td>static files、search index、sourcemap&lt;/td>
 &lt;td>測試與發布是否同一份產物&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Deploy&lt;/td>
 &lt;td>hosting、CDN、Pages、preview URL&lt;/td>
 &lt;td>cache invalidation 與路由是否正確&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy&lt;/a>&lt;/td>
 &lt;td>回退前一版 static artifact&lt;/td>
 &lt;td>是否保留可回復版本&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Build 階段負責產生 browser 實際會執行的內容。真實服務常見訊號是 bundle size、asset hash、base URL、環境變數與 static route 是否穩定；若 build 只能在開發機成功，CI 就要把 Node 版本、package lock、build command 與環境變數收斂成固定入口。&lt;/p>
&lt;p>Test 階段負責驗證使用者可見行為。前端常見測試包含 component test、browser regression、accessibility check 與 layout check；測試應盡量靠近 production artifact，讓 dev server 的寬鬆行為不會蓋掉實際部署問題。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">Artifact&lt;/a> 階段負責保存可發布產物。靜態檔、搜尋索引與 sourcemap 都可能影響使用者體驗與除錯能力；測試與發布共用同一份 artifact，可以避免「測試通過的是 A，發布出去的是 B」的漂移。&lt;/p>
&lt;p>Deploy 階段負責把 artifact 放到 hosting 或 CDN。真實風險通常集中在 HTML cache、asset cache、SPA fallback、preview URL 與 production domain 是否對齊。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy&lt;/a> 階段負責讓上一個可用 artifact 能重新服務使用者。前端 rollback 通常比後端快，但若 build time 環境變數、資料 schema 或 CDN cache 已變更，回退仍需要驗證頁面路由與 API 相容性。&lt;/p>
&lt;h2 id="常見注意事項">常見注意事項&lt;/h2>
&lt;ul>
&lt;li>CDN cache 要和 asset hash、HTML cache policy 分開看。&lt;/li>
&lt;li>Preview environment 要能對應 PR，讓 reviewer 看到真實 build。&lt;/li>
&lt;li>前端測試要跑在 production-like artifact 上，避免 dev server 行為遮蔽問題。&lt;/li>
&lt;li>環境變數若在 build time 注入，重新發布才會生效。&lt;/li>
&lt;li>SPA route 需要 fallback 設定，靜態站 route 需要檔案路徑與 base URL 對齊。&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="static-artifact-preview-flow/">前端 artifact 與 preview deployment 流程&lt;/a>&lt;/td>
 &lt;td>Static artifact and preview&lt;/td>
 &lt;td>串起 build、browser test、preview 與 rollback&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>前端 artifact 流程：讀 &lt;a href="static-artifact-preview-flow/">前端 artifact 與 preview deployment 流程&lt;/a>。&lt;/li>
&lt;li>本 blog 的靜態站案例：讀 &lt;a href="../blog-project-deploy/">本 blog 專案部署&lt;/a>。&lt;/li>
&lt;li>Gate 原理：讀 &lt;a href="../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界&lt;/a>。&lt;/li>
&lt;li>失敗處理：讀 &lt;a href="../github-actions-failure-flow/">CI 失敗到修復發布流程&lt;/a>。&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>前端部署 CI/CD 的核心責任是把瀏覽器可執行的靜態產物安全交付到 hosting、CDN 或 <a href="/blog/ci/knowledge-cards/preview-environment/" data-link-title="Preview Environment" data-link-desc="說明 pull request 變更如何在隔離部署環境中被驗證">preview environment</a>。前端部署常見輸出是 HTML、CSS、JavaScript、圖片與搜尋索引；它的風險集中在 build <a href="/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">artifact</a>、路由、cache、環境變數與使用者可見回歸。</p>
<h2 id="場域定位">場域定位</h2>
<p>前端部署和後端部署的差異在於 runtime 責任位置。前端產物通常在 build time 完成大部分工作，發布後由 browser、CDN 或 static hosting 提供服務；後端服務則要在 runtime 處理連線、資料庫、migration、狀態與 rollback。</p>
<table>
  <thead>
      <tr>
          <th>面向</th>
          <th>前端部署常見責任</th>
          <th>判讀訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Build</td>
          <td>bundle、static site、asset hashing</td>
          <td>build 是否可重現</td>
      </tr>
      <tr>
          <td>Test</td>
          <td>browser regression、a11y、layout</td>
          <td>Playwright / visual diff 是否通過</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">Artifact</a></td>
          <td>static files、search index、sourcemap</td>
          <td>測試與發布是否同一份產物</td>
      </tr>
      <tr>
          <td>Deploy</td>
          <td>hosting、CDN、Pages、preview URL</td>
          <td>cache invalidation 與路由是否正確</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy</a></td>
          <td>回退前一版 static artifact</td>
          <td>是否保留可回復版本</td>
      </tr>
  </tbody>
</table>
<p>Build 階段負責產生 browser 實際會執行的內容。真實服務常見訊號是 bundle size、asset hash、base URL、環境變數與 static route 是否穩定；若 build 只能在開發機成功，CI 就要把 Node 版本、package lock、build command 與環境變數收斂成固定入口。</p>
<p>Test 階段負責驗證使用者可見行為。前端常見測試包含 component test、browser regression、accessibility check 與 layout check；測試應盡量靠近 production artifact，讓 dev server 的寬鬆行為不會蓋掉實際部署問題。</p>
<p><a href="/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">Artifact</a> 階段負責保存可發布產物。靜態檔、搜尋索引與 sourcemap 都可能影響使用者體驗與除錯能力；測試與發布共用同一份 artifact，可以避免「測試通過的是 A，發布出去的是 B」的漂移。</p>
<p>Deploy 階段負責把 artifact 放到 hosting 或 CDN。真實風險通常集中在 HTML cache、asset cache、SPA fallback、preview URL 與 production domain 是否對齊。</p>
<p><a href="/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy</a> 階段負責讓上一個可用 artifact 能重新服務使用者。前端 rollback 通常比後端快，但若 build time 環境變數、資料 schema 或 CDN cache 已變更，回退仍需要驗證頁面路由與 API 相容性。</p>
<h2 id="常見注意事項">常見注意事項</h2>
<ul>
<li>CDN cache 要和 asset hash、HTML cache policy 分開看。</li>
<li>Preview environment 要能對應 PR，讓 reviewer 看到真實 build。</li>
<li>前端測試要跑在 production-like artifact 上，避免 dev server 行為遮蔽問題。</li>
<li>環境變數若在 build time 注入，重新發布才會生效。</li>
<li>SPA route 需要 fallback 設定，靜態站 route 需要檔案路徑與 base URL 對齊。</li>
</ul>
<h2 id="學習路線">學習路線</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="static-artifact-preview-flow/">前端 artifact 與 preview deployment 流程</a></td>
          <td>Static artifact and preview</td>
          <td>串起 build、browser test、preview 與 rollback</td>
      </tr>
  </tbody>
</table>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>前端 artifact 流程：讀 <a href="static-artifact-preview-flow/">前端 artifact 與 preview deployment 流程</a>。</li>
<li>本 blog 的靜態站案例：讀 <a href="../blog-project-deploy/">本 blog 專案部署</a>。</li>
<li>Gate 原理：讀 <a href="../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a>。</li>
<li>失敗處理：讀 <a href="../github-actions-failure-flow/">CI 失敗到修復發布流程</a>。</li>
</ul>
]]></content:encoded></item><item><title>後端部署 CI/CD</title><link>https://tarrragon.github.io/blog/ci/backend-deploy/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/backend-deploy/</guid><description>&lt;p>後端部署 CI/CD 的核心責任是把可執行服務安全推進到 runtime 環境。後端部署不只發布程式碼，還要處理資料庫 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明資料或結構變更如何在服務不中斷前提下受控推進">Migration&lt;/a>（backend 深入見 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明系統如何把資料、流量或結構從舊狀態移到新狀態">Migration&lt;/a>）、外部依賴、runtime config、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/readiness-health-check/" data-link-title="Readiness / Health Check" data-link-desc="說明服務存活與可接流量判斷在部署中的不同責任">Readiness / Health Check&lt;/a>（backend 深入見 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/readiness/" data-link-title="Readiness" data-link-desc="說明 instance 何時可以安全接收流量，以及 readiness 如何和部署平台協作">Readiness&lt;/a> / &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/health-check/" data-link-title="Health Check" data-link-desc="說明服務如何對外提供可供平台判斷狀態的健康回應">Health Check&lt;/a>）、流量切換與 rollback。&lt;/p>
&lt;h2 id="場域定位">場域定位&lt;/h2>
&lt;p>後端部署的主要風險來自有狀態依賴與長時間執行。API、worker、scheduler 與 consumer 會連到資料庫、queue、cache 與第三方服務；部署流程需要確認程式、資料與流量切換順序。&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>Build&lt;/td>
 &lt;td>binary、package、container image&lt;/td>
 &lt;td>build 是否可重現&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Test&lt;/td>
 &lt;td>unit、integration、contract、migration&lt;/td>
 &lt;td>是否覆蓋跨服務契約&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明資料或結構變更如何在服務不中斷前提下受控推進">Migration&lt;/a>&lt;/td>
 &lt;td>schema change、backfill、rollback path&lt;/td>
 &lt;td>是否可漸進、可停止、可驗證&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">Rollout Strategy&lt;/a>&lt;/td>
 &lt;td>rolling、canary、blue-green&lt;/td>
 &lt;td>health / readiness 是否可信&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy&lt;/a>&lt;/td>
 &lt;td>app rollback、migration rollback / forward fix&lt;/td>
 &lt;td>回復路徑是否演練&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Build 階段負責產生可部署服務。後端 build 常見形式是 binary、package 或 container image；判讀重點是版本是否能追到 commit、依賴是否固定、產物是否能在乾淨環境重建。&lt;/p>
&lt;p>Test 階段負責驗證服務契約。單元測試只能覆蓋局部邏輯，integration、contract 與 migration 測試才會揭露資料庫、queue、cache 與外部服務之間的相容性風險。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明資料或結構變更如何在服務不中斷前提下受控推進">Migration&lt;/a> 階段負責推進資料結構與資料狀態。真實服務要支援新舊程式短暫共存，因此 migration 應偏向可漸進、可重試、可觀測，必要時用 forward fix 取代直接回滾資料。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">Rollout Strategy&lt;/a> 階段負責把流量安全導向新版本。Rolling、canary 與 blue-green 都需要可靠的 health、readiness、metrics 與 log；若 readiness 只檢查 process alive，流量仍可能被送到尚未準備好的服務。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy&lt;/a> 階段負責在新版本失效時縮小影響範圍。後端 rollback 要同時考慮程式、資料、queue message、外部 side effect 與 config；只回退 image tag，通常不足以處理已寫入的資料變化。&lt;/p>
&lt;h2 id="常見注意事項">常見注意事項&lt;/h2>
&lt;ul>
&lt;li>Migration 要和 app rollout 分開設計，避免新舊版本不相容。&lt;/li>
&lt;li>Health check 只代表 process alive，readiness 才能判斷能否接流量。&lt;/li>
&lt;li>Worker / consumer 部署要考慮重複處理、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/idempotency/" data-link-title="Idempotency" data-link-desc="說明同一操作執行多次時如何保持結果一致">idempotency&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/consumer-lag/" data-link-title="Consumer Lag" data-link-desc="說明 consumer lag 如何反映訊息堆積、處理能力與容量風險">consumer lag&lt;/a>。&lt;/li>
&lt;li>Config rollout 需要版本化與回退路徑（深入見 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/config-rollout/" data-link-title="Config Rollout" data-link-desc="說明設定如何安全下發到正在運作的服務實例">Config Rollout&lt;/a>）。&lt;/li>
&lt;li>Rollback 不只回程式，也要處理資料與外部副作用（深入見 &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;/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="migration-rollout-rollback-flow/">後端 migration、rollout 與 rollback 流程&lt;/a>&lt;/td>
 &lt;td>Migration rollout and rollback&lt;/td>
 &lt;td>拆分資料變更、流量推進與回復路徑&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>後端發布主流程：讀 &lt;a href="migration-rollout-rollback-flow/">後端 migration、rollout 與 rollback 流程&lt;/a>。&lt;/li>
&lt;li>Gate 原理：讀 &lt;a href="../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界&lt;/a>。&lt;/li>
&lt;li>Backend reliability：讀 &lt;a href="https://tarrragon.github.io/blog/backend/06-reliability/" data-link-title="模組六：可靠性驗證流程" data-link-desc="用 SRE 領域詞彙建問題節點、以服務級案例庫累積驗證脈絡，先建概念與案例庫再進實作交接">模組六：可靠性驗證流程&lt;/a>。&lt;/li>
&lt;li>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>。&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>後端部署 CI/CD 的核心責任是把可執行服務安全推進到 runtime 環境。後端部署不只發布程式碼，還要處理資料庫 <a href="/blog/ci/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明資料或結構變更如何在服務不中斷前提下受控推進">Migration</a>（backend 深入見 <a href="/blog/backend/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明系統如何把資料、流量或結構從舊狀態移到新狀態">Migration</a>）、外部依賴、runtime config、<a href="/blog/ci/knowledge-cards/readiness-health-check/" data-link-title="Readiness / Health Check" data-link-desc="說明服務存活與可接流量判斷在部署中的不同責任">Readiness / Health Check</a>（backend 深入見 <a href="/blog/backend/knowledge-cards/readiness/" data-link-title="Readiness" data-link-desc="說明 instance 何時可以安全接收流量，以及 readiness 如何和部署平台協作">Readiness</a> / <a href="/blog/backend/knowledge-cards/health-check/" data-link-title="Health Check" data-link-desc="說明服務如何對外提供可供平台判斷狀態的健康回應">Health Check</a>）、流量切換與 rollback。</p>
<h2 id="場域定位">場域定位</h2>
<p>後端部署的主要風險來自有狀態依賴與長時間執行。API、worker、scheduler 與 consumer 會連到資料庫、queue、cache 與第三方服務；部署流程需要確認程式、資料與流量切換順序。</p>
<table>
  <thead>
      <tr>
          <th>面向</th>
          <th>後端部署常見責任</th>
          <th>判讀訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Build</td>
          <td>binary、package、container image</td>
          <td>build 是否可重現</td>
      </tr>
      <tr>
          <td>Test</td>
          <td>unit、integration、contract、migration</td>
          <td>是否覆蓋跨服務契約</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明資料或結構變更如何在服務不中斷前提下受控推進">Migration</a></td>
          <td>schema change、backfill、rollback path</td>
          <td>是否可漸進、可停止、可驗證</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">Rollout Strategy</a></td>
          <td>rolling、canary、blue-green</td>
          <td>health / readiness 是否可信</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy</a></td>
          <td>app rollback、migration rollback / forward fix</td>
          <td>回復路徑是否演練</td>
      </tr>
  </tbody>
</table>
<p>Build 階段負責產生可部署服務。後端 build 常見形式是 binary、package 或 container image；判讀重點是版本是否能追到 commit、依賴是否固定、產物是否能在乾淨環境重建。</p>
<p>Test 階段負責驗證服務契約。單元測試只能覆蓋局部邏輯，integration、contract 與 migration 測試才會揭露資料庫、queue、cache 與外部服務之間的相容性風險。</p>
<p><a href="/blog/ci/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明資料或結構變更如何在服務不中斷前提下受控推進">Migration</a> 階段負責推進資料結構與資料狀態。真實服務要支援新舊程式短暫共存，因此 migration 應偏向可漸進、可重試、可觀測，必要時用 forward fix 取代直接回滾資料。</p>
<p><a href="/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">Rollout Strategy</a> 階段負責把流量安全導向新版本。Rolling、canary 與 blue-green 都需要可靠的 health、readiness、metrics 與 log；若 readiness 只檢查 process alive，流量仍可能被送到尚未準備好的服務。</p>
<p><a href="/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy</a> 階段負責在新版本失效時縮小影響範圍。後端 rollback 要同時考慮程式、資料、queue message、外部 side effect 與 config；只回退 image tag，通常不足以處理已寫入的資料變化。</p>
<h2 id="常見注意事項">常見注意事項</h2>
<ul>
<li>Migration 要和 app rollout 分開設計，避免新舊版本不相容。</li>
<li>Health check 只代表 process alive，readiness 才能判斷能否接流量。</li>
<li>Worker / consumer 部署要考慮重複處理、<a href="/blog/backend/knowledge-cards/idempotency/" data-link-title="Idempotency" data-link-desc="說明同一操作執行多次時如何保持結果一致">idempotency</a> 與 <a href="/blog/backend/knowledge-cards/consumer-lag/" data-link-title="Consumer Lag" data-link-desc="說明 consumer lag 如何反映訊息堆積、處理能力與容量風險">consumer lag</a>。</li>
<li>Config rollout 需要版本化與回退路徑（深入見 <a href="/blog/backend/knowledge-cards/config-rollout/" data-link-title="Config Rollout" data-link-desc="說明設定如何安全下發到正在運作的服務實例">Config Rollout</a>）。</li>
<li>Rollback 不只回程式，也要處理資料與外部副作用（深入見 <a href="/blog/backend/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明事故期間如何判斷回滾、回切與暫停變更">Rollback Strategy</a>）。</li>
</ul>
<h2 id="學習路線">學習路線</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="migration-rollout-rollback-flow/">後端 migration、rollout 與 rollback 流程</a></td>
          <td>Migration rollout and rollback</td>
          <td>拆分資料變更、流量推進與回復路徑</td>
      </tr>
  </tbody>
</table>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>後端發布主流程：讀 <a href="migration-rollout-rollback-flow/">後端 migration、rollout 與 rollback 流程</a>。</li>
<li>Gate 原理：讀 <a href="../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a>。</li>
<li>Backend reliability：讀 <a href="/blog/backend/06-reliability/" data-link-title="模組六：可靠性驗證流程" data-link-desc="用 SRE 領域詞彙建問題節點、以服務級案例庫累積驗證脈絡，先建概念與案例庫再進實作交接">模組六：可靠性驗證流程</a>。</li>
<li>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>。</li>
</ul>
]]></content:encoded></item><item><title>App 部署 CI/CD</title><link>https://tarrragon.github.io/blog/ci/app-deploy/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/app-deploy/</guid><description>&lt;p>App 部署 CI/CD 的核心責任是把可安裝的 client &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">artifact&lt;/a> 安全送到發行通道。App 發布和 web 部署最大的差異是使用者裝置會保留舊版，app store 審核、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/app-signing/" data-link-title="App Signing" data-link-desc="說明行動與桌面應用的簽章憑證如何影響發布能力">App Signing&lt;/a>、版本號與分批發布會直接影響交付節奏。&lt;/p>
&lt;h2 id="場域定位">場域定位&lt;/h2>
&lt;p>App 部署的風險集中在 artifact 不可變、簽章憑證、store review 與版本分佈。後端可以快速 rollback，前端靜態站可以重新部署，但已安裝的 App 需要靠更新、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/feature-flag/" data-link-title="Feature Flag" data-link-desc="說明如何用可動態開關控制功能曝光與風險">feature flag&lt;/a> 或服務端相容性管理。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>面向&lt;/th>
 &lt;th>App 部署常見責任&lt;/th>
 &lt;th>判讀訊號&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Build&lt;/td>
 &lt;td>IPA、APK、AAB、desktop package&lt;/td>
 &lt;td>build number / version 是否遞增&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Signing&lt;/td>
 &lt;td>certificate、profile、keystore&lt;/td>
 &lt;td>secret 是否安全、是否可輪替&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Test&lt;/td>
 &lt;td>unit、UI、device matrix&lt;/td>
 &lt;td>是否覆蓋目標 OS 與裝置&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Release&lt;/td>
 &lt;td>store review、phased rollout&lt;/td>
 &lt;td>審核狀態與 rollout 百分比&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy&lt;/a>&lt;/td>
 &lt;td>hotfix、remote config、kill switch&lt;/td>
 &lt;td>是否能處理已安裝舊版&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Build 階段負責產生可安裝 artifact。Mobile 常見產物是 IPA、APK 或 AAB，desktop 則可能是 installer 或 signed package；版本號、build number 與 commit 對應關係會決定後續除錯與回報能否追溯。&lt;/p>
&lt;p>Signing 階段負責證明 artifact 由可信來源發布。憑證、profile、keystore 與 signing secret 都屬於發布能力；它們需要輪替、權限控管與備援流程，避免單一憑證問題中斷發布（安全治理延伸見 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/secret-management/" data-link-title="Secret Management" data-link-desc="說明 token、key、password 與憑證如何保存、輪替與撤銷">Secret Management&lt;/a>）。&lt;/p>
&lt;p>Test 階段負責驗證不同裝置與作業系統組合。App 測試常見風險是 emulator 通過但真機失敗、特定 OS 權限模型不同、背景執行限制不同；device matrix 要依使用者分佈與高風險功能選擇。&lt;/p>
&lt;p>Release 階段負責把 artifact 送進發行通道。Store review、phased rollout、internal testing、beta track 與 production track 都是 gate；發布節奏要把審核時間與分批比例納入 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">rollout strategy&lt;/a> 的風險控制（backend 延伸見 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/config-rollout/" data-link-title="Config Rollout" data-link-desc="說明設定如何安全下發到正在運作的服務實例">Config Rollout&lt;/a>）。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy&lt;/a> 階段負責處理已安裝版本。App 發布後會長期存在多個使用者版本，因此 hotfix、remote config、kill switch 與後端相容性要一起設計（相容治理延伸見 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/api-contract/" data-link-title="API Contract" data-link-desc="說明 request / response 邊界如何維持相容與可驗證">API Contract&lt;/a>）。&lt;/p>
&lt;h2 id="常見注意事項">常見注意事項&lt;/h2>
&lt;ul>
&lt;li>簽章憑證是發布能力的一部分，要用 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/secret-management/" data-link-title="Secret Management" data-link-desc="說明 token、key、password 與憑證如何保存、輪替與撤銷">Secret Management&lt;/a> 管理。&lt;/li>
&lt;li>版本號與 build number 要可追溯到 commit 與 artifact。&lt;/li>
&lt;li>Store review 會讓 rollback 和 hotfix 變慢，風險要提前用 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/feature-flag/" data-link-title="Feature Flag" data-link-desc="說明如何用可動態開關控制功能曝光與風險">feature flag&lt;/a> 控制。&lt;/li>
&lt;li>Client / server contract 要支援多版本共存。&lt;/li>
&lt;li>Crash reporting 與 phased rollout 是發布後 gate 的一部分。&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="signing-store-rollout-flow/">App 簽章、商店審核與分批發布流程&lt;/a>&lt;/td>
 &lt;td>Signing, review and rollout&lt;/td>
 &lt;td>管理簽章、審核、分批發布與多版本共存&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>App 發布主流程：讀 &lt;a href="signing-store-rollout-flow/">App 簽章、商店審核與分批發布流程&lt;/a>。&lt;/li>
&lt;li>Gate 原理：讀 &lt;a href="../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界&lt;/a>。&lt;/li>
&lt;li>失敗處理：讀 &lt;a href="../github-actions-failure-flow/">CI 失敗到修復發布流程&lt;/a>。&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>App 部署 CI/CD 的核心責任是把可安裝的 client <a href="/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">artifact</a> 安全送到發行通道。App 發布和 web 部署最大的差異是使用者裝置會保留舊版，app store 審核、<a href="/blog/ci/knowledge-cards/app-signing/" data-link-title="App Signing" data-link-desc="說明行動與桌面應用的簽章憑證如何影響發布能力">App Signing</a>、版本號與分批發布會直接影響交付節奏。</p>
<h2 id="場域定位">場域定位</h2>
<p>App 部署的風險集中在 artifact 不可變、簽章憑證、store review 與版本分佈。後端可以快速 rollback，前端靜態站可以重新部署，但已安裝的 App 需要靠更新、<a href="/blog/backend/knowledge-cards/feature-flag/" data-link-title="Feature Flag" data-link-desc="說明如何用可動態開關控制功能曝光與風險">feature flag</a> 或服務端相容性管理。</p>
<table>
  <thead>
      <tr>
          <th>面向</th>
          <th>App 部署常見責任</th>
          <th>判讀訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Build</td>
          <td>IPA、APK、AAB、desktop package</td>
          <td>build number / version 是否遞增</td>
      </tr>
      <tr>
          <td>Signing</td>
          <td>certificate、profile、keystore</td>
          <td>secret 是否安全、是否可輪替</td>
      </tr>
      <tr>
          <td>Test</td>
          <td>unit、UI、device matrix</td>
          <td>是否覆蓋目標 OS 與裝置</td>
      </tr>
      <tr>
          <td>Release</td>
          <td>store review、phased rollout</td>
          <td>審核狀態與 rollout 百分比</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy</a></td>
          <td>hotfix、remote config、kill switch</td>
          <td>是否能處理已安裝舊版</td>
      </tr>
  </tbody>
</table>
<p>Build 階段負責產生可安裝 artifact。Mobile 常見產物是 IPA、APK 或 AAB，desktop 則可能是 installer 或 signed package；版本號、build number 與 commit 對應關係會決定後續除錯與回報能否追溯。</p>
<p>Signing 階段負責證明 artifact 由可信來源發布。憑證、profile、keystore 與 signing secret 都屬於發布能力；它們需要輪替、權限控管與備援流程，避免單一憑證問題中斷發布（安全治理延伸見 <a href="/blog/backend/knowledge-cards/secret-management/" data-link-title="Secret Management" data-link-desc="說明 token、key、password 與憑證如何保存、輪替與撤銷">Secret Management</a>）。</p>
<p>Test 階段負責驗證不同裝置與作業系統組合。App 測試常見風險是 emulator 通過但真機失敗、特定 OS 權限模型不同、背景執行限制不同；device matrix 要依使用者分佈與高風險功能選擇。</p>
<p>Release 階段負責把 artifact 送進發行通道。Store review、phased rollout、internal testing、beta track 與 production track 都是 gate；發布節奏要把審核時間與分批比例納入 <a href="/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">rollout strategy</a> 的風險控制（backend 延伸見 <a href="/blog/backend/knowledge-cards/config-rollout/" data-link-title="Config Rollout" data-link-desc="說明設定如何安全下發到正在運作的服務實例">Config Rollout</a>）。</p>
<p><a href="/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy</a> 階段負責處理已安裝版本。App 發布後會長期存在多個使用者版本，因此 hotfix、remote config、kill switch 與後端相容性要一起設計（相容治理延伸見 <a href="/blog/backend/knowledge-cards/api-contract/" data-link-title="API Contract" data-link-desc="說明 request / response 邊界如何維持相容與可驗證">API Contract</a>）。</p>
<h2 id="常見注意事項">常見注意事項</h2>
<ul>
<li>簽章憑證是發布能力的一部分，要用 <a href="/blog/backend/knowledge-cards/secret-management/" data-link-title="Secret Management" data-link-desc="說明 token、key、password 與憑證如何保存、輪替與撤銷">Secret Management</a> 管理。</li>
<li>版本號與 build number 要可追溯到 commit 與 artifact。</li>
<li>Store review 會讓 rollback 和 hotfix 變慢，風險要提前用 <a href="/blog/backend/knowledge-cards/feature-flag/" data-link-title="Feature Flag" data-link-desc="說明如何用可動態開關控制功能曝光與風險">feature flag</a> 控制。</li>
<li>Client / server contract 要支援多版本共存。</li>
<li>Crash reporting 與 phased rollout 是發布後 gate 的一部分。</li>
</ul>
<h2 id="學習路線">學習路線</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="signing-store-rollout-flow/">App 簽章、商店審核與分批發布流程</a></td>
          <td>Signing, review and rollout</td>
          <td>管理簽章、審核、分批發布與多版本共存</td>
      </tr>
  </tbody>
</table>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>App 發布主流程：讀 <a href="signing-store-rollout-flow/">App 簽章、商店審核與分批發布流程</a>。</li>
<li>Gate 原理：讀 <a href="../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a>。</li>
<li>失敗處理：讀 <a href="../github-actions-failure-flow/">CI 失敗到修復發布流程</a>。</li>
</ul>
]]></content:encoded></item><item><title>Readiness / Health Check</title><link>https://tarrragon.github.io/blog/ci/knowledge-cards/readiness-health-check/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/knowledge-cards/readiness-health-check/</guid><description>&lt;p>Readiness / Health Check 的核心概念是「服務活著」與「服務可接流量」是兩個不同訊號。部署放行通常依賴 readiness，而非僅看 process alive。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Readiness / Health Check 位在 rollout、load balancer 與 runtime platform 之間，是流量切換前的核心 gate。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;ul>
&lt;li>部署後健康檢查綠燈但請求仍大量失敗。&lt;/li>
&lt;li>新版啟動中就提早接到流量。&lt;/li>
&lt;li>rollout 失敗時缺少可觀測放行條件。&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實服務的例子">接近真實服務的例子&lt;/h2>
&lt;p>Kubernetes liveness 通過只代表 process 存活；readiness 通過才代表連線池、依賴服務與必要資料都已準備完成。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Readiness / Health Check 要定義檢查內容、容錯窗口與失敗處理，讓 rollout decision 有可信訊號。&lt;/p></description><content:encoded><![CDATA[<p>Readiness / Health Check 的核心概念是「服務活著」與「服務可接流量」是兩個不同訊號。部署放行通常依賴 readiness，而非僅看 process alive。</p>
<h2 id="概念位置">概念位置</h2>
<p>Readiness / Health Check 位在 rollout、load balancer 與 runtime platform 之間，是流量切換前的核心 gate。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<ul>
<li>部署後健康檢查綠燈但請求仍大量失敗。</li>
<li>新版啟動中就提早接到流量。</li>
<li>rollout 失敗時缺少可觀測放行條件。</li>
</ul>
<h2 id="接近真實服務的例子">接近真實服務的例子</h2>
<p>Kubernetes liveness 通過只代表 process 存活；readiness 通過才代表連線池、依賴服務與必要資料都已準備完成。</p>
<h2 id="設計責任">設計責任</h2>
<p>Readiness / Health Check 要定義檢查內容、容錯窗口與失敗處理，讓 rollout decision 有可信訊號。</p>
]]></content:encoded></item><item><title>Container Registry</title><link>https://tarrragon.github.io/blog/ci/knowledge-cards/container-registry/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/knowledge-cards/container-registry/</guid><description>&lt;p>Container Registry 的核心概念是「管理可部署 image 的供應鏈節點」。它負責保存、授權、保留與推進已驗證影像。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Container Registry 位在 image build、scan、promotion 與 runtime deploy 之間，連接 CI 產物與環境發布。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;ul>
&lt;li>同一 tag 在不同環境對應內容不一致。&lt;/li>
&lt;li>部署因拉取權限或鏡像不存在失敗。&lt;/li>
&lt;li>線上 image 缺少來源與掃描紀錄的反查路徑。&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實服務的例子">接近真實服務的例子&lt;/h2>
&lt;p>團隊以 immutable digest 推進 staging 與 production，並透過 registry policy 控制 retention、pull 權限與 promotion 路徑。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Container Registry 要定義命名策略、權限模型、保留策略與來源追溯，讓 image 發布具備可審計性。&lt;/p></description><content:encoded><![CDATA[<p>Container Registry 的核心概念是「管理可部署 image 的供應鏈節點」。它負責保存、授權、保留與推進已驗證影像。</p>
<h2 id="概念位置">概念位置</h2>
<p>Container Registry 位在 image build、scan、promotion 與 runtime deploy 之間，連接 CI 產物與環境發布。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<ul>
<li>同一 tag 在不同環境對應內容不一致。</li>
<li>部署因拉取權限或鏡像不存在失敗。</li>
<li>線上 image 缺少來源與掃描紀錄的反查路徑。</li>
</ul>
<h2 id="接近真實服務的例子">接近真實服務的例子</h2>
<p>團隊以 immutable digest 推進 staging 與 production，並透過 registry policy 控制 retention、pull 權限與 promotion 路徑。</p>
<h2 id="設計責任">設計責任</h2>
<p>Container Registry 要定義命名策略、權限模型、保留策略與來源追溯，讓 image 發布具備可審計性。</p>
]]></content:encoded></item><item><title>Docker / Image 部署 CI/CD</title><link>https://tarrragon.github.io/blog/ci/docker-deploy/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/docker-deploy/</guid><description>&lt;p>Docker / image 部署 CI/CD 的核心責任是把可執行環境封裝成可追溯的 image。Image 同時承載 application、runtime、OS package、dependency 與安全掃描結果，因此它是可以被推進、掃描與回溯的部署產物；而 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/container-registry/" data-link-title="Container Registry" data-link-desc="說明容器產物儲存、權限與推進流程在 CD 中的責任">Container Registry&lt;/a> 提供保存與推進的供應鏈節點。&lt;/p>
&lt;h2 id="場域定位">場域定位&lt;/h2>
&lt;p>Image 部署常出現在後端、worker、batch job 與自架服務。它把「在哪個環境跑」前移到 build 階段，但也引入 registry、tag、base image、vulnerability scan、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/sbom/" data-link-title="SBOM" data-link-desc="說明 Software Bill of Materials 如何揭露 artifact 內含元件，支撐供應鏈掃描與例外治理">SBOM&lt;/a> 與 promotion 流程（platform 概念可對照 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/container/" data-link-title="Container" data-link-desc="說明容器如何包裝服務、隔離依賴與影響部署方式">Container&lt;/a>）。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>面向&lt;/th>
 &lt;th>Image 部署常見責任&lt;/th>
 &lt;th>判讀訊號&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Build&lt;/td>
 &lt;td>Dockerfile、multi-stage build&lt;/td>
 &lt;td>image 是否可重現、layer 是否合理&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Tag&lt;/td>
 &lt;td>semver、commit SHA、release tag&lt;/td>
 &lt;td>tag 是否能追到 source&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Scan&lt;/td>
 &lt;td>vulnerability、secret、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/sbom/" data-link-title="SBOM" data-link-desc="說明 Software Bill of Materials 如何揭露 artifact 內含元件，支撐供應鏈掃描與例外治理">SBOM&lt;/a>&lt;/td>
 &lt;td>是否有阻擋門檻與例外流程&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Registry&lt;/td>
 &lt;td>push、retention、promotion&lt;/td>
 &lt;td>prod image 是否來自已驗證 artifact&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Runtime&lt;/td>
 &lt;td>Kubernetes、Compose、ECS 等&lt;/td>
 &lt;td>health、readiness、rollback 是否存在&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Build 階段負責把 application 與 runtime 封裝成 image。Multi-stage build、dependency cache、base image 與 layer 順序會影響速度、安全性與可重現性；CI 應能從 Dockerfile 與 lockfile 重建同一類產物。&lt;/p>
&lt;p>Tag 階段負責讓 image 可追溯。Commit SHA、release tag 與 semver 各自服務不同查詢情境；production 需要能從 running image 反查 source、workflow run 與掃描結果。&lt;/p>
&lt;p>Scan 階段負責讓 image 風險可見。Vulnerability scan、secret scan 與 SBOM 能把 base image、OS package 與 dependency 風險顯性化；阻擋門檻要和例外流程一起定義，讓掃描結果能被分流處理。&lt;/p>
&lt;p>Registry 階段負責保存與推進 image。真實流程通常需要 retention、immutability、promotion 與權限控管；production image 應來自已驗證 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">artifact handoff&lt;/a>，讓各環境推進同一份產物（供應鏈治理可對照 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/artifact-provenance/" data-link-title="Artifact Provenance" data-link-desc="說明交付物的來源、完整性與簽章關聯如何建立信任">Artifact Provenance&lt;/a>）。&lt;/p>
&lt;p>Runtime 階段負責把 image 轉成可運行服務。Kubernetes、Compose、ECS 或其他平台都需要 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/health-check/" data-link-title="Health Check" data-link-desc="說明服務如何對外提供可供平台判斷狀態的健康回應">health check&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/readiness/" data-link-title="Readiness" data-link-desc="說明 instance 何時可以安全接收流量，以及 readiness 如何和部署平台協作">readiness&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/resource-limit/" data-link-title="Resource Limit" data-link-desc="說明服務可使用的 CPU、memory 與相關資源上限如何影響行為">resource limit&lt;/a>、secret injection（可對照 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/secret-management/" data-link-title="Secret Management" data-link-desc="說明 token、key、password 與憑證如何保存、輪替與撤銷">Secret Management&lt;/a>）與 rollback 設計，否則 image 成功不等於服務可用。&lt;/p>
&lt;h2 id="常見注意事項">常見注意事項&lt;/h2>
&lt;ul>
&lt;li>&lt;code>latest&lt;/code> 不適合當 production 追溯依據。&lt;/li>
&lt;li>Base image 要有更新節奏，否則掃描結果會持續惡化。&lt;/li>
&lt;li>Build secret 不應留在 image layer。&lt;/li>
&lt;li>Scan gate 要區分阻擋門檻與可接受例外。&lt;/li>
&lt;li>Promotion 應推進同一份 image，讓 staging 與 production 的差異集中在設定與流量。&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="image-supply-chain-flow/">Image build、scan、registry 與 promotion 流程&lt;/a>&lt;/td>
 &lt;td>Image supply chain&lt;/td>
 &lt;td>建立可追溯 tag、掃描 gate 與 registry 推進&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>Image 供應鏈流程：讀 &lt;a href="image-supply-chain-flow/">Image build、scan、registry 與 promotion 流程&lt;/a>。&lt;/li>
&lt;li>後端部署：讀 &lt;a href="../backend-deploy/">後端部署 CI/CD&lt;/a>。&lt;/li>
&lt;li>Gate 原理：讀 &lt;a href="../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界&lt;/a>。&lt;/li>
&lt;li>Backend deployment platform：讀 &lt;a href="https://tarrragon.github.io/blog/backend/05-deployment-platform/" data-link-title="模組五：部署平台與網路入口" data-link-desc="整理 Kubernetes、systemd、load balancer、container 與服務生命週期合約">模組五：部署平台與網路入口&lt;/a>。&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Docker / image 部署 CI/CD 的核心責任是把可執行環境封裝成可追溯的 image。Image 同時承載 application、runtime、OS package、dependency 與安全掃描結果，因此它是可以被推進、掃描與回溯的部署產物；而 <a href="/blog/ci/knowledge-cards/container-registry/" data-link-title="Container Registry" data-link-desc="說明容器產物儲存、權限與推進流程在 CD 中的責任">Container Registry</a> 提供保存與推進的供應鏈節點。</p>
<h2 id="場域定位">場域定位</h2>
<p>Image 部署常出現在後端、worker、batch job 與自架服務。它把「在哪個環境跑」前移到 build 階段，但也引入 registry、tag、base image、vulnerability scan、<a href="/blog/ci/knowledge-cards/sbom/" data-link-title="SBOM" data-link-desc="說明 Software Bill of Materials 如何揭露 artifact 內含元件，支撐供應鏈掃描與例外治理">SBOM</a> 與 promotion 流程（platform 概念可對照 <a href="/blog/backend/knowledge-cards/container/" data-link-title="Container" data-link-desc="說明容器如何包裝服務、隔離依賴與影響部署方式">Container</a>）。</p>
<table>
  <thead>
      <tr>
          <th>面向</th>
          <th>Image 部署常見責任</th>
          <th>判讀訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Build</td>
          <td>Dockerfile、multi-stage build</td>
          <td>image 是否可重現、layer 是否合理</td>
      </tr>
      <tr>
          <td>Tag</td>
          <td>semver、commit SHA、release tag</td>
          <td>tag 是否能追到 source</td>
      </tr>
      <tr>
          <td>Scan</td>
          <td>vulnerability、secret、<a href="/blog/ci/knowledge-cards/sbom/" data-link-title="SBOM" data-link-desc="說明 Software Bill of Materials 如何揭露 artifact 內含元件，支撐供應鏈掃描與例外治理">SBOM</a></td>
          <td>是否有阻擋門檻與例外流程</td>
      </tr>
      <tr>
          <td>Registry</td>
          <td>push、retention、promotion</td>
          <td>prod image 是否來自已驗證 artifact</td>
      </tr>
      <tr>
          <td>Runtime</td>
          <td>Kubernetes、Compose、ECS 等</td>
          <td>health、readiness、rollback 是否存在</td>
      </tr>
  </tbody>
</table>
<p>Build 階段負責把 application 與 runtime 封裝成 image。Multi-stage build、dependency cache、base image 與 layer 順序會影響速度、安全性與可重現性；CI 應能從 Dockerfile 與 lockfile 重建同一類產物。</p>
<p>Tag 階段負責讓 image 可追溯。Commit SHA、release tag 與 semver 各自服務不同查詢情境；production 需要能從 running image 反查 source、workflow run 與掃描結果。</p>
<p>Scan 階段負責讓 image 風險可見。Vulnerability scan、secret scan 與 SBOM 能把 base image、OS package 與 dependency 風險顯性化；阻擋門檻要和例外流程一起定義，讓掃描結果能被分流處理。</p>
<p>Registry 階段負責保存與推進 image。真實流程通常需要 retention、immutability、promotion 與權限控管；production image 應來自已驗證 <a href="/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">artifact handoff</a>，讓各環境推進同一份產物（供應鏈治理可對照 <a href="/blog/backend/knowledge-cards/artifact-provenance/" data-link-title="Artifact Provenance" data-link-desc="說明交付物的來源、完整性與簽章關聯如何建立信任">Artifact Provenance</a>）。</p>
<p>Runtime 階段負責把 image 轉成可運行服務。Kubernetes、Compose、ECS 或其他平台都需要 <a href="/blog/backend/knowledge-cards/health-check/" data-link-title="Health Check" data-link-desc="說明服務如何對外提供可供平台判斷狀態的健康回應">health check</a>、<a href="/blog/backend/knowledge-cards/readiness/" data-link-title="Readiness" data-link-desc="說明 instance 何時可以安全接收流量，以及 readiness 如何和部署平台協作">readiness</a>、<a href="/blog/backend/knowledge-cards/resource-limit/" data-link-title="Resource Limit" data-link-desc="說明服務可使用的 CPU、memory 與相關資源上限如何影響行為">resource limit</a>、secret injection（可對照 <a href="/blog/backend/knowledge-cards/secret-management/" data-link-title="Secret Management" data-link-desc="說明 token、key、password 與憑證如何保存、輪替與撤銷">Secret Management</a>）與 rollback 設計，否則 image 成功不等於服務可用。</p>
<h2 id="常見注意事項">常見注意事項</h2>
<ul>
<li><code>latest</code> 不適合當 production 追溯依據。</li>
<li>Base image 要有更新節奏，否則掃描結果會持續惡化。</li>
<li>Build secret 不應留在 image layer。</li>
<li>Scan gate 要區分阻擋門檻與可接受例外。</li>
<li>Promotion 應推進同一份 image，讓 staging 與 production 的差異集中在設定與流量。</li>
</ul>
<h2 id="學習路線">學習路線</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="image-supply-chain-flow/">Image build、scan、registry 與 promotion 流程</a></td>
          <td>Image supply chain</td>
          <td>建立可追溯 tag、掃描 gate 與 registry 推進</td>
      </tr>
  </tbody>
</table>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>Image 供應鏈流程：讀 <a href="image-supply-chain-flow/">Image build、scan、registry 與 promotion 流程</a>。</li>
<li>後端部署：讀 <a href="../backend-deploy/">後端部署 CI/CD</a>。</li>
<li>Gate 原理：讀 <a href="../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a>。</li>
<li>Backend deployment platform：讀 <a href="/blog/backend/05-deployment-platform/" data-link-title="模組五：部署平台與網路入口" data-link-desc="整理 Kubernetes、systemd、load balancer、container 與服務生命週期合約">模組五：部署平台與網路入口</a>。</li>
</ul>
]]></content:encoded></item><item><title>App Signing</title><link>https://tarrragon.github.io/blog/ci/knowledge-cards/app-signing/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/knowledge-cards/app-signing/</guid><description>&lt;p>App Signing 的核心概念是「簽章憑證即發布能力」。它決定 artifact 是否被平台接受與使用者裝置信任。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>App Signing 位在 app build 與 release channel 之間，涉及 certificate、provisioning profile、keystore 與 secret 管理。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;ul>
&lt;li>發布因簽章失敗中斷。&lt;/li>
&lt;li>憑證過期導致發版中斷。&lt;/li>
&lt;li>金鑰輪替缺乏演練造成交付風險。&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實服務的例子">接近真實服務的例子&lt;/h2>
&lt;p>iOS 發版需匹配正確 certificate 與 provisioning profile，Android 發版需維護 keystore 一致性與安全儲存。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>App Signing 要定義密鑰保存、輪替節奏、權限分離與緊急回復流程，確保發布能力可持續。&lt;/p></description><content:encoded><![CDATA[<p>App Signing 的核心概念是「簽章憑證即發布能力」。它決定 artifact 是否被平台接受與使用者裝置信任。</p>
<h2 id="概念位置">概念位置</h2>
<p>App Signing 位在 app build 與 release channel 之間，涉及 certificate、provisioning profile、keystore 與 secret 管理。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<ul>
<li>發布因簽章失敗中斷。</li>
<li>憑證過期導致發版中斷。</li>
<li>金鑰輪替缺乏演練造成交付風險。</li>
</ul>
<h2 id="接近真實服務的例子">接近真實服務的例子</h2>
<p>iOS 發版需匹配正確 certificate 與 provisioning profile，Android 發版需維護 keystore 一致性與安全儲存。</p>
<h2 id="設計責任">設計責任</h2>
<p>App Signing 要定義密鑰保存、輪替節奏、權限分離與緊急回復流程，確保發布能力可持續。</p>
]]></content:encoded></item><item><title>Serverless 部署 CI/CD</title><link>https://tarrragon.github.io/blog/ci/serverless-deploy/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/serverless-deploy/</guid><description>&lt;p>Serverless 部署 CI/CD 的核心責任是把函式型服務安全推進到受管執行環境。它和長駐服務不同，風險集中在 artifact 打包、runtime 相容、權限設定、版本別名與冷啟動行為。&lt;/p>
&lt;h2 id="場域定位">場域定位&lt;/h2>
&lt;p>Serverless 發布通常以函式版本為單位，並透過 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/function-alias/" data-link-title="Function Alias" data-link-desc="說明 serverless function alias 如何把穩定入口指向特定版本並支援流量切換與回復">Function Alias&lt;/a> 或流量權重切換。部署步驟看起來短，但對權限、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/event-source/" data-link-title="Event Source" data-link-desc="說明 serverless 與事件驅動流程中觸發來源如何影響 retry、dead-letter 與回復策略">Event Source&lt;/a>、重試政策與 observability 欄位要求很高。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>面向&lt;/th>
 &lt;th>Serverless 部署常見責任&lt;/th>
 &lt;th>判讀訊號&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Build&lt;/td>
 &lt;td>function bundle、dependency、runtime target&lt;/td>
 &lt;td>package 是否可重現&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Deploy&lt;/td>
 &lt;td>function version、alias、traffic shift&lt;/td>
 &lt;td>新舊版本是否可並存&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Permission&lt;/td>
 &lt;td>IAM、resource policy、secret scope&lt;/td>
 &lt;td>執行是否具最小權限&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Event Source&lt;/td>
 &lt;td>queue/topic/http trigger 設定&lt;/td>
 &lt;td>重試與死信策略是否明確&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Recovery&lt;/td>
 &lt;td>alias rollback、disable trigger&lt;/td>
 &lt;td>故障時是否可快速止血&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="常見注意事項">常見注意事項&lt;/h2>
&lt;ul>
&lt;li>部署前要先驗證 runtime 與依賴版本，避免 deploy 成功但 invocation 失敗。&lt;/li>
&lt;li>事件觸發型函式要明確設定 retry、dead-letter 或回放策略。&lt;/li>
&lt;li>權限設定要收斂到最小範圍，避免函式擴權風險。&lt;/li>
&lt;li>冷啟動與併發上限要納入發布後觀測指標。&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="function-version-event-flow/">Serverless function 版本、事件來源與回復流程&lt;/a>&lt;/td>
 &lt;td>Function version and event&lt;/td>
 &lt;td>管理版本別名、事件來源、權限與回復&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>Serverless 發布主流程：讀 &lt;a href="function-version-event-flow/">Serverless function 版本、事件來源與回復流程&lt;/a>。&lt;/li>
&lt;li>Gate 原理：讀 &lt;a href="../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界&lt;/a>。&lt;/li>
&lt;li>失敗處理：讀 &lt;a href="../github-actions-failure-flow/">CI 失敗到修復發布流程&lt;/a>。&lt;/li>
&lt;li>Backend 相關概念：讀 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/local-worker/" data-link-title="Local Worker" data-link-desc="說明同一個 process 內的背景工作模型與其生命週期邊界">Serverless / worker 相關知識卡&lt;/a>。&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Serverless 部署 CI/CD 的核心責任是把函式型服務安全推進到受管執行環境。它和長駐服務不同，風險集中在 artifact 打包、runtime 相容、權限設定、版本別名與冷啟動行為。</p>
<h2 id="場域定位">場域定位</h2>
<p>Serverless 發布通常以函式版本為單位，並透過 <a href="/blog/ci/knowledge-cards/function-alias/" data-link-title="Function Alias" data-link-desc="說明 serverless function alias 如何把穩定入口指向特定版本並支援流量切換與回復">Function Alias</a> 或流量權重切換。部署步驟看起來短，但對權限、<a href="/blog/ci/knowledge-cards/event-source/" data-link-title="Event Source" data-link-desc="說明 serverless 與事件驅動流程中觸發來源如何影響 retry、dead-letter 與回復策略">Event Source</a>、重試政策與 observability 欄位要求很高。</p>
<table>
  <thead>
      <tr>
          <th>面向</th>
          <th>Serverless 部署常見責任</th>
          <th>判讀訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Build</td>
          <td>function bundle、dependency、runtime target</td>
          <td>package 是否可重現</td>
      </tr>
      <tr>
          <td>Deploy</td>
          <td>function version、alias、traffic shift</td>
          <td>新舊版本是否可並存</td>
      </tr>
      <tr>
          <td>Permission</td>
          <td>IAM、resource policy、secret scope</td>
          <td>執行是否具最小權限</td>
      </tr>
      <tr>
          <td>Event Source</td>
          <td>queue/topic/http trigger 設定</td>
          <td>重試與死信策略是否明確</td>
      </tr>
      <tr>
          <td>Recovery</td>
          <td>alias rollback、disable trigger</td>
          <td>故障時是否可快速止血</td>
      </tr>
  </tbody>
</table>
<h2 id="常見注意事項">常見注意事項</h2>
<ul>
<li>部署前要先驗證 runtime 與依賴版本，避免 deploy 成功但 invocation 失敗。</li>
<li>事件觸發型函式要明確設定 retry、dead-letter 或回放策略。</li>
<li>權限設定要收斂到最小範圍，避免函式擴權風險。</li>
<li>冷啟動與併發上限要納入發布後觀測指標。</li>
</ul>
<h2 id="學習路線">學習路線</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="function-version-event-flow/">Serverless function 版本、事件來源與回復流程</a></td>
          <td>Function version and event</td>
          <td>管理版本別名、事件來源、權限與回復</td>
      </tr>
  </tbody>
</table>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>Serverless 發布主流程：讀 <a href="function-version-event-flow/">Serverless function 版本、事件來源與回復流程</a>。</li>
<li>Gate 原理：讀 <a href="../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a>。</li>
<li>失敗處理：讀 <a href="../github-actions-failure-flow/">CI 失敗到修復發布流程</a>。</li>
<li>Backend 相關概念：讀 <a href="/blog/backend/knowledge-cards/local-worker/" data-link-title="Local Worker" data-link-desc="說明同一個 process 內的背景工作模型與其生命週期邊界">Serverless / worker 相關知識卡</a>。</li>
</ul>
]]></content:encoded></item><item><title>Data Pipeline 部署 CI/CD</title><link>https://tarrragon.github.io/blog/ci/data-pipeline-deploy/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/data-pipeline-deploy/</guid><description>&lt;p>Data Pipeline 部署 CI/CD 的核心責任是把資料處理邏輯推進到生產環境，同時維持資料正確性與可回復性。它和 API 部署不同，重點在 schema 相容、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/backfill/" data-link-title="Backfill" data-link-desc="說明資料處理與 migration 中如何受控補算歷史資料">Backfill&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/checkpoint/" data-link-title="Checkpoint" data-link-desc="說明長時間任務如何記錄進度以支援接續、重跑與事故修復">Checkpoint&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rerun/" data-link-title="Rerun" data-link-desc="說明 CI/CD 與 data pipeline 中重跑任務前需要判斷的輸出語意與副作用">Rerun&lt;/a> 風險。&lt;/p>
&lt;h2 id="場域定位">場域定位&lt;/h2>
&lt;p>Data pipeline 常包含 batch job、stream processor、dbt model 或 workflow scheduler。部署判斷不只看程式可執行，還要看資料是否可追溯、可對帳、可修復。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>面向&lt;/th>
 &lt;th>Data pipeline 部署常見責任&lt;/th>
 &lt;th>判讀訊號&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Build&lt;/td>
 &lt;td>transform code、DAG、query model&lt;/td>
 &lt;td>版本是否可重現&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Validation&lt;/td>
 &lt;td>schema check、sample run、contract check&lt;/td>
 &lt;td>輸出是否維持相容&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Deploy&lt;/td>
 &lt;td>job version、schedule、trigger&lt;/td>
 &lt;td>新流程是否正確接管&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/backfill/" data-link-title="Backfill" data-link-desc="說明資料處理與 migration 中如何受控補算歷史資料">Backfill&lt;/a>&lt;/td>
 &lt;td>歷史資料補算與節流&lt;/td>
 &lt;td>是否有 checkpoint 與停損條件&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Recovery&lt;/td>
 &lt;td>rerun、rollback、forward fix&lt;/td>
 &lt;td>異常資料是否可修補&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="常見注意事項">常見注意事項&lt;/h2>
&lt;ul>
&lt;li>schema 變更要先定義相容窗口，再切換 downstream。&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/backfill/" data-link-title="Backfill" data-link-desc="說明資料處理與 migration 中如何受控補算歷史資料">Backfill&lt;/a> 要有節流與 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/checkpoint/" data-link-title="Checkpoint" data-link-desc="說明長時間任務如何記錄進度以支援接續、重跑與事故修復">Checkpoint&lt;/a>，避免壓垮上游與儲存層。&lt;/li>
&lt;li>部署後需比對新舊輸出一致性，建立 correctness check。&lt;/li>
&lt;li>重跑流程要有 runbook，避免人工臨場判斷失誤。&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="backfill-checkpoint-rerun-flow/">Data pipeline backfill、checkpoint 與 rerun 流程&lt;/a>&lt;/td>
 &lt;td>Backfill, checkpoint and rerun&lt;/td>
 &lt;td>控制歷史補算、重跑與資料修復&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>Data pipeline 發布主流程：讀 &lt;a href="backfill-checkpoint-rerun-flow/">Data pipeline backfill、checkpoint 與 rerun 流程&lt;/a>。&lt;/li>
&lt;li>後端資料遷移概念：讀 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明系統如何把資料、流量或結構從舊狀態移到新狀態">Migration&lt;/a>。&lt;/li>
&lt;li>資料修補與比對：讀 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/backfill/" data-link-title="Backfill" data-link-desc="說明如何為既有資料補上新欄位、新索引或新衍生狀態">Backfill&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/correctness-check/" data-link-title="Correctness Check" data-link-desc="說明遷移或重構期間如何驗證新舊結果是否符合規則">Correctness Check&lt;/a>。&lt;/li>
&lt;li>Gate 原理：讀 &lt;a href="../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界&lt;/a>。&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Data Pipeline 部署 CI/CD 的核心責任是把資料處理邏輯推進到生產環境，同時維持資料正確性與可回復性。它和 API 部署不同，重點在 schema 相容、<a href="/blog/ci/knowledge-cards/backfill/" data-link-title="Backfill" data-link-desc="說明資料處理與 migration 中如何受控補算歷史資料">Backfill</a>、<a href="/blog/ci/knowledge-cards/checkpoint/" data-link-title="Checkpoint" data-link-desc="說明長時間任務如何記錄進度以支援接續、重跑與事故修復">Checkpoint</a> 與 <a href="/blog/ci/knowledge-cards/rerun/" data-link-title="Rerun" data-link-desc="說明 CI/CD 與 data pipeline 中重跑任務前需要判斷的輸出語意與副作用">Rerun</a> 風險。</p>
<h2 id="場域定位">場域定位</h2>
<p>Data pipeline 常包含 batch job、stream processor、dbt model 或 workflow scheduler。部署判斷不只看程式可執行，還要看資料是否可追溯、可對帳、可修復。</p>
<table>
  <thead>
      <tr>
          <th>面向</th>
          <th>Data pipeline 部署常見責任</th>
          <th>判讀訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Build</td>
          <td>transform code、DAG、query model</td>
          <td>版本是否可重現</td>
      </tr>
      <tr>
          <td>Validation</td>
          <td>schema check、sample run、contract check</td>
          <td>輸出是否維持相容</td>
      </tr>
      <tr>
          <td>Deploy</td>
          <td>job version、schedule、trigger</td>
          <td>新流程是否正確接管</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/backfill/" data-link-title="Backfill" data-link-desc="說明資料處理與 migration 中如何受控補算歷史資料">Backfill</a></td>
          <td>歷史資料補算與節流</td>
          <td>是否有 checkpoint 與停損條件</td>
      </tr>
      <tr>
          <td>Recovery</td>
          <td>rerun、rollback、forward fix</td>
          <td>異常資料是否可修補</td>
      </tr>
  </tbody>
</table>
<h2 id="常見注意事項">常見注意事項</h2>
<ul>
<li>schema 變更要先定義相容窗口，再切換 downstream。</li>
<li><a href="/blog/ci/knowledge-cards/backfill/" data-link-title="Backfill" data-link-desc="說明資料處理與 migration 中如何受控補算歷史資料">Backfill</a> 要有節流與 <a href="/blog/ci/knowledge-cards/checkpoint/" data-link-title="Checkpoint" data-link-desc="說明長時間任務如何記錄進度以支援接續、重跑與事故修復">Checkpoint</a>，避免壓垮上游與儲存層。</li>
<li>部署後需比對新舊輸出一致性，建立 correctness check。</li>
<li>重跑流程要有 runbook，避免人工臨場判斷失誤。</li>
</ul>
<h2 id="學習路線">學習路線</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="backfill-checkpoint-rerun-flow/">Data pipeline backfill、checkpoint 與 rerun 流程</a></td>
          <td>Backfill, checkpoint and rerun</td>
          <td>控制歷史補算、重跑與資料修復</td>
      </tr>
  </tbody>
</table>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>Data pipeline 發布主流程：讀 <a href="backfill-checkpoint-rerun-flow/">Data pipeline backfill、checkpoint 與 rerun 流程</a>。</li>
<li>後端資料遷移概念：讀 <a href="/blog/backend/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明系統如何把資料、流量或結構從舊狀態移到新狀態">Migration</a>。</li>
<li>資料修補與比對：讀 <a href="/blog/backend/knowledge-cards/backfill/" data-link-title="Backfill" data-link-desc="說明如何為既有資料補上新欄位、新索引或新衍生狀態">Backfill</a> 與 <a href="/blog/backend/knowledge-cards/correctness-check/" data-link-title="Correctness Check" data-link-desc="說明遷移或重構期間如何驗證新舊結果是否符合規則">Correctness Check</a>。</li>
<li>Gate 原理：讀 <a href="../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a>。</li>
</ul>
]]></content:encoded></item><item><title>Backfill</title><link>https://tarrragon.github.io/blog/ci/knowledge-cards/backfill/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/knowledge-cards/backfill/</guid><description>&lt;p>Backfill 的核心概念是「用新邏輯受控補算既有資料」。它通常和 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明資料或結構變更如何在服務不中斷前提下受控推進">Migration&lt;/a> 共享相容窗口，並依賴 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/checkpoint/" data-link-title="Checkpoint" data-link-desc="說明長時間任務如何記錄進度以支援接續、重跑與事故修復">Checkpoint&lt;/a> 保存進度。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Backfill 位在資料 schema、transform logic 或歷史資料修補之後，常出現在 data pipeline、database migration、search index rebuild 與 feature store 更新。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;ul>
&lt;li>新欄位需要從既有資料補值。&lt;/li>
&lt;li>歷史 partition 需要用新版邏輯重新計算。&lt;/li>
&lt;li>補算任務需要節流、停損與對帳。&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實服務的例子">接近真實服務的例子&lt;/h2>
&lt;p>訂單報表新增 &lt;code>net_revenue&lt;/code> 欄位時，pipeline 先讓新資料寫入新欄位，再分批 backfill 過去 12 個月的 partition，並用 row count 與金額總和比對結果。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Backfill 要定義補算範圍、批次大小、checkpoint、停損條件與對帳方式，讓歷史資料修補成為可停止、可接續、可驗證的流程。&lt;/p></description><content:encoded><![CDATA[<p>Backfill 的核心概念是「用新邏輯受控補算既有資料」。它通常和 <a href="/blog/ci/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明資料或結構變更如何在服務不中斷前提下受控推進">Migration</a> 共享相容窗口，並依賴 <a href="/blog/ci/knowledge-cards/checkpoint/" data-link-title="Checkpoint" data-link-desc="說明長時間任務如何記錄進度以支援接續、重跑與事故修復">Checkpoint</a> 保存進度。</p>
<h2 id="概念位置">概念位置</h2>
<p>Backfill 位在資料 schema、transform logic 或歷史資料修補之後，常出現在 data pipeline、database migration、search index rebuild 與 feature store 更新。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<ul>
<li>新欄位需要從既有資料補值。</li>
<li>歷史 partition 需要用新版邏輯重新計算。</li>
<li>補算任務需要節流、停損與對帳。</li>
</ul>
<h2 id="接近真實服務的例子">接近真實服務的例子</h2>
<p>訂單報表新增 <code>net_revenue</code> 欄位時，pipeline 先讓新資料寫入新欄位，再分批 backfill 過去 12 個月的 partition，並用 row count 與金額總和比對結果。</p>
<h2 id="設計責任">設計責任</h2>
<p>Backfill 要定義補算範圍、批次大小、checkpoint、停損條件與對帳方式，讓歷史資料修補成為可停止、可接續、可驗證的流程。</p>
]]></content:encoded></item><item><title>IaC / Platform 部署 CI/CD</title><link>https://tarrragon.github.io/blog/ci/iac-platform-deploy/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/iac-platform-deploy/</guid><description>&lt;p>IaC / Platform 部署 CI/CD 的核心責任是把基礎設施變更轉成可審查、可追溯、可回復的流程。它和應用部署不同，主要風險在 state、權限、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/infrastructure-drift/" data-link-title="Infrastructure Drift" data-link-desc="說明真實基礎設施狀態與 IaC 宣告分叉時的偵測、判讀與修復責任">Infrastructure Drift&lt;/a> 與不可逆資源變更。&lt;/p>
&lt;h2 id="場域定位">場域定位&lt;/h2>
&lt;p>IaC 流程通常分成 plan、review、apply 三段，並依環境分層推進。部署成功不只代表指令完成，還代表資源狀態符合預期且未引入漂移。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>面向&lt;/th>
 &lt;th>IaC 部署常見責任&lt;/th>
 &lt;th>判讀訊號&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Plan&lt;/td>
 &lt;td>變更差異預覽與風險提示&lt;/td>
 &lt;td>是否包含高風險破壞性變更&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Review&lt;/td>
 &lt;td>審核資源變更與權限範圍&lt;/td>
 &lt;td>是否符合治理規範&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Apply&lt;/td>
 &lt;td>狀態寫入與資源同步&lt;/td>
 &lt;td>state lock / timeout 是否可控&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/infrastructure-drift/" data-link-title="Infrastructure Drift" data-link-desc="說明真實基礎設施狀態與 IaC 宣告分叉時的偵測、判讀與修復責任">Infrastructure Drift&lt;/a>&lt;/td>
 &lt;td>實際環境與宣告差異檢查&lt;/td>
 &lt;td>是否存在未受控手動變更&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Recovery&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>plan 與 apply 要用同一份輸入與版本，避免結果漂移。&lt;/li>
&lt;li>state backend 要有鎖定與權限隔離，避免併發覆寫。&lt;/li>
&lt;li>高風險資源變更需要額外 gate（人工審核或變更時窗）。&lt;/li>
&lt;li>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/infrastructure-drift/" data-link-title="Infrastructure Drift" data-link-desc="說明真實基礎設施狀態與 IaC 宣告分叉時的偵測、判讀與修復責任">Infrastructure Drift&lt;/a> 偵測要定期執行，並有修復責任人。&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="plan-apply-drift-recovery-flow/">IaC plan、apply、drift 與 recovery 流程&lt;/a>&lt;/td>
 &lt;td>Plan, apply, drift and recovery&lt;/td>
 &lt;td>控制基礎設施變更、漂移與回復&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>IaC 發布主流程：讀 &lt;a href="plan-apply-drift-recovery-flow/">IaC plan、apply、drift 與 recovery 流程&lt;/a>。&lt;/li>
&lt;li>環境保護：讀 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/environment-protection/" data-link-title="Environment Protection" data-link-desc="說明目標環境的審核、權限與放行條件如何保護發布">Environment Protection&lt;/a>。&lt;/li>
&lt;li>部署合約：讀 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/deployment-contract/" data-link-title="Deployment Contract" data-link-desc="說明服務與部署平台之間的生命週期約定">Deployment Contract&lt;/a>。&lt;/li>
&lt;li>變更放行：讀 &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;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>IaC / Platform 部署 CI/CD 的核心責任是把基礎設施變更轉成可審查、可追溯、可回復的流程。它和應用部署不同，主要風險在 state、權限、<a href="/blog/ci/knowledge-cards/infrastructure-drift/" data-link-title="Infrastructure Drift" data-link-desc="說明真實基礎設施狀態與 IaC 宣告分叉時的偵測、判讀與修復責任">Infrastructure Drift</a> 與不可逆資源變更。</p>
<h2 id="場域定位">場域定位</h2>
<p>IaC 流程通常分成 plan、review、apply 三段，並依環境分層推進。部署成功不只代表指令完成，還代表資源狀態符合預期且未引入漂移。</p>
<table>
  <thead>
      <tr>
          <th>面向</th>
          <th>IaC 部署常見責任</th>
          <th>判讀訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Plan</td>
          <td>變更差異預覽與風險提示</td>
          <td>是否包含高風險破壞性變更</td>
      </tr>
      <tr>
          <td>Review</td>
          <td>審核資源變更與權限範圍</td>
          <td>是否符合治理規範</td>
      </tr>
      <tr>
          <td>Apply</td>
          <td>狀態寫入與資源同步</td>
          <td>state lock / timeout 是否可控</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/infrastructure-drift/" data-link-title="Infrastructure Drift" data-link-desc="說明真實基礎設施狀態與 IaC 宣告分叉時的偵測、判讀與修復責任">Infrastructure Drift</a></td>
          <td>實際環境與宣告差異檢查</td>
          <td>是否存在未受控手動變更</td>
      </tr>
      <tr>
          <td>Recovery</td>
          <td>回退或補正策略</td>
          <td>失敗時是否有安全回復路徑</td>
      </tr>
  </tbody>
</table>
<h2 id="常見注意事項">常見注意事項</h2>
<ul>
<li>plan 與 apply 要用同一份輸入與版本，避免結果漂移。</li>
<li>state backend 要有鎖定與權限隔離，避免併發覆寫。</li>
<li>高風險資源變更需要額外 gate（人工審核或變更時窗）。</li>
<li><a href="/blog/ci/knowledge-cards/infrastructure-drift/" data-link-title="Infrastructure Drift" data-link-desc="說明真實基礎設施狀態與 IaC 宣告分叉時的偵測、判讀與修復責任">Infrastructure Drift</a> 偵測要定期執行，並有修復責任人。</li>
</ul>
<h2 id="學習路線">學習路線</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="plan-apply-drift-recovery-flow/">IaC plan、apply、drift 與 recovery 流程</a></td>
          <td>Plan, apply, drift and recovery</td>
          <td>控制基礎設施變更、漂移與回復</td>
      </tr>
  </tbody>
</table>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>IaC 發布主流程：讀 <a href="plan-apply-drift-recovery-flow/">IaC plan、apply、drift 與 recovery 流程</a>。</li>
<li>環境保護：讀 <a href="/blog/ci/knowledge-cards/environment-protection/" data-link-title="Environment Protection" data-link-desc="說明目標環境的審核、權限與放行條件如何保護發布">Environment Protection</a>。</li>
<li>部署合約：讀 <a href="/blog/backend/knowledge-cards/deployment-contract/" data-link-title="Deployment Contract" data-link-desc="說明服務與部署平台之間的生命週期約定">Deployment Contract</a>。</li>
<li>變更放行：讀 <a href="/blog/backend/knowledge-cards/release-gate/" data-link-title="Release Gate" data-link-desc="說明變更在正式釋出前如何通過或阻擋">Release Gate</a>。</li>
</ul>
]]></content:encoded></item><item><title>Checkpoint</title><link>https://tarrragon.github.io/blog/ci/knowledge-cards/checkpoint/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/knowledge-cards/checkpoint/</guid><description>&lt;p>Checkpoint 的核心概念是「保存可接續的處理進度」。它讓 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/backfill/" data-link-title="Backfill" data-link-desc="說明資料處理與 migration 中如何受控補算歷史資料">Backfill&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rerun/" data-link-title="Rerun" data-link-desc="說明 CI/CD 與 data pipeline 中重跑任務前需要判斷的輸出語意與副作用">Rerun&lt;/a> 可以從明確位置恢復，避免每次都從頭開始。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Checkpoint 位在長時間 job、stream processor、batch pipeline 與 migration 任務之間，常以 partition、offset、run id、cursor 或 processed marker 呈現。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;ul>
&lt;li>任務執行時間長，失敗後需要接續。&lt;/li>
&lt;li>重跑同一區間可能造成重複寫入。&lt;/li>
&lt;li>streaming consumer 需要保存 offset 或 event position。&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實服務的例子">接近真實服務的例子&lt;/h2>
&lt;p>資料回填每次處理一個日期 partition，完成後寫入 &lt;code>backfill_runs&lt;/code> 表。任務中斷時，下一次從最後成功 partition 的下一段開始。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Checkpoint 要定義進度格式、提交時機、失敗恢復、重跑覆寫與觀測欄位，讓長時間任務具備可恢復性。&lt;/p></description><content:encoded><![CDATA[<p>Checkpoint 的核心概念是「保存可接續的處理進度」。它讓 <a href="/blog/ci/knowledge-cards/backfill/" data-link-title="Backfill" data-link-desc="說明資料處理與 migration 中如何受控補算歷史資料">Backfill</a> 與 <a href="/blog/ci/knowledge-cards/rerun/" data-link-title="Rerun" data-link-desc="說明 CI/CD 與 data pipeline 中重跑任務前需要判斷的輸出語意與副作用">Rerun</a> 可以從明確位置恢復，避免每次都從頭開始。</p>
<h2 id="概念位置">概念位置</h2>
<p>Checkpoint 位在長時間 job、stream processor、batch pipeline 與 migration 任務之間，常以 partition、offset、run id、cursor 或 processed marker 呈現。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<ul>
<li>任務執行時間長，失敗後需要接續。</li>
<li>重跑同一區間可能造成重複寫入。</li>
<li>streaming consumer 需要保存 offset 或 event position。</li>
</ul>
<h2 id="接近真實服務的例子">接近真實服務的例子</h2>
<p>資料回填每次處理一個日期 partition，完成後寫入 <code>backfill_runs</code> 表。任務中斷時，下一次從最後成功 partition 的下一段開始。</p>
<h2 id="設計責任">設計責任</h2>
<p>Checkpoint 要定義進度格式、提交時機、失敗恢復、重跑覆寫與觀測欄位，讓長時間任務具備可恢復性。</p>
]]></content:encoded></item><item><title>Desktop Client 部署 CI/CD</title><link>https://tarrragon.github.io/blog/ci/desktop-client-deploy/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/desktop-client-deploy/</guid><description>&lt;p>Desktop Client 部署 CI/CD 的核心責任是把可安裝客戶端安全交付到使用者裝置，並維持可更新與可回退能力。它和 web 發布不同，重點在安裝包簽章、公證、更新通道與多平台相容。&lt;/p>
&lt;h2 id="場域定位">場域定位&lt;/h2>
&lt;p>Desktop client 常見於 Flutter Desktop、Electron、Tauri。部署流程通常要分平台建置（macOS、Windows、Linux），並處理安裝體驗、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/release-channel/" data-link-title="Release Channel" data-link-desc="說明 stable、beta、internal 等發行通道如何控制 artifact 接觸到的使用者範圍">Release Channel&lt;/a> 更新節奏與版本共存。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>面向&lt;/th>
 &lt;th>Desktop client 部署常見責任&lt;/th>
 &lt;th>判讀訊號&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Build&lt;/td>
 &lt;td>platform-specific bundle / installer&lt;/td>
 &lt;td>各平台產物是否可重現&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Signing&lt;/td>
 &lt;td>code signing、notarization、timestamp&lt;/td>
 &lt;td>安裝與啟動是否受信任&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Release&lt;/td>
 &lt;td>channel、staged rollout、notes&lt;/td>
 &lt;td>更新節奏是否可控&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Update&lt;/td>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/update-feed/" data-link-title="Update Feed" data-link-desc="說明桌面與客戶端應用如何透過更新來源取得已簽章版本與回復路徑">Update Feed&lt;/a>、delta package&lt;/td>
 &lt;td>升級是否穩定可回復&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Recovery&lt;/td>
 &lt;td>hotfix package、rollback channel&lt;/td>
 &lt;td>失敗時是否可快速回退&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="常見注意事項">常見注意事項&lt;/h2>
&lt;ul>
&lt;li>不同 OS 的簽章與公證流程需分開治理。&lt;/li>
&lt;li>Auto-update 要有版本相容策略與 fallback feed。&lt;/li>
&lt;li>崩潰回報與更新成功率應列為發布後 gate。&lt;/li>
&lt;li>若與 Flutter App 共用程式碼，要明確區分 mobile 與 desktop 的發布管線。&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="signing-notarization-update-flow/">Desktop client 簽章、公證與自動更新流程&lt;/a>&lt;/td>
 &lt;td>Signing, notarization and update&lt;/td>
 &lt;td>管理安裝包信任鏈、更新通道與回復&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>Desktop 發布主流程：讀 &lt;a href="signing-notarization-update-flow/">Desktop client 簽章、公證與自動更新流程&lt;/a>。&lt;/li>
&lt;li>行動與客戶端通用觀念：讀 &lt;a href="../app-deploy/">App 部署 CI/CD&lt;/a>。&lt;/li>
&lt;li>簽章治理：讀 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/app-signing/" data-link-title="App Signing" data-link-desc="說明行動與桌面應用的簽章憑證如何影響發布能力">App Signing&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/backend/knowledge-cards/secret-management/" data-link-title="Secret Management" data-link-desc="說明 token、key、password 與憑證如何保存、輪替與撤銷">Secret Management&lt;/a>。&lt;/li>
&lt;li>失敗處理：讀 &lt;a href="../github-actions-failure-flow/">CI 失敗到修復發布流程&lt;/a>。&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>Desktop Client 部署 CI/CD 的核心責任是把可安裝客戶端安全交付到使用者裝置，並維持可更新與可回退能力。它和 web 發布不同，重點在安裝包簽章、公證、更新通道與多平台相容。</p>
<h2 id="場域定位">場域定位</h2>
<p>Desktop client 常見於 Flutter Desktop、Electron、Tauri。部署流程通常要分平台建置（macOS、Windows、Linux），並處理安裝體驗、<a href="/blog/ci/knowledge-cards/release-channel/" data-link-title="Release Channel" data-link-desc="說明 stable、beta、internal 等發行通道如何控制 artifact 接觸到的使用者範圍">Release Channel</a> 更新節奏與版本共存。</p>
<table>
  <thead>
      <tr>
          <th>面向</th>
          <th>Desktop client 部署常見責任</th>
          <th>判讀訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Build</td>
          <td>platform-specific bundle / installer</td>
          <td>各平台產物是否可重現</td>
      </tr>
      <tr>
          <td>Signing</td>
          <td>code signing、notarization、timestamp</td>
          <td>安裝與啟動是否受信任</td>
      </tr>
      <tr>
          <td>Release</td>
          <td>channel、staged rollout、notes</td>
          <td>更新節奏是否可控</td>
      </tr>
      <tr>
          <td>Update</td>
          <td><a href="/blog/ci/knowledge-cards/update-feed/" data-link-title="Update Feed" data-link-desc="說明桌面與客戶端應用如何透過更新來源取得已簽章版本與回復路徑">Update Feed</a>、delta package</td>
          <td>升級是否穩定可回復</td>
      </tr>
      <tr>
          <td>Recovery</td>
          <td>hotfix package、rollback channel</td>
          <td>失敗時是否可快速回退</td>
      </tr>
  </tbody>
</table>
<h2 id="常見注意事項">常見注意事項</h2>
<ul>
<li>不同 OS 的簽章與公證流程需分開治理。</li>
<li>Auto-update 要有版本相容策略與 fallback feed。</li>
<li>崩潰回報與更新成功率應列為發布後 gate。</li>
<li>若與 Flutter App 共用程式碼，要明確區分 mobile 與 desktop 的發布管線。</li>
</ul>
<h2 id="學習路線">學習路線</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="signing-notarization-update-flow/">Desktop client 簽章、公證與自動更新流程</a></td>
          <td>Signing, notarization and update</td>
          <td>管理安裝包信任鏈、更新通道與回復</td>
      </tr>
  </tbody>
</table>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>Desktop 發布主流程：讀 <a href="signing-notarization-update-flow/">Desktop client 簽章、公證與自動更新流程</a>。</li>
<li>行動與客戶端通用觀念：讀 <a href="../app-deploy/">App 部署 CI/CD</a>。</li>
<li>簽章治理：讀 <a href="/blog/ci/knowledge-cards/app-signing/" data-link-title="App Signing" data-link-desc="說明行動與桌面應用的簽章憑證如何影響發布能力">App Signing</a> 與 <a href="/blog/backend/knowledge-cards/secret-management/" data-link-title="Secret Management" data-link-desc="說明 token、key、password 與憑證如何保存、輪替與撤銷">Secret Management</a>。</li>
<li>失敗處理：讀 <a href="../github-actions-failure-flow/">CI 失敗到修復發布流程</a>。</li>
</ul>
]]></content:encoded></item><item><title>Rerun</title><link>https://tarrragon.github.io/blog/ci/knowledge-cards/rerun/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/knowledge-cards/rerun/</guid><description>&lt;p>Rerun 的核心概念是「用明確條件重新執行同一段流程」。它和 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/flaky-test/" data-link-title="Flaky Test" data-link-desc="說明非決定性測試如何降低 CI gate 信任度與治理方式">Flaky Test&lt;/a> 的治理有關，也常依賴 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/checkpoint/" data-link-title="Checkpoint" data-link-desc="說明長時間任務如何記錄進度以支援接續、重跑與事故修復">Checkpoint&lt;/a> 判斷接續位置。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Rerun 位在測試失敗、部署預演失敗、資料任務失敗或 pipeline repair 之後，負責判斷重新執行是否會改變輸出或擴大副作用。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;ul>
&lt;li>同一 commit 的測試結果前後不一致。&lt;/li>
&lt;li>資料任務部分成功、部分失敗。&lt;/li>
&lt;li>部署 dry run 失敗後需要確認是否可安全再跑。&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實服務的例子">接近真實服務的例子&lt;/h2>
&lt;p>每日營收 pipeline 第三個 partition 寫入失敗。團隊先確認前兩個 partition 已完成且輸出可覆寫，再指定 run id 與 partition 範圍 rerun，避免重複計算全部歷史資料。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Rerun 要定義可重跑條件、輸出覆寫規則、idempotency、觀測結果與人工審核門檻，讓「再跑一次」成為受控恢復策略。&lt;/p></description><content:encoded><![CDATA[<p>Rerun 的核心概念是「用明確條件重新執行同一段流程」。它和 <a href="/blog/ci/knowledge-cards/flaky-test/" data-link-title="Flaky Test" data-link-desc="說明非決定性測試如何降低 CI gate 信任度與治理方式">Flaky Test</a> 的治理有關，也常依賴 <a href="/blog/ci/knowledge-cards/checkpoint/" data-link-title="Checkpoint" data-link-desc="說明長時間任務如何記錄進度以支援接續、重跑與事故修復">Checkpoint</a> 判斷接續位置。</p>
<h2 id="概念位置">概念位置</h2>
<p>Rerun 位在測試失敗、部署預演失敗、資料任務失敗或 pipeline repair 之後，負責判斷重新執行是否會改變輸出或擴大副作用。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<ul>
<li>同一 commit 的測試結果前後不一致。</li>
<li>資料任務部分成功、部分失敗。</li>
<li>部署 dry run 失敗後需要確認是否可安全再跑。</li>
</ul>
<h2 id="接近真實服務的例子">接近真實服務的例子</h2>
<p>每日營收 pipeline 第三個 partition 寫入失敗。團隊先確認前兩個 partition 已完成且輸出可覆寫，再指定 run id 與 partition 範圍 rerun，避免重複計算全部歷史資料。</p>
<h2 id="設計責任">設計責任</h2>
<p>Rerun 要定義可重跑條件、輸出覆寫規則、idempotency、觀測結果與人工審核門檻，讓「再跑一次」成為受控恢復策略。</p>
]]></content:encoded></item><item><title>Package / Library Release CI/CD</title><link>https://tarrragon.github.io/blog/ci/package-library-release/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/package-library-release/</guid><description>&lt;p>Package / Library Release CI/CD 的核心責任是把可重用套件安全發佈到分發平台，並維持版本語意與相容承諾。它和應用部署不同，重點在版本管理、相容邊界、發佈簽章與撤版策略。&lt;/p>
&lt;h2 id="場域定位">場域定位&lt;/h2>
&lt;p>套件發佈常見於 NPM、PyPI、Maven、Crates 等生態。發布後會被多個下游專案依賴，因此每次 release 都是公共契約變更。&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>面向&lt;/th>
 &lt;th>Package release 常見責任&lt;/th>
 &lt;th>判讀訊號&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Build&lt;/td>
 &lt;td>package artifact、metadata、lock input&lt;/td>
 &lt;td>產物是否可重現&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Validation&lt;/td>
 &lt;td>API/ABI 相容性、smoke test、publish dry-run&lt;/td>
 &lt;td>破壞性變更是否被識別&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Versioning&lt;/td>
 &lt;td>semver、pre-release、changelog&lt;/td>
 &lt;td>版本語意是否與變更一致&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Publish&lt;/td>
 &lt;td>registry token、scope、provenance&lt;/td>
 &lt;td>發版是否可追溯且權限正確&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Recovery&lt;/td>
 &lt;td>yank/deprecate/hotfix release&lt;/td>
 &lt;td>事故時是否可快速止損&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="release-發布類型分類">Release 發布類型分類&lt;/h2>
&lt;p>「發版」在中文討論裡常被當成單一動作，但實際上有五條互不重疊的通道，每條的觸發條件、產物形式、下游取用方式都不一樣。下游使用者讀 README 時若沒分清楚自己在走哪條通道，很容易踩到「文件寫了安裝指令，但對應通道還沒被建立」的情況。&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;th>代表生態&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Source release&lt;/td>
 &lt;td>git tag + tarball&lt;/td>
 &lt;td>&lt;code>git clone&lt;/code> 或 &lt;code>go install&lt;/code> 後編譯&lt;/td>
 &lt;td>tag push&lt;/td>
 &lt;td>Go module、許多 OSS 函式庫&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Registry publish&lt;/td>
 &lt;td>套件清單登錄&lt;/td>
 &lt;td>&lt;code>npm install&lt;/code> / &lt;code>pip install&lt;/code> 等&lt;/td>
 &lt;td>&lt;code>publish&lt;/code> 指令&lt;/td>
 &lt;td>npm、PyPI、crates.io、Maven&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Binary release&lt;/td>
 &lt;td>預編譯多平台執行檔，掛在 GitHub Release&lt;/td>
 &lt;td>下載 binary 或 installer script&lt;/td>
 &lt;td>tag push&lt;/td>
 &lt;td>cargo-dist、goreleaser 工具鏈&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Container image&lt;/td>
 &lt;td>OCI image&lt;/td>
 &lt;td>&lt;code>docker pull&lt;/code> / k8s manifest&lt;/td>
 &lt;td>tag 或 commit&lt;/td>
 &lt;td>Docker Hub、GHCR、ECR&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>OS package&lt;/td>
 &lt;td>&lt;code>.deb&lt;/code> / &lt;code>.rpm&lt;/code> / Homebrew formula&lt;/td>
 &lt;td>套件管理器 install&lt;/td>
 &lt;td>上游同步&lt;/td>
 &lt;td>apt、yum、Homebrew、winget&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這五類常常組合出現（例如同時推 source、registry、binary release）。組合愈多、上游維護成本愈高，但下游能用的入口也愈廣。判讀訊號：&lt;/p>
&lt;ul>
&lt;li>README 寫的是 &lt;code>pip install x&lt;/code> → 屬 registry，去 PyPI 確認版本&lt;/li>
&lt;li>README 寫的是 &lt;code>curl ... /releases/latest/download/...sh | sh&lt;/code> → 屬 binary release + installer，去 GitHub Releases 確認 asset 存在&lt;/li>
&lt;li>README 寫的是 &lt;code>git clone&lt;/code> 後 &lt;code>make&lt;/code> → 只走 source，沒任何打包通道&lt;/li>
&lt;li>README 寫的是 &lt;code>docker pull ghcr.io/...&lt;/code> → 屬 container image，去 registry 確認 tag&lt;/li>
&lt;/ul>
&lt;h2 id="常見注意事項">常見注意事項&lt;/h2>
&lt;ul>
&lt;li>發版前要明確區分 breaking / feature / fix，避免版本語意錯置。&lt;/li>
&lt;li>發版流程應固定化（tag 規則、changelog 來源、artifact provenance）。&lt;/li>
&lt;li>對外 SDK 要維持 contract 測試，避免下游升級破壞。&lt;/li>
&lt;li>套件來源與 token 權限要最小化，並定期輪替。&lt;/li>
&lt;li>README 安裝段落寫的通道，發版前要實際跑過一次 — 「workflow 寫好」不代表「通道已上線」。&lt;/li>
&lt;/ul>
&lt;h2 id="安裝路徑分層">安裝路徑分層&lt;/h2>
&lt;p>Package release 的文件建議同時提供兩條安裝路徑，讓不同風險場景有對應入口。&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;th>&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>快速路徑&lt;/td>
 &lt;td>本機快速試用、低風險場景&lt;/td>
 &lt;td>一行安裝命令（例如 `curl &amp;hellip;&lt;/td>
 &lt;td>sh`）&lt;/td>
 &lt;td>速度優先，依賴上游發布品質&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>可審計路徑&lt;/td>
 &lt;td>生產環境、受管設備、合規場景&lt;/td>
 &lt;td>下載產物 → 驗證 checksum/provenance → 執行&lt;/td>
 &lt;td>可追溯、可驗證、可稽核&lt;/td>
 &lt;td>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這個分層能避免單一路徑綁死全部使用者。上游維護者要確保兩條路徑都可用，且文件清楚標示使用時機。可審計路徑的具體範例可直接沿用 &lt;a href="binary-release-and-installer/">Binary release 與 installer 模式&lt;/a> 的最小安全基線。&lt;/p></description><content:encoded><![CDATA[<p>Package / Library Release CI/CD 的核心責任是把可重用套件安全發佈到分發平台，並維持版本語意與相容承諾。它和應用部署不同，重點在版本管理、相容邊界、發佈簽章與撤版策略。</p>
<h2 id="場域定位">場域定位</h2>
<p>套件發佈常見於 NPM、PyPI、Maven、Crates 等生態。發布後會被多個下游專案依賴，因此每次 release 都是公共契約變更。</p>
<table>
  <thead>
      <tr>
          <th>面向</th>
          <th>Package release 常見責任</th>
          <th>判讀訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Build</td>
          <td>package artifact、metadata、lock input</td>
          <td>產物是否可重現</td>
      </tr>
      <tr>
          <td>Validation</td>
          <td>API/ABI 相容性、smoke test、publish dry-run</td>
          <td>破壞性變更是否被識別</td>
      </tr>
      <tr>
          <td>Versioning</td>
          <td>semver、pre-release、changelog</td>
          <td>版本語意是否與變更一致</td>
      </tr>
      <tr>
          <td>Publish</td>
          <td>registry token、scope、provenance</td>
          <td>發版是否可追溯且權限正確</td>
      </tr>
      <tr>
          <td>Recovery</td>
          <td>yank/deprecate/hotfix release</td>
          <td>事故時是否可快速止損</td>
      </tr>
  </tbody>
</table>
<h2 id="release-發布類型分類">Release 發布類型分類</h2>
<p>「發版」在中文討論裡常被當成單一動作，但實際上有五條互不重疊的通道，每條的觸發條件、產物形式、下游取用方式都不一樣。下游使用者讀 README 時若沒分清楚自己在走哪條通道，很容易踩到「文件寫了安裝指令，但對應通道還沒被建立」的情況。</p>
<table>
  <thead>
      <tr>
          <th>類型</th>
          <th>產物形式</th>
          <th>下游取用方式</th>
          <th>典型觸發</th>
          <th>代表生態</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Source release</td>
          <td>git tag + tarball</td>
          <td><code>git clone</code> 或 <code>go install</code> 後編譯</td>
          <td>tag push</td>
          <td>Go module、許多 OSS 函式庫</td>
      </tr>
      <tr>
          <td>Registry publish</td>
          <td>套件清單登錄</td>
          <td><code>npm install</code> / <code>pip install</code> 等</td>
          <td><code>publish</code> 指令</td>
          <td>npm、PyPI、crates.io、Maven</td>
      </tr>
      <tr>
          <td>Binary release</td>
          <td>預編譯多平台執行檔，掛在 GitHub Release</td>
          <td>下載 binary 或 installer script</td>
          <td>tag push</td>
          <td>cargo-dist、goreleaser 工具鏈</td>
      </tr>
      <tr>
          <td>Container image</td>
          <td>OCI image</td>
          <td><code>docker pull</code> / k8s manifest</td>
          <td>tag 或 commit</td>
          <td>Docker Hub、GHCR、ECR</td>
      </tr>
      <tr>
          <td>OS package</td>
          <td><code>.deb</code> / <code>.rpm</code> / Homebrew formula</td>
          <td>套件管理器 install</td>
          <td>上游同步</td>
          <td>apt、yum、Homebrew、winget</td>
      </tr>
  </tbody>
</table>
<p>這五類常常組合出現（例如同時推 source、registry、binary release）。組合愈多、上游維護成本愈高，但下游能用的入口也愈廣。判讀訊號：</p>
<ul>
<li>README 寫的是 <code>pip install x</code> → 屬 registry，去 PyPI 確認版本</li>
<li>README 寫的是 <code>curl ... /releases/latest/download/...sh | sh</code> → 屬 binary release + installer，去 GitHub Releases 確認 asset 存在</li>
<li>README 寫的是 <code>git clone</code> 後 <code>make</code> → 只走 source，沒任何打包通道</li>
<li>README 寫的是 <code>docker pull ghcr.io/...</code> → 屬 container image，去 registry 確認 tag</li>
</ul>
<h2 id="常見注意事項">常見注意事項</h2>
<ul>
<li>發版前要明確區分 breaking / feature / fix，避免版本語意錯置。</li>
<li>發版流程應固定化（tag 規則、changelog 來源、artifact provenance）。</li>
<li>對外 SDK 要維持 contract 測試，避免下游升級破壞。</li>
<li>套件來源與 token 權限要最小化，並定期輪替。</li>
<li>README 安裝段落寫的通道，發版前要實際跑過一次 — 「workflow 寫好」不代表「通道已上線」。</li>
</ul>
<h2 id="安裝路徑分層">安裝路徑分層</h2>
<p>Package release 的文件建議同時提供兩條安裝路徑，讓不同風險場景有對應入口。</p>
<table>
  <thead>
      <tr>
          <th>路徑類型</th>
          <th>目標讀者</th>
          <th>流程</th>
          <th>風險控制</th>
          <th></th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>快速路徑</td>
          <td>本機快速試用、低風險場景</td>
          <td>一行安裝命令（例如 `curl &hellip;</td>
          <td>sh`）</td>
          <td>速度優先，依賴上游發布品質</td>
      </tr>
      <tr>
          <td>可審計路徑</td>
          <td>生產環境、受管設備、合規場景</td>
          <td>下載產物 → 驗證 checksum/provenance → 執行</td>
          <td>可追溯、可驗證、可稽核</td>
          <td></td>
      </tr>
  </tbody>
</table>
<p>這個分層能避免單一路徑綁死全部使用者。上游維護者要確保兩條路徑都可用，且文件清楚標示使用時機。可審計路徑的具體範例可直接沿用 <a href="binary-release-and-installer/">Binary release 與 installer 模式</a> 的最小安全基線。</p>
<h2 id="學習路線">學習路線</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="binary-release-and-installer/">Binary release 與 installer 模式</a></td>
          <td>Tag-driven binary release</td>
          <td>GitHub Release + cargo-dist / goreleaser 的發版鏈路</td>
      </tr>
  </tbody>
</table>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>想理解 binary release + installer 模式（curl &hellip; | sh）：讀 <a href="binary-release-and-installer/">Binary release 與 installer 模式</a>。</li>
<li>供應鏈與產物可信度：讀 <a href="/blog/backend/knowledge-cards/artifact-provenance/" data-link-title="Artifact Provenance" data-link-desc="說明交付物的來源、完整性與簽章關聯如何建立信任">Artifact Provenance</a>。</li>
<li>版本契約：讀 <a href="/blog/backend/knowledge-cards/api-contract/" data-link-title="API Contract" data-link-desc="說明 request / response 邊界如何維持相容與可驗證">API Contract</a> 與 <a href="/blog/backend/knowledge-cards/contract/" data-link-title="Boundary Contract" data-link-desc="說明跨邊界約定如何維持相容與可驗證">Contract</a>。</li>
<li>失敗處理：讀 <a href="../github-actions-failure-flow/">CI 失敗到修復發布流程</a>。</li>
</ul>
]]></content:encoded></item><item><title>Image Digest</title><link>https://tarrragon.github.io/blog/ci/knowledge-cards/image-digest/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/knowledge-cards/image-digest/</guid><description>&lt;p>Image Digest 的核心概念是「用內容雜湊識別不可變 image」。它補足 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/container-registry/" data-link-title="Container Registry" data-link-desc="說明容器產物儲存、權限與推進流程在 CD 中的責任">Container Registry&lt;/a> 的命名治理，讓 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">Artifact Handoff&lt;/a> 可以鎖定精準產物。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Image Digest 位在 image build、scan、registry promotion 與 runtime deploy 之間，通常以 &lt;code>sha256:...&lt;/code> 形式標識 image manifest 或 image index。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;ul>
&lt;li>&lt;code>latest&lt;/code> 或 mutable tag 造成 staging 與 production 內容分叉。&lt;/li>
&lt;li>production runtime 需要反查實際跑的 image。&lt;/li>
&lt;li>掃描結果需要和部署內容精準對齊。&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實服務的例子">接近真實服務的例子&lt;/h2>
&lt;p>CI build image 後推到 registry，scan 報告綁定 digest。Kubernetes manifest 在 production 使用同一個 digest，事故時可從 running pod 反查 workflow run 與 source commit。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Image Digest 要納入 deployment manifest、scan report、release note 與 rollback 記錄，讓 image 發布具備可追溯與可審計能力。&lt;/p></description><content:encoded><![CDATA[<p>Image Digest 的核心概念是「用內容雜湊識別不可變 image」。它補足 <a href="/blog/ci/knowledge-cards/container-registry/" data-link-title="Container Registry" data-link-desc="說明容器產物儲存、權限與推進流程在 CD 中的責任">Container Registry</a> 的命名治理，讓 <a href="/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">Artifact Handoff</a> 可以鎖定精準產物。</p>
<h2 id="概念位置">概念位置</h2>
<p>Image Digest 位在 image build、scan、registry promotion 與 runtime deploy 之間，通常以 <code>sha256:...</code> 形式標識 image manifest 或 image index。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<ul>
<li><code>latest</code> 或 mutable tag 造成 staging 與 production 內容分叉。</li>
<li>production runtime 需要反查實際跑的 image。</li>
<li>掃描結果需要和部署內容精準對齊。</li>
</ul>
<h2 id="接近真實服務的例子">接近真實服務的例子</h2>
<p>CI build image 後推到 registry，scan 報告綁定 digest。Kubernetes manifest 在 production 使用同一個 digest，事故時可從 running pod 反查 workflow run 與 source commit。</p>
<h2 id="設計責任">設計責任</h2>
<p>Image Digest 要納入 deployment manifest、scan report、release note 與 rollback 記錄，讓 image 發布具備可追溯與可審計能力。</p>
]]></content:encoded></item><item><title>SBOM</title><link>https://tarrragon.github.io/blog/ci/knowledge-cards/sbom/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/knowledge-cards/sbom/</guid><description>&lt;p>SBOM 的核心概念是「列出 artifact 內含軟體元件」。它把 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">Artifact&lt;/a> 的依賴組成顯性化，並支援 image scan、license review 與 vulnerability exception。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>SBOM 位在 build、scan、release 與 compliance review 之間，常見格式包含 SPDX、CycloneDX 或工具自訂輸出。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;ul>
&lt;li>團隊需要知道 image 或 package 包含哪些 dependency。&lt;/li>
&lt;li>漏洞公告需要快速判斷受影響 artifact。&lt;/li>
&lt;li>高治理環境要求 release 產物附帶供應鏈證據。&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實服務的例子">接近真實服務的例子&lt;/h2>
&lt;p>Container image 發布時同時產生 SBOM，scan gate 依 SBOM 對照 CVE 與 license policy。若 base image 發現 critical vulnerability，團隊可查哪些 release digest 受影響。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>SBOM 要定義產出時機、格式、保存位置、artifact 對應關係與例外審核流程，讓供應鏈風險可以被查詢與治理。&lt;/p></description><content:encoded><![CDATA[<p>SBOM 的核心概念是「列出 artifact 內含軟體元件」。它把 <a href="/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">Artifact</a> 的依賴組成顯性化，並支援 image scan、license review 與 vulnerability exception。</p>
<h2 id="概念位置">概念位置</h2>
<p>SBOM 位在 build、scan、release 與 compliance review 之間，常見格式包含 SPDX、CycloneDX 或工具自訂輸出。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<ul>
<li>團隊需要知道 image 或 package 包含哪些 dependency。</li>
<li>漏洞公告需要快速判斷受影響 artifact。</li>
<li>高治理環境要求 release 產物附帶供應鏈證據。</li>
</ul>
<h2 id="接近真實服務的例子">接近真實服務的例子</h2>
<p>Container image 發布時同時產生 SBOM，scan gate 依 SBOM 對照 CVE 與 license policy。若 base image 發現 critical vulnerability，團隊可查哪些 release digest 受影響。</p>
<h2 id="設計責任">設計責任</h2>
<p>SBOM 要定義產出時機、格式、保存位置、artifact 對應關係與例外審核流程，讓供應鏈風險可以被查詢與治理。</p>
]]></content:encoded></item><item><title>本 blog 專案部署</title><link>https://tarrragon.github.io/blog/ci/blog-project-deploy/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/blog-project-deploy/</guid><description>&lt;p>本 blog 專案部署是前端靜態站部署的一個具體案例。這個資料夾只記錄本專案實際使用的 Hugo、Pagefind、Playwright、GitHub Pages 與 Claude workflow，不把這些細節當成所有 CI/CD 場域的通用規則。&lt;/p>
&lt;h2 id="專案定位">專案定位&lt;/h2>
&lt;p>本專案的部署產物是靜態網站。Hugo 負責產生 HTML，Pagefind 負責產生搜尋索引，GitHub Pages 負責 hosting，Playwright 負責驗證搜尋與版面行為。&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>&lt;a href="github-actions-workflows/">GitHub Actions workflow&lt;/a>&lt;/td>
 &lt;td>記錄本專案 &lt;code>.github/workflows/&lt;/code> 的實際設定&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;h2 id="與通用-cicd-的關係">與通用 CI/CD 的關係&lt;/h2>
&lt;p>本資料夾是實例層。通用 gate 原理、不同部署場域差異與失敗處理流程放在上層文章；本資料夾只回答「這個 blog 專案現在怎麼部署、失敗時要看哪裡」。術語定義統一回連 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/" data-link-title="Knowledge Cards" data-link-desc="用原子化卡片整理 CI/CD 章節的核心術語，讓流程文章專注在判讀與決策">CI 知識卡片&lt;/a>。&lt;/p>
&lt;h2 id="下一步路由">下一步路由&lt;/h2>
&lt;ul>
&lt;li>本專案 workflow：讀 &lt;a href="github-actions-workflows/">GitHub Actions workflow&lt;/a>。&lt;/li>
&lt;li>前端部署通用注意事項：讀 &lt;a href="../frontend-deploy/">前端部署 CI/CD&lt;/a>。&lt;/li>
&lt;li>CI gate 原理：讀 &lt;a href="../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界&lt;/a>。&lt;/li>
&lt;li>Markdown CI 規則：讀 &lt;a href="https://tarrragon.github.io/blog/posts/blog-markdown-%E5%AF%AB%E4%BD%9C%E8%A6%8F%E7%AF%84%E8%88%87-mdtools-%E6%AA%A2%E6%9F%A5/" data-link-title="Blog Markdown 寫作規範與 mdtools 檢查" data-link-desc="本 blog 的 Markdown 排版規範權威契約。涵蓋 H1 禁用、MD024 siblings_only、反釣魚 TLD 校驗、卡片雙向完整性、front matter schema；改規則時要與 scripts/mdtools 實作同步。">Blog Markdown 寫作規範與 mdtools 檢查&lt;/a>。&lt;/li>
&lt;/ul></description><content:encoded><![CDATA[<p>本 blog 專案部署是前端靜態站部署的一個具體案例。這個資料夾只記錄本專案實際使用的 Hugo、Pagefind、Playwright、GitHub Pages 與 Claude workflow，不把這些細節當成所有 CI/CD 場域的通用規則。</p>
<h2 id="專案定位">專案定位</h2>
<p>本專案的部署產物是靜態網站。Hugo 負責產生 HTML，Pagefind 負責產生搜尋索引，GitHub Pages 負責 hosting，Playwright 負責驗證搜尋與版面行為。</p>
<table>
  <thead>
      <tr>
          <th>文件</th>
          <th>責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="github-actions-workflows/">GitHub Actions workflow</a></td>
          <td>記錄本專案 <code>.github/workflows/</code> 的實際設定</td>
      </tr>
  </tbody>
</table>
<h2 id="與通用-cicd-的關係">與通用 CI/CD 的關係</h2>
<p>本資料夾是實例層。通用 gate 原理、不同部署場域差異與失敗處理流程放在上層文章；本資料夾只回答「這個 blog 專案現在怎麼部署、失敗時要看哪裡」。術語定義統一回連 <a href="/blog/ci/knowledge-cards/" data-link-title="Knowledge Cards" data-link-desc="用原子化卡片整理 CI/CD 章節的核心術語，讓流程文章專注在判讀與決策">CI 知識卡片</a>。</p>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>本專案 workflow：讀 <a href="github-actions-workflows/">GitHub Actions workflow</a>。</li>
<li>前端部署通用注意事項：讀 <a href="../frontend-deploy/">前端部署 CI/CD</a>。</li>
<li>CI gate 原理：讀 <a href="../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a>。</li>
<li>Markdown CI 規則：讀 <a href="/blog/posts/blog-markdown-%E5%AF%AB%E4%BD%9C%E8%A6%8F%E7%AF%84%E8%88%87-mdtools-%E6%AA%A2%E6%9F%A5/" data-link-title="Blog Markdown 寫作規範與 mdtools 檢查" data-link-desc="本 blog 的 Markdown 排版規範權威契約。涵蓋 H1 禁用、MD024 siblings_only、反釣魚 TLD 校驗、卡片雙向完整性、front matter schema；改規則時要與 scripts/mdtools 實作同步。">Blog Markdown 寫作規範與 mdtools 檢查</a>。</li>
</ul>
]]></content:encoded></item><item><title>Release Channel</title><link>https://tarrragon.github.io/blog/ci/knowledge-cards/release-channel/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/knowledge-cards/release-channel/</guid><description>&lt;p>Release Channel 的核心概念是「用通道控制版本接觸範圍」。它是 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">Rollout Strategy&lt;/a> 的分發面，常和 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/app-signing/" data-link-title="App Signing" data-link-desc="說明行動與桌面應用的簽章憑證如何影響發布能力">App Signing&lt;/a> 與 update feed 一起設計。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Release Channel 位在 artifact 發布與使用者取得之間，常見通道包含 internal、alpha、beta、stable、enterprise、nightly 與 rollback channel。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;ul>
&lt;li>同一產品需要內測、公開測試與正式版本分流。&lt;/li>
&lt;li>錯誤版本需要停止擴散或切回回復通道。&lt;/li>
&lt;li>客戶端更新需要依風險分批推進。&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實服務的例子">接近真實服務的例子&lt;/h2>
&lt;p>桌面 app 先把 signed installer 推到 internal channel，驗證更新成功率後再推 beta channel，最後推 stable channel。若 stable 版本出現 crash，feed 可切回 rollback channel 或暫停更新。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Release Channel 要定義通道用途、進入條件、artifact 命名、可見範圍、停損條件與回復路徑，讓版本擴散具備控制面。&lt;/p></description><content:encoded><![CDATA[<p>Release Channel 的核心概念是「用通道控制版本接觸範圍」。它是 <a href="/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">Rollout Strategy</a> 的分發面，常和 <a href="/blog/ci/knowledge-cards/app-signing/" data-link-title="App Signing" data-link-desc="說明行動與桌面應用的簽章憑證如何影響發布能力">App Signing</a> 與 update feed 一起設計。</p>
<h2 id="概念位置">概念位置</h2>
<p>Release Channel 位在 artifact 發布與使用者取得之間，常見通道包含 internal、alpha、beta、stable、enterprise、nightly 與 rollback channel。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<ul>
<li>同一產品需要內測、公開測試與正式版本分流。</li>
<li>錯誤版本需要停止擴散或切回回復通道。</li>
<li>客戶端更新需要依風險分批推進。</li>
</ul>
<h2 id="接近真實服務的例子">接近真實服務的例子</h2>
<p>桌面 app 先把 signed installer 推到 internal channel，驗證更新成功率後再推 beta channel，最後推 stable channel。若 stable 版本出現 crash，feed 可切回 rollback channel 或暫停更新。</p>
<h2 id="設計責任">設計責任</h2>
<p>Release Channel 要定義通道用途、進入條件、artifact 命名、可見範圍、停損條件與回復路徑，讓版本擴散具備控制面。</p>
]]></content:encoded></item><item><title>Update Feed</title><link>https://tarrragon.github.io/blog/ci/knowledge-cards/update-feed/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/knowledge-cards/update-feed/</guid><description>&lt;p>Update Feed 的核心概念是「告訴已安裝客戶端該取得哪個版本」。它連接 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/release-channel/" data-link-title="Release Channel" data-link-desc="說明 stable、beta、internal 等發行通道如何控制 artifact 接觸到的使用者範圍">Release Channel&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/app-signing/" data-link-title="App Signing" data-link-desc="說明行動與桌面應用的簽章憑證如何影響發布能力">App Signing&lt;/a>，讓自動更新具備信任與回復能力。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Update Feed 位在 signed artifact、release channel 與已安裝 app 之間，常包含版本號、下載 URL、signature、checksum、release notes 與最低支援版本。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;ul>
&lt;li>客戶端需要自動偵測新版本。&lt;/li>
&lt;li>beta 與 stable 使用者需要看到不同版本。&lt;/li>
&lt;li>錯誤版本需要從更新來源撤下。&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實服務的例子">接近真實服務的例子&lt;/h2>
&lt;p>Electron app 啟動時讀取 stable feed，取得最新 signed installer 與 signature。若新版本 crash rate 升高，團隊先撤下 feed 指向，讓未更新使用者停止取得錯誤版本。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Update Feed 要定義簽章驗證、channel 分流、版本比較、fallback installer、撤版策略與 telemetry，讓已安裝客戶端安全升級。&lt;/p></description><content:encoded><![CDATA[<p>Update Feed 的核心概念是「告訴已安裝客戶端該取得哪個版本」。它連接 <a href="/blog/ci/knowledge-cards/release-channel/" data-link-title="Release Channel" data-link-desc="說明 stable、beta、internal 等發行通道如何控制 artifact 接觸到的使用者範圍">Release Channel</a> 與 <a href="/blog/ci/knowledge-cards/app-signing/" data-link-title="App Signing" data-link-desc="說明行動與桌面應用的簽章憑證如何影響發布能力">App Signing</a>，讓自動更新具備信任與回復能力。</p>
<h2 id="概念位置">概念位置</h2>
<p>Update Feed 位在 signed artifact、release channel 與已安裝 app 之間，常包含版本號、下載 URL、signature、checksum、release notes 與最低支援版本。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<ul>
<li>客戶端需要自動偵測新版本。</li>
<li>beta 與 stable 使用者需要看到不同版本。</li>
<li>錯誤版本需要從更新來源撤下。</li>
</ul>
<h2 id="接近真實服務的例子">接近真實服務的例子</h2>
<p>Electron app 啟動時讀取 stable feed，取得最新 signed installer 與 signature。若新版本 crash rate 升高，團隊先撤下 feed 指向，讓未更新使用者停止取得錯誤版本。</p>
<h2 id="設計責任">設計責任</h2>
<p>Update Feed 要定義簽章驗證、channel 分流、版本比較、fallback installer、撤版策略與 telemetry，讓已安裝客戶端安全升級。</p>
]]></content:encoded></item><item><title>Infrastructure Drift</title><link>https://tarrragon.github.io/blog/ci/knowledge-cards/infrastructure-drift/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/knowledge-cards/infrastructure-drift/</guid><description>&lt;p>Infrastructure Drift 的核心概念是「真實環境狀態與宣告檔分叉」。它會削弱 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/environment-protection/" data-link-title="Environment Protection" data-link-desc="說明目標環境的審核、權限與放行條件如何保護發布">Environment Protection&lt;/a> 與 deployment review 的可信度，並影響下一次 plan / apply 的安全性。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Infrastructure Drift 位在 IaC state、cloud resource、手動 hotfix 與外部 controller 之間，常由 console edit、事故修復、provider 預設值或自動調整造成。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;ul>
&lt;li>plan 顯示大量非預期變更。&lt;/li>
&lt;li>production 資源和 repository 宣告不一致。&lt;/li>
&lt;li>下次 apply 可能覆蓋事故 hotfix。&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實服務的例子">接近真實服務的例子&lt;/h2>
&lt;p>事故中工程師在雲端 console 手動放寬 security group。服務恢復後，IaC plan 顯示 security group 與宣告檔不同；團隊需要判斷這個變更是短期 hotfix 還是應回寫成正式規則。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Infrastructure Drift 要定義偵測頻率、owner、修復路由、state repair 與回寫規則，讓平台狀態重新回到可審查流程。&lt;/p></description><content:encoded><![CDATA[<p>Infrastructure Drift 的核心概念是「真實環境狀態與宣告檔分叉」。它會削弱 <a href="/blog/ci/knowledge-cards/environment-protection/" data-link-title="Environment Protection" data-link-desc="說明目標環境的審核、權限與放行條件如何保護發布">Environment Protection</a> 與 deployment review 的可信度，並影響下一次 plan / apply 的安全性。</p>
<h2 id="概念位置">概念位置</h2>
<p>Infrastructure Drift 位在 IaC state、cloud resource、手動 hotfix 與外部 controller 之間，常由 console edit、事故修復、provider 預設值或自動調整造成。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<ul>
<li>plan 顯示大量非預期變更。</li>
<li>production 資源和 repository 宣告不一致。</li>
<li>下次 apply 可能覆蓋事故 hotfix。</li>
</ul>
<h2 id="接近真實服務的例子">接近真實服務的例子</h2>
<p>事故中工程師在雲端 console 手動放寬 security group。服務恢復後，IaC plan 顯示 security group 與宣告檔不同；團隊需要判斷這個變更是短期 hotfix 還是應回寫成正式規則。</p>
<h2 id="設計責任">設計責任</h2>
<p>Infrastructure Drift 要定義偵測頻率、owner、修復路由、state repair 與回寫規則，讓平台狀態重新回到可審查流程。</p>
]]></content:encoded></item><item><title>State Lock</title><link>https://tarrragon.github.io/blog/ci/knowledge-cards/state-lock/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/knowledge-cards/state-lock/</guid><description>&lt;p>State Lock 的核心概念是「讓同一份基礎設施狀態一次只被一個 apply 修改」。它支撐 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/infrastructure-drift/" data-link-title="Infrastructure Drift" data-link-desc="說明真實基礎設施狀態與 IaC 宣告分叉時的偵測、判讀與修復責任">Infrastructure Drift&lt;/a> 的治理，避免 CI job 或人工操作併發覆寫 state。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>State Lock 位在 IaC state backend、plan / apply workflow 與平台資源之間，常由 Terraform backend、Pulumi state 或平台鎖定機制提供。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;ul>
&lt;li>多個 pipeline 同時 apply 同一個 workspace。&lt;/li>
&lt;li>state file 出現併發覆寫或 partial apply 後不一致。&lt;/li>
&lt;li>apply 長時間卡住需要判斷 lock 是否仍有效。&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實服務的例子">接近真實服務的例子&lt;/h2>
&lt;p>兩個 PR 同時修改 production network。第一個 workflow 取得 state lock 後進入 apply，第二個 workflow 等待或失敗，避免兩次變更同時寫入 state。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>State Lock 要定義 lock backend、timeout、人工解鎖條件、環境隔離與失敗處理，讓 IaC apply 保持序列化。&lt;/p></description><content:encoded><![CDATA[<p>State Lock 的核心概念是「讓同一份基礎設施狀態一次只被一個 apply 修改」。它支撐 <a href="/blog/ci/knowledge-cards/infrastructure-drift/" data-link-title="Infrastructure Drift" data-link-desc="說明真實基礎設施狀態與 IaC 宣告分叉時的偵測、判讀與修復責任">Infrastructure Drift</a> 的治理，避免 CI job 或人工操作併發覆寫 state。</p>
<h2 id="概念位置">概念位置</h2>
<p>State Lock 位在 IaC state backend、plan / apply workflow 與平台資源之間，常由 Terraform backend、Pulumi state 或平台鎖定機制提供。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<ul>
<li>多個 pipeline 同時 apply 同一個 workspace。</li>
<li>state file 出現併發覆寫或 partial apply 後不一致。</li>
<li>apply 長時間卡住需要判斷 lock 是否仍有效。</li>
</ul>
<h2 id="接近真實服務的例子">接近真實服務的例子</h2>
<p>兩個 PR 同時修改 production network。第一個 workflow 取得 state lock 後進入 apply，第二個 workflow 等待或失敗，避免兩次變更同時寫入 state。</p>
<h2 id="設計責任">設計責任</h2>
<p>State Lock 要定義 lock backend、timeout、人工解鎖條件、環境隔離與失敗處理，讓 IaC apply 保持序列化。</p>
]]></content:encoded></item><item><title>Function Alias</title><link>https://tarrragon.github.io/blog/ci/knowledge-cards/function-alias/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/knowledge-cards/function-alias/</guid><description>&lt;p>Function Alias 的核心概念是「用穩定名稱指向不可變函式版本」。它讓 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">Rollout Strategy&lt;/a> 可以套用在 serverless function 上，並讓 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy&lt;/a> 具備快速切換入口。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Function Alias 位在 function version、traffic weight、event source 與 invocation entrypoint 之間，常見於 Lambda alias 或其他 serverless 平台的版本別名。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;ul>
&lt;li>新舊 function version 需要短暫共存。&lt;/li>
&lt;li>部分流量需要導向新版本做 canary。&lt;/li>
&lt;li>事故時需要把入口切回上一個版本。&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實服務的例子">接近真實服務的例子&lt;/h2>
&lt;p>HTTP function 的 &lt;code>prod&lt;/code> alias 先把 5% 流量導向 version 42。若錯誤率穩定，逐步提高權重；若錯誤率升高，alias 切回 version 41。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Function Alias 要定義版本命名、流量權重、觀測指標、事件來源綁定與回復條件，讓函式發布具備可控入口。&lt;/p></description><content:encoded><![CDATA[<p>Function Alias 的核心概念是「用穩定名稱指向不可變函式版本」。它讓 <a href="/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">Rollout Strategy</a> 可以套用在 serverless function 上，並讓 <a href="/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy</a> 具備快速切換入口。</p>
<h2 id="概念位置">概念位置</h2>
<p>Function Alias 位在 function version、traffic weight、event source 與 invocation entrypoint 之間，常見於 Lambda alias 或其他 serverless 平台的版本別名。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<ul>
<li>新舊 function version 需要短暫共存。</li>
<li>部分流量需要導向新版本做 canary。</li>
<li>事故時需要把入口切回上一個版本。</li>
</ul>
<h2 id="接近真實服務的例子">接近真實服務的例子</h2>
<p>HTTP function 的 <code>prod</code> alias 先把 5% 流量導向 version 42。若錯誤率穩定，逐步提高權重；若錯誤率升高，alias 切回 version 41。</p>
<h2 id="設計責任">設計責任</h2>
<p>Function Alias 要定義版本命名、流量權重、觀測指標、事件來源綁定與回復條件，讓函式發布具備可控入口。</p>
]]></content:encoded></item><item><title>Event Source</title><link>https://tarrragon.github.io/blog/ci/knowledge-cards/event-source/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/knowledge-cards/event-source/</guid><description>&lt;p>Event Source 的核心概念是「觸發執行的事件入口」。它決定 serverless function 或 worker 何時執行、如何重試、如何進入 dead-letter，並影響 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/function-alias/" data-link-title="Function Alias" data-link-desc="說明 serverless function alias 如何把穩定入口指向特定版本並支援流量切換與回復">Function Alias&lt;/a> 的 rollout 與回復策略。&lt;/p>
&lt;h2 id="概念位置">概念位置&lt;/h2>
&lt;p>Event Source 位在 queue、topic、HTTP gateway、object storage、scheduler 與 function / worker 之間，負責把外部事件轉成執行請求。&lt;/p>
&lt;h2 id="可觀察訊號">可觀察訊號&lt;/h2>
&lt;ul>
&lt;li>函式部署成功，但 invocation 因 trigger 設定失敗。&lt;/li>
&lt;li>Queue event 重試造成同一筆資料被重複處理。&lt;/li>
&lt;li>事件 schema 漂移導致 subscriber 解析失敗。&lt;/li>
&lt;/ul>
&lt;h2 id="接近真實服務的例子">接近真實服務的例子&lt;/h2>
&lt;p>Queue 觸發的 function 以 batch 方式處理訊息。新版本解析失敗時，訊息進入 dead-letter queue；團隊先停用 trigger，再修復 function 或重放事件。&lt;/p>
&lt;h2 id="設計責任">設計責任&lt;/h2>
&lt;p>Event Source 要定義 trigger 條件、batch size、retry、dead-letter、replay、權限與 schema 契約，讓事件驅動發布具備可觀測回復路徑。&lt;/p></description><content:encoded><![CDATA[<p>Event Source 的核心概念是「觸發執行的事件入口」。它決定 serverless function 或 worker 何時執行、如何重試、如何進入 dead-letter，並影響 <a href="/blog/ci/knowledge-cards/function-alias/" data-link-title="Function Alias" data-link-desc="說明 serverless function alias 如何把穩定入口指向特定版本並支援流量切換與回復">Function Alias</a> 的 rollout 與回復策略。</p>
<h2 id="概念位置">概念位置</h2>
<p>Event Source 位在 queue、topic、HTTP gateway、object storage、scheduler 與 function / worker 之間，負責把外部事件轉成執行請求。</p>
<h2 id="可觀察訊號">可觀察訊號</h2>
<ul>
<li>函式部署成功，但 invocation 因 trigger 設定失敗。</li>
<li>Queue event 重試造成同一筆資料被重複處理。</li>
<li>事件 schema 漂移導致 subscriber 解析失敗。</li>
</ul>
<h2 id="接近真實服務的例子">接近真實服務的例子</h2>
<p>Queue 觸發的 function 以 batch 方式處理訊息。新版本解析失敗時，訊息進入 dead-letter queue；團隊先停用 trigger，再修復 function 或重放事件。</p>
<h2 id="設計責任">設計責任</h2>
<p>Event Source 要定義 trigger 條件、batch size、retry、dead-letter、replay、權限與 schema 契約，讓事件驅動發布具備可觀測回復路徑。</p>
]]></content:encoded></item><item><title>Artifact 與可重播性</title><link>https://tarrragon.github.io/blog/ci/artifact-reproducibility/</link><pubDate>Thu, 21 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/artifact-reproducibility/</guid><description>&lt;p>Artifact 可重播性的核心責任是讓每次發布都能追到同一份被驗證的產物。CI/CD 不只是在 runner 上跑命令；它要回答「測試通過的是哪份內容」「發布出去的是哪份內容」「事故時如何找回同一份內容」。&lt;/p>
&lt;h2 id="概念定位">概念定位&lt;/h2>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">Artifact&lt;/a> 是 CI/CD 流程中的交付單位。前端可能是 &lt;code>dist/&lt;/code>，後端可能是 binary 或 image，App 可能是 IPA / AAB，資料任務可能是 DAG 或 query package；不同形式的 artifact 都承擔同一個責任：把 source change 轉成可驗證、可保存、可推進的產物。&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>Build once&lt;/td>
 &lt;td>同一次變更只產生一次正式 artifact&lt;/td>
 &lt;td>build job 是否保存產物&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Verify once&lt;/td>
 &lt;td>測試同一份 artifact&lt;/td>
 &lt;td>test job 是否 download artifact&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">Artifact handoff&lt;/a>&lt;/td>
 &lt;td>在 job / workflow 間交接產物&lt;/td>
 &lt;td>checksum、digest、version 是否一致&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Promote same artifact&lt;/td>
 &lt;td>staging / production 推進同一份&lt;/td>
 &lt;td>production 是否重新 build&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Recover artifact&lt;/td>
 &lt;td>事故時找回上一份可用產物&lt;/td>
 &lt;td>retention、release、registry 是否保留&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Build once 的責任是降低環境漂移。若 test job 與 deploy job 各自 build，一個 lockfile、環境變數或 base image 差異就能讓兩份產物不同；此時 CI 綠燈不再能證明 production 內容可信。&lt;/p>
&lt;p>Verify once 的責任是把測試結果綁到具體產物。測試應輸出 artifact identity，例如 checksum、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/image-digest/" data-link-title="Image Digest" data-link-desc="說明 container image digest 如何作為不可變產物身分，支撐掃描、推進與 runtime 追溯">Image Digest&lt;/a>、release asset name 或 bundle version，讓 reviewer 能確認紅綠燈對應哪份內容。&lt;/p>
&lt;p>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">Artifact handoff&lt;/a> 的責任是在 job 邊界保留身分。Upload / download artifact、registry digest、release asset、package registry 與 object storage 都可以做 handoff；重點是交接時沿用既有產物。&lt;/p>
&lt;p>Promote same artifact 的責任是讓環境差異集中在設定與流量。Staging 驗證過的 image、package 或 static artifact 應被推進到 production；若 production 重新 build，就需要重新驗證 production 那份產物。&lt;/p>
&lt;p>Recover artifact 的責任是讓 rollback 有實體目標。沒有保留 artifact 的 rollback 會變成「從舊 commit 重新 build」，這會受到依賴、base image、registry、toolchain 與時間漂移影響。&lt;/p>
&lt;h2 id="可重播性檢查">可重播性檢查&lt;/h2>
&lt;p>可重播性檢查的責任是確認產物身分與建置條件足夠明確。嚴格 reproducible build 很難在所有專案做到，但 CI/CD 至少要達到「同一次 workflow 的產物可以被查詢、保存、驗證與重新部署」。&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>Source&lt;/td>
 &lt;td>artifact 對應哪個 commit&lt;/td>
 &lt;td>embed git SHA / release version&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Dependency&lt;/td>
 &lt;td>dependency 是否固定&lt;/td>
 &lt;td>lockfile、base image digest&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Environment&lt;/td>
 &lt;td>build 環境是否固定&lt;/td>
 &lt;td>runner image、toolchain version&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Identity&lt;/td>
 &lt;td>artifact 是否有不可變身分&lt;/td>
 &lt;td>checksum、digest、signature&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Retention&lt;/td>
 &lt;td>artifact 保留多久&lt;/td>
 &lt;td>release asset、registry retention&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Provenance&lt;/td>
 &lt;td>artifact 如何被產生&lt;/td>
 &lt;td>workflow run、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/sbom/" data-link-title="SBOM" data-link-desc="說明 Software Bill of Materials 如何揭露 artifact 內含元件，支撐供應鏈掃描與例外治理">SBOM&lt;/a>、attestation&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>這張表讓團隊知道自己目前在哪個成熟度。初期可以先做到 source、dependency、identity；高治理場景再補 SBOM、signature 與 provenance。&lt;/p></description><content:encoded><![CDATA[<p>Artifact 可重播性的核心責任是讓每次發布都能追到同一份被驗證的產物。CI/CD 不只是在 runner 上跑命令；它要回答「測試通過的是哪份內容」「發布出去的是哪份內容」「事故時如何找回同一份內容」。</p>
<h2 id="概念定位">概念定位</h2>
<p><a href="/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">Artifact</a> 是 CI/CD 流程中的交付單位。前端可能是 <code>dist/</code>，後端可能是 binary 或 image，App 可能是 IPA / AAB，資料任務可能是 DAG 或 query package；不同形式的 artifact 都承擔同一個責任：把 source change 轉成可驗證、可保存、可推進的產物。</p>
<table>
  <thead>
      <tr>
          <th>能力</th>
          <th>責任</th>
          <th>判讀訊號</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Build once</td>
          <td>同一次變更只產生一次正式 artifact</td>
          <td>build job 是否保存產物</td>
      </tr>
      <tr>
          <td>Verify once</td>
          <td>測試同一份 artifact</td>
          <td>test job 是否 download artifact</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">Artifact handoff</a></td>
          <td>在 job / workflow 間交接產物</td>
          <td>checksum、digest、version 是否一致</td>
      </tr>
      <tr>
          <td>Promote same artifact</td>
          <td>staging / production 推進同一份</td>
          <td>production 是否重新 build</td>
      </tr>
      <tr>
          <td>Recover artifact</td>
          <td>事故時找回上一份可用產物</td>
          <td>retention、release、registry 是否保留</td>
      </tr>
  </tbody>
</table>
<p>Build once 的責任是降低環境漂移。若 test job 與 deploy job 各自 build，一個 lockfile、環境變數或 base image 差異就能讓兩份產物不同；此時 CI 綠燈不再能證明 production 內容可信。</p>
<p>Verify once 的責任是把測試結果綁到具體產物。測試應輸出 artifact identity，例如 checksum、<a href="/blog/ci/knowledge-cards/image-digest/" data-link-title="Image Digest" data-link-desc="說明 container image digest 如何作為不可變產物身分，支撐掃描、推進與 runtime 追溯">Image Digest</a>、release asset name 或 bundle version，讓 reviewer 能確認紅綠燈對應哪份內容。</p>
<p><a href="/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">Artifact handoff</a> 的責任是在 job 邊界保留身分。Upload / download artifact、registry digest、release asset、package registry 與 object storage 都可以做 handoff；重點是交接時沿用既有產物。</p>
<p>Promote same artifact 的責任是讓環境差異集中在設定與流量。Staging 驗證過的 image、package 或 static artifact 應被推進到 production；若 production 重新 build，就需要重新驗證 production 那份產物。</p>
<p>Recover artifact 的責任是讓 rollback 有實體目標。沒有保留 artifact 的 rollback 會變成「從舊 commit 重新 build」，這會受到依賴、base image、registry、toolchain 與時間漂移影響。</p>
<h2 id="可重播性檢查">可重播性檢查</h2>
<p>可重播性檢查的責任是確認產物身分與建置條件足夠明確。嚴格 reproducible build 很難在所有專案做到，但 CI/CD 至少要達到「同一次 workflow 的產物可以被查詢、保存、驗證與重新部署」。</p>
<table>
  <thead>
      <tr>
          <th>檢查項</th>
          <th>判讀問題</th>
          <th>常見做法</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Source</td>
          <td>artifact 對應哪個 commit</td>
          <td>embed git SHA / release version</td>
      </tr>
      <tr>
          <td>Dependency</td>
          <td>dependency 是否固定</td>
          <td>lockfile、base image digest</td>
      </tr>
      <tr>
          <td>Environment</td>
          <td>build 環境是否固定</td>
          <td>runner image、toolchain version</td>
      </tr>
      <tr>
          <td>Identity</td>
          <td>artifact 是否有不可變身分</td>
          <td>checksum、digest、signature</td>
      </tr>
      <tr>
          <td>Retention</td>
          <td>artifact 保留多久</td>
          <td>release asset、registry retention</td>
      </tr>
      <tr>
          <td>Provenance</td>
          <td>artifact 如何被產生</td>
          <td>workflow run、<a href="/blog/ci/knowledge-cards/sbom/" data-link-title="SBOM" data-link-desc="說明 Software Bill of Materials 如何揭露 artifact 內含元件，支撐供應鏈掃描與例外治理">SBOM</a>、attestation</td>
      </tr>
  </tbody>
</table>
<p>這張表讓團隊知道自己目前在哪個成熟度。初期可以先做到 source、dependency、identity；高治理場景再補 SBOM、signature 與 provenance。</p>
<h2 id="常見反模式">常見反模式</h2>
<p>反模式的共同問題是讓「綠燈」失去指向性。當綠燈不知道對應哪份產物，CI/CD 只剩下命令執行紀錄。</p>
<table>
  <thead>
      <tr>
          <th>反模式</th>
          <th>風險</th>
          <th>替代做法</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>test 與 deploy 各自 build</td>
          <td>測試與發布內容漂移</td>
          <td>build once，artifact handoff</td>
      </tr>
      <tr>
          <td>rollback 重新 build 舊 commit</td>
          <td>舊 commit 可能產出不同內容</td>
          <td>保留上一份 release artifact</td>
      </tr>
      <tr>
          <td>只用人類可讀 tag</td>
          <td>tag 可被覆寫或語意不精準</td>
          <td>搭配 checksum / digest</td>
      </tr>
      <tr>
          <td>artifact retention 太短</td>
          <td>事故時找不到可回復版本</td>
          <td>對 release artifact 設長期保留</td>
      </tr>
  </tbody>
</table>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>Artifact 術語：讀 <a href="/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">Artifact</a>。</li>
<li>Artifact handoff：讀 <a href="/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">Artifact Handoff</a>。</li>
<li>Gate 邊界：讀 <a href="../ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a>。</li>
</ul>
]]></content:encoded></item><item><title>CI/CD 教學</title><link>https://tarrragon.github.io/blog/ci/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/</guid><description>&lt;p>CI/CD 教學的核心目標是把「變更如何被驗證、建置、交付」寫成可重播流程。&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/ci-pipeline/" data-link-title="CI Pipeline" data-link-desc="說明持續整合如何在合併前自動驗證變更品質與相容性">CI Pipeline&lt;/a> 負責驗證變更是否可信，&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/cd-pipeline/" data-link-title="CD Pipeline" data-link-desc="說明持續交付如何把已驗證產物推進到目標環境">CD Pipeline&lt;/a> 負責把可信 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">artifact&lt;/a> 交付到目標環境；兩者共享 gate、artifact、環境與回復路徑，但不同部署場域的細節差異很大。&lt;/p>
&lt;p>CI/CD 的責任是提供一致的判讀入口。當 workflow 顯示失敗時，團隊需要能快速判斷是 lint、test、build、package、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">Artifact Handoff&lt;/a>、deploy 還是 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy&lt;/a> 階段出問題，並知道下一步該回到本機重現、修正、重新提交，還是暫停發布。&lt;/p>
&lt;h3 id="前置知識卡片">&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/" data-link-title="Knowledge Cards" data-link-desc="用原子化卡片整理 CI/CD 章節的核心術語，讓流程文章專注在判讀與決策">前置知識卡片&lt;/a>&lt;/h3>
&lt;p>用原子化卡片整理 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">Artifact&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/required-checks/" data-link-title="Required Checks" data-link-desc="說明 pull request 的必要檢查如何作為合併 gate">Required Checks&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">Artifact Handoff&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/environment-protection/" data-link-title="Environment Protection" data-link-desc="說明目標環境的審核、權限與放行條件如何保護發布">Environment Protection&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/preview-environment/" data-link-title="Preview Environment" data-link-desc="說明 pull request 變更如何在隔離部署環境中被驗證">Preview Environment&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">Rollout Strategy&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明資料或結構變更如何在服務不中斷前提下受控推進">Migration&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/backfill/" data-link-title="Backfill" data-link-desc="說明資料處理與 migration 中如何受控補算歷史資料">Backfill&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/image-digest/" data-link-title="Image Digest" data-link-desc="說明 container image digest 如何作為不可變產物身分，支撐掃描、推進與 runtime 追溯">Image Digest&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/release-channel/" data-link-title="Release Channel" data-link-desc="說明 stable、beta、internal 等發行通道如何控制 artifact 接觸到的使用者範圍">Release Channel&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/infrastructure-drift/" data-link-title="Infrastructure Drift" data-link-desc="說明真實基礎設施狀態與 IaC 宣告分叉時的偵測、判讀與修復責任">Infrastructure Drift&lt;/a>、&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/function-alias/" data-link-title="Function Alias" data-link-desc="說明 serverless function alias 如何把穩定入口指向特定版本並支援流量切換與回復">Function Alias&lt;/a> 與 &lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/flaky-test/" data-link-title="Flaky Test" data-link-desc="說明非決定性測試如何降低 CI gate 信任度與治理方式">Flaky Test&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>&lt;a href="github-actions-failure-flow/">CI 失敗到修復發布流程&lt;/a>&lt;/td>
 &lt;td>Failure routing&lt;/td>
 &lt;td>從失敗 workflow 判斷下一步路由&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="ci-gate-workflow-boundary/">CI gate 與 workflow 邊界&lt;/a>&lt;/td>
 &lt;td>Workflow boundary&lt;/td>
 &lt;td>說明 required checks、needs 與 artifact handoff&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="frontend-deploy/">前端部署 CI/CD&lt;/a>&lt;/td>
 &lt;td>Frontend deployment&lt;/td>
 &lt;td>靜態站、SPA、CDN 與 preview environment&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="backend-deploy/">後端部署 CI/CD&lt;/a>&lt;/td>
 &lt;td>Backend deployment&lt;/td>
 &lt;td>API / worker 的 migration、rollout 與 rollback&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="app-deploy/">App 部署 CI/CD&lt;/a>&lt;/td>
 &lt;td>App deployment&lt;/td>
 &lt;td>mobile / desktop app 的簽章、審核與版本發布&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="docker-deploy/">Docker / Image 部署 CI/CD&lt;/a>&lt;/td>
 &lt;td>Image deployment&lt;/td>
 &lt;td>image build、scan、tag、registry 與 runtime&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="serverless-deploy/">Serverless 部署 CI/CD&lt;/a>&lt;/td>
 &lt;td>Serverless deployment&lt;/td>
 &lt;td>function 版本、權限、事件觸發與 alias rollback&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="data-pipeline-deploy/">Data Pipeline 部署 CI/CD&lt;/a>&lt;/td>
 &lt;td>Data pipeline deployment&lt;/td>
 &lt;td>schema 相容、backfill、checkpoint 與 rerun&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="iac-platform-deploy/">IaC / Platform 部署 CI/CD&lt;/a>&lt;/td>
 &lt;td>IaC deployment&lt;/td>
 &lt;td>plan/apply、drift、state 與環境治理&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="desktop-client-deploy/">Desktop Client 部署 CI/CD&lt;/a>&lt;/td>
 &lt;td>Desktop client deployment&lt;/td>
 &lt;td>桌面安裝包簽章、公證、更新通道與回退&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="package-library-release/">Package / Library Release CI/CD&lt;/a>&lt;/td>
 &lt;td>Package release deployment&lt;/td>
 &lt;td>SDK / NPM / PyPI 的版本、契約與發版供應鏈治理&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="blog-project-deploy/">本 blog 專案部署&lt;/a>&lt;/td>
 &lt;td>Project case&lt;/td>
 &lt;td>Hugo、Pagefind、GitHub Pages 與本專案 workflow&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="artifact-reproducibility/">Artifact 與可重播性&lt;/a>&lt;/td>
 &lt;td>Artifact reproducibility&lt;/td>
 &lt;td>讓 CI 產物能被測試與發布共用&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="flaky-test-governance/">Flaky test 治理&lt;/a>&lt;/td>
 &lt;td>Flaky governance&lt;/td>
 &lt;td>把不穩定測試從雜訊變成可處理任務&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>學習路線先從失敗處理與 gate 邊界開始，因為 CI/CD 的價值會在紅燈時最清楚。當讀者能判讀失敗位置與下一步路由，再依部署場域進入前端、後端、App、Docker 或本 blog 專案案例。&lt;/p></description><content:encoded><![CDATA[<p>CI/CD 教學的核心目標是把「變更如何被驗證、建置、交付」寫成可重播流程。<a href="/blog/ci/knowledge-cards/ci-pipeline/" data-link-title="CI Pipeline" data-link-desc="說明持續整合如何在合併前自動驗證變更品質與相容性">CI Pipeline</a> 負責驗證變更是否可信，<a href="/blog/ci/knowledge-cards/cd-pipeline/" data-link-title="CD Pipeline" data-link-desc="說明持續交付如何把已驗證產物推進到目標環境">CD Pipeline</a> 負責把可信 <a href="/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">artifact</a> 交付到目標環境；兩者共享 gate、artifact、環境與回復路徑，但不同部署場域的細節差異很大。</p>
<p>CI/CD 的責任是提供一致的判讀入口。當 workflow 顯示失敗時，團隊需要能快速判斷是 lint、test、build、package、<a href="/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">Artifact Handoff</a>、deploy 還是 <a href="/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy</a> 階段出問題，並知道下一步該回到本機重現、修正、重新提交，還是暫停發布。</p>
<h3 id="前置知識卡片"><a href="/blog/ci/knowledge-cards/" data-link-title="Knowledge Cards" data-link-desc="用原子化卡片整理 CI/CD 章節的核心術語，讓流程文章專注在判讀與決策">前置知識卡片</a></h3>
<p>用原子化卡片整理 <a href="/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">Artifact</a>、<a href="/blog/ci/knowledge-cards/required-checks/" data-link-title="Required Checks" data-link-desc="說明 pull request 的必要檢查如何作為合併 gate">Required Checks</a>、<a href="/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">Artifact Handoff</a>、<a href="/blog/ci/knowledge-cards/environment-protection/" data-link-title="Environment Protection" data-link-desc="說明目標環境的審核、權限與放行條件如何保護發布">Environment Protection</a>、<a href="/blog/ci/knowledge-cards/preview-environment/" data-link-title="Preview Environment" data-link-desc="說明 pull request 變更如何在隔離部署環境中被驗證">Preview Environment</a>、<a href="/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">Rollout Strategy</a>、<a href="/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy</a>、<a href="/blog/ci/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明資料或結構變更如何在服務不中斷前提下受控推進">Migration</a>、<a href="/blog/ci/knowledge-cards/backfill/" data-link-title="Backfill" data-link-desc="說明資料處理與 migration 中如何受控補算歷史資料">Backfill</a>、<a href="/blog/ci/knowledge-cards/image-digest/" data-link-title="Image Digest" data-link-desc="說明 container image digest 如何作為不可變產物身分，支撐掃描、推進與 runtime 追溯">Image Digest</a>、<a href="/blog/ci/knowledge-cards/release-channel/" data-link-title="Release Channel" data-link-desc="說明 stable、beta、internal 等發行通道如何控制 artifact 接觸到的使用者範圍">Release Channel</a>、<a href="/blog/ci/knowledge-cards/infrastructure-drift/" data-link-title="Infrastructure Drift" data-link-desc="說明真實基礎設施狀態與 IaC 宣告分叉時的偵測、判讀與修復責任">Infrastructure Drift</a>、<a href="/blog/ci/knowledge-cards/function-alias/" data-link-title="Function Alias" data-link-desc="說明 serverless function alias 如何把穩定入口指向特定版本並支援流量切換與回復">Function Alias</a> 與 <a href="/blog/ci/knowledge-cards/flaky-test/" data-link-title="Flaky Test" data-link-desc="說明非決定性測試如何降低 CI gate 信任度與治理方式">Flaky Test</a> 等核心術語。流程文章專注情境判讀與決策順序，術語背景交由卡片維持一致。</p>
<h2 id="學習路線">學習路線</h2>
<table>
  <thead>
      <tr>
          <th>章節</th>
          <th>主題</th>
          <th>核心責任</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="github-actions-failure-flow/">CI 失敗到修復發布流程</a></td>
          <td>Failure routing</td>
          <td>從失敗 workflow 判斷下一步路由</td>
      </tr>
      <tr>
          <td><a href="ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a></td>
          <td>Workflow boundary</td>
          <td>說明 required checks、needs 與 artifact handoff</td>
      </tr>
      <tr>
          <td><a href="frontend-deploy/">前端部署 CI/CD</a></td>
          <td>Frontend deployment</td>
          <td>靜態站、SPA、CDN 與 preview environment</td>
      </tr>
      <tr>
          <td><a href="backend-deploy/">後端部署 CI/CD</a></td>
          <td>Backend deployment</td>
          <td>API / worker 的 migration、rollout 與 rollback</td>
      </tr>
      <tr>
          <td><a href="app-deploy/">App 部署 CI/CD</a></td>
          <td>App deployment</td>
          <td>mobile / desktop app 的簽章、審核與版本發布</td>
      </tr>
      <tr>
          <td><a href="docker-deploy/">Docker / Image 部署 CI/CD</a></td>
          <td>Image deployment</td>
          <td>image build、scan、tag、registry 與 runtime</td>
      </tr>
      <tr>
          <td><a href="serverless-deploy/">Serverless 部署 CI/CD</a></td>
          <td>Serverless deployment</td>
          <td>function 版本、權限、事件觸發與 alias rollback</td>
      </tr>
      <tr>
          <td><a href="data-pipeline-deploy/">Data Pipeline 部署 CI/CD</a></td>
          <td>Data pipeline deployment</td>
          <td>schema 相容、backfill、checkpoint 與 rerun</td>
      </tr>
      <tr>
          <td><a href="iac-platform-deploy/">IaC / Platform 部署 CI/CD</a></td>
          <td>IaC deployment</td>
          <td>plan/apply、drift、state 與環境治理</td>
      </tr>
      <tr>
          <td><a href="desktop-client-deploy/">Desktop Client 部署 CI/CD</a></td>
          <td>Desktop client deployment</td>
          <td>桌面安裝包簽章、公證、更新通道與回退</td>
      </tr>
      <tr>
          <td><a href="package-library-release/">Package / Library Release CI/CD</a></td>
          <td>Package release deployment</td>
          <td>SDK / NPM / PyPI 的版本、契約與發版供應鏈治理</td>
      </tr>
      <tr>
          <td><a href="blog-project-deploy/">本 blog 專案部署</a></td>
          <td>Project case</td>
          <td>Hugo、Pagefind、GitHub Pages 與本專案 workflow</td>
      </tr>
      <tr>
          <td><a href="artifact-reproducibility/">Artifact 與可重播性</a></td>
          <td>Artifact reproducibility</td>
          <td>讓 CI 產物能被測試與發布共用</td>
      </tr>
      <tr>
          <td><a href="flaky-test-governance/">Flaky test 治理</a></td>
          <td>Flaky governance</td>
          <td>把不穩定測試從雜訊變成可處理任務</td>
      </tr>
  </tbody>
</table>
<p>學習路線先從失敗處理與 gate 邊界開始，因為 CI/CD 的價值會在紅燈時最清楚。當讀者能判讀失敗位置與下一步路由，再依部署場域進入前端、後端、App、Docker 或本 blog 專案案例。</p>
<h2 id="與其他教學的分工">與其他教學的分工</h2>
<p>CI/CD 教學負責日常工作流程與部署場域差異，Backend 可靠性模組負責系統層可靠性判斷。讀者想知道 workflow 失敗後怎麼修、發布 gate 怎麼切、前端與後端部署流程差在哪裡，讀本系列；想知道 CI 在 release gate、SLO、load test 與可靠性治理中的位置，回到 <a href="/blog/backend/06-reliability/" data-link-title="模組六：可靠性驗證流程" data-link-desc="用 SRE 領域詞彙建問題節點、以服務級案例庫累積驗證脈絡，先建概念與案例庫再進實作交接">模組六：可靠性驗證流程</a>。</p>
<p>Go、Python 或其他語言教材只需要保留測試寫法與本機命令。當內容開始涉及 workflow event、required checks、preview deployment、container registry、mobile signing、artifact、cache 或 branch protection，就應該移到本系列，讓不同語言共用同一套 CI/CD 操作語意。</p>
<h2 id="判讀訊號">判讀訊號</h2>
<ul>
<li>GitHub Actions 紅燈後，不知道該看哪個 job。</li>
<li>本機測試通過，但 CI 失敗。</li>
<li>測試失敗後仍有部署 workflow 啟動。</li>
<li>deploy 失敗時，團隊分不清 build artifact、部署權限與測試 gate 的責任。</li>
<li>前端、後端、App 與 Docker 使用同一套發布說明，導致場域細節混在一起。</li>
<li>workflow 只有命令清單，沒有說明失敗後的處理路由與部署場域邊界。</li>
</ul>
<h2 id="下一步路由">下一步路由</h2>
<ul>
<li>想處理 GitHub Actions 紅燈：讀 <a href="github-actions-failure-flow/">CI 失敗到修復發布流程</a>。</li>
<li>想理解 CI gate 原理：讀 <a href="ci-gate-workflow-boundary/">CI gate 與 workflow 邊界</a>。</li>
<li>想理解前端部署：讀 <a href="frontend-deploy/">前端部署 CI/CD</a>。</li>
<li>想理解後端部署：讀 <a href="backend-deploy/">後端部署 CI/CD</a>。</li>
<li>想理解 App 發布：讀 <a href="app-deploy/">App 部署 CI/CD</a>。</li>
<li>想理解 Docker / image 流程：讀 <a href="docker-deploy/">Docker / Image 部署 CI/CD</a>。</li>
<li>想理解 Serverless 發布：讀 <a href="serverless-deploy/">Serverless 部署 CI/CD</a>。</li>
<li>想理解資料處理任務發布：讀 <a href="data-pipeline-deploy/">Data Pipeline 部署 CI/CD</a>。</li>
<li>想理解 IaC / 平台變更發布：讀 <a href="iac-platform-deploy/">IaC / Platform 部署 CI/CD</a>。</li>
<li>想理解 Flutter/Electron/Tauri 類客戶端發布：讀 <a href="desktop-client-deploy/">Desktop Client 部署 CI/CD</a>。</li>
<li>想理解 SDK / NPM / PyPI 發版：讀 <a href="package-library-release/">Package / Library Release CI/CD</a>。</li>
<li>想維護本 blog 的 workflow：讀 <a href="blog-project-deploy/">本 blog 專案部署</a>。</li>
<li>想讓測試與發布共用同一份產物：讀 <a href="artifact-reproducibility/">Artifact 與可重播性</a>。</li>
<li>想治理不穩定測試：讀 <a href="flaky-test-governance/">Flaky test 治理</a>。</li>
<li>想理解可靠性層的 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>。</li>
<li>想理解發布 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>。</li>
<li>想理解 infra 變更的 plan / apply 流程怎麼走 CI：讀 <a href="/blog/infra/07-infra-as-pr/" data-link-title="模組七：infra 走 PR 流程與自動化護欄" data-link-desc="infra 變更走 PR → plan → review diff → 合併 → apply，配 fmt / validate / tflint / checkov / tfsec 與 Atlantis 自動化，讓基礎設施可審查、可回溯、可交接">Infra 走 PR 流程與自動化護欄</a>。</li>
</ul>
]]></content:encoded></item><item><title>Knowledge Cards</title><link>https://tarrragon.github.io/blog/ci/knowledge-cards/</link><pubDate>Wed, 06 May 2026 00:00:00 +0000</pubDate><guid>https://tarrragon.github.io/blog/ci/knowledge-cards/</guid><description>&lt;p>CI/CD 知識卡片的核心責任是建立共同語言。流程文章會使用 pipeline、gate、artifact、rollout、rollback、environment protection 等術語；卡片負責定義它們在系統中的位置、可觀察訊號與設計責任。&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;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/ci-pipeline/" data-link-title="CI Pipeline" data-link-desc="說明持續整合如何在合併前自動驗證變更品質與相容性">CI Pipeline&lt;/a>&lt;/td>
 &lt;td>變更如何在合併前被自動驗證&lt;/td>
 &lt;td>lint、test、build、security check&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/cd-pipeline/" data-link-title="CD Pipeline" data-link-desc="說明持續交付如何把已驗證產物推進到目標環境">CD Pipeline&lt;/a>&lt;/td>
 &lt;td>驗證後產物如何被安全推進到目標環境&lt;/td>
 &lt;td>deploy、promotion、release workflow&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/required-checks/" data-link-title="Required Checks" data-link-desc="說明 pull request 的必要檢查如何作為合併 gate">Required Checks&lt;/a>&lt;/td>
 &lt;td>PR 合併條件如何由檢查結果定義&lt;/td>
 &lt;td>branch protection、status checks&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">Artifact&lt;/a>&lt;/td>
 &lt;td>交付產物如何被追溯、保存與發布&lt;/td>
 &lt;td>build output、image、app bundle&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">Artifact Handoff&lt;/a>&lt;/td>
 &lt;td>測試與發布如何共用同一份產物&lt;/td>
 &lt;td>build artifact、package、deploy&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明資料或結構變更如何在服務不中斷前提下受控推進">Migration&lt;/a>&lt;/td>
 &lt;td>狀態變更如何在相容窗口內受控推進&lt;/td>
 &lt;td>schema change、backfill、release&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/branch-protection/" data-link-title="Branch Protection" data-link-desc="說明主線分支如何以規則保護合併與發布前置條件">Branch Protection&lt;/a>&lt;/td>
 &lt;td>主線合併條件如何由規則強制保護&lt;/td>
 &lt;td>required checks、review policy&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/readiness-health-check/" data-link-title="Readiness / Health Check" data-link-desc="說明服務存活與可接流量判斷在部署中的不同責任">Readiness / Health Check&lt;/a>&lt;/td>
 &lt;td>部署放行如何區分存活與可接流量訊號&lt;/td>
 &lt;td>rollout、probe、traffic switch&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/container-registry/" data-link-title="Container Registry" data-link-desc="說明容器產物儲存、權限與推進流程在 CD 中的責任">Container Registry&lt;/a>&lt;/td>
 &lt;td>image 供應鏈如何被保存與推進&lt;/td>
 &lt;td>push、retention、promotion&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/app-signing/" data-link-title="App Signing" data-link-desc="說明行動與桌面應用的簽章憑證如何影響發布能力">App Signing&lt;/a>&lt;/td>
 &lt;td>行動與桌面發版能力如何由簽章維持&lt;/td>
 &lt;td>certificate、profile、keystore&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/flaky-test/" data-link-title="Flaky Test" data-link-desc="說明非決定性測試如何降低 CI gate 信任度與治理方式">Flaky Test&lt;/a>&lt;/td>
 &lt;td>非決定性測試如何影響 gate 信任度&lt;/td>
 &lt;td>rerun noise、test governance&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/environment-protection/" data-link-title="Environment Protection" data-link-desc="說明目標環境的審核、權限與放行條件如何保護發布">Environment Protection&lt;/a>&lt;/td>
 &lt;td>目標環境如何設置審核與發布保護&lt;/td>
 &lt;td>production、staging、review gate&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/preview-environment/" data-link-title="Preview Environment" data-link-desc="說明 pull request 變更如何在隔離部署環境中被驗證">Preview Environment&lt;/a>&lt;/td>
 &lt;td>PR 變更如何在隔離環境中被提前驗證&lt;/td>
 &lt;td>frontend preview URL、review app&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">Rollout Strategy&lt;/a>&lt;/td>
 &lt;td>新版本如何分批推進以控制風險&lt;/td>
 &lt;td>rolling、canary、phased rollout&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy&lt;/a>&lt;/td>
 &lt;td>發布異常時如何回到已知可用狀態&lt;/td>
 &lt;td>deploy rollback、hotfix、forward fix&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/deployment-dry-run/" data-link-title="Deployment Dry Run" data-link-desc="說明發布前如何用預演檢查部署條件與風險">Deployment Dry Run&lt;/a>&lt;/td>
 &lt;td>發布前如何先驗證流程條件與權限&lt;/td>
 &lt;td>preflight check、artifact check、permission&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/backfill/" data-link-title="Backfill" data-link-desc="說明資料處理與 migration 中如何受控補算歷史資料">Backfill&lt;/a>&lt;/td>
 &lt;td>歷史資料如何受控補算&lt;/td>
 &lt;td>migration、data pipeline、repair&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/checkpoint/" data-link-title="Checkpoint" data-link-desc="說明長時間任務如何記錄進度以支援接續、重跑與事故修復">Checkpoint&lt;/a>&lt;/td>
 &lt;td>長時間任務如何保存接續位置&lt;/td>
 &lt;td>backfill、stream processor、rerun&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/rerun/" data-link-title="Rerun" data-link-desc="說明 CI/CD 與 data pipeline 中重跑任務前需要判斷的輸出語意與副作用">Rerun&lt;/a>&lt;/td>
 &lt;td>重跑流程如何避免擴大副作用&lt;/td>
 &lt;td>flaky test、data repair、pipeline recovery&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/image-digest/" data-link-title="Image Digest" data-link-desc="說明 container image digest 如何作為不可變產物身分，支撐掃描、推進與 runtime 追溯">Image Digest&lt;/a>&lt;/td>
 &lt;td>container image 如何取得不可變身分&lt;/td>
 &lt;td>registry、scan、runtime handoff&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/sbom/" data-link-title="SBOM" data-link-desc="說明 Software Bill of Materials 如何揭露 artifact 內含元件，支撐供應鏈掃描與例外治理">SBOM&lt;/a>&lt;/td>
 &lt;td>artifact 內含元件如何被揭露&lt;/td>
 &lt;td>image scan、release evidence、compliance&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/release-channel/" data-link-title="Release Channel" data-link-desc="說明 stable、beta、internal 等發行通道如何控制 artifact 接觸到的使用者範圍">Release Channel&lt;/a>&lt;/td>
 &lt;td>版本如何依使用者範圍分流&lt;/td>
 &lt;td>app、desktop、beta、stable&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/update-feed/" data-link-title="Update Feed" data-link-desc="說明桌面與客戶端應用如何透過更新來源取得已簽章版本與回復路徑">Update Feed&lt;/a>&lt;/td>
 &lt;td>已安裝客戶端如何取得新版本&lt;/td>
 &lt;td>desktop auto-update、rollback channel&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/infrastructure-drift/" data-link-title="Infrastructure Drift" data-link-desc="說明真實基礎設施狀態與 IaC 宣告分叉時的偵測、判讀與修復責任">Infrastructure Drift&lt;/a>&lt;/td>
 &lt;td>真實環境與 IaC 宣告如何分叉&lt;/td>
 &lt;td>Terraform、Pulumi、manual hotfix&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/state-lock/" data-link-title="State Lock" data-link-desc="說明 IaC apply 如何用狀態鎖避免併發變更覆寫基礎設施狀態">State Lock&lt;/a>&lt;/td>
 &lt;td>IaC apply 如何避免併發覆寫 state&lt;/td>
 &lt;td>Terraform backend、workspace、apply&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/function-alias/" data-link-title="Function Alias" data-link-desc="說明 serverless function alias 如何把穩定入口指向特定版本並支援流量切換與回復">Function Alias&lt;/a>&lt;/td>
 &lt;td>serverless 入口如何指向特定版本&lt;/td>
 &lt;td>alias rollback、traffic shift&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;a href="https://tarrragon.github.io/blog/ci/knowledge-cards/event-source/" data-link-title="Event Source" data-link-desc="說明 serverless 與事件驅動流程中觸發來源如何影響 retry、dead-letter 與回復策略">Event Source&lt;/a>&lt;/td>
 &lt;td>事件來源如何影響 retry 與回復&lt;/td>
 &lt;td>queue、topic、HTTP trigger、scheduler&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>卡片與流程文章分工清楚。卡片負責名詞與邊界，流程文章負責情境判讀與操作路由。&lt;/p></description><content:encoded><![CDATA[<p>CI/CD 知識卡片的核心責任是建立共同語言。流程文章會使用 pipeline、gate、artifact、rollout、rollback、environment protection 等術語；卡片負責定義它們在系統中的位置、可觀察訊號與設計責任。</p>
<h2 id="核心術語">核心術語</h2>
<table>
  <thead>
      <tr>
          <th>卡片</th>
          <th>核心問題</th>
          <th>常見出現位置</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/ci-pipeline/" data-link-title="CI Pipeline" data-link-desc="說明持續整合如何在合併前自動驗證變更品質與相容性">CI Pipeline</a></td>
          <td>變更如何在合併前被自動驗證</td>
          <td>lint、test、build、security check</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/cd-pipeline/" data-link-title="CD Pipeline" data-link-desc="說明持續交付如何把已驗證產物推進到目標環境">CD Pipeline</a></td>
          <td>驗證後產物如何被安全推進到目標環境</td>
          <td>deploy、promotion、release workflow</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/required-checks/" data-link-title="Required Checks" data-link-desc="說明 pull request 的必要檢查如何作為合併 gate">Required Checks</a></td>
          <td>PR 合併條件如何由檢查結果定義</td>
          <td>branch protection、status checks</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/artifact/" data-link-title="Artifact" data-link-desc="說明 CI/CD 中可被驗證、保存與發布的交付產物">Artifact</a></td>
          <td>交付產物如何被追溯、保存與發布</td>
          <td>build output、image、app bundle</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/artifact-handoff/" data-link-title="Artifact Handoff" data-link-desc="說明測試與部署如何共用同一份可追溯產物">Artifact Handoff</a></td>
          <td>測試與發布如何共用同一份產物</td>
          <td>build artifact、package、deploy</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/migration/" data-link-title="Migration" data-link-desc="說明資料或結構變更如何在服務不中斷前提下受控推進">Migration</a></td>
          <td>狀態變更如何在相容窗口內受控推進</td>
          <td>schema change、backfill、release</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/branch-protection/" data-link-title="Branch Protection" data-link-desc="說明主線分支如何以規則保護合併與發布前置條件">Branch Protection</a></td>
          <td>主線合併條件如何由規則強制保護</td>
          <td>required checks、review policy</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/readiness-health-check/" data-link-title="Readiness / Health Check" data-link-desc="說明服務存活與可接流量判斷在部署中的不同責任">Readiness / Health Check</a></td>
          <td>部署放行如何區分存活與可接流量訊號</td>
          <td>rollout、probe、traffic switch</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/container-registry/" data-link-title="Container Registry" data-link-desc="說明容器產物儲存、權限與推進流程在 CD 中的責任">Container Registry</a></td>
          <td>image 供應鏈如何被保存與推進</td>
          <td>push、retention、promotion</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/app-signing/" data-link-title="App Signing" data-link-desc="說明行動與桌面應用的簽章憑證如何影響發布能力">App Signing</a></td>
          <td>行動與桌面發版能力如何由簽章維持</td>
          <td>certificate、profile、keystore</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/flaky-test/" data-link-title="Flaky Test" data-link-desc="說明非決定性測試如何降低 CI gate 信任度與治理方式">Flaky Test</a></td>
          <td>非決定性測試如何影響 gate 信任度</td>
          <td>rerun noise、test governance</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/environment-protection/" data-link-title="Environment Protection" data-link-desc="說明目標環境的審核、權限與放行條件如何保護發布">Environment Protection</a></td>
          <td>目標環境如何設置審核與發布保護</td>
          <td>production、staging、review gate</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/preview-environment/" data-link-title="Preview Environment" data-link-desc="說明 pull request 變更如何在隔離部署環境中被驗證">Preview Environment</a></td>
          <td>PR 變更如何在隔離環境中被提前驗證</td>
          <td>frontend preview URL、review app</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/rollout-strategy/" data-link-title="Rollout Strategy" data-link-desc="說明新版本如何以可控節奏推進到全部流量">Rollout Strategy</a></td>
          <td>新版本如何分批推進以控制風險</td>
          <td>rolling、canary、phased rollout</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/rollback-strategy/" data-link-title="Rollback Strategy" data-link-desc="說明發布異常時如何快速回到已知可用狀態">Rollback Strategy</a></td>
          <td>發布異常時如何回到已知可用狀態</td>
          <td>deploy rollback、hotfix、forward fix</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/deployment-dry-run/" data-link-title="Deployment Dry Run" data-link-desc="說明發布前如何用預演檢查部署條件與風險">Deployment Dry Run</a></td>
          <td>發布前如何先驗證流程條件與權限</td>
          <td>preflight check、artifact check、permission</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/backfill/" data-link-title="Backfill" data-link-desc="說明資料處理與 migration 中如何受控補算歷史資料">Backfill</a></td>
          <td>歷史資料如何受控補算</td>
          <td>migration、data pipeline、repair</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/checkpoint/" data-link-title="Checkpoint" data-link-desc="說明長時間任務如何記錄進度以支援接續、重跑與事故修復">Checkpoint</a></td>
          <td>長時間任務如何保存接續位置</td>
          <td>backfill、stream processor、rerun</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/rerun/" data-link-title="Rerun" data-link-desc="說明 CI/CD 與 data pipeline 中重跑任務前需要判斷的輸出語意與副作用">Rerun</a></td>
          <td>重跑流程如何避免擴大副作用</td>
          <td>flaky test、data repair、pipeline recovery</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/image-digest/" data-link-title="Image Digest" data-link-desc="說明 container image digest 如何作為不可變產物身分，支撐掃描、推進與 runtime 追溯">Image Digest</a></td>
          <td>container image 如何取得不可變身分</td>
          <td>registry、scan、runtime handoff</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/sbom/" data-link-title="SBOM" data-link-desc="說明 Software Bill of Materials 如何揭露 artifact 內含元件，支撐供應鏈掃描與例外治理">SBOM</a></td>
          <td>artifact 內含元件如何被揭露</td>
          <td>image scan、release evidence、compliance</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/release-channel/" data-link-title="Release Channel" data-link-desc="說明 stable、beta、internal 等發行通道如何控制 artifact 接觸到的使用者範圍">Release Channel</a></td>
          <td>版本如何依使用者範圍分流</td>
          <td>app、desktop、beta、stable</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/update-feed/" data-link-title="Update Feed" data-link-desc="說明桌面與客戶端應用如何透過更新來源取得已簽章版本與回復路徑">Update Feed</a></td>
          <td>已安裝客戶端如何取得新版本</td>
          <td>desktop auto-update、rollback channel</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/infrastructure-drift/" data-link-title="Infrastructure Drift" data-link-desc="說明真實基礎設施狀態與 IaC 宣告分叉時的偵測、判讀與修復責任">Infrastructure Drift</a></td>
          <td>真實環境與 IaC 宣告如何分叉</td>
          <td>Terraform、Pulumi、manual hotfix</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/state-lock/" data-link-title="State Lock" data-link-desc="說明 IaC apply 如何用狀態鎖避免併發變更覆寫基礎設施狀態">State Lock</a></td>
          <td>IaC apply 如何避免併發覆寫 state</td>
          <td>Terraform backend、workspace、apply</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/function-alias/" data-link-title="Function Alias" data-link-desc="說明 serverless function alias 如何把穩定入口指向特定版本並支援流量切換與回復">Function Alias</a></td>
          <td>serverless 入口如何指向特定版本</td>
          <td>alias rollback、traffic shift</td>
      </tr>
      <tr>
          <td><a href="/blog/ci/knowledge-cards/event-source/" data-link-title="Event Source" data-link-desc="說明 serverless 與事件驅動流程中觸發來源如何影響 retry、dead-letter 與回復策略">Event Source</a></td>
          <td>事件來源如何影響 retry 與回復</td>
          <td>queue、topic、HTTP trigger、scheduler</td>
      </tr>
  </tbody>
</table>
<p>卡片與流程文章分工清楚。卡片負責名詞與邊界，流程文章負責情境判讀與操作路由。</p>
]]></content:encoded></item></channel></rss>