背景:
應用的部署結構是這樣的:使用rancher管理的Docker集群,有三台物理主機,二十多個Docker容器,
提供的功能是問題跟蹤(JIRA),文檔管理(Confluence),代碼托管(svn,gitlab),持續集成(jenkins,gitlab-ci + Docker),代碼質量管理(Sonar),構件管理(nexus3)和測試管理(TestLink)的功能.服務於1400多個研發人員
前端使用Apache來對后端的服務進行反向代理,同時Apache集成了CAS和LDAP 提供了單點登錄的功能
某天下午,用戶反饋,應用訪問的速度非常的慢,登錄的請求也無法響應了.
到Apache所在的主機上看資源的占用,,發現CPU占用率超過500%,apache沒有足夠的CPU資源來處理請求
通過在主機上ps這個CPU超過500%的進程,發現是一個java的進程,對應的是JIRA的容器
主機上顯示的進程號,和容器里邊的進程號是不一樣的
在容器里,JIRA對應的java進程是進程號為1的進程
進入JIRA容器
dump出來這個進程的所有的線程棧
jstack 1 > jirastack.log
查看這個進程中的占cpu最高的線程
top -Hp 1
得到的結果如下:
可以看到,有8個線程的CPU占用都超過了70左右,合計起來是 500%多
前邊的 49,53之類的是和線程棧里邊的線程的nxID對應的,只不過,nxID是用十六進制來表示的
我們把這些十進制的數字也變成十六進制
從dump出來的線程棧里邊找到了對應的線程:
從線程棧里邊搜索 nid=0x31,搜到了以下的線程
我們可以看到,這些都是垃圾回收的線程,垃圾回收的線程占據了所有的CPU的時間
查看JIRA的參數設置,發現了內存的設置較小 -Xms1024m -XmX2048m,使用的垃圾收集器是ParallelGC,因為主機的CPU有8個內核,所以就默認啟動了8個垃圾收集器的線程
系統的用戶大概有1.4K人,都是開發和測試人員來使用,對比另一個應用 confluence的內存配置(6G的堆大小,G1垃圾收集器),JIRA的堆的配置是有點小了,所以把JIRA的堆內存設置為4G(主機所在的內存還有很多沒有使用)然后針對增大的這個堆,啟用G1垃圾收集器,然后打印了垃圾收集的日志信息到一個日志文件里邊
配置如下:
JVM_MINIMUM_MEMORY 和 JVM_MAXIMUM_MEMORY的值都是4096m
然后,為了防止某個容器占用的資源過多,影響其他的容器(就是開頭的時候我們遇到的問題,JIRA占用的CPU過多,導致了apache無法響應請求,其他的應用都無法訪問了),所以,我們在rancher上對容器的資源做了一些限制
事后的反思
JIR的jvm參數設置不當和沒有對容器占用的主機的資源進行隔離這兩個原因共同導致了這個問題的發生
在使用容器作為生產環境的時候,要對自己在容器內所部署的應用的性能和配置參數等有足夠的了解,
對Docker容器所在的物理機的CPU和內存等資源所能支撐的容器的數量進行合理的預估,對容器所占用的物理機的資源進行限制,防止容器因配置不當占滿物理機的資源,影響其他容器