服務(wù) CPU 或 內(nèi)存偶爾飆高是部署環(huán)境中經(jīng)常遇到的問題握联,一般會采用記錄日志的方式來診斷洞翩,不過有些情況靠日志可能并不能分析出個所以然,面對實在無頭緒的問題也只能暫時使用重啟大法先恢復(fù)。
為了盡可能精準的定位問題首懈,掌握通過 dump 分析服務(wù)運行堆棧信息也是非常必要的,本文將分別介紹如何對 .NET Core 2.2 和 .NET Core 3.1 項目進行 dump 分析(這里只針對 Linux 下使用容器部署的方式)谨敛。
創(chuàng)建 dump 文件
在創(chuàng)建 dump 文件之前究履,最好先查看具體是服務(wù)中哪些線程引發(fā)的異常,然后針對特定線程進行分析脸狸,不然全掃一遍將是一件非常耗時的工作最仑。
進入容器后,安裝 htop:
apt-get update
apt-get install htop
通過 htop 查看資源使用情況:
以上是測試程序模擬的狀況炊甲,可知 PID 12 是需要關(guān)注的線程
執(zhí)行以下命令即可創(chuàng)建 dump 文件(這里以 2.2.8 為例泥彤,另外可通過 createdump --help
查看更多參數(shù)設(shè)置,容器內(nèi)默認 dotnet 進程對應(yīng) pid 均為 1):
/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/createdump 1
命令執(zhí)行完成后卿啡,將生成 dump 文件 /tmp/coredump.1
吟吝,我們需要通過 docker cp
或 kubectl cp
將 coredump.1
文件復(fù)制到主機目錄下,然后下載到用于 dump 分析的機器上颈娜。
注意:在 Docker 部署模式下剑逃,createdump
命令執(zhí)行需要有容器特權(quán),所以在容器啟動時需要加 --privileged = true
參數(shù)官辽。另外 dump 文件生成需要使用較大內(nèi)存蛹磺,需適當調(diào)整容器內(nèi)存限制參數(shù)。
.NET Core 2.2
目前大多使用 lldb 進行分析同仆,但從零開始搭建環(huán)境實在有些折騰萤捆,不推薦。網(wǎng)上已有封裝好的鏡像可直接使用,如:6opuc/lldb-netcore 俗或,6opuc/lldb-netcore
默認是基于 .NET Core SDK 2.2.8 構(gòu)建的鏡像市怎,如果當前要 dump 的服務(wù) .NET Core 版本非 2.2.8,則需要修改 lldb-netcore 源碼 重新構(gòu)建鏡像蕴侣。
執(zhí)行以下命令進入 lldb:
docker run --rm -it -v /root/coredump.1:/tmp/coredump 6opuc/lldb-netcore
查看當時運行的線程:
clrthreads -live
指定需要分析的線程編號(PID 12 的線程對應(yīng)的 16 進制為 c焰轻,所以找到 OSID 為 c 的記錄,對應(yīng)編號為 7【第一列】)
thread select 7
查看當前線程在托管代碼中的堆棧信息
clrstack
更多命令可通過執(zhí)行 soshelp 查看
.NET Core 3.1
.NET Core 3 開始昆雀,官方已提供 dotnet-dump 工具進行 dump 分析辱志,使用起來也相對簡單,當然我們依然可以繼續(xù)使用 lldb 的方式狞膘。
安裝 dotnet-dump
dotnet tool install --global dotnet-dump --version 3.1.141901
進入分析
dotnet-dump analyze /root/coredump.1
如果出現(xiàn)以下錯誤揩懒,說明 .NET Core SDK 沒有安裝到 /usr/shard/dotnet
路徑下,可通過 DOTNET_ROOT 單獨指定或重新安裝挽封。
查看正在運行的托管線程:
clrthreads
如果出現(xiàn)以下錯誤已球,是因為當前安裝的 .NET Core SDK 版本與容器內(nèi) createdump 使用的 SDK 版本不一致(如:createdump 使用 3.1.3,分析使用 3.1.12)辅愿。
也可根據(jù)輸出鏈接前往查看更多解決方案 https://go.microsoft.com/fwlink/?linkid=2135652
方式1:
dotnet-dump analyze /root/coredump.1 -c "setsymbolserver -ms"
方式2:
dotnet-dump analyze /root/coredump.1
setclrpath /usr/share/dotnet/shared/Microsoft.NETCore.App/3.1.21
指定當前需要分析的線程 DBG
setthread 7
查看當前線程在托管代碼中的堆棧信息
clrstack
更多 dotnet-dump 命令請查看:https://docs.microsoft.com/zh-cn/dotnet/core/diagnostics/dotnet-dump#analyze-sos-commands
案例說明
以下是生產(chǎn)環(huán)境中遇到的一個具體案例智亮,有一服務(wù)運行一段時間就會出現(xiàn) CPU 100%,而且也降不下來点待,如下監(jiān)控:
通過鎖定異常線程后阔蛉,多次 dump 并對堆棧信息進行分析,發(fā)現(xiàn)出問題時都和以下代碼相關(guān):
這里使用了一個表達式計算的開源組件 NCalc 癞埠,初步判斷可能是表達式本身的不合法引起的循環(huán)解析状原,通過 dumpobj 對方法參數(shù)的查看,發(fā)現(xiàn)都是很正常的表達式苗踪,所以猜測并不成立颠区。
繼續(xù)在 Github 項目中的 issues 進行查找可能存在的類似問題,發(fā)現(xiàn)在較早版本中通铲,確實存在卡死的現(xiàn)象 https://github.com/sklose/NCalc2/issues/22 毕莱,這個問題在新版本中已修復(fù),而我們出問題的這個服務(wù)使用的 NuGet 包確實是比較老的一個版本颅夺,所以問題基本上可以定位朋截,在經(jīng)過 NuGet 包版本升級后,這種現(xiàn)象終于消失了碗啄。
總結(jié)
實際在遇到棘手問題的時候,可能經(jīng)常毫無頭緒稳摄,太多問題都不是那么容易定位的稚字。在構(gòu)建服務(wù)支持業(yè)務(wù)能力的同時,要注意代碼本身的健壯性,在使用外部組件時胆描,需要多關(guān)注其生態(tài)情況瘫想,dump 分析只是一種協(xié)助解決問題的手段。