依附元件管理

回報問題 查看來源 夜間 7.2 7.1 7.0 6.5 6.4

翻閱先前的頁面時,有一個主題不斷重複: 管理 您自有的程式碼相當簡單,但管理其依附元件很 變得更困難依附元件有很多種:有時候 對特定工作的依賴 (例如「在將版本標示為版本前推送說明文件」 有時也會有依附關係 (例如「我需要 才能使用最新版的電腦視覺資料庫來建構程式碼」)。 有時,您有程式碼集的另一部分有內部依附元件,且 有時候,會對其他團隊擁有的程式碼或資料,造成外部依賴 (貴機構或第三方皆可)。但無論如何 我必須先瞭解這點」,才能說是重複一次的。」 以及管理依附元件 是建構系統的基本工作

處理模組和依附元件

使用 Bazel 等構件型建構系統的專案會拆分成一組 這些模組透過 BUILD 表示依附元件彼此 檔案。妥善整理這些模組和依附元件 對建構系統效能以及需要的工作量 維護。

使用精細的模組和 1:1:1 規則

建構以構件為基礎的版本時,第一個想出的問題是 決定個別模組應包含多少功能。在 Bazel 中 模組是以指定可建構單元為目標, java_librarygo_binary。例如,整項專案 將一個 BUILD 檔案放在根目錄,然後 以遞迴方式記錄專案的所有來源檔案。另一端 不過,幾乎每個來源檔案都能加入各自的模組,有效 要求每個檔案都列在 BUILD 檔案中,並依附於其他所有依附的檔案。

大多數專案介於這些極端之間 在效能和可維護性之間取得平衡針對 這可能表示您完全不需要輕觸 BUILD 檔案, 但這意味著建構系統必須 建議您一律同時建構整個專案也就是說 平行處理或分配建構作業的各個部分 預先建構完成每個檔案一個模組則相反:建構系統 對建構在快取和排程步驟方面擁有最大的彈性,但 因此工程師必須投入更多心力維護依附元件清單 就會變更檔案參照

雖然確切的精細程度會因語言而異 ( 語言),Google 傾向偏好小規模的模組,而非可能使用的字詞 通常用於以任務為基礎的建構系統在實際工作環境中的一般實際工作環境二進位檔 Google 通常仰賴數萬個目標,甚至規模適中 在程式碼集內可以擁有數百個目標對於像 Java 內建堅強的包裝概念,每個目錄通常 包含一個套件、目標和 BUILD 檔案 (褲子、另一個建構系統) 並呼叫 1:1:1 規則)。包裝較弱的語言 慣例通常會為每個 BUILD 檔案定義多個目標。

小型建構目標帶來的優勢確實會開始大規模顯示 可以加快分散式建構作業的速度,並降低重新建構目標的頻率。 測試輸入圖片後,優點更是更吸引人。 目標越精細,建構系統就能變得更聰明 只執行一部分可能受特定指定影響的測試 變更。因為 Google 認為採用 我們投注心力改善 工具,自動管理 BUILD 檔案,避免開發人員感到負擔。

其中一些工具 (例如 buildifierbuildozer) 可透過 執行 Bazel 作業的 buildtools 目錄

最小化模組顯示設定

Bazel 和其他建構系統允許每個目標指定瀏覽權限。 屬性來決定其他目標依附於該屬性。私人目標 只能在自己的 BUILD 檔案中參照。設定目標可能會授權更廣泛的 查看明確定義的 BUILD 檔案清單目標,或 並將瀏覽權限設為「公開」至工作區中的每個目標

如同大部分的程式設計語言,一般來說,最好盡量減少能見度 一般來說,只有在下列情況中,Google 團隊才會將目標設為公開: 這些目標代表 Google 所有團隊都能使用的工具。 團隊要求其他人協調使用程式碼前, 維護客戶目標許可清單,以便監控客戶的成效。每項 團隊的內部實作目標只能在目錄內 由這個團隊所擁有,而大部分的 BUILD 個檔案只會有一個排除目標 私人。

管理依附元件

模組必須能夠互相參照。將 Pod 名稱 建構到精細模組中,藉此管理依附元件 這些模組之間 (不過工具可以協助自動化這項作業)。傳達這些 依附元件通常是由 BUILD 檔案中的大量內容組成。

內部依附元件

在大型專案中細分為精細模組,大部分的依附元件 可能是內部資料;也就是部署於另一個目標上 原始碼存放區內部依附元件與外部依附元件的 而不是以預先建構的構件下載 一併執行建構作業這也表示 內部依附元件—目標及其所有內部依附元件一直都是 以存放區的相同修訂版本/修訂版本為基礎建構而成您要解決的問題 謹慎處理內部依附元件,也就是 遞移依附元件 (圖 1)。假設目標 A 依附於目標 B, 這取決於通用的程式庫目標 C。目標 A 應該可以使用類別 目標 C 所定義?

遞移依附元件

圖 1. 遞移依附元件

如果對基礎工具有疑慮,這並沒有問題;兩者皆是 建構時,B 和 C 會連結至目標 A,也就是說, C 則稱為 A。Bazel 已提供這樣的技術多年,但隨著 Google 的發展, 開始出現問題假設 B 經過重構,使其不再出現 並依附於 C 語言如果 B 對 C 的依附元件遭到移除,則 A 和任何其他 透過 B 上的依附元件使用 C 的目標則會中斷。實際上,目標 依附元件成為公開合約的一部分 已變更。也就是說,Google 會持續累積依附元件

為瞭解決這個問題,Google 最終採用了「嚴格遞移性」 依附元件模式」在此模式下,Bazel 會偵測目標是否嘗試 沒有直接參照符號的符號,如果是的話,將無法使用 錯誤和殼層指令,可用來自動插入 依附元件將這項變更導入 Google 的整個程式碼集 重構每個數百萬個建構目標,明確列出 依附元件通常需要多年時間,但絕對值得。我們的版本 提高目標的非必要依附元件數量 工程師有權移除不必要的依附元件 破壞依賴這些目標。

與往常一樣,強制執行嚴格的遞移依附元件也需要取捨。成果 建立檔案較為精簡,因為現在必須將常用程式庫 不會被迫進來,而是工程師 需要花費更多心力在 BUILD 檔案中新增依附元件。我們 並開發出可自動偵測 並在沒有開發人員的情況下將依附元件新增至 BUILD 檔案 或緩解措施但就算沒有這類工具,我們仍認為權衡利弊 等同於程式碼集調度資源:在 BUILD 檔案中明確新增依附元件 是一次性費用,但處理隱含的遞移依附元件可能會導致 持續性問題,只要建構目標存在即可。Bazel 強制執行嚴格的遞移依附元件 安裝在 Java 程式碼中

外部依附元件

如果依附元件不是內部,則該依附元件必須是外部項目。外部依附元件 在建構系統外建構和儲存的構件上,產生相應的預測結果。 依附元件是從構件存放區直接匯入 (通常是存取 ),並依原樣使用,而非從原始碼建構。下列其中一項 外部和內部依附元件的最大差異 外部依附元件有版本,且這些版本獨立存在 即可。

自動與手動依附元件管理

建構系統可允許管理外部依附元件的版本 手動或自動如果是手動管理時,建構檔 明確列出要從構件存放區下載的版本 經常使用語意版本字串 使用 1.1.4。如果自動管理,來源檔案會指定 且建構系統會一律下載最新版本。適用對象 例如,Gradle 允許將依附元件版本宣告為「1.+」 可接受任何次要或修補程式版本,只要 主要版本為 1

自動代管的依附元件對於小型專案是便捷的,但 往往是一種災難因應做法, 目前是由多位工程師合作自動指出的問題 這表示您無法控管 YAML 檔案 已更新。目前沒有任何方法能確保外部方不會造成破壞 更新 (即使他們聲稱使用語意版本管理),因此建構 隔天工作可能會分崩離析 或復原為工作狀態即使建構作業並未中斷 可能是無法追蹤的細微行為或成效變化。

相對的,因為手動管理的依附元件需要變更來源 所以您可以輕鬆尋找、復原 查看舊版存放區,使用較舊的依附元件進行建構。 Bazel 規定所有依附元件的版本都必須手動指定。甚至 中等規模,則手動版本管理的負擔非常值得 確保穩定性

單一版本規則

不同的程式庫版本通常以不同的構件表示 理論上來說 無法在建構系統中以不同名稱宣告依附元件。 如此一來,每個目標就能選擇想要的依附元件版本 相關單位會如何運用資料,並讓他們覺得自己 獲得充分告知,且能夠針對該使用方式表示同意這麼做會造成許多實務問題,因此 Google 嚴格執行相關政策 單一版本規則 用於程式碼集中的所有第三方依附元件

要允許多個版本的最大問題在於鑽石依賴性 問題。假設目標 A 依附於目標 B 和外部 v1 資源庫。如果目標 B 之後遭到重構,以便在 V2 版本相同的 v2 中新增依附元件 目標 A 將會破壞,因為現在其隱含仰賴兩個 同一個程式庫的不同版本而且,新增 將新的依附元件從目標轉移到擁有多個版本的第三方程式庫 因為該目標的使用者 可能取決於 版本。遵循一版規則會導致無法發生衝突 (假設 目標新增一個對第三方程式庫的依附元件,以及任何現有的依附元件 全都位於相同版本,因此大家可以快樂共存。

遞移外部依附元件

處理外部依附元件的遞移依附元件可能包括 就特別困難Maven Central 等許多構件存放區 藉此指定雲端中其他構件特定版本的依附元件 存放區Maven 或 Gradle 等建構工具通常會以遞迴方式下載 預設遞移依附元件,意味著在 可能會導致大量下載構件 。

這種做法可說是非常方便:在新的程式庫中新增依附元件時 必須逐一追蹤程式庫的遞移依附元件 並手動新增也有一大缺點: 程式庫可以依附同一個第三方程式庫的不同版本, 策略一定違反一版規則,而且會導向鑽石級 依附元件問題如果您的目標仰賴使用 同一依附元件的不同版本,系統不會告訴您 get。這也表示更新外部依附元件 如果新版本開始提取,則整個程式碼集中發生不相關的失敗錯誤 某些依附元件的衝突版本

因此,Bazel 不會自動下載遞移依附元件。 可惜的是,這沒有萬事通 — Bazel 的替代方案是 列出存放區外部每一個外部的單一檔案 整個依附元件中使用的依附元件和明確版本 Cloud Storage 也提供目錄同步處理功能幸好,Bazel 提供的工具能夠自動 產生這類檔案,當中包含一組 Maven 的遞移依附元件 導致學習失真性這項工具可讓您執行一次,以產生初始 WORKSPACE 檔案 您就能手動更新該檔案來調整版本 每個依附元件的結構中

當然,這裡的選擇是便利性和擴充性之間的考量。S 號 因此,專案可能不需費心管理遞移依附元件 並可能利用自動遞移性推動 依附元件隨著機構規模變革,這項策略越來越缺乏吸引力 隨著程式碼集不斷擴增 衝突和非預期的結果也變得越來越豐富 。採用更大規模的架構時,手動管理依附元件的成本大不相同 低於自動依附元件導致的問題處理成本 以自動化做法管理成本

使用外部依附元件快取建構結果

外部依附元件通常是由第三方提供 穩定版的程式庫,可能不需要提供原始碼。只有部分通知 機構也可能會選擇使用自己的程式碼 可讓其他程式碼片段依附於第三方 比內部依附元件多出理論上來說,如果構件是構件,即可加快建構作業的執行速度 但下載速度較慢

然而,這也會產生許多負擔和複雜性:必須有人 建構這些構件並上傳至 構件存放區,以及用戶端必須 最新版本。此外,偵錯作業也更加困難 系統元件的運作原理是 且不再為原始碼樹狀結構提供一致的檢視。

想要解決長時間建構的構件問題 如前所述,使用支援遠端快取的建構系統。如此 建構系統會將每次建構作業產生的構件儲存至位置 因此,如果開發人員依賴 模型會自動下載 不必實際建構如此一來,您就能享有 同時確保建構作業 就像一律從相同來源建構一樣這是 而 Bazel 也可設為使用遠端指令 快取。

外部依附元件的安全性和穩定性

根據第三方來源的構件,可能會有風險。還有 第三方來源 (例如構件存放區) 出現可用性風險 如果無法下載,整個版本可能會停滯不前 外部依附元件可能會有安全風險:若第三方系統 完全被攻擊者入侵,攻擊者就可以 可加入自行設計的構件 融入建構作業這兩個問題都可以透過鏡像方式 取決於您可以控管及禁止建構系統存取的伺服器 第三方構件存放區,例如 Maven Central缺點就是 建構這類架構需要耗費大量心力和資源維護,因此選擇是否 使用時,資源往往會因專案規模而異。此外,安全性問題也 藉由要求每個 Pod 的雜湊值 來源存放區中指定的第三方構件導致建構作業 就會失敗。另一種完全的替代方案 而問題是廠商專案的依附元件。專案 容器會整合所有依附元件 和 專案原始碼或二進位檔。這實際上意味著 專案的所有外部依附元件都會轉換為內部依附元件 依附元件Google 內部會使用這種做法,檢查每個第三方 整個 Google 參照的程式庫,並編入根目錄的 third_party 目錄中 這個開放原始碼專案的主要工具不過,這個做法只適用於 Google 經過特別設計的原始碼控管系統 能處理極大型的單聲道存放區 部分機構可能無法選擇供應商。