예: Prometheus, Grafana 및 Jaeger와 함께 OpenTelemetry 사용
이 예제에서는 메트릭 수집에 Prometheus, 대시보드 생성에 Grafana, 분산 추적 표시에 Jaeger를 사용합니다.
1. 프로젝트 만들기
Visual Studio에서 ASP.NET Core Empty 템플릿을 사용하거나 다음 .NET CLI 명령을 사용하여 간단한 웹 API 프로젝트를 만듭니다.
dotnet new web
2. 메트릭 및 활동 정의 추가
다음 코드는 API가 호출된 횟수에 대한 새 메트릭(greetings.count
)과 새 활동 소스(OtPrGrYa.Example
)를 정의합니다.
// Custom metrics for the application
var greeterMeter = new Meter("OtPrGrYa.Example", "1.0.0");
var countGreetings = greeterMeter.CreateCounter<int>("greetings.count", description: "Counts the number of greetings");
// Custom ActivitySource for the application
var greeterActivitySource = new ActivitySource("OtPrGrJa.Example");
3. API 엔드포인트 만들기
app.MapGet("/", SendGreeting);
async Task<String> SendGreeting(ILogger<Program> logger)
{
// Create a new Activity scoped to the method
using var activity = greeterActivitySource.StartActivity("GreeterActivity");
// Log a message
logger.LogInformation("Sending greeting");
// Increment the custom counter
countGreetings.Add(1);
// Add a tag to the Activity
activity?.SetTag("greeting", "Hello World!");
return "Hello World!";
}
참고 항목
API 정의는 OpenTelemetry와 관련된 항목을 사용하지 않습니다. 가시성에 대해 .NET API를 사용합니다.
4. OpenTelemetry 패키지 참조
NuGet 패키지 관리자 또는 명령줄을 사용하여 다음 NuGet 패키지를 추가합니다.
<ItemGroup>
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.9.0-beta.2" />
<PackageReference Include="OpenTelemetry.Exporter.Zipkin" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
</ItemGroup>
참고 항목
OTel API가 계속해서 발전하고 있으므로 최신 버전을 사용합니다.
5. 올바른 공급자를 사용하여 OpenTelemetry 구성
var tracingOtlpEndpoint = builder.Configuration["OTLP_ENDPOINT_URL"];
var otel = builder.Services.AddOpenTelemetry();
// Configure OpenTelemetry Resources with the application name
otel.ConfigureResource(resource => resource
.AddService(serviceName: builder.Environment.ApplicationName));
// Add Metrics for ASP.NET Core and our custom metrics and export to Prometheus
otel.WithMetrics(metrics => metrics
// Metrics provider from OpenTelemetry
.AddAspNetCoreInstrumentation()
.AddMeter(greeterMeter.Name)
// Metrics provides by ASP.NET Core in .NET 8
.AddMeter("Microsoft.AspNetCore.Hosting")
.AddMeter("Microsoft.AspNetCore.Server.Kestrel")
.AddPrometheusExporter());
// Add Tracing for ASP.NET Core and our custom ActivitySource and export to Jaeger
otel.WithTracing(tracing =>
{
tracing.AddAspNetCoreInstrumentation();
tracing.AddHttpClientInstrumentation();
tracing.AddSource(greeterActivitySource.Name);
if (tracingOtlpEndpoint != null)
{
tracing.AddOtlpExporter(otlpOptions =>
{
otlpOptions.Endpoint = new Uri(tracingOtlpEndpoint);
});
}
else
{
tracing.AddConsoleExporter();
}
});
이 코드는 ASP.NET Core 계측을 사용하여 ASP.NET Core에서 메트릭과 활동을 가져옵니다. 또한 메트릭과 추적에 대한 Metrics
공급자와 ActivitySource
공급자를 각각 등록합니다.
이 코드는 ASP.NET Core를 사용하여 엔드포인트를 호스트하는 메트릭에 Prometheus 내보내기를 사용하므로 다음을 추가해야 합니다.
// Configure the Prometheus scraping endpoint
app.MapPrometheusScrapingEndpoint();
6. 프로젝트 실행
프로젝트를 실행한 다음, 브라우저 또는 curl을 사용하여 API에 액세스합니다.
curl -k http://localhost:7275
페이지를 요청할 때마다 만들어진 인사말 수의 수가 증가합니다. 경로 /metrics
를 통해 동일한 기본 URL을 사용하여 메트릭 엔드포인트에 액세스할 수 있습니다.
6.1 로그 출력
코드의 로깅 문은 ILogger
를 사용하여 출력됩니다. 출력이 콘솔로 전달되도록 콘솔 공급자는 기본적으로 활성화됩니다.
.NET에서 로그를 송신하는 방법에는 몇 가지 옵션이 있습니다.
stdout
및stderr
출력은 Kubernetes와 같은 컨테이너 시스템에 의해 로그 파일로 리디렉션됩니다.- ILogger와 통합할 로깅 라이브러리를 사용하면 Serilog 또는 NLog가 포함됩니다.
- 아래에 표시된 OTLP 또는 Azure Monitor 내보내기와 같은 OTel에 대한 로깅 공급자를 사용합니다.
6.2 메트릭에 액세스
/metrics
엔드포인트를 사용하여 메트릭에 액세스할 수 있습니다.
curl -k https://localhost:7275/
Hello World!
curl -k https://localhost:7275/metrics
# TYPE greetings_count counter
# HELP greetings_count Counts the number of greetings
greetings_count 1 1686894204856
# TYPE current_connections gauge
# HELP current_connections Number of connections that are currently active on the server.
current_connections{endpoint="127.0.0.1:7275"} 1 1686894204856
current_connections{endpoint="[::1]:7275"} 0 1686894204856
current_connections{endpoint="[::1]:5212"} 1 1686894204856
...
메트릭 출력은 엔드포인트가 요청된 시점에 메트릭의 스냅샷입니다. 결과는 Prometheus 설명 형식으로 제공되며, 이는 사람이 읽을 수 있지만 Prometheus에서 더 잘 이해할 수 있습니다. 해당 항목은 다음 단계에서 다룹니다.
6.3 추적에 액세스
서버에 대한 콘솔을 보면 콘솔 추적 내보내기에서 출력이 표시되는데, 이는 사람이 읽을 수 있는 형식으로 정보를 출력합니다. 여기에는 사용자 지정 ActivitySource
및 ASP.NET Core의 두 가지 활동이 표시됩니다.
Activity.TraceId: 2e00dd5e258d33fe691b965607b91d18
Activity.SpanId: 3b7a891f55b97f1a
Activity.TraceFlags: Recorded
Activity.ParentSpanId: 645071fd0011faac
Activity.ActivitySourceName: OtPrGrYa.Example
Activity.DisplayName: GreeterActivity
Activity.Kind: Internal
Activity.StartTime: 2023-06-16T04:50:26.7675469Z
Activity.Duration: 00:00:00.0023974
Activity.Tags:
greeting: Hello World!
Resource associated with Activity:
service.name: OTel-Prometheus-Grafana-Jaeger
service.instance.id: e1afb619-bc32-48d8-b71f-ee196dc2a76a
telemetry.sdk.name: opentelemetry
telemetry.sdk.language: dotnet
telemetry.sdk.version: 1.5.0
Activity.TraceId: 2e00dd5e258d33fe691b965607b91d18
Activity.SpanId: 645071fd0011faac
Activity.TraceFlags: Recorded
Activity.ActivitySourceName: Microsoft.AspNetCore
Activity.DisplayName: /
Activity.Kind: Server
Activity.StartTime: 2023-06-16T04:50:26.7672615Z
Activity.Duration: 00:00:00.0121259
Activity.Tags:
net.host.name: localhost
net.host.port: 7275
http.method: GET
http.scheme: https
http.target: /
http.url: https://localhost:7275/
http.flavor: 1.1
http.user_agent: curl/8.0.1
http.status_code: 200
Resource associated with Activity:
service.name: OTel-Prometheus-Grafana-Jaeger
service.instance.id: e1afb619-bc32-48d8-b71f-ee196dc2a76a
telemetry.sdk.name: opentelemetry
telemetry.sdk.language: dotnet
telemetry.sdk.version: 1.5.0
첫 번째는 생성된 내부 사용자 지정 활동입니다. 두 번째는 요청에 대한 ASP.NET에서 생성되며 HTTP 요청 속성에 대한 태그를 포함합니다. 둘 다 동일한 TraceId
를 가지고 있다는 것을 알 수 있으며, 이는 단일 트랜잭션을 식별하고 분산 시스템에서 트랜잭션과 관련된 각 서비스의 추적을 상호 연결하는 데 사용할 수 있습니다. ID는 HTTP 헤더로 전송됩니다. ASP.NET Core는 요청을 받을 때 아무것도 없는 경우 TraceId
를 할당합니다. HttpClient
에는 기본적으로 아웃바운드 요청에 헤더가 포함됩니다. 각 활동에는 각 활동을 고유하게 식별하는 TraceId
와 SpanId
의 조합인 SpanId
가 있습니다. Greeter
활동은 HTTP 활동의 SpanId
에 매핑되는 ParentSpanId
를 통해 HTTP 활동에 부모로 지정됩니다.
이후 단계에서는 이 데이터를 Jaeger에 공급하여 분산 추적을 시각화합니다.
7. Prometheus를 사용하여 메트릭 수집
Prometheus는 메트릭 수집, 집계, 시계열 데이터베이스 시스템입니다. 각 서비스에 대한 메트릭 엔드포인트를 사용하여 이를 구성하면 주기적으로 값을 스크랩하여 시계열 데이터베이스에 저장합니다. 그런 다음, 필요에 따라 분석하고 처리할 수 있습니다.
Prometheus 형식으로 노출되는 메트릭 데이터는 프로세스 메트릭의 지정 시간 스냅샷입니다. 메트릭 엔드포인트에 대한 요청이 있을 때마다 현재 값을 보고합니다. 현재 값이 흥미로울 수 있지만, 이를 과거 값과 비교하여 추세를 확인하고 값이 이례적인지 감지할 때 더 가치가 있습니다. 일반적으로 서비스는 하루 중 특정 시간대나 연말 쇼핑과 같은 세계 이벤트에 따라 사용량이 급증합니다. 과거 추세와 비교하여 값이 비정상적인지 아니면 시간이 지남에 따라 메트릭이 서서히 악화되는지 감지할 수 있습니다.
이 프로세스는 이러한 메트릭 스냅샷의 기록을 저장하지 않습니다. 프로세스에 해당 기능을 추가하는 것은 리소스 집약적일 수 있습니다. 또한 분산 시스템에서는 일반적으로 각 노드의 인스턴스가 여러 개 있으므로, 모든 노드에서 메트릭을 수집한 다음 집계하고 이를 과거 값과 비교하고자 할 것입니다.
7.1 Prometheus 설치 및 구성
https://prometheus.io/download/에서 플랫폼에 대한 Prometheus를 다운로드하고 다운로드의 콘텐츠를 추출합니다.
실행 중인 서버의 출력 상단을 확인하여 http 엔드포인트의 포트 번호를 가져옵니다. 예시:
info: Microsoft.Hosting.Lifetime[14]
Now listening on: https://localhost:7275
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5212
Prometheus YAML 구성 파일을 수정하여 HTTP 스크래핑 엔드포인트의 포트를 지정하고 더 낮은 스크래핑 간격을 설정합니다. 예시:
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: "prometheus"
# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.
scrape_interval: 1s # poll very quickly for a more responsive demo
static_configs:
- targets: ["localhost:5212"]
Prometheus를 시작하고 실행 중인 포트에 대한 출력(일반적으로 9090)을 확인합니다.
>prometheus.exe
...
ts=2023-06-16T05:29:02.789Z caller=web.go:562 level=info component=web msg="Start listening for connections" address=0.0.0.0:9090
브라우저에서 이 URL을 엽니다. 이제 Prometheus UI에서 메트릭을 쿼리할 수 있습니다. 다음 이미지에서 강조 표시된 단추를 사용하여 사용 가능한 모든 메트릭을 표시하는 메트릭 탐색기를 엽니다.
greetings_count
메트릭을 선택하여 값 그래프를 확인합니다.
8. Grafana를 사용하여 메트릭 대시보드 만들기
Grafana는 Prometheus 또는 기타 데이터 원본을 기반으로 대시보드와 경고를 만들 수 있는 대시보드 제품입니다.
플랫폼에 대한 지침에 따라 https://grafana.com/oss/grafana/에서 Grafana의 OSS 버전을 다운로드하고 설치합니다. 설치되면 Grafana는 일반적으로 포트 3000에서 실행되므로 브라우저에서 http://localhost:3000
를 엽니다. 로그인해야 합니다. 기본 사용자 이름과 암호는 모두 admin
입니다.
햄버거 메뉴에서 연결을 선택한 다음, 텍스트 prometheus
를 입력하여 엔드포인트 형식을 선택합니다. Prometheus 데이터 원본 만들기를 선택하여 새 데이터 원본을 추가합니다.
다음 속성을 설정해야 합니다.
- Prometheus 서버 URL: 해당하는 포트를 변경하는
http://localhost:9090/
저장 & 테스트를 선택하여 구성을 확인합니다.
성공 메시지가 표시되면 대시보드를 구성할 수 있습니다. 성공 메시지 팝업에 표시된 대시보드 빌드 링크를 클릭합니다.
시각화 추가를 선택한 다음, 방금 데이터 원본으로 추가한 Prometheus 데이터 원본을 선택합니다.
대시보드 패널 디자이너가 표시됩니다. 화면 아래쪽에서 쿼리를 정의할 수 있습니다.
greetings_count
메트릭을 선택한 다음, 쿼리 실행을 선택하여 결과를 확인합니다.
Grafana를 사용하면 다양한 메트릭을 추적하는 정교한 대시보드를 디자인할 수 있습니다.
.NET의 각 메트릭은 데이터를 분할하는 데 사용할 수 있는 키-값 쌍인 추가 차원이 있을 수 있습니다. ASP.NET 메트릭은 모두 카운터에 적용할 수 있는 여러 차원을 특징으로 합니다. 예를 들어 Microsoft.AspNetCore.Hosting
의 current-requests
카운터는 다음과 같은 차원을 갖습니다.
Attribute | Type | 설명 | 예제 | 현재 상태 |
---|---|---|---|---|
method |
string |
HTTP 요청 메서드입니다. | Always | |
scheme |
string |
사용된 프로토콜을 식별하는 URI 체계입니다. | http : https |
Always |
host |
string |
요청을 받은 로컬 HTTP 서버의 이름입니다. | localhost |
Always |
port |
int |
요청을 받은 로컬 HTTP 서버의 포트입니다. | 8080 |
기본값이 아닌 경우 추가됨(http의 경우 80, https의 경우 443) |
Grafana의 그래프는 일반적으로 각각의 고유한 차원 조합에 따라 분할됩니다. Grafana 쿼리에서 차원을 사용하여 데이터를 필터링하거나 집계할 수 있습니다. 예를 들어 그래프 current_requests
를 그리면 각 차원 조합에 따라 분할된 값이 표시됩니다. 호스트만을 기준으로 필터링하려면 Sum
작업을 추가하고 host
를 레이블 값으로 사용합니다.
9. Jaeger를 사용한 분산 추적
6단계에서는 분산 추적 정보가 콘솔에 노출되는 것을 확인했습니다. 이 정보는 활동의 작업 단위를 추적합니다. 요청 처리를 나타내기 위해 ASP.NET 활동과 같은 일부 활동은 플랫폼에서 자동으로 생성되며 라이브러리와 앱 코드도 활동을 만들 수 있습니다. 인사말 예제에는 Greeter
활동이 있습니다. 활동은 TraceId
, SpanId
, ParentId
태그를 사용하여 상관 관계가 지정됩니다.
분산 시스템의 각 프로세스는 자체적인 활동 정보 스트림을 생성하며, 메트릭과 마찬가지로 각 트랜잭션에 대해 수행된 작업을 시각화할 수 있도록 활동을 수집, 저장, 상호 연결하는 시스템이 필요합니다. Jaeger는 이러한 수집과 시각화를 가능하게 하는 오픈 소스 프로젝트입니다.
https://www.jaegertracing.io/download/에서 플랫폼을 위한 Jaeger의 최신 바이너리 배포 아카이브를 다운로드합니다.
그런 다음, 쉽게 액세스할 수 있는 로컬 위치에 다운로드를 추출합니다. jaeger-all-in-one(.exe) 실행 파일을 실행합니다.
./jaeger-all-in-one --collector.otlp.enabled
콘솔 출력을 확인하여 gRPC를 통해 OTLP 트래픽을 수신 대기하는 포트를 찾습니다. 예시:
{"level":"info","ts":1686963686.3854616,"caller":"otlpreceiver@v0.78.2/otlp.go:83","msg":"Starting GRPC server","endpoint":"0.0.0.0:4317"}
이 출력은 0.0.0.0:4317
에서 수신 대기 중임을 알려 주므로 해당 포트를 OTLP 내보내기 대상으로 구성할 수 있습니다.
프로젝트에 대한 AppSettings.json
파일을 열고 다음 줄을 추가하여 해당하는 경우 포트를 변경합니다.
"OTLP_ENDPOINT_URL" : "http://localhost:4317/"
속성 변경을 선택하고 추적 정보를 Jaeger로 보낼 수 있도록 인사말 프로세스를 다시 시작합니다.
이제 웹 브라우저의 http://localhost:16686/
에서 Jaeger UI를 볼 수 있습니다.
추적 목록을 보려면 서비스 드롭다운에서 OTel-Prometheus-grafana-Jaeger
를 선택합니다. 추적을 선택하면 추적의 일부로 활동의 간트 차트가 표시되어야 합니다. 각 작업을 클릭하면 활동에 대한 자세한 내용이 표시됩니다.
분산 시스템에서 모든 프로세스에서 동일한 Jaeger 설치로 추적을 전송하여 시스템 전체에서 트랜잭션의 상관 관계를 지정하고자 할 수 있습니다.
앱이 자신에 대한 HTTP 호출을 만들게 하면 앱을 좀 더 흥미롭게 만들 수 있습니다.
애플리케이션에
HttpClient
팩터리 추가builder.Services.AddHttpClient();
중첩된 인사말 호출을 만들기 위한 새로운 엔드포인트 추가
app.MapGet("/NestedGreeting", SendNestedGreeting);
추적할 수도 있는 HTTP 호출을 만들도록 엔드포인트를 구현합니다. 이 경우 인공 루프에서 자신을 다시 호출합니다(실제로는 데모 시나리오에만 적용 가능).
async Task SendNestedGreeting(int nestlevel, ILogger<Program> logger, HttpContext context, IHttpClientFactory clientFactory) { // Create a new Activity scoped to the method using var activity = greeterActivitySource.StartActivity("GreeterActivity"); if (nestlevel <= 5) { // Log a message logger.LogInformation("Sending greeting, level {nestlevel}", nestlevel); // Increment the custom counter countGreetings.Add(1); // Add a tag to the Activity activity?.SetTag("nest-level", nestlevel); await context.Response.WriteAsync($"Nested Greeting, level: {nestlevel}\r\n"); if (nestlevel > 0) { var request = context.Request; var url = new Uri($"{request.Scheme}://{request.Host}{request.Path}?nestlevel={nestlevel - 1}"); // Makes an http call passing the activity information as http headers var nestedResult = await clientFactory.CreateClient().GetStringAsync(url); await context.Response.WriteAsync(nestedResult); } } else { // Log a message logger.LogError("Greeting nest level {nestlevel} too high", nestlevel); await context.Response.WriteAsync("Nest level too high, max is 5"); } }
이렇게 하면 각 수준이 이전 호출의 응답을 대기하므로 요청에 대한 피라미드 모양의 더 흥미로운 그래프가 생성됩니다.
.NET