API's From Dev to Production - Part 5 - Healthchecks

Pete King - Mar 12 '21 - - Dev Community

Series Introduction

Welcome to Part 5 of this blog series that will go from the most basic example of a .net 5 webapi in C#, and the journey from development to production with a shift-left mindset. We will use Azure, Docker, GitHub, GitHub Actions for CI/C-Deployment and Infrastructure as Code using Pulumi.

In this post we will be looking at CIS Issues:

  • Add a healthcheck to the Dockerfile

  • Add an item to the CIS allowedlist.yaml


TL;DR

Healthchecks are important to ensure you are aware of the state of your services, particularly when you have monitoring solutions that inform you if something is not quite right. No one wants customers telling you your product doesn’t work when there are adequate ways to deal with incidents before they could potentially impact customers.

It can be good to have the Docker healthcheck instruction, but not strictly necessary when we are not running Docker Swam, but can be handing with Docker Compose - Therefore, I recommend adding it, but maybe comment it out, add CIS-DI-006 to the allowedlist.yaml so it is ignored by the container scan.


GitHub Repository

GitHub logo peteking / Samples.WeatherForecast-Part-5

This repository is part of the blog post series, API's from Dev to Production - Part 5 on dev.to. Based on the standard .net standard Weather API sample.


Introduction

In Part 4 we were able to get into security concerns and looked into both container scanning and CIS. We solved the the very important CIS-DI-001. Here we will look further into CIS-DI-006.

Alt Text


Requirements

We will be picking-up where we left off in Part 4, which means you’ll need the end-result from GitHub Repo - Part 4 to start with.

If you have followed this series all the way through, and I would encourage you to do so, but it isn't necessary if previous posts are knowledge to you already.


Add HEALTHCHECK

Healthchecks are an important piece of surfacing health of components of a system, you might ask why you’d want to know? Well, if you service is not servicing requests you’d want to know before your customers staff calling you!

.NET 5 has some cool built in functionality to help with all the boilerplate of adding healthchecks, but remember, the Dockle tool itself is not checking for that. It’s actually checking for the HEALHCHECK instruction in the Dockerfile.

….BUT, we still need a healthcheck in our API, otherwise, there’s nothing to actually check!

In the src folder, the .NET 5 API, you should be familiar with the Startup.cs file.

Below is an snippet, and we need to add in line 4.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddHealthChecks();

    services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new OpenApiInfo { Title = "Samples.WeatherForecast.Api", Version = "v1" });
    });
}
Enter fullscreen mode Exit fullscreen mode

Another place we need to modify is further down, I’ve added in line 19.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseSwagger();
        app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Samples.WeatherForecast.Api v1"));
    }

    app.UseHttpsRedirection();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
        endpoints.MapHealthChecks("/health");
    });
}
Enter fullscreen mode Exit fullscreen mode

That’s all there is to it on the .NET side of things, however, Docker won’t call it yet - So we need to update our Dockerfile.

# Final stage/image
FROM mcr.microsoft.com/dotnet/runtime-deps:${VERSION}

RUN addgroup -g 1000 dotnet && \
    adduser -u 1000 -G dotnet -s /bin/sh -D dotnet

WORKDIR /app
COPY --chown=dotnet:dotnet --from=publish /out .

ENV ASPNETCORE_URLS=http://*:8080

HEALTHCHECK --interval=60s --timeout=3s --retries=3 \
    CMD wget localhost:8080/health -q -O - > /dev/null 2>&1

USER dotnet
EXPOSE 8080
ENTRYPOINT ["./Samples.WeatherForecast.Api"]
Enter fullscreen mode Exit fullscreen mode

Above is a snippet (the lower half of our Dockerfile), we’ve added in line 12 & 13.

For more information about the HEALTHCHECK instruction, please see, Docker Docs - Builder - Healthcheck

From the options we are using, we have set the interval to 60 seconds, a timeout of 3 seconds, and we retry 3 times if it fails. On the continued line 13 we execute a command.

You may be wondering why we are simply not just using curl, well curl would be great, however, it’s isn’t even installed! This is how stripped-back this container image is, if we wanted to use curl we’ve have use apt-get etc.

So we are using wget instead, where we call our /health endpoint and pipe out the response check the values for output.


Build & test time!

docker build -t samples-weatherforecast:v5 .

docker run -it --rm -p 8080:8080 samples-weatherforecast:v5

Once it’s running you’ll want to check the status of the container, you can do this with the following command:

docker container ls

Remember, you have a minute before it starts checking!

If you’re quick, you’ll see something like the below, note the status is, starting.

Alt Text


After 60 seconds, we should see a status of healthy.

Alt Text

It works!


What have we learned?

We have learned the very basics and simple steps to add a minimal healthcheck to our API, and ensure Docker will call this and mitigate CIS-DI-006; however, the big question is, do we need the HEALTHCHECK instruction if we are going to be hosting this on anything other than Docker Swarm OR Docker Compose - My view is we actually don’t!

But… We should have a healthcheck in our API, because whatever we host it in, in our case it will be Azure’s Web App for Containers, we will need a healthcheck.

So you may think this is a pointless exercise, but not really, we’ve got the API healthcheck, but we can safely remove the HEALTHCHECK OR comment-out the instructions from our Dockerfile.


Up next

Part 6 in this series will be about:

  • Unit Testing - with Docker and GitHub Actions

More information

. . . . . . . . . . . . . . . .