愛奇藝視訊生產Kubernetes叢集優化實踐:感知業務優先順序

陶然陶然發表於2022-06-07

  本文介紹愛奇藝針對視訊生產場景、在 Kubernetes(以下簡稱 K8s) 叢集優化方面的實踐:如何使高優先順序任務獲得更多的 CPU 資源,更快完成任務。

   01 背景

  視訊生產叢集所面臨的一個挑戰是 K8s 原生機制無法區分業務優先順序。視訊生產任務擁有不同的業務優先順序和資源需求,業務優先順序是根據視訊節目的重要程度及上線時間的緊迫程度進行區分的,重點節目相關的任務優先順序高,上線時間緊迫的任務優先順序高,其他節目的優先順序低。

  資源需求是從任務型別維度區分,如超清視訊任務需要 8 CPU,普通視訊任務需要 4 CPU。因此存在低優先順序任務和高優先順序任務申請相同數量 CPU 的情況,例如高優先順序超清視訊任務和低優先順序超清視訊任務都申請 8 CPU。

  K8s 原生機制並不感知業務優先順序,在上述情況下會給兩者分配同樣多的 CPU 資源。使用者期望高優先順序任務可以搶佔低優先順序任務的 CPU 資源,從而更快執行。

  業界也提出了類似於業務優先順序的概念,如文章 [1] 中提出的應用優先順序,但是並未給出具體的解決方案。本文介紹了一種允許高優先順序任務搶佔低優先順序任務 CPU 資源,加速高優先順序任務執行的方案。該方案不僅可以在提交任務時指定優先順序,還可以對執行過程中的任務進行優先順序調整,滿足視訊生產效率要求。

   02 原理概述

  為了給高優先順序任務提供更多的 CPU 資源,我們在叢集中每個物理節點上執行一個代理服務程式,該代理程式會區分節點上任務的優先順序,將高優先順序任務對應的 Cgroups cpu 子系統中控制 CPU 使用量的值調高,從而允許高優先順序任務可以搶佔低優先順序任務的 CPU 資源,使其更快地執行。這個方案依賴於 K8s 叢集中各個節點關閉 CPU 限流,從而使高優先順序任務可以自由搶佔低優先順序任務的 CPU 資源,不會受到 CPU 限流的影響。

   03 高優先順序任務 CPU 搶佔

  1、前提:關閉節點 CPU 限流

  在 K8s 中,任務以 Pod 形式存在,其 CPU 資源分配和使用受到 Pod 後設資料中兩個欄位控制:CPU Resource Requests 和 CPU Resource Limits,Pod 可使用的 CPU 總量介於兩者之間。CPU Resource Requests 為 Pod 提供了最少 CPU 保證,該欄位的值會寫入到 Pod 對應的 Cgroups cpu 子系統 cpu.shares 檔案中,通過 cpu.shares 保證 Pod 使用的資源量。CPU Resource Limits 為 Pod 提供 CPU 使用上限,它的值對應到 Cgroups cpu 子系統的 cpu.cfs_quota_us 和 cpu.cfs_period_us 檔案,具體對應關係請參考文章 [2]。

  為了解除 CPU Resource Limits 對 Pod 的 CPU 限流,我們在視訊生產 K8s 叢集中通過 kubelet 的配置項 '--cpu-cfs-quota=false' 關閉了 CPU CFS Quota,允許高優先順序 Pod 自由搶佔低優先順序 Pod 的 CPU 資源而不會受到限流。

  順便一提,關閉 CPU CFS Quota 之後,Pod 在需要時,可以超額使用節點上存空閒的 CPU,提升了叢集的 CPU 利用率和 Pod 的執行效率。

  2、整體設計

  該方案的名字為 cpu-share-syncer,基本原理章節中已經概述了其思想,整體設計如圖 1 所示:

  

  圖1 cpu-share-syncer 總體設計

  該方案的核心元件是 cpu-share-syncer 代理服務,它執行於 K8s 各個工作節點之上,並通過與 K8s master 節點之上的 kube-apiserver 程式通訊,獲取其所在節點上的 Pod 資訊,然後識別出節點上的高優先順序 Pod,將這些 Pod 對應的 Cgroups cpu 子系統中 cpu.shares 檔案值設定為使用者指定的較高的值,從而賦予其較高的 CPU 權重,允許這些 Pod 搶佔其他 Pod 的 CPU 資源。

  3、使用方式

  cpu-share-syncer 代理服務採用 K8s 中 DaemonSet 控制器的方式進行部署,使得每個 K8s 工作節點之上都執行了一個代理服務 Pod,這些 Pod 通過 hostpath 的方式掛載了宿主機 Cgroups 路徑以調整 Pod 對應的 cpu.shares 檔案。

  代理服務如何知道哪些 Pod 是高優先順序任務呢?這需要使用者在建立高優先順序 Pod 時,在 Pod 的註解欄位中新增一個表示較高 CPU 權重的 annotation:iqiyi.com/cpu-share,該註解值的型別為整數,在含義上與 CPU Resource Requests 類似,因此使用者可以根據 CPU Resource Requests 欄位的規則來設定 iqiyi.com/cpu-share 的值,只是 iqiyi.com/cpu-share 僅支援以一個 CPU 的 1/1024 為單位。例如,如果期望 Pod 在執行時可以使用 10 CPU 的資源,iqiyi.com/cpu-share 的值應該設定為10 * 1024 = 10240,這和 Cgroups 中對 CPU 的計量單位保持一致。對於已經執行的 Pod,使用者可以通過 patch 方式在 Pod 註解欄位中新增 iqiyi.com/cpu-share 註解改變任務的優先順序。

  cpu-share-syncer 代理服務檢測到 Pod 中存在這個 annotation,就會將其值寫入到 Pod 對應 cpu.shares 檔案中,達到使用者指定 Pod CPU 權重的目的。

  因此,對於設定了 iqiyi.com/cpu-share annotation 的 Pod,會按照 CPU Resource Requests 欄位的值進行排程,按照 iqiyi.com/cpu-share的值在節點上分配 CPU 資源。

  iqiyi.com/cpu-share 的值越大,Pod 對 CPU 的權重就越高,搶佔其他 Pod CPU 資源就會越多。對於高優先順序 Pod,使用者可以根據需要將 iqiyi.com/cpu-share 的值設定為較高的值,獲取更多的 CPU 資源。

  4、技術細節

  cpu-share-syncer 代理服務的詳細工作流程如圖 2 所示:

  

  圖2 cpu-share-syncer 詳細處理流程

  cpu-share-syncer 代理是一個長生命週期的任務,它會週期性地將使用者通過 Pod annotation 指定的 CPU 權重寫入到 Pod 對應的 cpu.shares 檔案中,週期性執行的原因是防止 Pod 由於某種原因重啟之後,使用者指定的 CPU 權重丟失。週期可以由使用者進行配置,預設與 kubelet 程式中 CPUManager 同步週期(預設 10 秒)保持一致。需要注意的是,kubelet 中 CPUManager 會週期性的同步 Pod 中容器對應的 cpu.shares 檔案,該操作僅會影響 Pod 內容器之間的相對優先順序,詳細資訊可參考 [3]。而 cpu-share-syncer 代理同步的是 Pod 自身對應的 cpu.shares 檔案,該同步工作會改變整個 Pod 相對於其他 Pod 的優先順序。兩個同步操作互不影響。

  cpu-share-syncer 代理會從 master上的 kube-apiserver 程式獲取其所在節點上的 Pod 資訊,為了避免每次全量從 kube-apiserver 獲取節點上所有 Pod 資訊,減輕 kube-apiserver 的負擔,使用 List-Watch 的方式獲取。

  在獲取節點上所有 Pod 之後,cpu-share-syncer 代理會對這些 Pod 進行遍歷,並從 Pod 註解中取出 iqiyi.com/cpu-share 對應的值。然後根據 Pod UID 找到其對應的 cpu.shares 檔案,將獲取的值寫入到這個檔案中。對於使用者未指定優先順序的 Pod,cpu-share-syncer 不進行處理,這些 Pod 對應的 cpu.shares 值仍為 CPU Resource Requests 對應的值,例如 1 核的 CPU 申請對應的 cpu.shares 仍為 1024,沒有被改動。這樣高優先順序 Pod 在節點上就被賦予更高的 CPU 權重,因而可以搶佔其他 Pod 的 CPU 資源,從而更快地執行。

   04 cpu-share-syncer 測試結果

  為了驗證 cpu-share-syncer 的效果及其帶來的收益,我們進行了單機功能驗證和線上環境灰度測試。我們在 K8s 叢集中的一個節點上,執行了兩個 Pod,將它們的 CPU Resource Requests 值都設定為 10,並分別設定其 iqiyi.com/cpu-share 的值為 8192 及 16384,兩個 Pod 中執行相同的壓力測試命令 'stress --cpu 40',表示每個 Pod 啟動 40 個執行緒對 CPU 效能進行壓力測試。使用 prometheus 收集相關指標,測試結果如下圖所示:

  

  圖3 兩個 Pod 對比驗證

  圖中橫軸表示時鐘時間,縱軸表示 Pod 執行所佔用的 CPU 總時間,單位為秒,藍色折線表示權重為 16384 的 Pod 執行所佔用的 CPU 時間,黃色折線表示權重為 8192 的 Pod 執行所佔用的 CPU 時間,可觀察到這兩個 Pod 所耗費的 CPU 時間比例大約為 2:1,說明Pod CPU 權重已經生效,圖中由於兩個 Pod 啟動先後順序問題,存在一定的誤差。

  隨後在相同的節點上執行 3 個 CPU Resource Requests 為 10 CPU 的 Pod,不指定 CPU 權重,在執行一段時間後,再次啟動一個 CPU Resource Requests 為 10 CPU 的 Pod,並指定其權重值為 262144 (表示極高的優先順序,這是 cpu.shares 允許的最大值,參考 [4] ),在這四個 Pod 中執行的命令同樣為 'stress --cpu 40',測試結果如下圖所示:

  

  圖4 高優先順序 Pod 搶佔 CPU 時間

  圖中座標軸含義與上圖一致,黃綠藍三條折線表示先啟動的三個 Pod,紅色折線表示後啟動的一個高優先順序的 Pod。可以明顯觀察到,高優先順序的 Pod 啟動後,它所佔用的 CPU 總時間大幅度高速增長,而早期啟動的三個 Pod 由於受到高優先順序 Pod 的擠壓,所佔用的 CPU 總時間幾乎保持不變,說明高優先順序任務搶佔了早期 Pod 的 CPU 資源。

  隨後我們在一個線上 K8s 叢集中,進行了灰度測試,對 50% 的常規視訊生產任務指定了 CPU 權重 262144,另外 50% 生產任務未指定 CPU 權重,並觀察其兩組任務的平均執行效率,得到如下所示的結果:

  

  圖5 生產環境灰度驗證

  圖中橫軸為時鐘時間,縱軸為任務執行耗時比(執行時間/介質大小),耗時比越小效率越高,綠色折線表示未指定 CPU 權重的任務,藍色折線表示指定了高 CPU 權重的任務。發現相較於未新增 CPU 權重的任務,高 CPU 權重的 Pod 執行效率提升了約 16%,實現了對高優先順序任務加速的目的。

  需要注意的是,從 CPU 權重(cpu.shares)比例來看,當高優先順序任務存在且持續忙碌時,低優先順序任務僅能獲得非常少的 CPU 資源,參考 [5],效率提升會遠不止 16%。經過分析,我們發現灰度環境中的高優先順序視訊生產任務即使在完全放開 CPU 限流時,也不會吃掉機器上所有的 CPU 資源,它只佔用自己所需的 CPU 資源。因此,灰度環境中的高優先順序任務只是搶佔了一部分低優先順序任務的 CPU 資源滿足自己的需求。另外,視訊生產任務在整個生命週期中,需要經過從遠端伺服器下載原始資料、叢集中處理、將結果上傳到遠端伺服器三個過程。在下載、上傳資料等待 IO 的過程中,高優先順序任務不需要佔用 CPU 資源,此時,低優先順序任務可以再次獲得一部分 CPU 資源。綜合上面兩部分原因,我們認為高優先順序任務執行效率提升 16% 是合理的。

   05 總結及展望

  我們通過 cpu-share-syncer 在關閉了 CPU CFS Quota 的 K8s 叢集中提供了高優先順序任務優先使用 CPU 的能力,實現了對高優先順序任務的加速。目前 cpu-share-syncer 已經廣泛執行在愛奇藝視訊生產叢集中,為高優先順序任務護航。

  cpu-share-syncer 目前僅執行在工作節點上,未在排程層面對高優先順序任務進行識別,可能會出現多個高優先順序任務排程到同一個節點,併發生資源競爭的情況,後續我們會繼續在排程層面進行優化,使高優先順序任務儘量均勻分佈在各個節點之上。

來自 “ 愛奇藝技術產品團隊 ”, 原文作者:技術產品團隊;原文連結:https://mp.weixin.qq.com/s/Sqge85YXiBcJIpIJ77ZdDQ,如有侵權,請聯絡管理員刪除。

相關文章