ASP.NET Core 網站應用程式如何設定健康檢查

ASP.NET Core 提供了健康檢查的 Middleware,可以讓我們很輕鬆的知道應用程式目前的本身的狀態以及應用程式所依賴其他服務的狀態;也可以讓容器協調器進行重新啟動容器或是暫停服務,比如 K8s 的 Liveness、Readiness

令人驚喜的是 AspNetCore.HealthChecks.UI 提供 UI / Alert 讓監視系統變得更有效率了

開發環境

  • Windows 11
  • Rider 2022.2.2
  • .NET 6

ASP.NET Core 6 快速配置

先新增一個 ASP.NET Core WebAPI專案

dotnet new webapi -o Lab.HealthCheck.WebApi --framework net6.0

 

註冊健康檢查方法

builder.Services.AddHealthChecks()

 

註冊健康檢查 Middleware

配置一個位置給它

app.MapHealthChecks("/_hc")

 

代碼如下

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHealthChecks();
var app = builder.Build();
app.MapHealthChecks("/_hc");
app.Run();

 

執行結果

自訂健康狀態檢查

實作 IHealthCheck

public class SampleHealthCheck : IHealthCheck
{
   public Task<HealthCheckResult> CheckHealthAsync(
       HealthCheckContext context, CancellationToken cancellationToken = default)
   {
       var isHealthy = true;
       // 實作檢查方式…
       if (isHealthy)
       {
           return Task.FromResult(
               HealthCheckResult.Healthy("A healthy result."));
       }
       return Task.FromResult(
           new HealthCheckResult(
               context.Registration.FailureStatus, "An unhealthy result."));
   }
}

 

註冊自訂健康狀態檢查方法

builder.Services.AddHealthChecks()
   .AddCheck<SampleHealthCheck>("Sample");

 

AddCheck 這個方法有幾個關鍵參數

  • name:健康狀態的名稱
  • failureStatus:檢查失敗時的狀態,預設是 null,當失敗時會得到 HealthStatus.Unhealthy
  • tags:設定標籤,可以用它做為篩選條件
builder.Services.AddHealthChecks()
   .AddCheck<SampleHealthCheck>(
       "Sample",
       failureStatus: HealthStatus.Degraded,
       tags: new[] { "sample" });

更進階的用法還可以注入參數給 IHealthCheck,請參考

ASP.NET Core 中的健康狀態檢查 | Microsoft Docs

更多的健康檢查方法,可以參考以下套件,裡面實作了很多服務的檢查機制,很適合放在 readiness,要注意的是這不屬於微軟維護的範圍

https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks

 

篩選健康檢查

預設,健康檢查的 Middleware 會執行所有已經註冊的檢查方法,但是 HealthCheckOptions.Predicate 條件可以過濾只想要執行的檢查方法,看看下面的例子,則是執行 tag name 包含 sample

app.MapHealthChecks("/_hc", new HealthCheckOptions
{
   Predicate = healthCheck => healthCheck.Tags.Contains("sample")
});

 

自訂輸出

HealthCheckOptions.ResponseWriter 指定委派方法,下面的例子是輸出 OK

app.MapHealthChecks("/_hc",new HealthCheckOptions()
{
    ResponseWriter = (context, report) =>
    {
        context.Response.ContentType = MediaTypeNames.Text.Plain;
        return context.Response.WriteAsync("OK");
    }
});

 

也可以印出更多訊息

app.MapHealthChecks("/_hc",
    new HealthCheckOptions()
    {        
        ResponseWriter = async (context, report) =>
        {
            var result = JsonSerializer.Serialize(
                new
                {
                    status = report.Status.ToString(),
                    errors = report.Entries
                        .Select(e =>
                            new
                            {
                                key = e.Key,
                                value = Enum.GetName(typeof(HealthStatus), e.Value.Status)
                            })
                });
            context.Response.ContentType = MediaTypeNames.Application.Json;
            await context.Response.WriteAsync(result);
        }
    })

 

Readiness and Liveness 探針

在某一些服務(K8S) 使用一對健康檢查來區分兩種狀態

  • Liveness:表示應用程式是否已當機,且必須重新開機。
  • Readiness:表示應用程式是否正常執行,但尚未準備好接收要求;應用程式所依賴的服務還沒有準備好

HealthCheckOptions.Predicate = false,表示不執行任何註冊的健康檢查,也就是 builder.Services.AddHealthChecks() 裡面的檢查都不會執行

app.MapHealthChecks("/_readiness", new HealthCheckOptions
{
    //檢查應用程式所依賴的服務
});

app.MapHealthChecks("/_liveness", new HealthCheckOptions
{
    Predicate = _ => false//只檢查應用程式本身
});

有關 K8S 的範例,請參考

https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-6.0#kubernetes-example

 

健康檢查 UI

安裝以下套件

dotnet add package AspNetCore.HealthChecks.UI --version 6.0.5
dotnet add package AspNetCore.HealthChecks.UI.Client --version 6.0.5
dotnet add package AspNetCore.HealthChecks.UI.InMemory.Storage --version 6.0.5
dotnet add package AspNetCore.HealthChecks.Uris --version 6.0.3
dotnet add package AspNetCore.HealthChecks.NpgSql --version 6.0.3

 

註冊健康檢查 UI,並指定存放位置

builder.Services.AddHealthChecksUI()
    .AddInMemoryStorage();

 

加入 google 的官網 url 健康檢查

builder.Services.AddHealthChecks()
    .AddUrlGroup(new Uri("https://www.google.com"), "3rd api health check");

 

加入PostgreSQL 健康檢查

builder.Services.AddHealthChecks()
    .AddNpgSql(
        npgsqlConnectionString: "Host=localhost;Port=5432;Database=member_service;Username=postgres;Password=guest",
        healthQuery: "SELECT 1;",
        name: "db",
        failureStatus: HealthStatus.Unhealthy,
        tags: new[] { "db", "sql", "PostgreSQL" })
    ;

 

設定兩個端點,分別是

  • _liveness:只檢查應用程式本身
  • _readiness:檢查應用程式所依賴的服務

 

app.MapHealthChecks("/_liveness", new HealthCheckOptions()
{
    Predicate = _ => false, //只檢查應用程式本身
    ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});

 

app.MapHealthChecks("/_readiness",
    new HealthCheckOptions()
    {
        ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
    });

 

  • UIResponseWriter.WriteHealthCheckUIResponse:輸出特定的資料,這是 Health Check UI 所需要的資料,內容如下:
{
 "status": "Unhealthy",
 "totalDuration": "00:00:00.1704878",
 "entries": {
   "3rd API": {
     "data": {},
     "description": "No such host is known. (www.google.com1:443)",
     "duration": "00:00:00.1643206",
     "exception": "No such host is known. (www.google.com1:443)",
     "status": "Unhealthy",
     "tags": [
       "3rd API",
       "google"
     ]
   },
   "db": {
     "data": {},
     "duration": "00:00:00.0135077",
     "status": "Healthy",
     "tags": [
       "db",
       "sql",
       "PostgreSQL"
     ]
   }
 }
}

 

指定 Health Check UI 的位置

app.UseHealthChecksUI(options =>
{
    options.UIPath = "/_hc";
});

 

在 appsetting.json 設定

"HealthChecks-UI": {
  "HealthChecks": [
    {
      "Name": "Readiness",
      "Uri": "_readiness"
    },
    {
      "Name": "Liveness",
      "Uri": "_liveness"
    }
  ],
  "EvaluationTimeOnSeconds": 10,
  "MinimumSecondsBetweenFailureNotifications": 60
}

 

或是使用程式碼配置

builder.Services.AddHealthChecksUI(p=>
    {
        p.AddHealthCheckEndpoint("Readiness", "/_readiness");
        p.AddHealthCheckEndpoint("Liveness", "/_liveness");
    })
    .AddInMemoryStorage();

 

更多的設定可以參考

Configuration

Failure notification WebHooks

 

執行結果如下:

 

這樣就可以知道應用程式所依賴的服務有沒有問題

專案位置

sample.dotblog/Health Check/Lab.HealthCheck at master · yaochangyu/sample.dotblog (github.com)

若有謬誤,煩請告知,新手發帖請多包涵


Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET

Image result for microsoft+mvp+logo