Prometheus无法采集ASP.NET Core API指标的问题排查与疑问
我正在按照微软《使用OpenTelemetry实现.NET可观测性》文档进行集成,目前Jaeger已能正常采集追踪数据,但Prometheus无法从我的ASP.NET Core Web API采集指标数据。我采用微软TaskWeaver仓库提供的Docker Compose部署架构,当前prometheus-config.yml中配置的采集目标为optl-collector:9464,而我的ASP.NET Core Web API在https://localhost:5001/metrics端点暴露指标,我怀疑应将采集目标修改为host.docker.internal:5000,但不确定修改后optl-collector在架构中的作用。
相关配置代码
扩展类代码
public static class ServiceCollectionExtensions { public static IServiceCollection AddObservabilityServices(this IServiceCollection services, IHostEnvironment environment, IConfiguration configuration) { ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(environment); ArgumentNullException.ThrowIfNull(configuration); services.AddOpenTelemetry() .ConfigureResource(resource => resource .AddService(serviceName: environment.ApplicationName) .AddAttributes(new Dictionary<string, object> { ["deployment.environment"] = environment.EnvironmentName })) .WithMetrics(metrics => metrics .AddAspNetCoreInstrumentation() .AddMeter(Metrics.SourceName) .AddMeter("Microsoft.AspNetCore.Hosting") .AddMeter("Microsoft.AspNetCore.Server.Kestrel") .AddPrometheusExporter()) .WithTracing(tracing => { tracing.AddAspNetCoreInstrumentation(); tracing.AddHttpClientInstrumentation(); tracing.AddSource(DistributedTracing.SourceName); // Export to Jaeger tracing.AddOtlpExporter(otlpOptions => { otlpOptions.Endpoint = new Uri("http://localhost:4317"); }); }); return services; } public static IApplicationBuilder MapObservabilityEndpoints(this IApplicationBuilder app) { ArgumentNullException.ThrowIfNull(app); app.UseOpenTelemetryPrometheusScrapingEndpoint(); return app; } }
docker-compose.yaml
version: '3.9' services: optl-collector: image: otel/opentelemetry-collector:0.96.0 command: ["--config=/etc/collector-config.yaml"] volumes: - ./collector-config.yaml:/etc/collector-config.yaml ports: - "4317:4317" # Expose the gRPC receiver port for the first collector depends_on: - jaeger jaeger: image: jaegertracing/all-in-one:1.54 ports: - "16686:16686" # Jaeger UI prometheus: image: prom/prometheus:latest ports: - "9090:9090" # Prometheus UI volumes: - ./prometheus-config.yml:/etc/prometheus/prometheus.yml command: ["--config.file=/etc/prometheus/prometheus.yml"] depends_on: - optl-collector grafana: image: grafana/grafana-enterprise:latest ports: - "3000:3000" # Grafana UI environment: - GF_SECURITY_ADMIN_PASSWORD=secret # You should change 'secret' to a password of your choosing - GF_USERS_ALLOW_SIGN_UP=false volumes: - grafana_data:/var/lib/grafana depends_on: - prometheus volumes: grafana_data:
collector-config.yaml
receivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 http: endpoint: 0.0.0.0:4318 exporters: debug: verbosity: detailed otlp: endpoint: "jaeger:4317" tls: insecure: true prometheus: endpoint: "0.0.0.0:9464" service: pipelines: traces: receivers: [otlp] exporters: [otlp] metrics: receivers: [otlp] exporters: [prometheus] logs: receivers: [otlp] exporters: [debug]
prometheus-config.yml
scrape_configs: - job_name: optl-collector scrape_interval: 5s static_configs: - targets: ["optl-collector:9464"]
问题根源
你的API当前是直接通过PrometheusExporter暴露了Prometheus格式的指标端点,但架构中的OTel Collector配置的是接收OTLP格式的指标,而你的API并没有把指标发送给Collector。这就导致Collector的9464端口没有任何指标数据,Prometheus去拉取自然为空。
方案一:通过OTel Collector转发指标(推荐)
这种方式符合可观测性统一管控的设计理念,Collector可以统一处理指标的过滤、聚合,再转发给Prometheus,同时保持追踪、指标的链路统一。
修改ASP.NET Core扩展类代码
移除直接暴露Prometheus端点的配置,换成将指标通过OTLP发送给Collector:public static class ServiceCollectionExtensions { public static IServiceCollection AddObservabilityServices(this IServiceCollection services, IHostEnvironment environment, IConfiguration configuration) { ArgumentNullException.ThrowIfNull(services); ArgumentNullException.ThrowIfNull(environment); ArgumentNullException.ThrowIfNull(configuration); services.AddOpenTelemetry() .ConfigureResource(resource => resource .AddService(serviceName: environment.ApplicationName) .AddAttributes(new Dictionary<string, object> { ["deployment.environment"] = environment.EnvironmentName })) .WithMetrics(metrics => metrics .AddAspNetCoreInstrumentation() .AddMeter(Metrics.SourceName) .AddMeter("Microsoft.AspNetCore.Hosting") .AddMeter("Microsoft.AspNetCore.Server.Kestrel") // 替换PrometheusExporter为OTLP Exporter,发送指标到Collector .AddOtlpExporter(otlpOptions => { otlpOptions.Endpoint = new Uri("http://localhost:4317"); })) .WithTracing(tracing => { tracing.AddAspNetCoreInstrumentation(); tracing.AddHttpClientInstrumentation(); tracing.AddSource(DistributedTracing.SourceName); tracing.AddOtlpExporter(otlpOptions => { otlpOptions.Endpoint = new Uri("http://localhost:4317"); }); }); return services; } public static IApplicationBuilder MapObservabilityEndpoints(this IApplicationBuilder app) { ArgumentNullException.ThrowIfNull(app); // 移除Prometheus端点,指标现在通过Collector转发 return app; } }保持现有Collector与Prometheus配置不变
你的collector-config.yaml已经正确配置了metrics pipeline:接收OTLP指标并转换成Prometheus格式暴露在9464端口,Prometheus继续采集optl-collector:9464即可。
方案二:Prometheus直接采集API端点
如果不想通过Collector转发指标,可以直接让Prometheus拉取API的/metrics端点,此时Collector仍负责追踪数据的中转工作。
修改Prometheus配置
新增一个采集任务指向API的宿主机地址(Docker容器内通过host.docker.internal访问宿主机),注意API用的是HTTPS端口5001:scrape_configs: - job_name: optl-collector scrape_interval: 5s static_configs: - targets: ["optl-collector:9464"] # 保留,若后续有其他服务通过Collector发送指标 - job_name: aspnet-api scrape_interval: 5s scheme: https tls_config: insecure_skip_verify: true # 自签名证书需开启此配置 static_configs: - targets: ["host.docker.internal:5001"]OTel Collector的作用说明
即使修改了Prometheus的采集目标,optl-collector依然是追踪数据的核心中转节点:你的API会把追踪数据通过OTLP发送给Collector,再由Collector转发到Jaeger(这也是Jaeger能正常工作的原因)。后续如果有其他服务接入可观测性,Collector可以统一管理所有追踪、指标、日志的接收与转发,无需每个服务单独对接后端工具。
内容的提问来源于stack exchange,提问作者nop

