java 程序员 nginx 云计算 Python mysql shell 编程 Windows Firefox linux centos 开源 Ubuntu google wordpress Android php apache 微软

Docker應用問題診斷與性能調優指南(一)- 先談談容器化性能調優

性能調優是一個老生常談的話題,通常情況下,一個應用在上線之前會進行容量規劃、壓力測試並進行驗證,而性能調優則是在容量規劃與驗證結果之間出現差異時會進行的必然手段。從某種角度來講,性能調優是一個非常需要經驗的領域,需要調優人員對應用的架構、調用的鏈路、使用的語言、操作系統的差異、內核的參數表現等等都有完整的了解。大部分情況下,系統性能調優都是通過各種各樣的工具監聽、跟蹤、分析、檢測來檢查解決的。所以通常情況下,性能調優的老手都有一套自己的診斷工具集以及相應的診斷方式。但是當性能調優遇到Docker的時候,很多事情發生了轉變甚至惡化。

經常有客戶抱怨說應用進入Docker後,應用的QPS無法和ECS進行媲美,並且時常會出現DNS查詢超時、短連接TIME_OUT、網絡丟包等等,這極大打擊了客戶對使用容器技術的信心與決心。誠然,容器化的方式根據網絡模型的不同,會在傳輸效率上面略有差異,但遠遠達不到客戶反饋的性能損耗,而在容器中進行調優與診斷的效果因為安裝工具的復雜度大大折扣。

那麽到底該如何針對容器的場景進行調優呢?

性能調優的“望聞問切”

在討論容器化場景的性能調優之前,想先和大家談一下性能調優中的“望聞問切”的問題。對於性能問題,大部分人第一個想到的是CPU利用率高,但是得到CPU利用率高這個現象後,我們改如何解決呢?從某種意義來講,這個只是現象,並不是癥狀。為了方便大家理解,在此我打一個比方:感冒的時候我們去醫院看病,病人跟大夫描述的是現象,包括頭部發熱、流鼻涕等等;而大夫通過探查、化驗,得到的醫學癥狀是病人的白細胞較多,喉嚨有紅腫等等,然後大夫確診是細菌性感冒,給你開了999感冒靈。診斷病情的過程和性能調優是一樣的,也需要找到現象、癥狀和解法。我們回到剛才CPU利用率高的例子:我們已知的現象是CPU的利用率高,然後我們通過strace進行檢查,發現futex_wait系統調用占用了80%的CPU時間,而這才是真正的癥狀,根據這個癥狀,我們業務邏輯代碼降低了線程切換,CPU利用率降低。

大部分的性能調優問題都可以通過發現現象、探測癥狀、解決問題這三個步驟來進行,而這在容器的性能調優中就更為重要的,因為在主機的性能調優過程中,我們有很多的經驗可以快速找到癥狀,但是在容器的場景中,很多客戶只能告訴我們的是現象。從某種角度來講是由於客戶並不了解使用的容器引擎的工作原理以及容器化架構的實現方式。那麽接下來我們來看下容器化場景中性能調優面對的挑戰。

容器化性能調優的難點

VM級別的調優方式在容器中實現難度較大 
在VM級別我們看到的即是所有,網絡棧是完整暴漏在我們面前的,CPU、內存、磁盤等也是完全沒有限制的。性能調優老司機的工具箱安個遍,診斷流程走一趟基本問題就查個八九不離十了,但是在容器中,很多時候,都是默認不自帶診斷、調優工具的,很多時候連ping或者telnet等等基礎命令都沒有,這導致大部分情況下我們需要以黑盒的方式看待一個容器,所有的癥狀只能從VM層的鏈路來看。但是我們知道容器通過namespace的隔離,具備完整網絡棧,CPU、內存等通過隔離,只能使用limit的資源,如果將容器當做黑盒會導致很多時候問題癥狀難以快速發現。排查問題的方式變難了。
容器化後應用的鏈路邊長導致排查問題成本變大 
容器的場景帶來很多酷炫的功能和技術,比如故障自動恢復,彈性伸縮,跨主機調度等等,但是這一切的代價是需要依賴容器化的架構,比如Kubernetes網絡中需要FullNat的方式完成兩層網絡的轉發等等,這會給排查問題帶來更復雜的問題,當你不清楚編排引擎的架構實現原理的時候,很難將問題指向這些平時不會遇到的場景。例如上面這個例子中,FullNat的好處是降低了網絡整體方案的復雜性,但是也引入了一些NAT場景下的常見問題,比如短連接場景中的SNAT五元組重合導致包重傳的問題等等。排查問題的方位變大了。
不完整隔離帶來的調優復雜性 
容器技術本質是一種虛擬化技術,提到虛擬化技術就離不開隔離性,雖然我們平時並不需要去考慮隔離的安全性問題,但是當遇到性能調優的時候,我們發現內核的共享使我們不得不面對的是一個更復雜的場景。舉個,由於內核的共享, 系統的proc是以只讀的方式進行掛載的,這就意味著系統內核參數的調整會帶來的宿主機級別的變更。在性能調優領域經常有人提到C10K或者C100K等等類似的問題,這些問題難免涉及到內核參數的調整,但是越特定的場景調優的參數越不同,有時會有彼之蜜糖,我之毒藥的效果。因此同一個節點上的不同容器會出現非常離奇的現象。
不同語言對cgroup的支持 
這個問題其實大多數場景下我們是不去考慮的,但是在此我們把他列在第四位的原因是期望能夠引起大家的重視。一次在和Oracel JAVA基礎庫的負責同學聊天中了解到Java針對與Cgroup的場景做了大量的優化,而且時至今日,在Java的標準庫中對於Cgroup的支持還是不完全的,好在這點在大多數的場景中是沒有任何影響,也就不過多的討論。排查問題的腦洞更大了。
網絡方案不同帶來的特定場景的先天缺欠 
提到容器架構我們逃不掉的話題是網絡、存儲和調度,網絡往往是一個容器架構好壞的最根本的評判標準,不同的網絡方案也會有不同的實現方式與問題。比如在阿裏雲的Kubernetes中我們使用了Flannel的CNI插件實現的網絡方案,標準Flannel支持的Vxlan的網絡方案,Docker的Overlay的macVlan,ipvlan的方案等等。這些不同的網絡方案無一例外都是分布式的網絡方案而存儲的數據都會存放在一個中心存儲中,因此越大型的集群對網絡中心存儲的壓力也就越大,出錯的可能性就越大。此外跨宿主機的二層網絡很多都會通過一些封包解包的方式來進行數據傳輸,這種方式難免會增加額外的系能損耗,這是一種先天的缺欠,並不是調優能夠解決的問題。有的時候排查出問題也只能繞過而不是調優。
鏡像化的系統環境、語言版本的差異 
應用容器化是一個需要特別值得註意的問題,很多公司其實並沒有嚴格的配管流程,比如系統依賴的內核版本、語言的小版本等等,進行應用容器化很多時候都是選擇一個大概的版本,這會帶來很多語言層級的BUG,比如PHP7.0中PHP-fpm的詭異行為,而這個現象在客戶原本的5.6版本上已經修復過了。環境的問題本是一個嚴肅的問題,需要嚴格個管控,但是當遇到了容器,很多時候我們會分不清哪些不經意的行為會帶來嚴重的問題。警惕性因為容器鏡像能夠正常啟動而降低。

最後

這篇文章中我們主要討論了基礎的性能調優的方式以及容器化場景中性能調優的難點,在下篇文章中我們會來套路下不同的性能瓶頸現象對應的診斷和調優方法。

延伸阅读

    评论