.NET Core dump 分析

服務 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 cpkubectl cpcoredump.1 文件複製到主機目錄下,然後下載到用於 dump 分析的機器上。

注意:在 Docker 部署模式下,createdump 命令執行需要有容器特權,所以在容器啓動時需要加 --privileged = true 參數。另外 dump 文件生成需要使用較大內存,需適當調整容器內存限制參數。

.NET Core 2.2

目前大多使用 lldb 進行分析,但從零開始搭建環境實在有些折騰,不推薦。網上已有封裝好的鏡像可直接使用,如:6opuc/lldb-netcore6opuc/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)。

指定當前需要分析的線程 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 分析只是一種協助解決問題的手段。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章