AI 工作實務
CD 發布演練:驗證 Artifact 雜湊、切換版本與失敗回滾
發布流程真正需要回答的,是目前上線哪一份產物、驗收失敗會發生什麼,以及能否回到已知可用版本。這份練習用 Python、暫存目錄與本機 HTTP 服務,完成產物雜湊檢查、版本切換、故障後自動復原和明確回滾。

發布流程真正需要回答的,是目前上線哪一份產物、驗收失敗會發生什麼,以及能否回到已知可用版本。這份練習用 Python、暫存目錄與本機 HTTP 服務,完成產物雜湊檢查、版本切換、故障後自動復原和明確回滾。
專題編輯:Derek Chou|AI 技術與架構編輯 · 文件核對 2026.11.09

從治理清單走到可執行的發布練習
如果你正在釐清審批角色、驗收回執或 CI/CD 管理關卡,可以先讀CI/CD 發布、讀回與回滾檢核。本文接續做一件更具體的事:讓「錯誤版本不可留在線上」成為可以故意觸發並觀察的程式行為。
Continuous Delivery 著重讓變更維持可發布狀態,正式上線仍可保留人工決定;Continuous Deployment 則進一步把通過條件的變更自動部署到正式環境。兩者不能只靠 workflow 檔名分辨。AWS 對持續交付的說明
本練習是一個發布機制的縮小模型:建立 ZIP 產物、驗證 SHA-256、放入版本資料夾、切換 current 指向,再以 HTTP 讀回版本。沒有雲端帳號、網站寫入、正式資料庫、真實客戶或 production 權限。你能從中學會辨識發布失敗與復原結果;不能把練習通過寫成公司的雲端部署、核准或災難復原已完成。
開始前,先把「同一份產物」說清楚
常見的不確定性出現在兩次建置之間:CI 測了一份程式,部署階段又重新抓取依賴、重新打包,最後上線的位元組未必相同。此練習建立一次 ZIP,之後的 staging 與 promotion 都使用該 ZIP 內的同一份 release.json,不在發布時重新 build。
SHA-256 可以幫助確認內容是否符合預期,但雜湊值本身不會證明來源可信。若攻擊者能同時換掉 ZIP 和預期雜湊,檢查仍可能成功。正式環境需要把產物、來源提交、受信任的建置紀錄與核准決定連在一起;有需要時再加入簽章或 attestations。本文只驗證已給定預期雜湊的位元組一致性,沒有實作完整供應鏈信任機制。GitHub 產物來源證明說明
步驟一:準備隔離環境與練習檔
使用 macOS 或 Linux、Python 3.12 或相容的 Python 3 環境,並允許開啟本機 loopback port。程式使用標準函式庫,不需要 pip 套件、雲端金鑰或網路部署工具。Windows 的 symbolic link 權限與檔案替換行為可能不同,本文沒有提供 Windows 環境的驗收保證。
下載完整練習檔,解壓後包含:
release_lab.py
delivery.yml
README.md
進入該資料夾後執行:
python3 --version
python3 release_lab.py --output evidence
程式會建立自己的暫存目錄與空閒本機 port,結束時關閉服務並清理暫存發布目錄;evidence/ 保留結果 JSON 與三份合成產物,方便重算雜湊。不要把此程式的暫存路徑改成現有網站根目錄;這不是可直接接上正式站的通用部署工具。
若環境禁止本機 listener,會看到權限或 bind 錯誤。這表示環境不能執行本練習,不能改寫成測試通過。可改在允許本機服務的隔離開發環境或 CI runner 執行,並保存實際使用環境的證據。
步驟二:先驗證 SHA,再放入 staging 版本目錄
build() 建立 v1、v2、v3 的 ZIP;stage() 先比對檔案雜湊,接著確認 ZIP 只含預期的 release.json 並通過 CRC。這個單一檔案白名單同時讓範例避免接受任意路徑或額外檔案,不使用未檢查的整包解壓。
核心順序為:
if digest(artifact) != expected_sha:
raise ValueError("Artifact SHA256 mismatch; nothing promoted")
with zipfile.ZipFile(artifact) as archive:
if archive.namelist() != ["release.json"]:
raise ValueError("Unexpected archive")
content = archive.read("release.json")
內容還須符合此練習的版本命名與 ready 欄位,才能寫入 releases/v1 等目錄。這裡的 ready 是合成範例資料,並非替代真實 CI 測試回執;正式流程不能只相信產物自己寫著「我已測試」。
演練會故意把 v2 的預期 SHA 設成錯誤值。正確結果是 staging 拒絕,HTTP 仍回傳 v1;不能只檢查函式丟出例外,卻沒有確認現行服務是否被改動。這個負面案例用來證明驗證發生在版本提升之前。
步驟三:切換指向後,以 HTTP 確認版本
驗證完成的版本各自放在獨立資料夾。switch() 先建立 current.next symbolic link,再以 os.replace() 替換 current。在符合條件的同一檔案系統上,這個替換操作提供原子性的名稱切換;它不等於跨主機、跨資料庫的分散式交易,也不保證正在處理的請求完全不中斷。Python os.replace 文件
練習的 HTTP handler 每次從 current/release.json 讀取版本,因此 v2 提升成功後,GET /health 應同時符合 HTTP 200 與 version=v2。只看 200 不夠,因為舊版或錯誤路由也可能回 200;只看磁碟 symbolic link 也不夠,因為真實服務可能仍載入舊程序或快取。
本例沒有外部負載平衡器或 CDN,所以這個讀回只驗證本機服務。正式系統需根據架構檢查真正對外入口、版本標記及關鍵業務路徑,並考慮健康檢查與使用者流量的差異。
步驟四:讓新版本故障,再驗證回滾
v3 的 SHA 是正確的,格式也符合本練習契約,但它故意在 HTTP 階段回 503。這代表「位元組正確」和「服務可用」是不同門檻。promote() 先記住原有版本,再切換與讀回;若狀態碼或版本不符,或讀回發生連線、逾時、JSON 解析例外,就恢復先前的指向,再以 HTTP 確認舊版本。若回復後仍無法讀回,程式會明確標示「指向已恢復,但 HTTP 復原未確認」,不冒稱服務已恢復。初次發布若沒有前版可回復,則移除失敗版本的 current 指向並回報沒有前版。
腳本接著確認 HTTP 已恢復為 v2,而不是只相信程式執行過 rollback。另以明確標示的故障注入分別觸發連線、逾時與 JSON 例外;注入只替代第一次驗收,回復後仍透過真實本機 HTTP 讀回。這些案例驗證例外處理,沒有模擬真實網路中斷的全部行為。最後再明確切回 v1,重新驗證 v1 的 HTTP 回應。正常完成時,evidence/result.json 應包含下列七項檢查,終端輸出 CD_LAB_PASS:
| 演練情境 | 需要看到的結果 |
|---|---|
| 初次發布 v1 | SHA 一致,HTTP 200 且版本為 v1 |
| v2 的預期 SHA 錯誤 | 拒絕 staging,HTTP 保持 v1 |
| 合法提升 v2 | 使用原產物,HTTP 200 且版本為 v2 |
| v3 提升後回 503 | 發布失敗,自動恢復並讀回 v2 |
| 注入連線、逾時與 JSON 驗收例外 | 三種例外各自恢復 v2,並使用真實 HTTP 確認 |
| 初次發布驗收失敗 | 移除 current,明確回報沒有前版可回復 |
| 明確回到 v1 | HTTP 200,版本確實為 v1 |
只要其中一項不符,程式會以非零狀態結束;不會因為曾經產生 ZIP 或曾成功切換路徑就宣告整體完成。結果 JSON 亦保存 Python 版本、平台與產物 SHA,讓下一位接手者能知道這次證據涵蓋的環境。
回滾程式版本,為什麼還不能保證資料安全?
本練習沒有資料庫,因此沒有證明資料遷移可回復。真實系統若先刪掉舊欄位或改寫資料格式,回到舊程式也可能無法讀取資料;還有訊息佇列、外部支付與已寄出的通知等副作用,不會隨程式版本自動消失。
對含資料遷移的服務,應先寫明新舊版本可同時接受哪些資料,再規劃擴充、切換與移除的順序。這是工程設計建議,實際策略取決於資料模型、交易與業務要求。發布計畫至少需要區分:哪些可用程式回滾處理、哪些要前向修復、哪些須由備份還原,以及每條路徑可能造成的資料損失。
同樣地,灰度發布與藍綠部署都涉及真實流量控制;本機切換一個 link 沒有演練流量比例、跨節點一致性、連線排空或觀察時間。不要把縮小模型的成功外推成已驗證零停機部署。
放進 GitHub Actions 時,核准條件在哪裡?
附件的 delivery.yml 只執行這個隔離演練並保存 evidence artifact。它沒有 production environment,也不讀取部署機密。若把它複製到專案,需將 workflow 放入 .github/workflows/,並讓 run 路徑對應 release_lab.py 的實際位置。
要建立真實部署核准,不能只寫 environment: production。GitHub 的 environment 必須設定適用的保護規則;若 workflow 引用尚不存在的 environment,可能建立一個沒有 reviewers 或 secrets 的環境。保護功能亦受方案和 repository 可見性影響,應核對自己帳號實際可用的能力。GitHub environment 文件
正式部署前,應以具體的產物版本與變更摘要供核准者審閱,並在既有權限模型中驗證該角色能否批准、是否禁止自我核准、哪個分支可部署。本文沒有替讀者建立或測試任何這類帳號設定,練習的自動版本切換也不是人工核准紀錄。
下一次發布,可以直接帶走的驗收欄位
在實際專案中記錄來源提交、產物 SHA、部署目標、前一版本、啟動時間、HTTP 或業務驗收結果、失敗原因及恢復後版本。沒有觀察到的欄位保留待核,不要把 pipeline successful 擴大解讀成全部使用者都已取得正確結果。
這套練習讓發布具備一個可重現的失敗路徑:錯誤產物在切換前被拒絕,可驗證但不可用的新版本在切換後被復原。當團隊能留下兩種證據,再把對應機制接到真實部署架構,才有條件討論自動化到哪一步最合適。
資料來源與素材說明
官方文件核對日:2026 年 11 月 9 日。程式、合成版本與故障情境為原創教學;配圖是概念示意,非真實 production 部署畫面。本篇沒有宣稱完成雲端核准、正式站部署、流量灰度或資料庫回復測試。
內容提供:TARAISG。本文由 AI 協助整理與查核;專題編輯為天地人 AI 編輯計畫的策展角色,未宣稱經具名人工或專家審核。
本文由 TARAISG(TIANDIREN AI SOLUTION GROUP)使用 AI 模型產生正文、整理來源並設計實務工具。方法依據與限制列於文末;案例為明確標示的教學假設。未有具名紀錄時,不宣稱經人工或專家審核。
查看內容製作與更正說明