[{"data":1,"prerenderedAt":417},["ShallowReactive",2],{"article-\u002Farticles\u002Fblue-green-canary-feature-toggle":3},{"id":4,"title":5,"body":6,"date":404,"description":394,"excerpt":405,"extension":406,"meta":407,"navigation":408,"path":409,"seo":410,"stem":411,"tags":412,"__hash__":416},"articles\u002Farticles\u002Fblue-green-canary-feature-toggle.md","藍綠部署、金絲雀部署、Feature Toggle：三種降低上線風險的部署機制",{"type":7,"value":8,"toc":393},"minimark",[9,13,22,29,33,43,50,56,73,78,104,108,113,119,123,138,142,168,172,177,183,186,212,216,227,231,262,266,269,337,341,352,355,376,387,390],[10,11,12],"h2",{"id":12},"三者要解決的共同問題",[14,15,16,17,21],"p",{},"傳統的部署方式是把舊版本整個停掉、換上新版本，中間有一段停機時間，而且新版本一上線就要面對 100% 的流量——如果新版本有問題，影響的是",[18,19,20],"strong",{},"所有使用者","，發現問題後也只能匆忙回滾。",[14,23,24,25,28],{},"藍綠部署、金絲雀部署、Feature Toggle 都是想解決同一件事：",[18,26,27],{},"把「上線」這個高風險動作，拆成更小、更可控、可以隨時喊停的步驟","，差別在於它們各自在哪一層做這件事——藍綠部署換的是整套執行環境，金絲雀部署換的是流量比例，Feature Toggle 換的是應用程式裡的一段邏輯開關。",[10,30,32],{"id":31},"藍綠部署blue-green-deployment","藍綠部署（Blue-Green Deployment）",[14,34,35,38,39,42],{},[18,36,37],{},"運作方式","：準備兩套一模一樣的正式環境，一套叫 Blue（目前正在服務的版本），一套叫 Green（要上線的新版本）。新版本先完整部署到 Green、做健康檢查與暖機，確認沒問題後，透過負載平衡器把流量",[18,40,41],{},"整批切換","到 Green；如果切換後發現問題，只要把路由切回 Blue 就能立即恢復。",[14,44,45],{},[46,47],"img",{"alt":48,"src":49},"藍綠部署示意圖：負載平衡器把流量整批指向 Blue 或 Green 其中一套環境","\u002Fimages\u002Farticles\u002Fblue-green-deployment.svg",[14,51,52,55],{},[18,53,54],{},"適合的情境","：",[57,58,59,63,66],"ul",{},[60,61,62],"li",{},"想要幾乎零停機時間的上線方式——切換是路由層面的操作，不是重啟服務",[60,64,65],{},"想要「出事能立刻整批回滾」，而不是重新部署舊版本",[60,67,68,69,72],{},"新舊版本之間",[18,70,71],{},"不需要同時服務流量","、資料相容性問題相對單純的情境",[14,74,75,55],{},[18,76,77],{},"使用風險",[57,79,80,86,92,98],{},[60,81,82,85],{},[18,83,84],{},"雙倍資源成本","：兩套環境要同時常駐（至少在切換過渡期），資源花費接近兩倍",[60,87,88,91],{},[18,89,90],{},"全有或全無，沒有漸進空間","：切換是瞬間的，如果 Green 有邏輯性錯誤但沒被健康檢查攔下來，切換的當下就是 100% 的使用者受影響，沒有「先讓一小部分人試試看」的餘地",[60,93,94,97],{},[18,95,96],{},"資料庫相容性最麻煩","：如果 Blue 跟 Green 共用同一個資料庫，切換瞬間、甚至切回去的那一刻，新舊版本都可能對同一份資料做讀寫，資料庫的 schema 變更必須同時讓新舊版本都能運作",[60,99,100,103],{},[18,101,102],{},"有狀態連線的處理","：像 WebSocket 這種長連線，整批切換會讓連線瞬間中斷，需要額外設計優雅排空（Drain）或重連機制",[10,105,107],{"id":106},"金絲雀部署canary-deployment","金絲雀部署（Canary Deployment）",[14,109,110,112],{},[18,111,37],{},"：新版本先只導入一小部分流量（例如 5%），跟舊版本同時上線、同時服務使用者，透過監控指標觀察新版本是否正常；指標健康就逐步擴大流量比例（5% → 25% → 50% → 100%），指標異常就立即把金絲雀流量退回 0%，全程只有一小部分使用者受到影響。",[14,114,115],{},[46,116],{"alt":117,"src":118},"金絲雀部署示意圖：負載平衡器依比例把流量分給舊版本與金絲雀新版本，指標正常逐步擴大比例","\u002Fimages\u002Farticles\u002Fcanary-deployment.svg",[14,120,121,55],{},[18,122,54],{},[57,124,125,132,135],{},[60,126,127,128,131],{},"想要用",[18,129,130],{},"真實流量","驗證新版本，而不是只靠測試環境或人工驗收",[60,133,134],{},"使用者基數夠大，切出 5% 的流量仍然是有代表性的樣本",[60,136,137],{},"團隊已經有一定的可觀測性基礎（延遲、錯誤率等關鍵指標的監控），能在流量還很小的時候就判斷「這個版本健不健康」——如果團隊已經定義了 SLO，金絲雀期間消耗掉的錯誤預算，本身就是一個很自然的自動回滾依據",[14,139,140,55],{},[18,141,77],{},[57,143,144,150,156,162],{},[60,145,146,149],{},[18,147,148],{},"新舊版本必須同時服務、同時讀寫同一份資料","：這比藍綠部署的相容性要求更長時間——只要金絲雀流量還沒到 100%，新舊版本就會同時存在一段時間，新版本寫入的資料格式，舊版本（還在服務剩餘流量）也必須看得懂，反之亦然",[60,151,152,155],{},[18,153,154],{},"流量分配要有代表性","：如果分流方式設計不好（例如剛好把同一個地區、同一種裝置的使用者導進金絲雀），5% 的樣本可能無法代表真實的流量特性，觀察到的指標就沒有意義",[60,157,158,161],{},[18,159,160],{},"需要自動化的判斷與回滾機制","：金絲雀的意義在於「小範圍先試」，如果沒有自動化告警、自動回滾，全靠人工盯著儀表板看，實際上就退化成手動、慢半拍的藍綠切換",[60,163,164,167],{},[18,165,166],{},"路由層要求更高","：需要能依比例動態分流的基礎設施（Kubernetes 搭配 Service Mesh、或雲端負載平衡器的加權路由），比藍綠部署單純的「切全部」複雜",[10,169,171],{"id":170},"feature-toggle功能開關","Feature Toggle（功能開關）",[14,173,174,176],{},[18,175,37],{},"：在程式碼裡用一個開關（Flag）包住新功能，讓「部署」跟「發布」變成兩件互相獨立的事——新程式碼可以先完整部署上線，但功能保持關閉狀態，之後只要透過設定或後台切換開關，就能立即讓功能生效，不需要重新部署。",[14,178,179],{},[46,180],{"alt":181,"src":182},"Feature Toggle 示意圖：同一個應用程式版本內部依 Feature Flag 狀態走不同邏輯分支","\u002Fimages\u002Farticles\u002Ffeature-toggle.svg",[14,184,185],{},"Feature Toggle 常見可以再分幾種用途：",[57,187,188,194,200,206],{},[60,189,190,193],{},[18,191,192],{},"Release Toggle（釋出開關）","：短期使用，功能穩定全開之後就該把開關和舊邏輯一起移除",[60,195,196,199],{},[18,197,198],{},"Experiment Toggle（實驗開關）","：用來做 A\u002FB 測試，不同使用者群組看到不同邏輯",[60,201,202,205],{},[18,203,204],{},"Ops Toggle（維運開關）","：緊急時可以手動關閉某個功能或繞過某個故障中的外部依賴，降低系統負載或止血",[60,207,208,211],{},[18,209,210],{},"Permission Toggle（權限開關）","：依角色或訂閱方案決定功能是否可見，本質上是長期存在的權限控制，不是臨時性的釋出開關",[14,213,214,55],{},[18,215,54],{},[57,217,218,221,224],{},[60,219,220],{},"想要把「部署」的風險跟「對使用者開放功能」的風險分開處理，兩者用不同的節奏推進",[60,222,223],{},"功能要不要開放，需要由業務端自主決定時機（例如配合行銷活動時間點），而不是綁死在部署排程上",[60,225,226],{},"需要對特定使用者群組（VIP、內部員工、特定地區）先行開放做驗證",[14,228,229,55],{},[18,230,77],{},[57,232,233,244,250,256],{},[60,234,235,238,239,243],{},[18,236,237],{},"開關地獄（Flag Hell）","：Release Toggle 如果沒有在功能穩定後清理掉，程式碼裡會累積大量 ",[240,241,242],"code",{},"if (flag)"," 分支，多個開關交叉組合時，程式碼的實際執行路徑數量會指數增長，變得極難測試、極難理解",[60,245,246,249],{},[18,247,248],{},"測試矩陣爆炸","：QA 理論上要覆蓋每個開關「開／關」兩種狀態的組合，開關一多，完整測試所有組合在實務上幾乎不可能，通常只能挑重點組合驗證",[60,251,252,255],{},[18,253,254],{},"開關依賴本身是新的故障點","：如果開關狀態是從遠端的 Feature Flag 服務即時查詢，那個服務本身的可用性、延遲，都會直接影響應用程式；務必設計好查詢失敗時的預設值（Fail Open 或 Fail Closed 要依功能性質決定），不然開關系統本身變成單點故障",[60,257,258,261],{},[18,259,260],{},"同樣有相容性問題","：如果開關控制的功能牽涉資料格式（例如新邏輯寫入新欄位），開關關閉時期寫入的舊格式資料，開啟後的新邏輯要能正確處理，這跟藍綠、金絲雀面對的是同一類問題，只是換了個地方發生",[10,263,265],{"id":264},"三者可以疊加使用不是互斥的選擇","三者可以疊加使用，不是互斥的選擇",[14,267,268],{},"這三個機制作用的層次不同，實務上經常疊加使用：先用金絲雀部署讓新版本的程式碼只接收一小部分流量、驗證程式本身沒有崩潰或效能問題；與此同時，程式碼裡新功能的 Feature Toggle 仍然保持關閉，等到金絲雀擴大到 100%、確認新版本本身穩定之後，才透過 Feature Toggle 真正對使用者開放這個功能。「部署」跟「發布」在這個流程裡被完全拆開成兩個獨立的決策點。",[270,271,272,291],"table",{},[273,274,275],"thead",{},[276,277,278,282,285,288],"tr",{},[279,280,281],"th",{},"機制",[279,283,284],{},"切換的對象",[279,286,287],{},"粒度",[279,289,290],{},"回滾速度",[292,293,294,309,323],"tbody",{},[276,295,296,300,303,306],{},[297,298,299],"td",{},"藍綠部署",[297,301,302],{},"整套執行環境",[297,304,305],{},"環境層級（全有或全無）",[297,307,308],{},"快（路由切回），但影響面是全量",[276,310,311,314,317,320],{},[297,312,313],{},"金絲雀部署",[297,315,316],{},"流量比例",[297,318,319],{},"流量層級（可微調百分比）",[297,321,322],{},"快，且只影響金絲雀那一部分流量",[276,324,325,328,331,334],{},[297,326,327],{},"Feature Toggle",[297,329,330],{},"程式邏輯分支",[297,332,333],{},"功能層級（可依使用者群組）",[297,335,336],{},"最快（改設定，不必重新部署）",[10,338,340],{"id":339},"共同的核心風險向下相容","共同的核心風險：向下相容",[14,342,343,344,347,348,351],{},"三個機制都建立在同一個前提上：",[18,345,346],{},"新舊版本（或新舊資料格式）必須在同一段時間內同時存在、同時被使用","。藍綠部署的重疊時間最短（切換前後那一刻），金絲雀部署的重疊時間較長（整個逐步放量的過程），Feature Toggle 的重疊時間可能最長（開關關閉的整段期間），但本質上都要面對同一個問題：",[18,349,350],{},"向下相容（Backward Compatibility）","。",[14,353,354],{},"資料庫層面常見的做法是「擴張—收縮模式」（Expand-Contract Pattern）：",[356,357,358,364,370],"ol",{},[60,359,360,363],{},[18,361,362],{},"擴張","：新增欄位或新增表格，但不刪除、不修改舊有結構，讓舊版本完全不受影響地繼續運作",[60,365,366,369],{},[18,367,368],{},"雙寫過渡","：新版本上線期間，視情況同時寫入新舊兩種格式，確保無論流量落在新版本或舊版本，資料都是完整、一致的",[60,371,372,375],{},[18,373,374],{},"收縮","：等所有流量都已經切到新版本、確認沒有回滾的需求之後，才移除舊欄位或舊表格",[14,377,378,379,382,383,386],{},"API 層面的原則相同：新版本的 API 要嘛保持跟舊版本相容，要嘛做好版本控制（同時提供 ",[240,380,381],{},"\u002Fv1","、",[240,384,385],{},"\u002Fv2","），不能讓還沒升級完的呼叫方，因為後端搶先升級就直接壞掉。",[10,388,389],{"id":389},"一句話總結",[14,391,392],{},"藍綠部署換的是整套環境、金絲雀部署換的是流量比例、Feature Toggle 換的是程式邏輯分支，三者可以疊加使用、各自處理不同粒度的上線風險；但不管用哪一種，只要新舊版本會同時存在，向下相容就是繞不過去的前提，資料庫與 API 的變更都該用「先擴張、再收縮」的方式，讓舊版本在切換的過程中不會被突然打壞。",{"title":394,"searchDepth":395,"depth":395,"links":396},"",2,[397,398,399,400,401,402,403],{"id":12,"depth":395,"text":12},{"id":31,"depth":395,"text":32},{"id":106,"depth":395,"text":107},{"id":170,"depth":395,"text":171},{"id":264,"depth":395,"text":265},{"id":339,"depth":395,"text":340},{"id":389,"depth":395,"text":389},"2026-06-11",null,"md",{},true,"\u002Farticles\u002Fblue-green-canary-feature-toggle",{"title":5,"description":394},"articles\u002Fblue-green-canary-feature-toggle",[413,414,299,313,327,415],"部署策略","DevOps","SRE","w1ve6NLczHCmkxTQPML44nVp1fkbtFB2iBdbkKpauys",1792959150193]