每年有數十億使用者與 Google 的產品和服務互動。搜尋、Gmail、地圖、YouTube、Chrome 和Google Cloud等主要產品已與現代生活緊密整合,有助於定義 21 世紀的體驗。這項全球性的影響力,是我們產品品質有目共睹的成果,也反映出使用者對 Google 服務隨時可用的期待。
在 Google Cloud,我們會持續對產品和服務進行程式碼變更,確保提供最佳使用者體驗、提升安全性和可靠性,並遵守法規和法規遵循要求。不過,無論變更大小,有時都可能導致問題。 為解決這項風險,我們會在整個變更生命週期中,優先確保變更安全。
本文說明 Google Cloud 團隊如何運用 Google 數十年來在卓越開發方面的投資,導入可靠性最佳做法和工程標準,以滿足客戶對開發速度和可靠性的期望。 Google Cloud
變更的生命週期 Google Cloud
Google Cloud 產品團隊與 Google 其他工程團隊共用許多管理程序和工具。我們採用標準軟體開發方法進行變更管理,優先考量持續整合 (CI) 和持續推送軟體更新 (CD)。CI 包括經常提議、測試及提交變更,通常任何特定產品或服務每天都會進行多次。CD 是 CI 的擴充功能,工程師會根據程式碼庫的最新穩定快照,持續準備候選版本。
這種做法的優先考量是盡快為Google Cloud 客戶推出變更,但也會盡量確保安全。我們會在編寫任何程式碼前考量變更安全性,即使在實際工作環境中推出變更後,我們仍會持續關注安全性。我們的變更管理模型一般分為四個階段:設計、開發、資格認證和推出。下圖顯示這四個階段,本文也會詳細說明:
融入安全性考量的設計
我們瞭解,即使是開發程序初期的微小錯誤,也可能在後續造成重大問題,大幅影響顧客體驗。因此,所有重大變更都必須先取得核准的設計文件。工程團隊可使用通用設計文件範本,提議重大變更。這份通用設計文件有助於我們一致評估各項產品的重大變更。Google Cloud 下圖顯示重大變更的標準設計程序:
軟體開發人員提出變更建議,以滿足業務、技術、成本和維護需求時,設計階段就開始了。提交變更後,資深專家 (包括可靠性和安全性專家,以及技術主管) 會啟動全面審查和核准程序。只有在設計提案工程師解決專家提出的所有意見,且每位專家都核准設計後,才能開始實作變更。這個設計和審查程序可降低產品團隊開始進行變更的可能性,避免對正式版中的客戶造成負面影響。Google Cloud
開發時已確保安全
我們的程式碼開發流程可提高程式碼的品質和可靠性。 提案變更獲得核准後,開發程序就會開始,包括為新工程師提供全面的入門訓練,例如訓練、指導,以及針對提案程式碼變更提供詳細意見回饋。我們採用多層開發和測試方法,透過手動和自動測試,持續驗證開發各階段的程式碼。每項程式碼變更都會經過徹底審查,確保符合 Google 的高標準。
下圖顯示我們的大致開發流程:
工程師開始編寫程式碼,以及對應的單元和整合測試時,開發階段就開始了。在這個階段,工程師可以執行自己編寫的測試和一系列預先提交測試,確保新增和變更的程式碼有效。完成程式碼變更並執行測試後,工程師會請熟悉程式碼的人員進行手動審查。這項人工審查程序通常會反覆進行,並可能導致需要進行額外的程式碼修訂。作者和審查人員達成共識後, 作者會提交程式碼。
程式碼標準可確保變更品質
Google 的工程文化、實務和工具旨在確保程式碼正確、清楚、簡潔且有效率。Google 的程式碼開發作業是在單一存放區中進行,這是全球最大的整合式程式碼存放區。這個單一存放區包含數百萬個來源檔案、數十億行程式碼,以及數億個稱為變更清單的提交項目。每天都有數以萬計的新變更清單加入,因此這個資料庫持續快速成長。單一存放區的主要優點是方便重複使用程式碼、簡化依附元件管理,以及在產品和服務中強制執行一致的開發人員工作流程。
重複使用程式碼很有幫助,因為我們已充分瞭解重複使用的程式碼在正式環境中的效能。運用現有的優質程式碼,變更作業本質上會更穩健,也更容易維持在所需標準。這項做法不僅能節省時間和精力,還可確保程式碼庫的整體健康狀態維持在高水準,進而打造出更可靠的產品。
Google Cloud 以高品質開放原始碼軟體為基礎的服務,可能會使用另一個存放區 (通常是 Git) 補充單一存放區,以便使用分支管理開放原始碼軟體。
訓練注意事項
工程師加入團隊後,就應開始投入程式碼品質。如果工程師是 Google 新人,或對團隊的基礎架構和架構較不熟悉,則會接受廣泛的入門訓練。在新手上路期間,他們會研讀樣式指南、最佳做法和開發指南,並手動進行實務練習。此外,新工程師提交的每個變更清單都必須經過額外層級的核准。只有通過嚴格的專業知識測驗,並在特定程式設計語言中獲得可讀性認證的工程師,才能核准該語言的變更。任何工程師都能取得程式設計語言的可讀性認證,大多數團隊都有多位程式設計語言的核准者。
提早測試可安全地提高速度
「提早測試」原則是指在開發程序中,提早進行測試和驗證。根據我們的觀察,在發布流程中越晚發現並修正錯誤,成本就會大幅增加。舉例來說,假設客戶在正式版中發現錯誤。這個錯誤可能會對客戶的工作負載和應用程式造成負面影響,而且客戶可能也需要完成 Cloud Customer Care 程序,相關工程團隊才能減輕這個錯誤的影響。如果負責解決問題的工程師與最初導入含有錯誤變更的工程師不同,新工程師就必須熟悉程式碼變更,這可能會增加重現及最終修正錯誤所需的時間。整個過程需要客戶和 Google Cloud's 支援團隊投入大量時間,工程師也必須放下手邊工作來修正問題。
反之,假設工程師正在開發變更,而自動測試失敗時發現錯誤。工程師看到測試失敗後,可以立即修正問題。由於我們的編碼標準,工程師甚至無法提交測試失敗的變更。提早偵測到錯誤,工程師就能修正錯誤,不會影響客戶,也不會產生情境切換的負擔。
對所有相關人員來說,後者是無比理想的情況。因此,多年來 Google Cloud 已投入大量資源,落實「左移」原則,將傳統上在變更資格和推出階段執行的測試,直接移至開發迴圈。現在,工程師提出程式碼變更時,所有單元測試、除了最大型整合測試以外的所有測試,以及廣泛的靜態和動態分析,都會平行完成。
自動預先提交測試會強制執行程式碼標準
預先提交測試是指在特定目錄中提交任何變更之前執行的檢查。預先提交測試可以是針對變更的單元和整合測試,也可以是針對任何變更執行的一般測試 (例如靜態和動態分析)。在過去,預先提交測試會在有人將變更提交至程式碼基底之前,做為最後一個步驟執行。如今,部分原因在於「左移」原則和我們實作的 CI,因此當工程師在開發環境中變更程式碼,以及將變更合併至單一存放區之前,我們會持續執行預先提交測試。工程師也可以在開發使用者介面中,按一下滑鼠手動執行預先提交測試套件,且每項預先提交測試都會在人工程式碼審查前,自動為每個變更清單執行。
預先提交測試套件通常涵蓋單元測試、模糊測試 (模糊化)、密封整合測試,以及靜態和動態程式碼分析。如要變更核心程式庫或 Google 廣泛使用的程式碼,開發人員會執行全域預先提交作業。全域預先提交會針對整個 Google 程式碼庫測試變更,盡量降低提議變更對其他專案或系統造成負面影響的風險。
單元測試和整合測試
徹底測試是程式碼開發流程中不可或缺的一環。所有人都必須為程式碼變更編寫單元測試,我們也會持續追蹤專案層級的程式碼涵蓋範圍,確保驗證預期行為。此外,我們要求所有重要使用者歷程都必須進行整合測試,驗證所有必要元件和依附元件的功能。
單元測試和最大的整合測試以外的所有測試,都設計為能迅速完成,並在分散式環境中以高度平行方式逐步執行,因此能快速且持續地提供自動化開發意見回饋。
模糊測試
單元和整合測試可協助我們使用預先決定的輸入和輸出內容,驗證預期行為,而模糊測試則是一種技術,會以隨機輸入內容轟炸應用程式,目的是找出可能導致安全漏洞或當機的隱藏缺陷或弱點。模糊測試可協助我們主動找出並解決軟體中的潛在弱點,在客戶與變更互動前,提升產品的整體安全性和可靠性。這項測試的隨機性特別實用,因為使用者有時會以我們意想不到的有趣方式與產品互動,而模糊測試有助於我們考量手動未考慮到的情境。
靜態分析
靜態分析工具在我們的開發工作流程中扮演重要角色,可確保程式碼品質。靜態分析從早期使用規則運算式進行 Linting,以找出有問題的 C++ 程式碼模式,到現在已大幅演進。目前靜態分析涵蓋所有 Google Cloud 正式版 語言,可找出錯誤、效率不彰或已淘汰的程式碼模式。
透過進階編譯器前端和 LLM,我們可以在工程師編寫程式碼時,自動建議改善方式。系統會透過靜態分析,審查每項建議的程式碼變更。隨著我們陸續新增靜態檢查,系統會持續掃描整個程式碼集,確保符合規定,並自動產生修正內容送審。
動態分析
靜態分析的重點在於找出可能導致問題的已知程式碼模式,動態分析則採取不同做法。這包括編譯及執行程式碼,找出僅在執行期間才會浮現的問題,例如記憶體違規和競爭條件。Google 運用動態分析的歷史悠久,甚至還與廣大開發人員社群分享了幾項工具,包括:
- AddressSanitizer: 偵測緩衝區溢位和釋放後使用等記憶體錯誤
- ThreadSanitizer (C++、 Go): 找出資料競爭和其他執行緒錯誤
- MemorySanitizer: 找出未初始化的記憶體用量
這類工具對於找出複雜的錯誤至關重要,因為單靠靜態分析無法偵測到這類錯誤。Google 會同時使用靜態和動態分析,盡力確保程式碼結構良好、沒有已知問題,且在實際情況下能正常運作。
人工程式碼審查會驗證變更和測試結果
當工程師的程式碼達到重要里程碑,並想將程式碼整合到主要存放區時,他們會提議變更清單,藉此啟動程式碼審查。程式碼審查要求包含下列項目:
- 說明變更目的和任何其他背景資訊
- 實際修改後的程式碼
- 修改後程式碼的單元測試和任何整合測試
- 自動預先提交測試結果
在開發過程中,此時需要另一位人員介入。一或多位指定審查員會仔細檢查變更清單的正確性和清楚程度,並以隨附的測試和預先提交結果做為指引。每個程式碼目錄都有一組指定審查人員,負責確保該程式碼集子集的品質,且必須獲得他們的核准,才能在該目錄中進行變更。審查人員和工程師會共同找出並解決提案程式碼變更可能引發的任何問題。如果變更清單符合我們的標準,審查人員就會核准 (「LGTM」,是「looks good to me」的縮寫)。不過,如果工程師仍在接受所用程式設計語言的訓練,則需要獲得專家額外核准,該專家必須已取得該程式設計語言的讀取能力。
變更清單通過測試和自動檢查並收到 LGTM 後,提出變更的工程師只能對程式碼進行最少的變更。如果大幅修改,核准就會失效,必須再次送審。即使是微小變更,系統也會自動標記並通知原始審查員。工程師提交最終程式碼後,系統會再次進行完整的預先提交測試,然後將變更清單合併至單一存放區。如果任何測試失敗,系統會拒絕提交,並向開發人員和審查人員發出快訊,請他們採取修正措施,然後再試一次。
安全發布資格
雖然預先提交測試很全面,但這並非 Google 測試程序的終點。團隊通常會進行其他測試,例如大規模整合測試,但這類測試無法在初步程式碼審查期間執行 (可能需要較長時間才能執行,或需要高保真度測試環境)。此外,團隊也必須留意因外部依附元件異動等無法控制的因素而導致的任何失敗。
因此,Google 會在開發階段後要求進行資格認證階段。 如以下圖表所示,這項資格認證階段會使用持續建構和測試程序:
這個程序會定期針對上次執行後,直接或間接變更的所有程式碼執行測試。系統會自動將任何失敗情況轉交給負責的工程團隊。在許多情況下,系統可以自動找出導致中斷的變更清單,並將其復原。這些大規模整合測試會在各種預先發布環境中執行,從部分模擬環境到整個實體位置都有。
測試的資格目標種類繁多,從基本可靠性和安全性到商業邏輯都有。這些資格測試包括測試程式碼是否符合下列條件:
- 提供必要功能的能力,這項能力會透過大規模整合測試進行測試
- 滿足業務需求的能力,並透過客戶工作負載的合成表示法進行測試
- 能夠承受底層基礎架構故障,這項能力會透過在整個堆疊中插入故障來測試
- 維持服務容量的能力,可透過負載測試架構進行測試
- 安全復原的能力
安全推出異動內容
即使開發、測試和資格認證程序再嚴謹,有時仍會出現瑕疵,進而對使用者造成負面影響。本節將說明 Google Cloud 推出程序 如何限制有缺陷的變更所造成的影響,並確保快速偵測到任何回歸。我們對部署至正式環境的所有類型變更都採用這種做法,包括二進位檔、設定、結構定義更新、容量變更和存取控制清單。
變更傳播和監督
我們採用一致的方法部署變更,盡量減少對客戶的負面影響,並將問題隔離到個別邏輯和實體故障網域。 Google Cloud 這項程序以我們數十年來的SRE 可靠性做法和全球規模的監控系統為基礎,可盡快偵測並減輕不良變更的影響。快速偵測功能可讓我們更快通知客戶,並採取修正措施,有系統地防止類似的故障再次發生。
大多數 Google Cloud 產品都是區域或可用區層級。也就是說,在 A 區域執行的區域產品,與在 B 區域執行的相同產品是獨立的。同樣地,在 A 區域 C 可用區中執行的區域產品,與在 A 區域 D 可用區中執行的相同區域產品無關。這種架構可將服務中斷的風險降到最低,避免影響其他區域或單一區域內的其他可用區。部分服務 (例如 IAM 或Google Cloud 控制台) 提供涵蓋所有區域的全球一致層,因此稱為「全球服務」。全域服務會跨區域複製,避免單點故障並縮短延遲時間。共用的 Google Cloud 推出平台會判斷服務是區域、地區還是全球服務,並適當安排生產環境變更。
Google Cloud 推出程序會將部署到多個目標位置的服務,其所有副本分成多個階段。第一波更新會包含少量副本,並依序進行更新。初始階段的平衡會盡量保護大多數客戶工作負載,同時盡可能接觸各種工作負載,以便盡早偵測到問題,並納入模擬常見客戶工作負載模式的合成工作負載。
如果服務副本在目標位置更新後,推出作業仍順利進行,後續推出波段的大小會逐步增加,並導入更多平行處理。雖然為了考量 Google Cloud 地點數量,必須進行某種程度的平行處理,但我們不允許同時更新不同波次的地點。如果發布階段延續到晚上或週末,該階段可以完成進度,但要等到負責發布作業的團隊開始上班,才能啟動新的階段。
下圖為工作流程示例,說明我們在 Google Cloud 區域產品和服務中使用的推出邏輯:
Google Cloud 發布程序會使用初期測試分析服務 (CAS),在發布期間自動進行 A/B 測試。部分副本會成為 Canary (也就是在服務中部分部署變更,且時間有限),其餘副本則會組成控制組,不包含變更。推出程序中的每個步驟都有烘烤時間,以便在進入下一個步驟前,找出緩慢燃燒的問題,確保服務的所有功能都能正常運作,且 CAS 能偵測到潛在異常狀況。我們仔細定義烘烤時間,以兼顧偵測緩慢燃燒的問題和開發速度。 Google Cloud 推出作業通常需要一週時間。
下圖簡要說明 CAS 工作流程:
工作流程開始時,推出工具會將變更部署至 Canary 副本。然後,推出工具會向 CAS 要求判定結果。CAS 會根據控制組評估 Canary 副本,並傳回「通過」或「失敗」的結果。如果任何健康狀態信號失敗,系統會為服務擁有者產生快訊,並暫停或回溯發布的執行步驟。如果變更導致外部客戶服務中斷,系統會宣告外部事件,並使用個人化服務健康狀態服務通知受影響的客戶。事件也會觸發內部審查。 Google 的事後檢討哲學可確保找出並套用適當的修正措施,盡量避免類似的失敗再次發生。
監控信號和推出後安全
軟體瑕疵不一定會立即顯現,有些可能需要特定情況才會觸發。因此,我們會在推出完成後,持續監控生產系統。多年來,我們發現即使發布作業沒有立即引發任何問題,不良的發布作業仍最有可能導致實際工作環境事件。因此,我們的生產作業手冊會指示事件應變人員將近期發布作業與觀察到的問題相互關聯,如果事件應變人員無法排除近期變更做為事件根本原因的可能性,則預設會還原近期發布作業。
推出後監控功能會使用與推出期間自動 A/B 分析相同的監控信號。 Google Cloud 監控和警報哲學結合了兩種監控類型:內省 (也稱為白箱) 和綜合 (也稱為黑箱)。內省式監控會使用 CPU 使用率、記憶體使用率和其他內部服務資料等指標。從顧客的角度來看,綜合監控會觀察系統行為,追蹤服務錯誤率,以及探測器服務對綜合流量的回應。綜合監控著重於症狀,可找出目前的問題;內省監控則可診斷已確認的問題,有時還能找出即將發生的問題。
為協助偵測只影響部分客戶的事件,我們會將客戶工作負載分組,歸入相關工作負載的同類群組。只要同類群組的成效偏離常態,系統就會觸發快訊。即使匯總成效看似正常,我們也能透過這些快訊偵測到特定客戶的衰退情形。
軟體供應鏈保護
每當團隊進行變更時,我們都會使用稱為 Borg 適用的二進位授權 (BAB) 的安全檢查,保護軟體供應鏈和 Cloud 客戶免於內部風險。 Google Cloud BAB 會在程式碼審查階段啟動,並建立部署至實際工作環境的程式碼和設定稽核追蹤記錄。為確保製作完整性,BAB 只允許符合下列條件的變更:
- 經過簽署且無法竄改
- 來自已識別的建構方和來源位置
- 已由程式碼作者以外的第三方審查並明確核准
如果您有興趣在自己的軟體開發生命週期中套用部分相同概念,我們已在稱為「軟體構件供應鏈級別 (SLSA)」的開放規格中,納入 BAB 的重要概念。SLSA 可做為安全架構,在正式環境中開發及執行程式碼。
結論
Google Cloud Google 投入數十年心血,致力於開發卓越技術,程式碼健康度和生產健康度是 Google 所有工程團隊的文化原則。我們的設計審查程序可確保及早考量程式碼和生產健康狀態的影響。我們的開發程序以「左移」原則和廣泛測試為基礎,可確保設計概念安全無虞地正確實作。我們的資格認證程序會進一步擴大測試範圍,涵蓋大規模整合和外部依附元件。最後,我們的推出平台可讓我們逐步建立信心,確保特定變更確實如預期運作。從概念發想到生產,我們的做法可滿足客戶對開發速度和可靠性的期望。Google Cloud