只有累積,沒有奇蹟

2023年2月6日 星期一

[NETCore] 如何設定 ASP.NET Core 健康檢查(Health Check)功能

前言
過去當應用程式開發完成之後,另外一項重要的事情就是建立上線後的監控機制,尤其是重要性高的服務更是不可或缺的事情,有遇過公司是在站台底下放置一個檔案內容文字是OK,在透過工具固定時間去確認網站底下這檔案是否有正常回傳,就可代表網站是否存活者,各種不同的實作方式都可以達到此目的; ASP.NET Core 2.2 開始提供 Health Check 中介層 (Middleware),透過 HTTP 方式可以即時取得應用程式的健康狀況,在使用與設定上容易上手,此篇介紹在 ASP.NET Core 中如何使用及設定 Health Check 的方式,即時的取得應用程式健康狀況若有問題或是錯誤的地方歡迎網路的高手大大給予指導

基本使用
Health Check 為 ASP.NET Core 2.2 內建,因此使用前不需要透過 nuget 下載任何 package 套件,只需要簡單的註冊服務就可以使用,首先在  Sartup.cs  中的 ConfigureServices 方法中加上 AddHealthChecks 註冊 Health Checks 服務,接著在 configure 中新增 UseHealthChecks middleware 並指定位置,即可完成基本的 Health Chcek 健康檢查的應用,代碼如下
public void ConfigureServices(IServiceCollection services)
{
    services.AddHealthChecks();
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseHealthChecks("/health");

    app.Run(async (context) =>
    {
        await context.Response.WriteAsync("Navigate to /health to see the health status.");
    });
} 
執行應用程式,透過瀏覽器開啟 /health 就可以看到回傳的結果

HealthCheckOptions
上面介紹了最基本的使用方式,實務上常常會需要得到更多資訊,有時候可能會需要自定義 Health Check 回傳的 HTTP Status 狀態,或是輸出不同的格式情境時,就可以使用  HealthCheclOptions  來自訂不同的檢查行為,以下範例是自訂檢查輸出為 Json,並輸出檢查細節的所有內容資訊
app.UseHealthChecks("/health", new HealthCheckOptions
{
    ResponseWriter = async (context, report) =>
    {
        var json = JsonConvert.SerializeObject(report);
        context.Response.ContentType = "application/json";
        await context.Response.WriteAsync(json);
    }
});
回傳結果為目前應用程式狀態是正常 (Status=2=Health),以及 Health Check 所花費的時間
{
    "Entries": {},
    "Status": 2,
    "TotalDuration": "00:00:00.0000048"
}
當然如果只想要內容跟結果,可以調整 HealthCheckOptions 內容輸出指定需要的資訊,舉例來說我只想看 Status 跟 error 內容,代碼如下
app.UseHealthChecks("/health",
    new HealthCheckOptions
    {
        ResponseWriter = async (context, report) =>
        {
            var result = JsonConvert.SerializeObject(
                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);
        }
    });
回傳結果如下
{
    "Status": 2,
    "error": []
}

檢查依賴項目健康狀態
應用程式在開發中會使用到資料庫或是不同服務,BeatPulse 提供多個檢查項目的 API,透過這些 API 可以檢查依賴內容的健康狀況,舉例來說可以檢查 SQL、Redis、Network、Kafka、Aws.S3 等不同依賴於應用程式所使用的服務,如果要使用時後僅需要下載對應 package 和使用擴充方法就健康資訊。
舉例來說我的 ASP.NET Core API 有資料庫是使用 MS SQL,Cache 是使用 Redis,另外還有使用到第三方 API,以上可以在 Health Check 中設定,首先須先下載用到的 package
Install-Package AspNetCore.HealthChecks.SqlServer
Install-Package AspNetCore.HealthChecks.Redis
Install-Package AspNetCore.HealthChecks.Uris
接著在ConfigureServices 方法中加上 AddSqlServer、AddRedis、AddUrlGroup 等擴充方法,並指定其連線字串及網址內容 
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

    services.AddHealthChecks()
        .AddSqlServer(Configuration["ConnectionStrings:DefaultConnection"], name: "SQL Server HealthCheck")
        .AddRedis(Configuration["ConnectionStrings:RedisConn"], name: "Redis HealthCheck")
        .AddUrlGroup(new Uri("https://marcus116.blogspot.com"), "ThirdParty API HealthCheck", HealthStatus.Degraded);
}
執行應用程式並開啟 health 頁面,可以看到檢查 MSSQL、Redis 以及外部服務 Domain 的結果
在每個 Health Check 方法也支援參數指定其標籤、名稱、失敗時的狀態等資訊,以下為 SQL Server 檢查,如果有需要的話也可以建立多個 SQL Server 健康檢查
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

    services.AddHealthChecks()
                .AddSqlServer(
                    connectionString: Configuration["ConnectionStrings:DefaultConnection"],
                    healthQuery: "select 1",
                    name: "MSSQL Check",
                    failureStatus: HealthStatus.Degraded,
                    tags: new string[] {"database", "sqlServer"});
}
更多細節及說明可以參考 GitHub 上的官網內容 : 傳送門 

自訂健康檢查
假設在 AspNetCore.Diagnostics.HealthChecks 都不符合您的需求,可以透過實作  IHealthCheck  介面來自己定義需要健檢的項目內容,舉例來說可以檢查應用程式記憶體 (Memory) 的使用量,如果使用超過設定值時就會報告狀態為不健康有可能需要降級的動作,下列代碼為參考 MSDN 定義的 MemoryHealthCheck 方法與 MemoryCheckOptions 類別,定義 Memory 上限水位為 1G
public class MemoryHealthCheck : IHealthCheck
{
    private readonly IOptionsMonitor<MemoryCheckOptions> _options;

    public MemoryHealthCheck(IOptionsMonitor<MemoryCheckOptions> options)
    {
        _options = options;
    }

    public string Name => "memory_check";

    public Task<HealthCheckResult> CheckHealthAsync(
        HealthCheckContext context,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        var options = _options.Get(context.Registration.Name);

        // Include GC information in the reported diagnostics.
        var allocated = GC.GetTotalMemory(forceFullCollection: false);
        var data = new Dictionary<string, object>()
        {
            { "AllocatedBytes", allocated },
            { "Gen0Collections", GC.CollectionCount(0) },
            { "Gen1Collections", GC.CollectionCount(1) },
            { "Gen2Collections", GC.CollectionCount(2) },
        };

        var status = (allocated < options.Threshold) ?
            HealthStatus.Healthy : HealthStatus.Unhealthy;

        return Task.FromResult(new HealthCheckResult(
            status,
            description: "Reports degraded status if allocated bytes " +
                         $">= {options.Threshold} bytes.",
            exception: null,
            data: data));
    }
}

public class MemoryCheckOptions
{
    // Failure threshold (in bytes)
    public long Threshold { get; set; } = 1024L * 1024L * 1024L;
}
接著可以透過兩種方式加入檢查

AddMemoryHealthCheck 擴充方法
要來寫  AddMemoryHealthCheck  擴充方法,在 ConfigureServices 才可以用到
public static class GCInfoHealthCheckBuilderExtensions
{
    public static IHealthChecksBuilder AddMemoryHealthCheck(
        this IHealthChecksBuilder builder,
        string name,
        HealthStatus? failureStatus = null,
        IEnumerable<string> tags = null,
        long? thresholdInBytes = null)
    {
        // Register a check of type GCInfo.
        builder.AddCheck<Startup.MemoryHealthCheck>(
            name, failureStatus ?? HealthStatus.Degraded, tags);

        // Configure named options to pass the threshold into the check.
        if (thresholdInBytes.HasValue)
        {
            builder.Services.Configure<Startup.MemoryCheckOptions>(name, options =>
            {
                options.Threshold = thresholdInBytes.Value;
            });
        }

        return builder;
    }
}
下一步就是與之前說明的相同,到 ConfigureServices 加上 AddMemoryHealthCheck 方法,另外還想知道自訂檢查中的 GC 記憶體用量狀況 (GenCollections 0、1、2),因此 Config 內容也調整為輸出全部資訊
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

    services.AddHealthChecks()
        .AddMemoryHealthCheck("Memory Health Check");
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    app.UseHealthChecks("/health",
        new HealthCheckOptions
        {
            ResponseWriter = async (context, report) =>
            {
                var result = JsonConvert.SerializeObject(report);
                context.Response.ContentType = MediaTypeNames.Application.Json;
                await context.Response.WriteAsync(result);
            }
        });

    // 省略 
}
重新執行應用程式,成功輸出其內容資訊

AddCheck<T>
使用  AddCheck  注入的方式加入檢查,如下  
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

    services.AddHealthChecks()
        .AddCheck<MemoryHealthCheck>("Memory Health Check");

        services.AddSingleton<MemoryHealthCheck>();
    // 省略
}
結果與使用 AddMemoryHealthCheck 相同,這裡就不在重複貼。

感想
介紹了很多 ASP.NET Core 新功能 Health Check 的基本使用,相信對於在 ASP.NET Core 中使用 health check 了解更多,以上算是簡單的使用情境說明,當應用程式不只一台或是要健檢的項目變多的時候,確認 health check 回傳的 status 狀況上就會變得不單純,因此下一篇會介紹 Health Check UI 的設定及如何使用 Publisher 方式推到某一台方便進行監控及管理,希望這篇介紹可以有幫助到有需要的朋友,謝謝

參考
Health checks in ASP.NET Core
AspNetCore.Diagnostics.HealthChecks
ASP.NET Core MVC - 2.2 Health Checks
AspNetCore 2.2 新特性---HealthCheck
How to set up ASP.NET Core 2.2 Health Checks 
Using the Microsoft.AspNetCore.HealthChecks Package
Health Checks in ASP.NET Core

0 意見:

張貼留言

Copyright © m@rcus 學習筆記 | Powered by Blogger

Design by Anders Noren | Blogger Theme by NewBloggerThemes.com