Integritätsprüfungen (Health Checks) werden verwendet, um den Status einer App in Echtzeit zu überwachen. Durch die Integration von Webhooks werden fehlerhafte Integritätsprüfungen sofort gemeldet (z.B. MS Teams, Slack, Email, etc…). Dies ermöglicht eine schnellere und präzisere Handlungsweise und Untersuchung des Problems. Health Checks werden von der Anwendung als HTTP-Endpunkt(e) verfügbar gemacht, über den die Komponente der App-Infrastruktur und deren Wechselwirkung überprüft werden. Übliche Integritätsprüfungen überwachen u.A. :
ASP.NET Core bietet die built-in Health Checks Middleware und Bibliothek, die wir benutzen werden, um Integritätsprüfungen für unsere Anwendung zu erstellen. Im Folgenden Abschnitt zeigen wir den benötigten Setup und Konfiguration der Middleware.
Für eine primitive Nutzung reicht es, den Health Check Service in Startup.cs hinzuzufügen.
public class Startup { public void ConfigureServices(IServiceCollection services) { // ... services.AddHealthChecks(); // ... } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // ... app.UseEndpoints(endpoints => { endpoints.MapHealthChecks("/health", new HealthCheckOptions() { Predicate = _ => true, ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse }); }); } }
Über den von uns gesetzten Endpunkt „/health“ können wir nun den Status der Anwendung abfragen. Im nächsten Schritt definieren wir ein paar Health Checks.
Health Checks Pakete enthalten Integritätsprüfungen für mehrere Szenarien/Abhängigkeiten, die in der Industrie häufig vorkommen oder verwendet werden. Eine Liste dieser Abhängigkeiten finden wir in der offiziellen Dokumentation. Um ein Health Check zu verwenden, brauchen wir nur das entsprechende Paket zu installieren und den Service hinzuzufügen. Zum Beispiel, unsere Anwendung benutzt, die CosmosDb. Die Registrierung des Health Checks erfolgt in zwei Schritte.
Install-Package AspNetCore.HealthChecks.CosmosDb
public void ConfigureServices(IServiceCollection services) { services .AddHealthChecks() .AddCosmosDb(); }
Jeder Health-Check muss die IHealthCheck Schnittstelle implementieren. Die Methode CheckHealthAsync gibt ein HealthCheckResult zurück. Ein Health Check Ergebnis kann einen der folgenden Werte betragen:
public class ExampleHealthCheck : IHealthCheck { public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default) { try { var ping = new Ping(); var reply = ping.Send("www.inoteq.com"); if (reply.Status != IPStatus.Success) return HealthCheckResult.Unhealthy(); if (reply.RoundtripTime > 100) return HealthCheckResult.Degraded(); return HealthCheckResult.Healthy(); } catch { return HealthCheckResult.Unhealthy(); } } }
Um diesen Health-Check anwenden zu können, müssen wir den in Startup.cs registrieren.
services
.AddHealthChecks()
.AddCheck<ExampleHealthCheck>("Ping Check");
Es kann für kompliziertere Anwendungen mit umfangreicheren Integritätsprüfungen von Vorteil sein, die verschiedene Checks nach Kategorie zu trennen. Die Health-Checks Bibliothek ermöglicht uns, bei der Registrierung der Checks Tags zu definieren. Darauf basierend können wir dann die Health Endpunkte zuordnen.
services.AddHealthChecks() .AddCheck("CosmosDb Check", () => HealthCheckResult.Healthy("CosmosDb is healthy"), tags: new[] { "azure" }) .AddCheck("Blob Storage Check", () => HealthCheckResult.Unhealthy("Blob Storage is unhealthy"), tags: new[] { "azure" }) .AddCheck("Memory Check", () => HealthCheckResult.Healthy(""), tags: new[] { "system" });
app.UseEndpoints(endpoints => { endpoints.MapHealthChecks("/health/azure", new HealthCheckOptions() { Predicate = (check) => check.Tags.Contains("azure") }); });
Es ist auch möglich die Berichte der HealthChecks an anderen Entitäten mittels Publishers weiterzuleiten. Dafür muss die IHealthCheckPublisher Schnittstelle implementiert werden. Der Publisher führt die Health-Checks aus und ruft die PublishAsync Methode auf. Detaillierte Dokumentation der Schnittstelle und der Optionen eines HealthCheckPublisher können Sie hier finden. Die Bibliothek unterstützt übliche Monitoring Systeme wie z.B.
services.AddHealthChecks()
.AddCheck<ExampleHealthCheck>("Ping Check")
.AddApplicationInsightsPublisher()
Health Checks UI ist eine minimale Benutzeroberfläche zum Speichern und Darstellen von Berichten konfigurierter Health-Checks Endpunkten. Um den Service zu integrieren, müssen wir den in gleicher Weise wie die Health-Checks Middleware zu der Service Collection hinzufügen. Der Endpunkt zu der UI ist per Default „/healthchecks-ui“.
public class Startup { public void ConfigureServices(IServiceCollection services) { services .AddHealthChecksUI() .AddInMemoryStorage(); } public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app .UseEndpoints(config => { config.MapHealthChecksUI(); }); } }
Mittels AddInMemoryStorage() ermöglichen wir das Speichern der Health-Checks Ergebnisse im Arbeitsspeicher. Für Production ist dennoch die Nutzung einer Datenbank zu empfehlen. Aktuell sind folgende Datenbanken unterstützt:
Dies sorgt u.A. dafür eine Historie der Health-Checks erfassen zu können.
Logischerweise möchten wir benachrichtigt werden, wenn eine Komponente unserer Anwendung nicht richtig funktioniert (i.e. der Health-Check gibt ein Unhealthy/Degraded Status zurück). Dafür müssen wir AddWebhookNotification Methode benutzen. Den Payload der Benachrichtigung können wir an der Anforderungen des Empfängers (Teams, Slack, email, etc…) anpassen. Wenn die Anwendung oder die fehlerhafte Komponente zum normalen Stand wiedergebracht wird, wird ein RestorePayload gesendet werden. HealthChecksUI unterstützt aktuell die folgenden Bookmarks
Webhooks können in appsettings.json konfiguriert werden oder in Startup.cs wie folgend.
s.AddWebhookNotification("Teams", uri: teamsWebhookUri, payload: teamsPayload.FailurePayload(), restorePayload: teamsPayload.RestorePayload(), customMessageFunc: (report) => { var failing = report.Entries.Where(e => e.Value.Status == UIHealthStatus.Unhealthy); return $"{failing.Count()} healthchecks are failing"; }, customDescriptionFunc: report => { var failing = report.Entries.Where(e => e.Value.Status == UIHealthStatus.Unhealthy); return $"HealthChecks with names {string.Join("/", failing.Select(f => f.Key))} are failing"; });
Im obigen Code-Schnipsel ändern wir den Default [[ FAILURE ]] und [[ DESCRIPTIONS ]] mit jeweils customMessageFunc und customDescriptionFunc. Der Payload und RestorePayload werden vom teamsPayload Objekt generiert. Die TeamsPayload Klasse haben wir selber implementiert, um den Code sauber zu halten.
Dieser Artikel thematisierte die Wichtigkeit und Nützlichkeit der Integration von Health Checks in einer Anwendung. Für die Demo haben wir eine ASP.NET Core Anwendung als Use-Case genommen, für welche wir einen Health Check implementiert haben, der einen Server anpingt und anhand der Antwort einen Status zurückgibt. Dazu haben wir die HealthChecksUI SPA integriert, um die Liste der Health Checks darzustellen. Außerdem haben wir ein paar Features der HealthChecks Bibliothek benutzt, indem wir das Alerting bei fehlerhaften Checks eingebaut haben. Die Bibliothek unterstützt eine Vielzahl Konfigurationsalternativen für verschiedene Szenarien und App-Infrastrukturen, die wir in der Demo nicht gebraucht haben. (Authentifizierung, Styling/Branding der UI, Health Check Ergebnisse mit Key/Value Data zusätzlich zur Beschreibung des Ergebnisses). Weitere Infos zu der Bibliothek können Sie in der Git Repository und der Dokumentation von Microsoft finden.