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
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.
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" });
});
}
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");
});
}
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"]
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.
After 60 seconds, we should see a status of healthy.
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 ourDockerfile
.
Up next
Part 6 in this series will be about:
- Unit Testing - with Docker and GitHub Actions