NET6 Core distributed tracing

分散式追蹤系統

系統架構從單體轉變成微服務之後,使用者發出的單次請求往往會涉及到多個服務之間的呼叫,以往採用的日誌服務也較難以窺見全貌,當我們需要追蹤某一次請求中間發生了那些事情,我們期望能獲取的資訊不外乎是這次的請求,總共經過哪些服務,每個服務花費了多久的時間,呼叫的順序等等,這就是分散式追蹤系統能夠幫我們做到的事情。只不過每一家系統的 API 都不太一樣,所以 W3C 也有提供一份Trace Context - W3C,讓大家都用同一個標準來實現分散式追蹤。

在這之前看到一堆像是 RequestId, TraceId, SpanId, ParentId, ActionId,可能還有很多我沒有列出來的名詞,真的是有看沒有懂,現在至少可以從 W3C 的建議中明確知道一些名詞與他們的用途,其他的應該就是各家廠商自己的定義了,那就有用到再說囉

說明

依據 Trace Context - W3C。這份建議提供了一個標準的定義,讓各家廠商可以遵循,使得各家的追蹤系統可以不在各自為政。所以在version00的情況下,traceparent的格式就是下面這四個東西的組成

  1. version
  2. trace-id
  3. parent-id/span-id
  4. trace-flags

所以整個概念就像是下面這張圖一樣

圖片取自Using W3C Trace Context standard in distributed tracing

這條呼叫鍊上,則是共用同一個 traceId,每一個應用程式自己的 scope 就是用相同的 spanId,在這條呼叫鍊的下一個應用程式則會是另外一個 spanId,前一個應用程式需要將 traceparenttracestate 這兩個 header 資訊正確的傳遞給下一個應用程式。

事前準備

日誌服務:Seq

1
2
3
4
5
# create volume folder
mkdir -p D:\docker-volumes\seq

# docker-cli
docker run --name seq -d --restart unless-stopped -e ACCEPT_EULA=Y -v D:\docker-volumes\seq:/data -p 8090:80 -p 5341:5341 datalust/seq

REF:使用 Serilog 和 Seq 紀錄 Log

分佈式追踪系統:zipkin

直接使用下列指令可以透過 docker 執行 zipkin,資料則是暫時儲存於記憶體中不保留,若需要保留資料,zipkin也支援elasticSearchmySqlcassandra

REF:Storage - zipkin Github

1
docker run -d -p 9411:9411 openzipkin/zipkin-slim:2

測試專案

測試目標
確認當前 .NET6 Web 網站是否有遵循 W3C 的 traceparent context

測試情境

測試環境由三個應用程式構成

Web(clientApp) -> Proxy(FrontEndApp) -> WebAPI(BackEndApp)

範例程式碼:Github:distributed-tracing-demo

使用者透過瀏覽器開啟網頁,Web 從後端呼叫 Proxy,而 Proxy 則轉發請求給 WebAPI 查詢取得資料後回應給使用者

測試步驟

開啟 clientApp 之後,透過中斷點檢視 FrontEndAppBackEndApp 的 request Header

frontEnd Header

backEnd Header

可以注意到 header 裡面的 keyValuePair 有一組是traceparent , 00-6ef05abe949ce579cc110ab5b289df14-be6d36f4ecc27214-00

traceparent的格式為 version "-" version-format,而version-formatversion00的定義,又由三個部分組成trace-id "-" parent-id "-" trace-flags

實作分佈式跟踪

實作日誌紀錄

接下來的就是要將這些資訊放到日誌服務 Seq 之內,此處選擇使用 Serilog 套件

1
2
3
4
5
6
7
8
# dotnet Core Serilog 套件
dotnet add package Serilog.AspNetCore
# 讓 Log 可以在 Console 顯示
dotnet add package Serilog.Sinks.Console
# 讓 Log 可以送到 Seq
dotnet add package Serilog.Sinks.Seq
# 使其支援 W3C traceparent
dotnet add package Serilog.Enrichers.Span
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// program.cs
using Serilog;

Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
// .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
.Enrich.WithSpan()
.WriteTo.Console()
.WriteTo.Seq("http://localhost:5341")
.CreateLogger();

try
{
// 原先內容
// ... 略
builder.Host.UseSerilog();
// ... 略
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}

確認 Seq 是否有紀錄到正確資訊

開啟 frontEndApp 跟 backendApp,並透過中斷點檢視 backendApp所接收到的 traceparent

接著到 seq 查看是否有紀錄TraceIdSpanId

使用 zipkin 檢視 tracing 紀錄

安裝 nuget 套件(照著套件名稱搜尋,並勾選 prerelease)

1
2
3
4
5
6
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.2.0-rc5" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol.Logs" Version="1.0.0-rc9.2" />
<PackageReference Include="OpenTelemetry.Exporter.Zipkin" Version="1.2.0-rc5" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.0.0-rc9.2" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.0.0-rc9.2" />
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.0.0-rc9.2" />

將應用程式的資料直接透過 exporter 傳遞給 zipkin

1
2
3
4
5
6
7
// program.cs
// .略.
builder.Services.AddControllersWithViews();
builder.Services.AddOpenTelemetryTracing(b => b
.SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(builder.Configuration.GetValue<string>("Zipkin:ServiceName")))
.AddAspNetCoreInstrumentation()
.AddZipkinExporter());

appsetting.json裡面的ServiceName是提供給 zipkin 顯示的服務名稱

1
2
3
4
5
6
7
8
//appsetting.json
{
// .略.
"Zipkin": {
"ServiceName": "frontendApp",
"Endpoint": "http://localhost:9411/api/v2/spans"
}
}

REF

  1. 3.2 Traceparent Header - W3C
  2. Serilog.Enrichers.Span - Github
  3. Using W3C Trace Context standard in distributed tracing
  4. Improvements in .NET Core 3.0 for troubleshooting and monitoring distributed apps
  5. zipkin Docker images - Github
  6. 使用 OpenTelemetry 來追蹤 ASP.NET Core