21 September 2018
Implementing a Docker HEALTHCHECK using ASP.Net Core 2.2
It’s easy enough to tell if a container is running, but more difficult to tell whether the container is ready to accept instructions. Docker supports a health check mechanism that allows you check that a container is doing its job correctly.
There are several ways of setting this up, but the HEALTHCHECK instruction allows you to bake this kind of readiness checking into an image definition. This directive specifies a shell command that returns a zero if all is well and one if the container is unhealthy. It can be picked up by an orchestrator, such as a readiness probe in Kubernetes or the system health reporting in Service Fabric.
A typical health check declaration uses curl’s --fail option to call an HTTP endpoint and regard any error response status as unhealthy. The example below augments the shell command with some switches to set an interval along with retries and a timeout:
HEALTHCHECK --interval=5s --timeout=10s --retries=3 CMD curl --fail http://localhost:80/healthcheck || exit 1
Implementing health checks in ASP.Net Core
Given that a Docker HEALTHCHECK allows you to define a shell command you are free to use any mechanism for returning the result. An HTTP end-point is the most obvious approach for an ASP.Net Core application, and curl is included in the aspnetcore-runtime Docker images.
Although you are free to implement your own end-point, a health check implementation is being added into version 2.2 of Asp.Net Core via some new extension methods.
You can set up health-checking in your StartUp class by invoking extension methods for the services collection and application builder as shown below:
public void ConfigureServices(IServiceCollection services){ services.AddHealthChecks();} public void Configure(IApplicationBuilder app, IHostingEnvironment env){ app.UseHealthChecks("/healthcheck");}
You implement checking mechanisms via the services extension method. The example below creates a simple health check for HTTP requests that checks that the API root is accepting requests.
services.AddHealthChecks() .AddAsyncCheck("Http", async () => { using (HttpClient client = new HttpClient()) { try { var response = await client.GetAsync("http://localhost:80"); if (!response.IsSuccessStatusCode) { throw new Exception("Url not responding with 200 OK"); } } catch (Exception) { return await Task.FromResult(HealthCheckResult.Unhealthy()); } } return await Task.FromResult(HealthCheckResult.Healthy()); });
You can modify this output using the extension methods on the application builder. The example below dumps out the contents of the HealthReport object to provide more verbose output.
app.UseHealthChecks("/healthcheck", new HealthCheckOptions { ResponseWriter = async (context, report) => { var result = JsonConvert.SerializeObject( new { status = report.Status.ToString(), checks = report.Entries.Select(c => new { check = c.Key, result = c.Value.Status.ToString() }), }); context.Response.ContentType = MediaTypeNames.Application.Json; await context.Response.WriteAsync(result); } });
{ "status":"Healthy", "checks":[ { "check":"Http", "result":"Healthy" }]}
Checking the output
You can see a health check in action by inspecting the running image. If you execute a docker ps command immediately after running a container then you’ll see the health status set to “starting”. Once the check has been executed successfully it will switch to “healthy”.
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESe603c74c840a example:latest "dotnet example.dll" 7 seconds ago Up 5 seconds (health: starting) 80/tcp unruffled_morseCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESe603c74c840a example:latest "dotnet example.dll" 7 seconds ago Up 20 seconds (healthy) 80/tcp unruffled_morse
Filed under .Net Core , Docker , Microservices.