服務 CPU 或 內存偶爾飆高是部署環境中經常遇到的問題,一般會采用記錄日志的方式來診斷,不過有些情況靠日志可能并不能分析出個所以然,面對實在無頭緒的問題也只能暫時使用重啟大法先恢復。
為了盡可能精準的定位問題,掌握通過 dump 分析服務運行堆棧信息也是非常必要的,本文將分別介紹如何對 .NET Core 2.2 和 .NET Core 3.1 項目進行 dump 分析(這里只針對 Linux 下使用容器部署的方式)。
創建 dump 文件
在創建 dump 文件之前,最好先查看具體是服務中哪些線程引發的異常,然后針對特定線程進行分析,不然全掃一遍將是一件非常耗時的工作。
進入容器后,安裝 htop:
apt-get update
apt-get install htop
通過 htop 查看資源使用情況:
以上是測試程序模擬的狀況,可知 PID 12 是需要關注的線程
執行以下命令即可創建 dump 文件(這里以 2.2.8 為例,另外可通過 createdump --help
查看更多參數設置,容器內默認 dotnet 進程對應 pid 均為 1):
/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/createdump 1
命令執行完成后,將生成 dump 文件 /tmp/coredump.1
,我們需要通過 docker cp
或 kubectl cp
將 coredump.1
文件復制到主機目錄下,然后下載到用于 dump 分析的機器上。
注意:在 Docker 部署模式下,createdump
命令執行需要有容器特權,所以在容器啟動時需要加 --privileged = true
參數。另外 dump 文件生成需要使用較大內存,需適當調整容器內存限制參數。
.NET Core 2.2
目前大多使用 lldb 進行分析,但從零開始搭建環境實在有些折騰,不推薦。網上已有封裝好的鏡像可直接使用,如:6opuc/lldb-netcore ,6opuc/lldb-netcore
默認是基于 .NET Core SDK 2.2.8 構建的鏡像,如果當前要 dump 的服務 .NET Core 版本非 2.2.8,則需要修改 lldb-netcore 源碼 重新構建鏡像。
執行以下命令進入 lldb:
docker run --rm -it -v /root/coredump.1:/tmp/coredump 6opuc/lldb-netcore
查看當時運行的線程:
clrthreads -live
指定需要分析的線程編號(PID 12 的線程對應的 16 進制為 c,所以找到 OSID 為 c 的記錄,對應編號為 7【第一列】)
thread select 7
查看當前線程在托管代碼中的堆棧信息
clrstack
更多命令可通過執行 soshelp 查看
.NET Core 3.1
.NET Core 3 開始,官方已提供 dotnet-dump 工具進行 dump 分析,使用起來也相對簡單,當然我們依然可以繼續使用 lldb 的方式。
安裝 dotnet-dump
dotnet tool install --global dotnet-dump --version 3.1.141901
進入分析
dotnet-dump analyze /root/coredump.1
如果出現以下錯誤,說明 .NET Core SDK 沒有安裝到 /usr/shard/dotnet
路徑下,可通過 DOTNET_ROOT 單獨指定或重新安裝。
查看正在運行的托管線程:
clrthreads
如果出現以下錯誤,是因為當前安裝的 .NET Core SDK 版本與容器內 createdump 使用的 SDK 版本不一致(如:createdump 使用 3.1.3,分析使用 3.1.12)。
也可根據輸出鏈接前往查看更多解決方案 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
案例說明
以下是生產環境中遇到的一個具體案例,有一服務運行一段時間就會出現 CPU 100%,而且也降不下來,如下監控:
通過鎖定異常線程后,多次 dump 并對堆棧信息進行分析,發現出問題時都和以下代碼相關:
這里使用了一個表達式計算的開源組件 NCalc ,初步判斷可能是表達式本身的不合法引起的循環解析,通過 dumpobj 對方法參數的查看,發現都是很正常的表達式,所以猜測并不成立。
繼續在 Github 項目中的 issues 進行查找可能存在的類似問題,發現在較早版本中,確實存在卡死的現象 https://github.com/sklose/NCalc2/issues/22 ,這個問題在新版本中已修復,而我們出問題的這個服務使用的 NuGet 包確實是比較老的一個版本,所以問題基本上可以定位,在經過 NuGet 包版本升級后,這種現象終于消失了。
總結
實際在遇到棘手問題的時候,可能經常毫無頭緒,太多問題都不是那么容易定位的。在構建服務支持業務能力的同時,要注意代碼本身的健壯性,在使用外部組件時,需要多關注其生態情況,dump 分析只是一種協助解決問題的手段。