docker-compose部署微服務發(fā)生資源搶占的問題解決
2019-06-26
在阿里云172.16.2.221服務器上的微服務都不可用了,查看情況為,三個微服務一直在重啟,分別是ace-announcement,ace-notice和app-advertising。一直重啟也造成cpu使用率一直處于90%以上。查找原因,發(fā)現(xiàn)任意啟動兩個微服務都是可以的,只要同時運行三個微服務,就會造成全部微服務的重啟。臨時的解決辦法是將其中一個微服務轉(zhuǎn)移到222服務器上運行,先保證系統(tǒng)的正常運行。然后再查原因發(fā)現(xiàn),啟動兩個微服務后,系統(tǒng)的內(nèi)存僅剩1G多一點,當再啟動另外一個微服務時,就會因為內(nèi)存不足,而導致所有微服務都重啟的現(xiàn)象發(fā)生!
既然找到了原因,就想改如何解決這個問題。其中一個比較簡單的辦法就是升級服務器的內(nèi)存,順便也可以把cpu升級了。升級到8核32GB,大概需要2800元。但是升級服務器總得有個合適的理由吧,比如拿出一些數(shù)據(jù)來證明真的是服務器的資源不足導致的問題發(fā)生。但是從另外一個角度講,服務占用內(nèi)存的情況已經(jīng)是最優(yōu)的了嗎?是否還可以進行調(diào)整,使得內(nèi)存占用更小,且保證微服務的正常運行。
于是我的挑戰(zhàn)就來了,我想要證明,微服務已經(jīng)是最優(yōu)的了,并且是可控的。如果這樣還是不行,才能通過升級服務器配置的方法去解決問題。
我想通過調(diào)整微服務的運行內(nèi)存,來控制內(nèi)存的占用情況。一頓查啊,發(fā)現(xiàn)docker是可以通過加-m來限制內(nèi)存大小的,那么docker-compose啟動的容器是如何限制內(nèi)存的呢?發(fā)現(xiàn),version:3以上的版本是通過添加resources來限制cpu和mem資源的,但是前提條件是必須是在docker stack swarm下才能生效;docker-compose是自動忽略這個參數(shù)項的。
version:2中使用的是mem_limit來限制資源的使用,version:3不支持此配置項。
所以為了使用mem_limit,我將docker-compose.yaml中的version版本改為了2,再加上mem_limit: 500m,以為這樣就可以限制內(nèi)存的使用情況了。而實際情況是,內(nèi)存最大限制是生效了,但是因為限制了最大內(nèi)存,導致微服務的jvm虛擬機無法啟動,總是啟動到一半就自動退出,然后重啟···改成1000m,也還是一樣的情況,所以判斷是jvm啟動所需要的內(nèi)存大小要超過1000m,這就很雞肋了。我運行微服務本來是為了少占用一些系統(tǒng)資源,結(jié)果每個微服務都會占用約1.7GB的內(nèi)存,多個微服務疊加在一起,可不就導致內(nèi)存占滿了嘛。而且跟開發(fā)人員溝通發(fā)現(xiàn),微服務本身不會占用那么大的內(nèi)存空間,應該是都被微服務依賴的jdk所占據(jù)了,并非是微服務應用本身。
所以調(diào)整了排查方向,想通過改變jdk基礎鏡像,來降低和控制jvm占用的內(nèi)存空間大小,經(jīng)過上網(wǎng)查找發(fā)現(xiàn),openjdk:8-jre-alpine是個不錯的選擇。于是下載了該jdk,然后替換到Dockerfile中,并且添加上了:
ENTRYPOINT ["java","-XX:+UnlockExperimentalVMOptions","-XX:+UseCGroupMemoryLimitForHeap","-jar","/root/ace-notice.jar"]
用來保證你的Java進程不會因為內(nèi)存問題被容器Kill。
這樣,配合在docker-compose.yaml中添加mem_limit: 500m,再啟動微服務,就可以正常啟動了,并且查看該微服務資源占用情況:

可以看到,內(nèi)存占用已經(jīng)被限制住了!?。?/p>
補充:
如果不更換jdk,繼續(xù)使用java:8-alpine,并且添加上了:
"-XX:+UnlockExperimentalVMOptions","-XX:+UseCGroupMemoryLimitForHeap"
則會在啟動微服務時報錯,提示該jdk版本不支持UseCGroupMemoryLimitForHeap:
Unrecognized VM option 'UseCGroupMemoryLimitForHeap'
Error: Could not create the Java Virtual
Machine.
不對內(nèi)存做限制,ace-notice對內(nèi)存占用的情況為1.66G:

此時服務器內(nèi)存剩余情況為:

對內(nèi)存做了限制之后:
執(zhí)行:docker?stats 容器ID

此時服務器內(nèi)存剩余情況為:

可以看到,對容器的啟動內(nèi)存做了限制之后,微服務對內(nèi)存的占用情況小了很多,這也是為什么沒有做內(nèi)存限制之前,服務器的內(nèi)存占用情況很大的原因。當內(nèi)存不足時,各微服務就會發(fā)生資源的爭搶,最終導致該臺服務器上的所有微服務一直在重啟(jvm)。所以,從根本上解決問題,還是限制內(nèi)存,避免發(fā)生資源爭搶的情況發(fā)生。當然,也可以升級服務器的內(nèi)存,從物理機制上解決資源不足的問題。
重新對所有微服務都進行了調(diào)整后,運行正常,資源占用明顯變小,服務器內(nèi)存剩余變多,這樣就不用升級服務器了,再運行其他應用都綽綽有余了。這才是解決問題的正確方式?。?!