Health Checks in ASP.NET Core for .NET Microservices with Actuator

In a microservices architecture, ensuring that each service is running and healthy is crucial for maintaining the overall system's reliability. Actuator, a library inspired by Spring Boot Actuator, can be used to expose operational information about an application running in production. This guide will walk through the implementation of health checks for two .NET microservices, Product and Order services, using Actuator.

Setting Up Actuator in ASP.NET Core

Actuator in the context of .NET is typically managed through libraries like AspNetCore.Diagnostics.HealthChecks offers various health check implementations and features.

1. Create Product and Order Services

Assume you already have two microservices: Product and Order. We'll add health checks using Actuator-like features to these services.

2. Install Required Packages

First, install the necessary NuGet packages for health checks.

dotnet add package AspNetCore.HealthChecks.UI
dotnet add package AspNetCore.HealthChecks.UI.Client
dotnet add package AspNetCore.HealthChecks.UI.InMemory.Storage
dotnet add package AspNetCore.HealthChecks.SqlServer

3. Configure Health Checks in Startup.cs

In each service, configure health checks in the Startup.cs file.

Product Service

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        
        // Add health checks
        services.AddHealthChecks()
                .AddCheck("self", () => HealthCheckResult.Healthy())
                .AddSqlServer(Configuration.GetConnectionString("ProductDatabase"), name: "ProductDB-check");

        // Add HealthChecks UI
        services.AddHealthChecksUI()
                .AddInMemoryStorage();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            
            // Map health checks
            endpoints.MapHealthChecks("/health", new HealthCheckOptions()
            {
                Predicate = _ => true,
                ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
            });
            
            // Map HealthChecks UI
            endpoints.MapHealthChecksUI(setup => setup.UIPath = "/health-ui");
        });
    }
}

Order Service

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        
        // Add health checks
        services.AddHealthChecks()
                .AddCheck("self", () => HealthCheckResult.Healthy())
                .AddSqlServer(Configuration.GetConnectionString("OrderDatabase"), name: "OrderDB-check");

        // Add HealthChecks UI
        services.AddHealthChecksUI()
                .AddInMemoryStorage();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            
            // Map health checks
            endpoints.MapHealthChecks("/health", new HealthCheckOptions()
            {
                Predicate = _ => true,
                ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
            });

            // Map HealthChecks UI
            endpoints.MapHealthChecksUI(setup => setup.UIPath = "/health-ui");
        });
    }
}

4. Run and Test Health Checks

Run the services and test the health check endpoints.

  • For Product Service
    • Health Check: http://localhost:<port>/health
    • Health Check UI: http://localhost:<port>/health-ui
  • For Order Service
    • Health Check: http://localhost:<port>/health
    • Health Check UI: http://localhost:<port>/health-ui

You should see a JSON response indicating the health status of the service on the /health endpoint and a detailed UI on the /health-ui endpoint.

5. Aggregating Health Checks with Kubernetes or Docker

When deploying to Kubernetes or Docker, you can use readiness and liveness probes to ensure that your services are healthy.

Kubernetes Example

apiVersion: apps/v1
kind: Deployment
metadata:
  name: product-service
spec:
  replicas: 1
  selector:
    matchLabels:
      app: product-service
  template:
    metadata:
      labels:
        app: product-service
    spec:
      containers:
        - name: product-service
          image: your-docker-image
          ports:
            - containerPort: 80
          readinessProbe:
            httpGet:
              path: /health
              port: 80
            initialDelaySeconds: 5
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /health
              port: 80
            initialDelaySeconds: 5
            periodSeconds: 10

Conclusion

Implementing health checks in ASP.NET Core for microservices like Product and Order services is straightforward and enhances the reliability of your system. By configuring health checks and a health check UI, you can monitor the status of your services effectively. Integrating this setup with orchestration tools like Kubernetes further ensures robust deployment strategies and the smooth operation of your microservices architecture.