İlkay İlknur

ASP.NET Health Check Mekanizması ve Azure App Servicelerde Health Check Kullanımı

September 25, 2020

Health check mekanizmaları uygulamamızın sağlığının yerinde olup olmadığını görebildiğimiz ve gerekli durumlarda da dışarıya bildirebildiğimiz mekanizmalar. Uygulamanızı monitör ederken bu mekanizmadan gelen sonuçları değerlendirebileceğiniz gibi aynı zamanda bu API'ları load balancer veya container orchestratora verip onların bu bilgileri kullanarak trafik yönlendirmesi veya instanceları restart etmek gibi kararlar verebilmelerini sağlayabilirsiniz.

ASP.NET içerisindeki health check mekanizmasını kullanmak için aşağıdaki gibi health check middleware'ini ekleyip konfigüre edebiliriz.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddHealthChecks()
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
        endpoints.MapHealthChecks("/health");
    });
}

Uygulamamızı ayağa kaldırıp "/health" endpointine gittiğimizde sonuç olarak HTTP 200 ve bodyde de plain text olarak Healthy çıktısını alırız.

Buradaki health check mekanizması aslında kullanabileceğimiz en basit mekanizma. Amaç uygulamanız dışarıdan gelen requestlere cevap verebiliyor mu bunu anlamak. Bunun yanında tabi uygulamanız içerisinde kullandığınız çeşitli servislerin de durumlarını takip etmeniz gerekebilir. Uygulamanız database'e bağlanabiliyor mu veya redis'e erişebiliyor mu gibi durumları da kontrol etmemiz gerekebilir. Entity Framework kullanıyorsanız Microsoft tarafından geliştirilen Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore paketini projenize ekleyerek hızlı bir şekilde health check ekleyebilirsiniz.

dotnet add package Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore

Ekledikten sonra basit bir konfigürasyonla DbContext üzerinden health check mekanizmasını ayağa kaldırabiliyoruz.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddDbContext<BlogContext>(options =>
    {
        options.UseSqlServer(Configuration.GetConnectionString("Blog"));
    });
    services.AddHealthChecks()
            .AddDbContextCheck<BlogContext>();
}

Bu noktada aslında health endpointine request geldiğinde basitçe context.Database.CanConnectAsync(); kodu çalışıyor ve bunun sonucu geriye döndürülüyor. Sadece bu kodu çalıştırmak yerine database'e sorgu gönderip bu sorgunun sonucuna göre de dışarıya sonuç raporlamak isteyebilirsiniz. Bunun için DbContextCheck metodunu çağırırken customTestQuery parametresini kullanabiliriz.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddDbContext<BlogContext>(options =>
    {
        options.UseSqlServer(Configuration.GetConnectionString("Blog"));
    });
    services.AddHealthChecks()
            .AddDbContextCheck<BlogContext>(customTestQuery:(context,cancellationToken) => {
                //Buraya context kullanarak özel query yazılabilir. 
            });
}

Microsoft tarafından geliştirilen ve desteği verilen hazır health check mekanizmaları bunlar. Bunun yanında open-source olarak geliştirilen ve pek çok farklı servisin durumunu check edebileceğimiz bir kütüphane de bulunmakta. Örnek olarak redis tarafını check etmemiz gerekirse ilk olarak ilgili redis health check paketini projemize ekliyoruz.

dotnet add package AspNetCore.HealthChecks.Redis

Sonrasında aşağıdaki gibi health check mekanizmasını konfigüre edebiliriz.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddHealthChecks().AddRedis(Configuration["RedisConnectionString"]);
}

Birden fazla bağımlı servisiniz olduğunda hepsinin durumlarını ayrı ayrı check edip bunların sonucunda toplu olarak bir sonuç dönmek isteyebilirsiniz. Bunun için şu şekilde çoklu health check eklememiz mümkün.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddDbContext<BlogContext>(options =>
    {
        options.UseSqlServer(Configuration.GetConnectionString("Blog"));
    });
    services.AddHealthChecks()
            .AddDbContextCheck<BlogContext>()
            .AddRedis(Configuration.GetConnectionString("Redis"));
}

Custom Health Check Yazmak

Bazı durumlarda elimizde bulunan kütüphaneler işimizi görmeyebilir ve kendi health check kodumuzu yazmamız gerekebilir. Bunun için IHealthCheck interface'ini implemente eden bir class yaratıp içerisine kodumuzu yazabiliriz.

public class CustomHealthCheck : IHealthCheck
{
    public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        HealthCheckResult result;
        //HealthCheck kodu
        //Başarılı ise 
        result = HealthCheckResult.Healthy();
 
        //Başarısız ise 
        result = HealthCheckResult.Unhealthy();
 
        return result;
    }
}

Health Check Datasını Monitör Etmek

API üzerinden health check kodlarını çalıştırabildiğimiz gibi belirli aralıklarla bu kodların çalışıp sonucunun çeşitli araçlara publish edilmesini de otomatik olarak sağlayabiliriz. Bunun için yukarıda kullanmış olduğumuz open source proje içerisinde aşağıdaki araçlara otomatik olarak health check sonuçlarını publish eden paketler bulunmakta.

  • Application Insights
  • Datadog
  • Prometheus
  • Seq

Örneğin, health check sonuçlarını application insights'a göndermek istersek önce ilgili paketi projeye ekleyip sonra konfigüre edebiliriz.

dotnet add package AspNetCore.HealthChecks.Publisher.ApplicationInsights

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddDbContext<BlogContext>(options =>
    {
        options.UseSqlServer(Configuration.GetConnectionString("Blog"));
    });
    services.AddHealthChecks()
            .AddDbContextCheck<BlogContext>()
            .AddRedis(Configuration.GetConnectionString("Redis"))
            .AddApplicationInsightsPublisher("[Instrumentation Key]", saveDetailedReport: true);
}

Health Check Datasının Azure App Service Tarafından Kullanılması

Yazının başında health check verisinin load balancerlar, container orchestratorlar tarafında kullanılabileceğinden bahsetmiştik. Azure App Service tarafında da health check API'ları load balancer tarafından instance'ın sağlıklı olup olmadığını kontrol etme amaçlı olarak kullanılmakta. Bunu konfigüre etmek için app service sayfasında monitoringin altındaki health check seçeneğine tıklıyoruz.

Buradan health check seçeneğini enable edip, health check API'ını Azure App Service'e bildirebiliriz.

Azure App Service burada verdiğimiz endpointe her iki dakikada bir requestte bulunuyor. Bu API'dan HTTP 200-299 arası bir sonuç aldığı durumda instance'ı healthy olarak düşünmekte. Aksi bir durumda instance unhealthy olarak işaretlenmekte ve load balancer içerisinden çıkarılmakta. Azure App Service instance'ı load balancerdan çıkardıktan sonra bu endpointe requestte bulunmaya devam ediyor. Eğer sonuç tekrardan HTTP 200-299 arasında bir değere geri dönerse instance tekrardan load balancera geri dönüyor. Dönmediği takdirde Azure App Service instance'ı healthy duruma getirmek için instance'a restart atıyor.

Gördüğünüz üzere health check mekanizmaları uygulamalarımızı monitör ederken kullanabileceğimiz önemli bileşenlerden biri. Aynı zamanda load balancerlar tarafından instance'ınızın sağlıklık olup olmadığıyla ilgili kararların alınması açısından da oldukça önemli bir rol oynamakta.

Bir sonraki yazıda görüşmek üzere

Kaynaklar