ASP.NET health checks
Tags: dotnet, devops, kubernetes, aspnetcore
Published: 2021-10-11 13:12:17

# Health check types
Sometimes you need to implement endpoint, which tells outside world if everything is OK with your application. For example you need to tell k8s that your application in unhealthy and let it restart pod.

There are 3 different types of health checks: 
* **Startup** - happens after your app is started.
* **Liveness** - happens time to time after app is started and tells if it needs to be restarted.
* **Readiness** - happens time to time after app is started and tells if it able to handle requests.

Startup and Liveness checks are the most important, so let's focus on them. 

Any health check can be **Dumb** or **Smart**: 
* **Dumb** checks only indicate that app is not crushed and check as few as possible.
* **Smart** checks indicate that app is working correctly and check as much as possible (db, service bus, storage connections).

And there is the rule: startup checks should be smart and readiness checks should be dumb. You don't want to check your db connection every 5 seconds and stop your container if connection is gone.

# Implementation of dumb liveness check
Of course you can implement endpoint which always returns 200. But let's check what ASP.NET provides us!

To implement heals check you need to inherit from IHealthCheck interface:
```csharp
public class LivenessHealthCheck : IHealthCheck
{
    public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
    {
        return Task.FromResult(HealthCheckResult.Healthy());
    }
}
```
This is very simple health check which always healthy. So it can be used as liveness check.

Now we need to register it in startup:
```csharp
public void ConfigureServices(IServiceCollection services)
{
...
    services.AddHealthChecks()
        .AddCheck<LivenessHealthCheck>(LivenessHealthCheck.Name, tags: new[] { "liveness" })
...
}
...
public void Configure(IApplicationBuilder app, IHostApplicationLifetime appLifetime)
{
...
    app.UseEndpoints(endpoints => 
    { 
        endpoints.MapHealthChecks("/health", new HealthCheckOptions { Predicate = x => x.Tags.Contains("liveness") });});
        endpoints.MapControllers(); 
    });
...
}
```
So now we have /health which invoke all checks marked with tag "liveness". In our case it is LivenessHealthCheck which returns true if app is not crashed.
# Implementation of smart startup check
To implement startup check we could do same as for liveness check - implement IHealthCheck.CheckHealthAsync method, check connections to required services and register them in startup. But it's already implemented! 

Check [this](https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks/tree/master/src) githab repository. It has implementation of most common checks. Everything you need is add required NuGet and register it in Startup!
```csharp
public void ConfigureServices(IServiceCollection services)
{
...
    services.AddHealthChecks()
        .AddNpgSql(config.DbConnectionString, tags: new[] { "startup" })
        .AddAzureServiceBusTopic(config.MessageBusConnectionString, config.MessageBusTopic, tags: new[] { "startup" })
        .AddAzureBlobStorage(config.StorageConnectionString, tags: new[] { "startup" });
...
}
...
public void Configure(IApplicationBuilder app, IHostApplicationLifetime appLifetime)
{
...
    app.UseEndpoints(endpoints => 
    { 
        endpoints.MapHealthChecks("/health/startup", new HealthCheckOptions { Predicate = x => x.Tags.Contains("startup") });        
        endpoints.MapControllers(); 
    });
...
}
```
This code creates new "/health/startup" endpoint which returns 200 only if all 3 checks marked with "startup" tag (sql, service bus and blob) returns success.

GLHF
# Links
1. https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks
1. https://andrewlock.net/deploying-asp-net-core-applications-to-kubernetes-part-6-adding-health-checks-with-liveness-readiness-and-startup-probes/