API's From Dev to Production - Part 5 - Healthchecks
Tags: dotnet, docker, csharp, github
Published: 2021-03-12 17:45:07

## 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**](https://dev.to/peteking/shift-left-engineering-4fpp) 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 https://github.com/peteking/Samples.WeatherForecast-Part-5 no-readme %}

---

## 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h0e2oian6rechcawl00x.png)

---

## 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](https://github.com/peteking/Samples.WeatherForecast-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`.

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

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

---

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

```csharp
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");
    });
}
```

---

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`.

```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"]
```

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](https://docs.docker.com/engine/reference/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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cn0m1g6mr02ary0wzu5h.png)

---

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

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ezam0u8037g9cn4ji6es.png)

**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](https://dev.to/peteking/api-s-from-dev-to-production-part-6-1p34) in this series will be about:

* Unit Testing - with Docker and GitHub Actions

---

## More information
* https://www.azure.com
* https://dotnet.microsoft.com/
* https://www.github.com
* https://www.docker.com
* https://www.pulumi.com