随着容器和云技术的发展, 大量的应用运行在云上的容器中, 它们的好处是毋庸置疑的, 例如极大的提高了我们的研发部署速度, 快速的扩缩容等等, 但是也存在一些小小的问题, 例如难以调试.
基于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/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co