最近公司在搞降本的事,出于好奇,查看了很多服務的Pod,資源實際使用率都是非常低的,但是資源申請又非常高,于是就開始思考一些問題,今天把我認為的常見問題分享下。
個人見解,如有不對,請糾正。
K8s環境下,unused committed memory 可以被共享嗎?
一般從JVM的Prometheus采集的metric里會看到有不同的memory定義,主要由Max Memory,Used Memory,Committed Memory。其中前兩個比較好理解,這里主要說下Committed Memory。
Java 文檔中對已提交內存的定義
represents the amount of memory (in bytes) that is guaranteed to be available for use by the Java virtual machine. The amount of committed memory may change over time (increase or decrease). The Java virtual machine may release memory to the system and committed could be less than init. committed will always be greater than or equal to used.
大意:用于保證JVM正常運行的可用內存。已提交的內存會隨時發生變化(升高或降低)。JVM會釋放已提交但未使用的內存給OS,同時已提交的內存可能會小于-Xms,但已提交的內存總是大于等于已使用的內存。
根據定義,基本可以知道committed memory屬于Java進程獨占的,不可以被其他進程使用,所以也就不存在共享。
但再使用Java 11 G1 時,常常會看到metric里committed memory 久久不能被釋放,盡管文檔說GC會嘗試方式committed memory。
查閱相關文檔,在Java 11 中G1對unused committed memory 釋放的條件,在一般情況下比較難以達到
條件
- Full GC;(G1的設計之初,就是想通過分散成多次小GC等盡量避免觸發Full GC)
- 執行concurrent cycle;
這情況在k8s下,其實更是不友好,因為這些unused committed memory依然會產生費用,導致資源使用率下降。
所以,日常我們選擇Pod型號時,在滿足應用正常啟動及運行中產生對象的情況下,多冗余部分(小于50%,視成本和性能而定)的內存。
K8s里的資源配置requests與limits
K8s里這兩個參數很關鍵,是Pod被快速schedule的關鍵因素之一。
requests:Pod(Container) 里進程啟動所需要的最小的資源配置,它是k8s承諾給到使用者的資源(CPU和Memory);
limits:Pod(Container) 里進程運行時可向OS申請的最大資源配置,它不是k8s承諾的資源,言外之意是被schedule到的node節點可能沒有limits的資源。
limits的這種設計,主要是考慮到pod在啟動時應該能夠被很容易的schedule,而不是因為node上的可用資源不夠,導致創建新的node階段,從而導致跟多的小塊資源未被使用。
在日常配置時,可以將requests配置成 JVM的 -Xms ,而limits配置成大于-Xmx的配比值,因為要保證非堆及其他的內存使用。
K8s環境下如何合理配置JVM heap?
根據上面requests和limits的說明,理論上 requests >= -Xms,而 limits > -Xmx ,可根據實際實際的調優結果,選擇更合適的資源配比。
配置合理的JVM
- 需要分析應用的類型
比如有些應用啟動時會加載大量的數據到內存中,那啟動就需要更多的內存,一般屬于內存型應用,該種應用應該配置更多的內存,以避免發生fault page,甚至是OOM;
比如有些應用運行時,會發生大量的運算,所以需要配置更合理CPU及內存,一般屬于CPU型應用,這種應用內存也不能太小; - 思考K8s下heap與資源配置
可以將-Xms 與-Xmx 配置相同,即requests = -Xms = -Xmx ,思路是
(1) 能夠保證Java應用在k8s應用上的安全運行,即不會因為內存不夠問題發生OOM;
(2)一般應用運行,會出現很快出現Committed Memory達到了-Xmx的情況,我們知道GC在向OS申請內存,會發生抖動,影響性能;
以上只是一些個人參考的思路,歡迎討論。
K8s環境下如何看待OOM?
K8s里核心的設計理念之一 —— 容錯性,就是說當發生意外后,Pod能夠被快速從中恢復。
也就是說,我們應該用新的視角來看到Pod的宕機等問題,而非像使用物理機,EC2等一樣,發生問題,可以快速重啟恢復,這是一種思路上的轉變。
但這不意味著,OOM就變得正常了,OOM對于Java應用來說,依然是比較關鍵的問題。一個應用經常發生OOM,可能是因為內存使用不對,或者程序內代碼有問題等等,我們依然需要去關注,去定位。但OOM在K8s下變得更不可控,也就是說概率變大了,因為limits無法保證,所以有時候,就需要重新思考這個問題。
K8s環境下是否適合使用大資源的pod?
是否可以使用大資源的Pod?當然沒有問題,但是否合適,可曾想過?
我有見過8CPU_32GB,10CPU_20GB的應用跑在k8s上,資源使用率實際上不是很高,幸好這樣的應用不會很多。
從K8s的schedule的角度考慮,我們應該使用小Pod,因為便于被schedule。
從服務拆分的角度考慮,當一個服務復雜到不得不用大資源運行時,應該可以考慮拆分了。
從應用運行的角度考慮,不是采用一個大資源的Pod處理所有請求,而是將大資源的Pod拆分成多個小資源Pod均衡運行。
Reference
https://openjdk.org/jeps/346
https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/