健康檢查、結構化日誌、帶抖動的重試策略。所有撰寫「生產就緒檢查清單」的人都會列出同樣的項目,這些項目本身並沒有錯。但如果你的服務都依賴同一個共享領域模型,那麼這份清單並不是最優先該關注的。
這個月我花了一段時間把我們領域專案轉成帶版本的 NuGet 套件,推送到私有的 Artifactory 來源,並建立一個機器人,專門負責提升版本號並把更新推送給所有使用該套件的微服務儲存庫。
這聽起來只是底層管線工作,確實如此。但正是這些管線工作決定了「生產就緒」究竟是只針對單一服務,還是適用於整個服務群。
它解決的問題是這樣的:當你把共享的領域邏輯——實體、值物件、需要多個服務共用的商業規則——從單體應用拆分到共用函式庫時,你同時建立了一個原本不存在的相依性圖譜。每個使用該函式庫的服務,都會對它所使用的版本有(不管是隱含或明確的)意見。在我們妥善打包之前,這些意見是靠複製貼上與部落知識來維持。沒有人真正知道哪個服務在使用哪個版本的哪個規則。
NuGet 解決了打包的一半問題;Artifactory 則提供私有來源,讓套件不會外洩,並留下誰、何時下載的稽核軌跡。這兩者本身並不特別有趣。真正有趣、也真正決定這套做法是幫助還是悄悄毀掉你的,是那個機器人。
機器人監控領域套件儲存庫。當新版本發行時,它會對每個下游微服務儲存庫開啟 PR,更新參考。這就是它的全部工作。沒有什麼聰明設計,只是把原本需要手動執行、常常被遺忘的依賴更新自動化。
在這一點上,我要反駁你在半數「正確的微服務」文章中看到的觀點:自動化更新固然是好事,但前提是你必須承認自己建立了一個內部的 Dependabot,而內部 Dependabot 需要和外部 Dependabot 相同的紀律——大多數團隊會跳過這道紀律,因為「這是我們自己的程式碼,我們會抓到問題」。
你不會抓到它。至少無法可靠地抓到。
如果機器人開啟 PR,而團隊像審核記錄函式庫的修補程式更新一樣草率地核准合併領域套件更新,那麼只要一次語意版本號標錯,就可能讓破壞性變更在同一個下午影響十幾個服務。共享的領域函式庫不是葉依賴,它是承重結構。該處的主版本更新,應該像資料庫結構遷移一樣受到重視,因為它的實際影響就是如此。
因此,真正的生產就緒問題不是「我們有沒有版本更新機器人」,而是「每個使用服務是否都有 CI 閘道,在 PR 可合併前,先針對新套件版本執行自己的整合測試套件——而不是之後」。我們一開始並沒有這個閘道。機器人開啟 PR、綠色建置只代表「可以編譯」,而合併按鈕就在那裡。這不是就緒,這只是把速度包裝成就緒。
把閘道做好,意味著我們必須刻意讓機器人慢下來。它還是會立即開啟 PR,但除非使用服務的完整整合測試套件(不只是單元測試,還包括大家都不愛跑的慢速測試)通過,否則不會在小版本更新以上自動合併。這個改變把失敗模式從生產環境的無聲崩潰,變成需要有人查看的紅色 CI 檢查。這不算刺激,但正確。
這裡確實存在權衡,我不會假裝它不存在:這會讓領域修正傳遞到每個服務的速度變慢。在舊的複製貼上世界裡,共享值物件的錯誤修正只會傳到有人記得要更新的服務,而且什麼時候更新都行——雖然慢,但不會有人因為它而在隔天早上建置失敗。在新世界裡,修正會快速傳播到所有連接到機器人的服務,而任何與本地假設衝突的服務,都會在 CI 階段立刻發現,而不是三週後在事故頻道才知道。
我每次都會選擇第二種失敗模式。快速且可見,勝過緩慢且隱形。但代價是真實存在的,而第一次在週五下午五點因為更新而導致建置失敗時,假裝沒有代價,正是這些系統失去團隊信任的原因。
還有一件事要說清楚:這一切並不會取代常見的檢查清單。服務仍然需要健康檢查、合理的重試策略,以及能在請求間真正關聯的日誌。打包與版本更新自動化帶來的,是讓生產就緒檢查清單在整個服務群都成立,而不只是對你碰巧仔細測試過的那一個服務成立。一個服務可能在隔離狀態下通過清單上的每一項,卻在共享函式庫以無人察覺的方式改變時瞬間失效。
如果你正在建立共享的 .NET 領域函式庫,卻還沒決定誰要審核主版本更新、哪一套測試必須通過才能合併,以及當機器人的 PR 導致下游兩個服務出問題時,誰要負責,那麼你並沒有一個生產就緒的服務群。你只有一個曾經生產就緒的服務,以及一個正在悄悄決定「那個狀態何時結束」的機器人。
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.