隨著容器和雲技術的發展, 大量的應用執行在雲上的容器中, 它們的好處是毋庸置疑的, 例如極大的提高了我們的研發部署速度, 快速的擴縮容等等, 但是也存在一些小小的問題, 例如難以偵錯.
基於VM的部署我們可以通過安全的方式登入到主機上做一些你想做的事情, 但是雲上的容器那就是不太方便了(目前AWS的ECS已經有類似docker exec的方式直接進入容器中了, 其他的雲未作了解).
但是就算能進入容器也不意味著偵錯就好做了, 通常來說使用的映象都是經過優化和精簡的(如果要調式可能需要安裝大量的元件).
所以, 接下來介紹一下使用dotnet-monitor 來記憶體轉儲(memory dump)執行在容器中的 dotnet 程式.
Announcing dotnet monitor in .NET 6 官方部落格的原文:
Running a .NET application in diverse environments can make collecting diagnostics artifacts (e.g., logs, traces, process dumps) challenging. dotnet monitor is a tool that provides an unified way to collect these diagnostic artifacts regardless of whether running you’re running on your desktop machine or in a kubernetes cluster.
There are two different mechanisms for collection of these diagnostic artifacts:
An HTTP API for on demand collection of artifacts. You can call these API endpoints when you already know your application is experiencing an issue and you are interested in gathering more information.
Triggers for rule-based configuration for always-on collection of artifacts. You may configure rules to collect diagnostic artifacts when a desired condition is met, for example, collect a process dump when you have sustained high CPU.
google翻譯:
在不同的環境中執行 .NET 應用程式會使收集診斷工件(例如,紀錄檔、跟蹤、程序轉儲)具有挑戰性。dotnet monitor是一個工具,它提供了一種統一的方式來收集這些診斷工件,無論您是在桌上型電腦上執行還是在 kubernetes 叢集中執行。
收集這些診斷工件有兩種不同的機制:
用於按需收集工件的HTTP API 。當您已經知道您的應用程式遇到問題並且您有興趣收集更多資訊時,您可以呼叫這些 API 端點。
基於規則的設定觸發器,用於始終線上收集工件。您可以設定規則以在滿足所需條件時收集診斷工件,例如,當您持續使用高 CPU 時收集程序轉儲。
借用官方部落格中的一張圖說明一下dotnet-monitor工作在什麼地方
dotnet-monitor 可以連線到dotnet執行時公開的一個診斷埠(diagnostic port)(3.0新提供的新功能), 並通過自定義協定(ipc protocol)與執行時互動,
更多偵錯知識和工具例如ETW, eventpipe, lldb, dotnet-trace, dotent-counters 等可以檢視 dotnet diagnostics.
首先, 我們得讓我們被偵錯的目標程式公開這個診斷埠, 因為預設情況下這個診斷埠只能由執行這個程式的使用者或者root使用者來存取, 顯然sidecar 模式啟動的dotnet-monitor是不可能和目標程式用的是同一個使用者的.
未作特別宣告的話, 後文給出的實驗都是基於
AWS Fargate
和Linux
設定.
#新增環境變數
DOTNET_DiagnosticPorts=/my_diagnostic_volume/diag.sock,suspend,connect
/my_diagnostic_volume/diag.sock
指 Unix Domain Socket
檔案路徑, my_diagnostic_volume
是掛載的一個volume.
suspend
意思是讓執行時等待dotnet-monitor 連線進來之後在執行受控程式碼.
connect
接受dotnet-monitor連線, 詳細解釋看這裡diagnostic ports
上述設定的完整語法結構是 address[,(listen|connect)][,(suspend|nosuspend)]
詳情請檢視檔案configure additional diagnostic ports
如果我們的需要dump記憶體檔案, 可能會遇到WriteDumpAsync failed - HRESULT: 0x00000000
issues 1783這樣的錯誤, 是因為許可權問題.
比如我在AWS Fargate
中遇到的就是 /dump
API 返回400錯誤 Write dump failed - HRESULT: 0x00000000
, 目標程式輸出紀錄檔 ptrace(ATTACH, 1) FAILED Operation not permitted
.
解決這個需要吧SYS_PTRACE許可權給到目標程式. AWS Fargate
是編輯任務定義的json檔案增加這一部分, docker 啟動是通過增加--cap-add=SYS_PTRACE
引數.
{
"linuxParameters": {
"capabilities": {
"add": [
"SYS_PTRACE"
]
}
}
}
最後, 設定目標程式容器依賴dotnet-monitor容器, 這樣可以先讓dotnet-monitor容器啟動後, 在啟動目標程式容器.
到此, 目標程式容器的設定就完成了, 接下來設定dotnet-monitor
52323
#dotnet-monitor對映埠.--no-auth
# 簡單粗暴的讓所有的API都不要鑑權.DOTNETMONITOR_DiagnosticPort__ConnectionMode
=Listen
# 必須的.DOTNETMONITOR_DiagnosticPort__EndpointName
=/my_diagnostic_volume/diag.sock
# 目標容器設定的DOTNET_DiagnosticPorts中的address.DOTNETMONITOR_Storage__DumpTempFolder
=/my_diagnostic_volume/dump_files
# dump記憶體是用的目錄.DOTNETMONITOR_Urls
=http://+:52323
# dotnet-monitor要提供服務在什麼埠上. dotnet-monitor預設用的就是52323.詳細的檔案解釋看這裡
至此, 所有的設定就都完成了.
Get 請求 /dump
endpoint 即可下載記憶體轉儲檔案.
wget ip:52323/dump -O my_target_application_memory_dump.dmp
當前可以用API's列表, 詳情請看這裡API's
| Route | Description | Version Introduced |
| ---------------- | ------------------------------------------------------------------ | ------------------ |
| /processes | Gets detailed information about discoverable processes. | 6.0 |
| /dump | Captures managed dumps of processes without using a debugger. | 6.0 |
| /gcdump | Captures GC dumps of processes. | 6.0 |
| /trace | Captures traces of processes without using a profiler. | 6.0 |
| /metrics | Captures metrics of a process in the Prometheus exposition format. | 6.0 |
| /livemetrics | Captures live metrics of a process. | 6.0 |
| /stacks | [Experimental] Gets the current callstacks of all .NET threads. | 7.0 |
| /logs | Captures logs of processes. | 6.0 |
| /info | Gets info about dotnet monitor. | 6.0 |
| /operations | Gets egress operation status or cancels operations. | 6.0 |
| /collectionrules | Gets the current state of collection rules. | 6.3 |
在之後的對記憶體檔案的分析可以使用dotnet-dump
, lldb等程式.
更多高階用法請檢視, 例如可以設定記憶體每增加100Mb就觸發dump記憶體檔案.
https://learn.microsoft.com/zh-cn/dotnet/core/diagnostics/dotnet-monitor
https://learn.microsoft.com/zh-cn/dotnet/core/diagnostics/dotnet-dump
https://learn.microsoft.com/zh-cn/dotnet/core/diagnostics/diagnostic-port
https://github.com/dotnet/dotnet-monitor/blob/main/README.md
https://devblogs.microsoft.com/dotnet/announcing-dotnet-monitor-in-net-6/