.NET 容器环境下创建应用 dump 文件
.NET 容器環境下創建應用 dump 文件
Intro
有時候我們的應用會出現一些異常的情況,比如內存飆升,線程死鎖等等,通過一些 metrics 我們可能大概的了解內存是增長了,但是具體是哪里增長了,單單看內存的變化很難看出來哪里導致的內存變化,所以很多時候我們還是需要對應用進行 dump,通過分析應用 dump 文件來進一步分析是哪里出現的問題。
Prepare
在容器中進行 dump 的時候我們需要 Linux 中的 SYS_PTRACE 權限,所以需要在部署或運行容器的時候指定一下(默認是沒有的)
如果你是直接用 docker run 執行的話可以在執行 docker run ?的時候指定 docker run --cap-add=SYS_PTRACE
如果你使用 docker compose 來運行,可以在 docker-compose.yml 配置文件中配置 cap_add,下面是一個示例
version:?'2.1' services:reservation:build:context:?.dockerfile:?Dockerfilecontainer_name:?reservationcap_add:-?SYS_PTRACE如果你在 k8s 中使用,需要在 k8s 的 yaml 文件中配置,示例如下:
apiVersion:?apps/v1 kind:?Deployment metadata:name:?sparktodo-apilabels:app:?sparktodo-api spec:replicas:?1revisionHistoryLimit:?0template:????spec:containers:-?name:?sparktodo-apiimage:?weihanli/sparktodo-api:latestimagePullPolicy:?IfNotPresentresources:requests:memory:?"64Mi"cpu:?"20m"limits:memory:?"128Mi"cpu:?"50m"ports:-?containerPort:?80 +?????????securityContext: +???????????capabilities: +?????????????add:?["SYS_PTRACE"]
我們也可以在容器中檢測是否已經開啟了 SYS_PTRACE 權限,可以通過命令 capsh --print 來查看已經配置的權限,如果提示命令不存在,則需要安裝一個 package,alpine 安裝 libcap,Ubuntu 安裝 libcap2-bin
#?alpine apk?add?libcap#?ubuntu apt-get?install?libcap2-bincreatedump
createdump 是隨著 .NET Core runtime 一起發布的一個創建 dump 的一個工具,createdump 可以收集托管信息和 Native 信息,默認?createdump 會創建一個 mini dump,會創建一個比較小的一個 dump,僅包含一些必要的托管信息和堆棧信息,我們也可以通過參數來控制生成全量的 dump(full dump),全量 dump 生成的 dump 文件會比較大,但是包含的信息會比較多,內存中所有的信息都會包含在內。mini dump 沒有 full dump 包含那么多有用的信息,所以大小會比較小,通常你請別人幫忙分析 dump 的時候都是選擇 full dump。
createdump 一般位于 /usr/share/dotnet/shared/Microsoft.NETCore.App/<version>/createdump
使用起來直接用 createdump <pid> 就可以了,默認會創建一個 minidump,如果要創建一個 full dump,需要指定參數,createdump <pid> -u 或者 createdump <pid> --full
-f,?--name?-?dump?path?and?file?name.?The?%p,?%e,?%h?%t?format?characters?are?supported.?The?default?is?'/tmp/coredump.%p' -n,?--normal?-?create?minidump. -h,?--withheap?-?create?minidump?with?heap?(default). -t,?--triage?-?create?triage?minidump. -u,?--full?-?create?full?core?dump. -d,?--diag?-?enable?diagnostic?messages.我們可以使用下面的格式來作為 dump 文件中的占位符:
%%??A?single?%?character. %d??PID?of?dumped?process?(for?backwards?createdump?compatibility). %p??PID?of?dumped?process. %e??The?process?executable?filename. %h??Hostname?return?by?gethostname(). %t??Time?of?dump,?expressed?as?seconds?since?the?Epoch,?1970-01-01?00:00:00?+0000?(UTC).dotnet diagnostics tool
微軟提供了一系列的 dotnet 診斷工具可以幫助我們診斷應用程序中的問題,
dotnet-counters: ?一個性能監視工具,用于初級運行狀況監視和性能調查
dotnet-dump: 可在不使用本機調試器的情況下收集和分析 Windows 和 Linux ?的 Core Dump
dotnet-gcdump: 可用于為活動 .NET 進程收集 GC dump
dotnet-trace: 可以使用來自應用的有意思的分析數據,分析數據通過 .NET Core 中的 EventPipe 公開,這些數據可幫助你分析應用運行緩慢的根本原因。
dotnet-stack: 可以快速打印正在運行的 .net 進程中的所有線程的托管堆棧
可以使用安裝 global tool 的方式安裝,在運行時容器中安裝 dotnet tool 可以參考之前一篇文章介紹,借助多階段構建來方便的安裝 dotnet tool,示例 dockerfile 如下:
FROM?mcr.microsoft.com/dotnet/aspnet:5.0-alpine?AS?base #?https://www.abhith.net/blog/docker-sql-error-on-aspnet-core-alpine/ RUN?apk?add?icu-libs ENV?DOTNET_SYSTEM_GLOBALIZATION_INVARIANT?false #?use?forward?headers ENV?ASPNETCORE_FORWARDEDHEADERS_ENABLED=true LABEL?Maintainer="WeihanLi" EXPOSE?80FROM?mcr.microsoft.com/dotnet/sdk:5.0-alpine?AS?build-env WORKDIR?/app#?安裝?dotnet?tool RUN?dotnet?tool?install?--global?dotnet-dump RUN?dotnet?tool?install?--global?dotnet-gcdump RUN?dotnet?tool?install?--global?dotnet-countersCOPY?SparkTodo.Shared/SparkTodo.Shared.csproj?SparkTodo.Shared/ COPY?SparkTodo.API/SparkTodo.API.csproj?SparkTodo.API/ RUN?dotnet?restore?SparkTodo.API/SparkTodo.API.csproj#?copy?everything?and?build COPY?.?.WORKDIR?/app/SparkTodo.API RUN?dotnet?publish?-c?Release?-o?out#?build?runtime?image FROM?base?AS?final#?從前面的?SDK?鏡像中拷貝?dotnet?tools?到?runtime?鏡像中 COPY?--from=build-env?/root/.dotnet/tools?/root/.dotnet/tools #?設置?PATH?以方便使用 ENV?PATH="/root/.dotnet/tools:${PATH}"WORKDIR?/app COPY?--from=build-env?/app/SparkTodo.API/out?. ENTRYPOINT?["dotnet",?"SparkTodo.API.dll"]你也可以按照上面的方式安裝自己想要安裝的 dotnet tool,按上面安裝之后我們就可以直接使用 dotnet-dump/dotnet-gcdump/dotnet-counters 命令了
dump 之旅
container for test
了解一些基礎知識之后,我們就來開始創建 dump 吧,示例應用在 Github 上 https://github.com/WeihanLi/SparkTodo,你可以下載這個應用來做嘗試,或者在自己的應用上來試驗
首先我們 build 一個 docker 鏡像,在項目根目錄執行 docker build -t sparktodo-api . 來構建鏡像
鏡像構建好之后,運行一個容器來進行測試
docker?run?-d?--name?sparktodo-api?--cap-add=SYS_PTRACE?sparktodo-api容器運行成功之后,可以使用下面的命令進入到容器中,出現下圖提示則成功的進入容器中了
docker?exec?-it?sparktodo-api?shCreatedump
首先我們來使用自帶的 createdump 來創建一個應用的 dump,
有兩種方式找到我們的 createdump,
我們可以先切換目錄到 /usr/share/dotnet/shared/Microsoft.NETCore.App,然后 ls 查看當前的 runtime 版本,切換到版本目錄下即可找到 createdump
另外一種方式,我們可以通過 dotnet --info 來查看當前 runtime 版本,然后直接切換到對應的 /usr/share/dotnet/shared/Microsoft.NETCore.App/<dotnet-version> 目錄下即可
通常情況下,我們的容器中只有一個進程,進程 Id 為 1的進程通常就是我們的應用進程,如果不確定的話也可以使用 ps -ef 命令來查看當前的進程信息
可以看到,PID 為 1 的進程就是我們的應用所在的進程,然后就可以通過下面的命令來創建 dump 了
./createdump?1?-u看到這樣的提示,我們的 dump 就創建成功了,這樣我們可以使用 LLDB 或者 dotnet-dump 進行 dump 分析了
dotnet-dump
dotnet-dump 是微軟提供的一個很方便的 dump 分析和收集工具,dotnet-dump 只能分析托管代碼,但是絕大部分場景下對我們來說已經足夠了,而且 dotnet-dump 是一個 dotnet tool,可以在各個平臺下提供完全一致的體驗,不像 LLDB 每個系統的安裝方式可能都不一樣,安裝的版本也可能不同,不如 dotnet-dump 體驗良好
首先我們可以使用 dotnet-dump ps 來查看可以進行 dump 的 dotnet 進程,然后使用 dotnet-dump collect -p <pid> 來創建 dump 文件
dotnet-gcdump
dotnet-gcdump 也是微軟提供的一個用來分析應用程序問題的 dotnet tool,dotnet-dump 創建的 dump 會比較大,包含除了 gc 之外的其他信息,比如 lock 情況,線程堆棧信息等,使用 dotnet-gcdump 創建的 dump 僅包含 gc 堆的信息,dump 文件會小很多,并且 gcdump 文件可以直接用 VS 打開,可以比較方便的查看 GC 內存占用信息,dotnet-gcdump 的使用和 dotnet-dump 類似,并且新版本(.NET 5.0)的 dotnet-gcdump 可以通過 dotnet-gcdump report -p <pid> 直接從某一個進程導出 GC 內存使用信息
通過 dotnet-gcdump collect -p <pid> 來創建 GC dump 文件
More
容器中的 dump 生成之后可以通過 docker cp(k8s 中可以用 kubectl cp)將 dump 文件拷貝到本地來進行分析,新版本的 dump 文件可以直接使用 VS 打開
微軟的官方文檔真的是一個寶庫,有很多很不錯的文檔,關于 dotnet 診斷工具也有幾篇很有用的介紹,沒事的時候看看文檔,說不定就能發現寶藏~~
References
https://docs.microsoft.com/en-us/dotnet/core/diagnostics/debug-linux-dumps
https://www.programmersought.com/article/81354675797/#dotnetdotnetdump_45
https://man7.org/linux/man-pages/man7/capabilities.7.html
https://stackoverflow.com/questions/43621959/how-can-i-find-out-which-capabilities-a-container-has-been-given
https://stackoverflow.com/questions/38758627/how-can-we-add-capabilities-to-a-running-docker-container
https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities
https://stackoverflow.com/questions/6903329/minidump-vs-fulldump
https://github.com/dotnet/diagnostics/blob/main/documentation/design-docs/dotnet-tools.md
https://github.com/WeihanLi/SparkTodo
總結
以上是生活随笔為你收集整理的.NET 容器环境下创建应用 dump 文件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 日志ILog(文件日志/控制台日志/控件
- 下一篇: 一秒创建高级查询服务