Sanırım “uygulamalarımızın sağlığı” konusu, mimari yaklaşım farketmeksizin hepimiz için her zaman önemli ve ortak bir endişe noktası olmuştur.
Bildiğimiz gibi uygulamalarımızın sağlıklı olup olmadığını görünür bir hale getirebilmek veya kolay bir yoldan öğrenebilmek, bir çok durum karşısında uygulamanın riskini en aza indirgemektedir.
Özellikle uygulamalarımız bir load-balancer arkasında multi instance olarak çalışıyorsa, trafiğin yönlendirilme işlemi veya uygulamanın yeni bir versiyona update işlemi sırasında, trafiğin hangi instance’lara gönderilip gönderilmeyeceği bilgisini load-balancer’a health check endpoint’leri ile bildirmemiz gerekmektedir. Böylece trafiğin, henüz tüm bağımlılıkları ile ayağa kalkmamış bir instance’a yönlendirilmemesini sağlamış oluyoruz.
Örneğin microservice’lerimizi kubernetes ile orchestrate etmek istiyorsak, kubernetes’in ilgili container’ın trafiği kabul etmeye hazır olup olmadığına karar verebilmesi için uygulamanın health check endpoint’ini container’ın rediness probe’u içerisinde tanımlamamız gerekmektedir.
Bu makale kapsamında ise, ASP.NET Core 2.2 den bu yana built-in olarak bizlere sunulan health checks özelliğinden bahsedip, Azure Application Insights ile nasıl entegre bir hale getirebiliriz konusuna değinmeye çalışacağım.
ASP.NET Core Health Checks, “Sql Server“, “MySql“, “Oracle“, “Mongo“, “RabbitMQ” veya “Elasticsearch” gibi uygulamanın bağımlılıklarını da dikkate alarak, health check’ler tanımlayabilmemize olanak sağlayan harika bir özelliktir.
Haydi kodlayarak bakalım.
ASP.NET Core Health Checks’in Implementasyonu
Öncelikle örnek amaçlı aşağıdaki gibi bir ASP.NET Core 3.1 Web API projesi oluşturalım.
dotnet new webapi -n Todo
Ardından “Microsoft.AspNetCore.Diagnostics.HealthChecks” paketini NuGet üzerinden projeye dahil edelim.
dotnet add package Microsoft.AspNetCore.Diagnostics.HealthChecks
“Todo” ismiyle oluşturmuş olduğumuz bu API‘ın, data source olarak MongoDB kullanan bir microservice olduğunu varsayalım.
Bunun için aşağıdaki komut satırını kullanarak, Docker üzerinde hızlıca bir MongoDB instance’ı ayağa kaldıralım.
docker run -d --name my-mongo -p 27017:27017 mongo
Ardından MongoDB‘nin health durumunu gözlemleyebilmemiz için, “AspNetCore.HealthChecks.MongoDb” paketini de NuGet üzerinden projeye dahil edelim.
Şimdi API‘ın “Startup” class’ına girelim ve “ConfigureServices” method’u içererisinde health checks service’ini aşağıdaki gibi configure edelim.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
string mongoDBConnection = Configuration.GetValue("mongoDB:connection");
services.AddHealthChecks()
.AddMongoDb(mongodbConnectionString: mongoDBConnection,
name: "todo-db-check",
failureStatus: HealthStatus.Unhealthy,
tags: new string[] { "todo-api", "mongodb" });
}
Yukarıdaki kod bloğuna baktığımızda, çok basit bir şekilde MongoDB için bir health check tanımlaması yaptığımızı görebiliriz.
App settings içerisinde ise MongoDB için connection bilgilerini aşağıdaki gibi tanımlayalım.
"mongoDB:connection": "mongodb://localhost:27017"
Şimdi ise health check’i, “/hc” path’i ile aşağıdaki gibi request pipeline’ına ekleyelim.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHealthChecks("/hc");
});
Ardından ilk test işlemini gerçekleştirebilmek için, API‘ı çalıştıralım ve “/hc” endpoint’ine browser üzerinden erişelim.
Gördüğümüz gibi API, database’i ile birlikte sağlıklı bir durumda.
Health check output’u default olarak “text/plain” dir. Bu nedenle sadece “Healthy” veya “Unhealthy” gibi bir response görürüz. Tüm bağımlılıklar dahil edilmiş detaylı bir health check output’u görebilmek için ise, health check’i request pipeline’ına eklerken “HealthCheckOptions” üzerinden “ResponseWriter” özelliği ile özelleştirmemiz gerekmektedir.
Custom bir kod yazmamak için ise, “AspNetCore.HealthChecks.UI.Client” paketi ile gelen “UIResponseWriter.WriteHealthCheckUIResponse” writer’ını kullanabiliriz.
Bunun için öncelikle projeye “AspNetCore.HealthChecks.UI” paketini NuGet üzerinden dahil edelim. Ardından health check’i request pipeline’ına eklerken, aşağıdaki gibi configure edelim.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHealthChecks("/hc", new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
});
Ardından tekrar API‘ı çalıştıralım ve “/hc” endpoint’ine browser üzerinden erişelim.
Gördüğümüz gibi şimdi daha detaylı bir health check output’una sahibiz.
Kendi Health Check’imizi Implemente Edelim
MongoDB‘nin health durumunu gözlemleyebilmek için, bizlere hazır sunulan “AspNetCore.HealthChecks.MongoDb” paketini kullandık.
NOT: Tüm hazır sunulan health checks listesine buradan erişebilirsiniz.
Peki ya custom bir ihtiyacımız varsa? Bu gibi durumlar karşısında ise, “IHealthCheck” interface’ini implemente ederek kendi health check’lerimizi oluşturabilmek mümkündür.
Örneğin “TodoHealthCheck” isminde bir class oluşturalım ve aşağıdaki gibi implemente edelim.
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Diagnostics.HealthChecks;
namespace dotnetcore_healthchecks_sample
{
public class TodoHealthCheck : IHealthCheck
{
public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
// ...
return Task.FromResult(HealthCheckResult.Healthy());
}
}
}
Ardından health checks service’ine aşağıdaki gibi dahil edelim.
services.AddHealthChecks()
.AddMongoDb(mongodbConnectionString: mongoDBConnection,
name: "todo-db-check",
failureStatus: HealthStatus.Unhealthy,
tags: new string[] { "todo-api", "mongodb" })
.AddCheck("todo-custom-check");
Hepsi bu kadar. Şimdi tekrar “/hc” endpoint’ine browser üzereinden erişelim.
Gördüğümüz gibi oluşturmuş olduğumuz custom health check, health check output’u içerisine “todo-custom-check” ismi ile dahil edildi.
Görselleştirelim
Health check output’unu görselleştirebilmemiz için, çok da hoş bir UI mevcut. Bunun için projeye “AspNetCore.HealthChecks.UI” paketini NuGet üzerinden dahil etmemiz gerekiyor.
Bir önceki adımda, detaylı bir health check output’u elde edebilmek için zaten bu paketi projeye dahil etmiştik. Şimdi ise etkinleştirebilmek için configure etmemiz gerekmektedir.
Öncelikle aşağıdaki gibi UI‘ı, service collection’ına dahil edelim.
services.AddHealthChecksUI(setupSettings: setup =>
{
setup.AddHealthCheckEndpoint("Todo API", "https://localhost:5555/hc");
});
Ardından request pipeline’ına, “MapHealthChecksUI” method’u ile aşağıdaki gibi ekleyelim.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHealthChecks("/hc", new HealthCheckOptions()
{
Predicate = _ => true,
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
endpoints.MapHealthChecksUI();
});
Health Checks UI endpoint’i default olarak, “/healthchecks-ui” olarak gelmektedir. Dilerseniz bu değeri “MapHealthChecksUI” method’u üzerinden özelleştirerek değiştirebilirsiniz.
Şimdi test edebilmek için, “/healthchecks-ui” endpoint’ine browser üzerinden erişelim.
UI üzerinden kolay bir şekilde uygulamanın ve bağımlılıklarının health durumlarını tarihsel olarak görebilmek mümkün. Ayrıca UI için bir storage provider’ı da seçerek, ilgili health check output’larını persist edebilmekte mümkündür. Provider listesine ise, buradan erişebilirsiniz.
Azure Application Insight’ın Kullanımı
Eğer sizde uygulamalarınızı Azure üzerinde host ediyorsanız, health check output’larını Azure Application Insight‘a göndermek iyi bir seçenek olacaktır.
Böylece uygulamalarımızın availability durumlarına özel chart’lar hazırlayabilir, herhangi bir hata anında ise alert’ler üretebiliriz.
Bunun için öncelikle projeye “AspNetcore.HealthChecks.Publisher.ApplicationInsights” paketini NuGet üzerinden dahil edelim.
NOT: Eğer Azure Application Insight‘a sahip değilseniz, buradan oluşturabilirsiniz.
Ardından health checks service’ine, Application Insights publisher’ı aşağıdaki gibi dahil edelim.
string instrumentationKey = Configuration.GetValue("azure:instrumentationKey");
services.AddHealthChecks()
.AddMongoDb(mongodbConnectionString: mongoDBConnection,
name: "todo-db-check",
failureStatus: HealthStatus.Unhealthy,
tags: new string[] { "todo-api", "mongodb" })
.AddCheck("todo-custom-check")
.AddApplicationInsightsPublisher(instrumentationKey: instrumentationKey);
Publisher’ı ekledikten sonra ise, Application Insights‘ın Azure Portal üzerinden bulabileceğimiz “instrumentationKey” bilgisini, API‘ın app settings dosyasına ekleyelim.
Hepsi bu kadar.
Şimdi API‘ı çalıştıralım ve ardından Docker üzerinde ayağa kaldırmış olduğumuz MongoDB instance’ını, aşağıdaki gibi durduralım.
Browser üzerinden “/hc” endpoint’ine eriştiğimizde, aşağıdaki gibi bir output görüyor olmalıyız.
Gördüğümüz gibi “todo-db-check” in durumu “Unhealthy” olduğu için, API‘ın da genel durumu “Unhealthy“.
Şimdi Azure Portal üzerinden, ilgili Application Insights instance’ına erişelim. Ardından “Monitoring” tab’ı altında bulunan, “Metrics” sekmesine tıklayalım.
Daha sonra filtre kısmındaki “Metric Namespace” bölümünü “azure.applicationinsights” olarak ve ardından “Metric” bölümünü “AspNetCoreHealthCheckStatus” olarak seçelim. Böylece API‘ın availability durumunu, aşağıdaki gibi görselleştirebileceğiz.
Gördüğümüz gibi API‘ın availability durumunu, zaman dilimlerine göre görselleştirebilmekteyiz. MongoDB instance’ını durdurduğumuz zaman diliminde ise, API‘ın available olmadığını yukarıdaki graph’dan görebilmekteyiz.
Ayrıca “Monitoring > Logs” sekmesi altından da daha detaylı health bilgilerine, aşağıdaki gibi “customEvents” query’leri oluşturarak erişebilmek mümkündür.
Referanslar
https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-3.1&WT.mc_id=DT-MVP-5003382
https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks