.NET Core Workerservice - how to provide a health check endpoint without HTTP?

Viewed 2473

I have built a ASP.NET Core Worker Service (it processes messages off a queue) that is running on kubernetes. Since it is a background service, it currently does not have any HTTP endpoints. Now I would like to add a health/liveness endpoint for k8s to it - ideally leveraging the .NET Core standard health checks.

So my question is: Is there a way to expose those health checks without adding the overhead of HTTP endpoints in a way that k8s can use them (TCP or generic command)? Any pointers to examples would be great! thanks!

1 Answers

Thanks to the pointers of @pinkfloydx33 I was able to build this solution:

Program.cs

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
    .ConfigureServices((hostContext, services) =>
    {
        services.AddHealthChecks() // Register some real health check here ==>
            .AddCheck("test", () => DateTime.UtcNow.Minute % 2 == 0 ? HealthCheckResult.Healthy() : HealthCheckResult.Unhealthy());
        services.AddSingleton<IHealthCheckPublisher, HealthCheckPublisher>();
        services.Configure<HealthCheckPublisherOptions>(options =>
        {
            options.Delay = TimeSpan.FromSeconds(5);
            options.Period = TimeSpan.FromSeconds(20);
        });

        // Register the actual message processor service
        services.AddHostedService<QueueProcessorService>();
    })

HealthCheckPublisher.cs

public class HealthCheckPublisher : IHealthCheckPublisher
{
    private readonly string _fileName;
    private HealthStatus _prevStatus = HealthStatus.Unhealthy;

    public HealthCheckPublisher()
    {
        _fileName = Environment.GetEnvironmentVariable("DOCKER_HEALTHCHECK_FILEPATH") ??
                    Path.GetTempFileName();
    }

    /// <summary>
    /// Creates / touches a file on the file system to indicate "healtyh" (liveness) state of the pod
    /// Deletes the files to indicate "unhealthy"
    /// The file will then be checked by k8s livenessProbe
    /// </summary>
    /// <param name="report"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public Task PublishAsync(HealthReport report, CancellationToken cancellationToken)
    {
        var fileExists = _prevStatus == HealthStatus.Healthy;

        if (report.Status == HealthStatus.Healthy)
        {
            using var _ = File.Create(_fileName);
        }
        else if (fileExists)
        {
            File.Delete(_fileName);
        }

        _prevStatus = report.Status;

        return Task.CompletedTask;
    }
}

k8s deployment.yaml (Original Source: https://medium.com/spire-labs/utilizing-kubernetes-liveness-and-readiness-probes-to-automatically-recover-from-failure-2fe0314f2b2e)

livenessProbe:
  exec:
    command:
    - find
    - /tmp/healthy
    - -mmin
    - '-1'
  initialDelaySeconds: 5
  periodSeconds: 10
Related