dotnet-monitor 是 .NET 运行时诊断工具,它以独立进程方式运行,并通过 Diagnostic IPC 协议连接目标 .NET 应用。传统上在物理机或虚拟机上,我们可以直接运行 dotnet-monitor collect 命令,然后通过浏览器或 REST API 查看计数器、触发 GC、收集内存转储。但在容器环境中,这个工具必须被封装成镜像,并与业务容器共享必要的文件系统和进程信息,才能正常连接目标进程。容器化本身并不复杂,复杂的是配置传递、密钥管理和多容器协作。

从镜像构建与启动参数入手
最省事的做法是直接使用官方镜像 mcr.microsoft.com/dotnet/monitor,截至较新的 .NET 版本,镜像中已经包含了 dotnet-monitor 可执行文件以及依赖的共享库。如果你不需要改写镜像内部文件,完全可以在 Kubernetes 或 Docker Compose 中直接引用官方镜像,通过环境变量注入配置。只有当你需要预置证书、自定义 CA 或加入额外的诊断脚本时,才需要编写 Dockerfile。
下面是一个自定义镜像的示例,它在官方镜像基础上挂载一个诊断输出目录,并通过环境变量指定 HTTP 监听地址和默认共享路径。这样镜像启动后,所有转储文件会统一写入 /diag 目录,便于挂载到宿主机或对象存储。
FROM mcr.microsoft.com/dotnet/monitor:8.0 WORKDIR /app EXPOSE 52323 ENV DOTNETMONITOR_Urls=http://*:52323 ENV DOTNETMONITOR_Storage__DefaultSharedPath=/diag VOLUME ["/diag"]
需要注意的是,dotnet-monitor 的配置系统基于 .NET 的 ConfigurationBuilder,环境变量采用双下划线分隔层级。例如 JSON 中的 Storage:DefaultSharedPath 对应环境变量 DOTNETMONITOR_Storage__DefaultSharedPath。这种映射关系非常关键,因为很多同学在 Kubernetes 里写环境变量时只用了单下划线,结果容器能启动,但配置完全不生效。如果拿不准配置键,可以先在本地用 docker run 加上 --env DOTNETMONITOR_Logging__LogLevel__Default=Debug 来观察详细日志。
启动命令方面,官方镜像的 ENTRYPOINT 是 dotnet-monitor,因此容器启动时可以直接传 collect 子命令和相关参数。例如在 Docker 中运行:docker run --rm -p 52323:52323 -v /tmp/monitor:/diag mcr.microsoft.com/dotnet/monitor:8.0 collect --urls http://*:52323。Kubernetes 中则使用 args 字段覆盖默认命令。
配置 API 密钥与认证
dotnet-monitor 默认启用 MonitorApiKey 认证,这是为了防止未授权访问诊断端点。任何对 /processes、/dump、/metrics 等 API 的调用都需要在请求头中携带 Negotiate 信息。因此,容器化之后的第一件事不是直接暴露端口,而是生成一对 Subject 和 PublicKey,并把它们注入到容器环境变量中。
生成密钥最简单的办法是运行临时容器:
docker run --rm mcr.microsoft.com/dotnet/monitor:8.0 generatekey
该命令会输出一个 JSON 片段,其中包含 Subject 和 PublicKey 两个字段。把这两个字段原样写入 Kubernetes Secret 或 Docker 环境变量即可。需要注意的是,PublicKey 很长,包含斜杠和加号等字符,放入 Secret 时不要做 Base64 之外的额外编码。环境变量映射为 DOTNETMONITOR_Authentication__MonitorApiKey__Subject 和 DOTNETMONITOR_Authentication__MonitorApiKey__PublicKey。
客户端调用时,需要构造 Authorization 头。其值为 Negotiate 后接一个空格,再接 Base64 编码的 Subject:Secret 组合。Secret 是生成密钥时输出的私钥部分,不会存入容器。通常只有管理员或自动化脚本持有 Secret。对于 Prometheus 这类抓取工具,如果只需要访问 /metrics 端点,可以单独配置一个无认证的 Metrics 地址,而不要把主 API 端口完全暴露。
在 Kubernetes 中部署并暴露指标
把 dotnet-monitor 放入 Kubernetes,最合理的形态是作为 Sidecar 容器运行在业务 Pod 内部。这样可以通过 localhost 或共享卷直接访问目标进程的诊断套接字,不需要跨节点网络。下面给出一个简化 Deployment 配置,其中 monitor 容器与业务容器共享同一个 /tmp 目录,并启用进程命名空间共享。
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp-with-monitor
spec:
replicas: 1
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
shareProcessNamespace: true
containers:
- name: webapp
image: your-registry/webapp:latest
volumeMounts:
- name: diag-socket
mountPath: /tmp
- name: monitor
image: mcr.microsoft.com/dotnet/monitor:8.0
args: ["collect", "--urls", "http://*:52323", "--metricUrls", "http://*:52325"]
ports:
- containerPort: 52323
name: http
- containerPort: 52325
name: metrics
env:
- name: DOTNETMONITOR_Urls
value: http://*:52323
- name: DOTNETMONITOR_Metrics__Endpoints
value: http://*:52325
- name: DOTNETMONITOR_Authentication__MonitorApiKey__Subject
value: monitor
- name: DOTNETMONITOR_Authentication__MonitorApiKey__PublicKey
valueFrom:
secretKeyRef:
name: monitor-api-key
key: publicKey
volumeMounts:
- name: diag-socket
mountPath: /tmp
- name: diag-output
mountPath: /diag
volumes:
- name: diag-socket
emptyDir: {}
- name: diag-output
emptyDir: {}
这里有两个容易忽略的点。第一,shareProcessNamespace 必须设置为 true,否则 monitor 容器看不到业务容器中的 dotnet 进程,无法通过 PID 连接 Diagnostic IPC。第二,/tmp 目录必须由两个容器共享,因为 .NET 的诊断套接字默认创建在 /tmp 下。如果只共享了 /diag 而没有共享 /tmp,调用 /processes 接口会一直返回空列表或连接超时。
指标抓取方面,dotnet-monitor 可以从目标进程收集运行时计数器,并通过 /metrics 端点以 Prometheus 格式暴露。Prometheus 只需配置一个 scrape_config,指向 monitor 容器的 52325 端口即可。因为该端点默认不要求认证,所以抓取配置相对简单。
scrape_configs:
- job_name: 'dotnet-monitor'
scrape_interval: 15s
static_configs:
- targets: ['webapp-with-monitor:52325']
如果部署在 Kubernetes Service 中,可以为 monitor 容器单独创建一个 Service 并指定 targetPort: metrics。此时 Service 名称就是 targets 中的主机名。不过要注意,/metrics 只提供 monitor 自身和已配置的计数器,和主 API 端口 52323 的认证规则不同,不要混用。
常见故障排查与最终建议
容器化 dotnet-monitor 遇到最多的报错是 Failed to connect to process 或 Unable to find a diagnostic endpoint。这类问题基本都出在共享卷和进程命名空间上。可以先进入 monitor 容器执行 ls /tmp,确认能看到类似 dotnet-diagnostic-* 的套接字文件。如果没有,检查业务容器的 volumeMounts 是否也挂载了同一个 emptyDir 到 /tmp,以及 Pod spec 中是否设置了 shareProcessNamespace: true。
另一个高频问题是 PublicKey 环境变量中的换行符。生成密钥时输出的 JSON 可能带有换行,直接复制到 Secret 后,如果保留了换行符,dotnet-monitor 会因解析失败而拒绝启动。排查方法是用 kubectl describe pod 查看事件,往往会看到 Invalid public key 之类的错误。解决方法是确保 Secret 中 PublicKey 为单行字符串,不包含回车或空格。
从生产实践看,dotnet-monitor 容器化之后最合适的角色是作为诊断 Sidecar,而不是独立 Deployment。把 monitor 和业务容器放在同一个 Pod 中,可以避免跨节点认证和网络策略带来的额外复杂度,同时让诊断操作更接近目标进程。对于不需要实时抓取的应用,也可以将 monitor 作为 Job 按需启动,用完即删,这样更节省资源。无论选择哪种方式,把 /tmp 和输出目录分开挂载、严格控制 API 端口暴露范围,都是保证容器化诊断方案稳定运行的基础。
dotnet-monitor容器化诊断数据修改时间:2026-09-27 09:58:28