werf中容器鏡像“智能”清理的問題及解決方案

werf中容器鏡像“智能”清理的問題及解決方案

本文討論了在交付給 Kubernetes 的雲端原生應用程式的現代 CI/CD 管道的現實中清理容器註冊表(Docker 註冊表及其類似物)中累積的映像的問題。 給出了影像相關性的主要標準以及由此產生的自動化清潔、節省空間和滿足團隊需求的困難。 最後,我們將使用特定開源專案的範例來告訴您如何克服這些困難。

介紹

容器註冊表中的鏡像數量可能會快速成長,佔用更多的儲存空間,從而顯著增加其成本。 為了控制、限製或維持註冊表中所佔用空間的可接受的成長,接受:

  1. 對影像使用固定數量的標籤;
  2. 以某種方式清理圖像。


第一個限制有時對小團隊來說是可以接受的。 如果開發者有足夠的永久標籤(latest, main, test, boris 等等),註冊表的大小不會膨脹,很長一段時間你根本不需要考慮清理它。 畢竟,所有不相關的圖像都被刪除,並且根本沒有剩餘的清理工作(一切都是由常規垃圾收集器完成的)。

然而,這種方法極大地限制了開發,並且很少適用於現代 CI/CD 專案。 開發的一個組成部分是 自動化,它允許您更快地測試、部署並向用戶交付新功能。 例如,在我們所有的專案中,每次提交都會自動建立一個 CI 管道。 在其中,映像被組裝、測試,並部署到各種 Kubernetes 電路以進行調試和剩餘檢查,如果一切順利,更改將到達最終用戶。 這不再是複雜的科學,而是許多人每天都會遇到的事情——很可能對你來說,因為你正在閱讀這篇文章。

由於修復錯誤和開發新功能是並行的,並且一天可以進行多次發布,因此顯然開發過程伴隨著大量的提交,這意味著 註冊表中存在大量圖像。 因此,出現了組織有效清理註冊表的問題,即刪除不相關的影像。

但如何確定影像是否相關呢?

影像相關性的標準

在絕大多數情況下,主要標準是:

1. 第一個(最明顯也是最關鍵的)是影像 目前在 Kubernetes 中使用。 刪除這些映像可能會導致大量的生產停機成本(例如,複製可能需要這些映像)或抵消團隊在任何循環上調試的努力。 (為此我們還特別做了一個 普羅米修斯出口商,它追蹤任何 Kubernetes 叢集中是否存在此類鏡像。)

2. 第二(不太明顯,但也非常重要,並且再次與剝削有關)- 圖像 如果發現嚴重問題則需要回滾 在目前版本中。 例如,對於 Helm,這些是在發布的保存版本中使用的圖像。 (順便說一句,預設情況下,Helm 的限制是 256 個修訂,但不太可能有人真正需要保存 大量版本?..)畢竟,我們特別儲存版本,以便以後可以使用它們,即如有必要,「回滾」給他們。

3. 第三—— 開發者需求:與他們當前工作相關的所有圖像。 例如,如果我們正在考慮 PR,那麼保留與最後一次提交(例如前一次提交)相對應的圖像是有意義的:這樣開發人員可以快速返回到任何任務並使用最新的更改。

4. 第四——圖像 對應我們應用程式的版本, IE。 最終產品:v1.0.0、20.04.01/XNUMX/XNUMX、sierra 等。

注意:這裡定義的標準是根據與來自不同公司的數十個開發團隊的互動經驗制定的。 然而,當然,根據開發過程的具體情況和所使用的基礎設施(例如不使用 Kubernetes),這些標準可能會有所不同。

資格和現有解決方案

通常,具有容器註冊表的流行服務會提供自己的映像清理策略:您可以在其中定義從註冊表中刪除標籤的條件。 然而,這些條件受到名稱、建立時間和標籤數量*等參數的限制。

* 取決於特定的容器註冊表實作。 我們考慮了以下解決方案的可能性:Azure CR、Docker Hub、ECR、GCR、GitHub Packages、GitLab ContainerRegistry、HarborRegistry、JFrog Artifactory、Quay.io - 截至 2020 年 XNUMX 月。

這組參數足以滿足第四個標準──也就是選擇與版本對應的影像。 然而,對於所有其他標準,人們必須選擇某種折衷的解決方案(更嚴厲或相反更寬鬆的政策)——這取決於期望和財務能力。

例如,第三個標準 - 與開發人員的需求相關 - 可以透過在團隊內組織流程來解決:影像的特定命名、維護特殊的允許清單和內部協定。 但最終它仍然需要自動化。 如果現成的解決方案的功能不夠,您就必須自己做一些事情。

前兩個標準的情況類似:如果不從外部系統(部署應用程式的系統(在我們的範例中為 Kubernetes))接收數據,就無法滿足它們。

Git 中的工作流程圖示

假設您正在 Git 中進行類似的工作:

werf中容器鏡像“智能”清理的問題及解決方案

圖中帶頭的圖示表示目前部署在 Kubernetes 中供任何使用者(最終使用者、測試人員、管理人員等)使用或由開發人員用於調試和類似目的的容器鏡像。

如果清理策略只允許保留映像(而不是刪除),會發生什麼 透過給定的標籤名稱?

werf中容器鏡像“智能”清理的問題及解決方案

顯然,這樣的場景不會讓任何人感到高興。

如果政策允許圖片不被刪除,會發生什麼變化? 根據給定的時間間隔/最後提交的次數?

werf中容器鏡像“智能”清理的問題及解決方案

結果已經好很多了,但還遠遠不夠理想。 畢竟,我們仍然有開發人員需要註冊表中的映像(甚至部署在 K8s 中)來調試錯誤...

總結目前的市場狀況:容器註冊中心提供的功能在清理時沒有提供足夠的彈性,主要原因是 無法與外界互動。 事實證明,需要這種靈活性的團隊被迫使用 Docker 註冊表 API(或相應實現的本機 API)「從外部」獨立實現映像刪除。

然而,我們正在尋找一種通用的解決方案,可以使用不同的註冊表為不同的團隊自動進行圖像清理...

我們的通用影像清潔之路

這種需求從何而來? 事實上,我們不是一個獨立的開發人員群體,而是一個同時為許多人提供服務的團隊,幫助全面解決 CI/CD 問題。 主要的技術工具是開源實用程序 韋爾夫。 它的獨特之處在於它不執行單一功能,而是伴隨著從組裝到部署的所有階段的持續交付流程。

將映像發佈到註冊表*(在建立映像後立即)是此類實用程式的明顯功能。 由於圖像被放置在那裡進行存儲,那麼 - 如果您的存儲不是無限的 - 您需要負責它們的後續清理。 我們將進一步討論我們如何在這方面取得成功並滿足所有指定標準。

* 儘管註冊表本身可能不同(Docker 註冊表、GitLab 容器註冊表、Harbor 等),但它們的用戶面臨相同的問題。 我們案例中的通用解決方案不依賴註冊表的實現,因為在註冊表本身之外運行,並為每個人提供相同的行為。

儘管我們使用 werf 作為範例實現,但我們希望所使用的方法對面臨類似困難的其他團隊有用。

於是我們就開始忙碌起來 外部的 實作清理影像的機制 - 而不是那些已經內建在容器註冊表中的功能。 第一步是使用 Docker 註冊表 API 為標籤數量及其建立時間建立相同的原始策略(如上所述)。 添加到他們 基於已部署基礎架構中使用的映像的允許列表, IE。 庫伯內斯。 對於後者,使用 Kubernetes API 迭代所有已部署的資源並取得值清單就足夠了 image.

這個簡單的解決方案解決了最關鍵的問題(標準 1),但這只是我們改進清潔機制之旅的開始。 下一步 - 也是更有趣的 - 一步是決定 將發布的圖像與 Git 歷史記錄關聯起來.

標記方案

首先,我們選擇了一種方法,其中最終圖像應儲存清潔所需的信息,並在標記方案上建置流程。 發布圖像時,使用者選擇了特定的標記選項(git-branch, git-commitgit-tag)並使用相應的值。 在 CI 系統中,這些值是根據環境變數自動設定的。 實際上 最終影像與特定的 Git 原語相關聯,在標籤中儲存清潔所需的資料。

這種方法產生了一組允許 Git 作為單一事實來源的策略:

  • 當在 Git 中刪除分支/標籤時,登錄中關聯的鏡像會自動刪除。
  • 與 Git 標籤和提交關聯的圖像數量可以透過所選模式中使用的標籤數量以及建立關聯提交的時間來控制。

總的來說,最終的實施滿足了我們的需求,但新的挑戰很快就等著我們。 事實是,在使用基於 Git 原語的標記方案時,我們遇到了許多缺點。 (由於他們的描述超出了本文的範圍,大家可以自行熟悉細節 這裡.) 因此,在決定改用更有效的標記方法(基於內容的標記)後,我們不得不重新考慮影像清理的實作。

新演算法

為什麼? 透過基於內容的標記,每個標記都可以滿足 Git 中的多次提交。 清理圖像時,您不能再假設 來自將新標籤新增至註冊表的提交。

對於新的清潔演算法,決定放棄標記方案並構建 元影像處理,每個存儲一堆:

  • 執行發布的提交(在容器註冊表中是否添加、更改映像或保持不變並不重要);
  • 以及與組裝影像相對應的內部識別碼。

換句話說,它提供了 將發布的標籤與 Git 中的提交連結起來.

最終配置和通用演算法

配置清理時,使用者現在可以存取選擇目前映像的策略。 每個此類策略的定義如下:

  • 許多參考文獻,即掃描期間使用的 Git 標籤或 Git 分支;
  • 以及集合中每個參考的搜尋圖像限制。

為了說明這一點,預設策略配置如下所示:

cleanup:
  keepPolicies:
  - references:
      tag: /.*/
      limit:
        last: 10
  - references:
      branch: /.*/
      limit:
        last: 10
        in: 168h
        operator: And
    imagesPerReference:
      last: 2
      in: 168h
      operator: And
  - references:  
      branch: /^(main|staging|production)$/
    imagesPerReference:
      last: 10

此配置包含三個符合下列規則的策略:

  1. 儲存最後 10 個 Git 標籤的圖像(按標籤建立日期)。
  2. 對於上周有活動的不超過 2 個帖子,保存上週發布的圖像不超過 10 張。
  3. 儲存 10 張樹枝圖像 main, staging и production.

最終的算法可歸結為以下步驟:

  • 從容器註冊表中檢索清單。
  • 排除 Kubernetes 中使用的鏡像,因為我們已經透過輪詢 K8s API 預先選擇了它們。
  • 掃描Git歷史記錄並根據指定策略排除影像。
  • 刪除剩餘影像。

回到我們的插圖,這就是 werf 發生的情況:

werf中容器鏡像“智能”清理的問題及解決方案

然而,即使您不使用 werf,類似的高級圖像清理方法 - 在一種實現或另一種實現中(根據圖像標記的首選方法) - 也可以應用於其他系統/實用程式。 要做到這一點,記住出現的問題並在堆疊中找到那些機會就足夠了,這些機會可以讓您盡可能順利地整合他們的解決方案。 我們希望我們所走過的道路能幫助您以新的細節和想法來看待您的特定案例。

結論

  • 大多數團隊遲早都會遇到註冊表溢出的問題。
  • 在尋找解決方案時,首先需要確定影像相關性的標準。
  • 流行的容器註冊表服務提供的工具可讓您組織非常簡單的清理,而無需考慮「外部世界」:Kubernetes 中使用的映像以及團隊工作流程的特殊性。
  • 靈活且高效的演算法必須了解 CI/CD 流程,並且不僅要操作 Docker 映像資料。

聚苯乙烯

另請閱讀我們的博客:

來源: www.habr.com

添加評論