Grab 的實時資料平台團隊 Coban 一直在 Kubernetes 上運作其流處理架構,如Plumbing atscale中詳細介紹的。我們還撰寫了另一篇關于垂直 Pod 自動擴充 (VPA) 以及使用它的好處的文章(擴充 Kafka 消費者)。
在本文中,我們将介紹 Kubernetes 上的 Go 應用程式遇到的性能瓶頸和其他問題。
背景
我們注意到某些管道上的 CPU 節流問題導緻消費滞後,這意味着資料生産和消費之間存在延遲。這是一個問題,因為資料在被使用後可能不再相關或準确。這導緻了錯誤的資料驅動結論、代價高昂的錯誤等等。
在調試此問題時,我們主要關注 SinktoS3 管道。它本質上用于将資料從 Kafka 主題接收到 AWS S3。根據您的要求,資料下沉主要用于存檔目的,也可用于分析目的。
調查
經過認真排查,我們發現主要問題有兩個:
- 資源節流
- VPA 問題
資源節流
我們重新設計了 SinktoS3 管道架構,以使用并行 goroutine(工作線程)同時執行 CPU 密集型操作。這提高了性能并大大減少了消費者延遲。
但高性能架構需要更密集的資源配置。正如擴充 kafka 消費者中提到的,VPA 有助于消除手動資源配置。是以,我們決定讓 SinktoS3 管道在 VPA 上運作,但這暴露了一系列新問題。
我們在具有并行 goroutine(勞工)的最高流量管道之一上測試了我們的假設。當管道在 VPA 上運作時,它嘗試通過慢慢地将2.5 個核心減少到 2.05 個核心,然後減少到 1.94 個核心來優化資源。
CPU 請求從 2.05 個核心下降到 1.94 個核心,因為在大約 1.7 個核心時可以看到最大性能。
從上圖可以看出,VPA 将 CPU 核心更改為少于 2 個後,CPU 使用率和性能顯着下降。管道最終有大量積壓需要清理,盡管 pod 上有資源(大約 1.94 個核心),但處理速度并沒有更快,反而顯着減慢,導緻節流。
從上圖中我們可以看到,在 VPA 将 CPU 限制縮小到每個 Pod 1.94 個核心後,每個 Pod 中的 CPU 使用率突然下降。
流生産率
可以看到,21:00時,CPU使用率達到最高80%。這個值在10:00到12:00之間下降到50%左右,這是我們連續的峰值生産率。
消費率較Day_Before大幅下降
消費者在等待消費的記錄和分鐘數方面存在滞後
在上圖中,我們将此資料與之前資料的趨勢進行了比較,其中紫色線表示前一天。我們注意到,與前一天相比,消費率大幅下降,導緻消費滞後。這種下降令人驚訝,因為我們沒有調整應用程式配置。唯一的改變是由 VPA 完成的,它将 CPU 請求和限制降低到少于 2 個核心。
為了恢複此更改,我們通過保留相同的應用程式設定但将最小 VPA 限制調整為 2 個核心來重新部署管道。這有助于防止 VPA 将 CPU 核心數降低到 2 個以下。通過這個簡單的更改,性能和 CPU 使用率幾乎立即得到改善。
CPU 使用率回升至約 95%
與 Day_Before 相比的管道消耗率
在上圖中,我們将資料與前一天的趨勢(以紫色表示)進行了比較,其中管道滞後并且有大量積壓。您可以看到,消耗率的提高甚至比前一天更好,并且應用程式消耗了更多的記錄。這是因為它正在彌補之前消費者滞後造成的積壓。
深入探究根本原因
僅将 CPU 配置設定從 1.94 增加到 2 個核心就能帶來如此顯着的改進,這是出乎意料的,因為我們AUTO-GOMAXPROCS 在 SPF 管道中啟用了該功能,并且這隻使用 CPU 的整數值。
經過進一步調查GOMAXPROCS,我們發現 當 kubernetes Cgroup 屏蔽了節點的實際 CPU 核心時,這對于控制 golang 在 kubernetes 節點上使用的 CPU 很有用。GOMAXPROCS 僅配置設定 Pod 請求的資源,是以正确配置該值有助于運作時預配置設定正确的 CPU 資源。
如果不配置GOMAXPROCS,go 運作時會假設節點的整個 CPU 容量都可用于其執行,當我們在 Kubernetes 上運作 Golang 應用程式時,這不是最優的。是以,正确配置非常重要GOMAXPROCS ,以便您的應用程式根據 CPU 資源預先配置設定正确數量的線程。更多詳細資訊可以在這篇文章中找到。
讓我們看看GOMAXPROCS下表中 Kubernetes 資源與值的關系:
庫伯内特資源 | GOMAXPROCS 值 | 評論 |
2.5核 | 2 | Go 運作時隻會有效地擷取和利用 2 個核心。 |
2核 | 2 | 如果工作負載需要,Go 運作時将有效地擷取并利用 pod 的最大 CPU。 |
1.5核 | 1 | AUTO-GOMAXPROCS 會将值設定為1 ,因為它将非整數CPU 值 向下舍入 為整數。是以,性能與擁有 1 核 CPU 的性能相同。 |
0.5芯 | 1 | AUTO-GOMAXPROCS 會将值設定為1 CPU,因為 GOMAXPROCS 的最小允許值為1。在這裡,我們将看到一些限制,因為 Kubernetes 隻會提供 0.5 個核心,但運作時會自行配置為 1 個核心,是以它将缺乏幾個 CPU 周期。 |
VPA 問題
垂直 Pod 自動縮放器使您 能夠輕松地垂直縮放 Pod,是以無需進行手動調整。它根據使用情況自動配置設定資源并允許适當的排程,以便每個 Pod 都有适當的可用資源。然而,在我們的案例中,節流和 CPU 饑餓問題是因為 VPA 将資源減少到少于 2 個核心。
為了更好地形象化這個問題,讓我們舉一個例子。假設此應用程式需要大約1.7 個核心 來執行其所有操作,而無需任何資源限制。讓我們看看這種情況下的 VPA 旅程是什麼樣的,以及它在哪裡無法正确擴充。
時間線 | VPA推薦 | CPU使用率 | AUTO-GOMAX程式 | 評論 |
T0 | 0.5芯 | >90% | 1 | 受到 Kubernetes Cgroup 的限制,因為它隻提供 0.5 個核心。 |
T1 | 1 核 | >90% | 1 | 由于應用程式的 GOMAXPROCS 設定保持不變,CPU 使用率仍将> 90%。事實上,它還需要更多。 |
T2 | 1.2核心 | <85% | 1 | 由于應用程式實際上需要更多資源,VPA 設定了一個非整數值,但 GOMAXPROCS 從未利用過該額外資源并繼續限制。現在,VPA 計算出 CPU 未得到充分利用,并且不會進一步擴充。 |
T3 | 2芯(手動超控) | 80-90% | 2 | 由于應用程式擁有足夠的資源,是以它将在沒有限制的情況下以最佳方式執行,并且具有最大吞吐量。 |
解決方案
在我們的調查過程中,我們發現它AUTO-GOMAXPROCS設定了一個整數值(最小值 1)。為了避免 CPU 限制,我們需要 VPA 在縮放時提出整數值。
在VPA v0.13中,此功能 可用,但僅适用于≥1.25 的Kubernetes 版本 - 請參閱下圖中的 #5313。
我們承認,如果我們為 Coban 的流處理管道定義預設的最小整數 CPU 值 1 個核心,那麼對于那些隻需要少于 1 個核心的管道來說,這可能會過多。是以,我們建議僅對 資源需求量大且需要 1 個以上核心的管道啟用此預設設定。
也就是說,您應該通過評估應用程式的需求來做出此決定。例如,一些 Coban 管道仍然在少于一個核心的 VPA 上運作,但它們沒有遇到任何延遲。正如我們之前提到的,在這種情況下 AUTO-GOMAXPROCS 将配置為 1,但它們仍然可以趕上消息生成速率。然而,從技術上講,這些管道實際上受到了限制,并且無法以最佳狀态運作,但這些管道沒有消費者延遲。
當我們從單一 goroutine 處理轉向并發 goroutine 處理時,我們需要更密集的 CPU 配置設定。在下表中,我們考慮了一些場景,其中有一些工作負載繁重的管道無法跟上生産率。
實際CPU需求 | VPA推薦(更新到v0.13後) | GOMAXPROCS 值 | 評論 |
0.8 | 1 核 | 1 | 該管道的最佳設定。它不應該滞後,并且應該通過并發 goroutine 最佳地利用 CPU 資源。 |
1.2 | 2 | 2 | 沒有 CPU 節流,也沒有延遲。但成本效益不是很高。 |
1.8 | 2 | 2 | 最佳性能,無延遲且具有成本效益。 |
經驗教訓/結論
從這次經曆中,我們學到了一些東西:
- 不正确的 GOMAXPROCS 配置可能會導緻嚴重的限制和 CPU 饑餓問題。
- 自動縮放解決方案很重要,但隻能帶您到目前為止。根據您的應用程式需求,可能仍然需要手動幹預以確定最佳性能。
作者: Shubham Badkur 和 Alvis Chew
出處:https://engineering.grab.com/performance-bottlenecks-go-apps