This repo contains the code of the NuGet package mu88.Shared
, providing the following features:
- Add and configure certain OpenTelemetry features (metrics and traces)
- MSBuild target for building multi-platform Docker images using Microsoft's .NET SDK Container Building Tools
- Provide a minimalistic health check tool for .NET apps
I use this NuGet package to share features and configurations between different .NET apps, hence avoiding the need to implement it repeatedly.
By calling the extension method ConfigureOpenTelemetry
on an instance of Microsoft.Extensions.Hosting.IHostApplicationBuilder
, the following OpenTelemetry features will be enabled:
- Metrics
- Tracing
To export these data, the .NET configuration parameter OTEL_EXPORTER_OTLP_ENDPOINT
for the OpenTelemetry endpoint receiving the exported metrics and traces must be configured, e.g. via an environment variable. See the official OpenTelemetry docs for more information.
With .NET 8, the support for building Docker images without Dockerfile
s has been integrated into the .NET SDK. However, it is not (yet 🫰🏻) possible to build multi-platform Docker images, i.e. a single Docker image built with the .NET SDK Container Building Tools can one target a single platform (e.g. either arm64
or x64
).
The GitHub issue "Design multi-manifest (aka multi-architecture or multi-RID) publishing" tracks the process of adding this functionality to the .NET SDK. Furthermore, it also contains a very first draft of MSBuild logic on how it could work (kudos to the user baronfel). As it's quite a lot of lines, I integrated it into this NuGet package and therefore can reuse it easily.
By installing the NuGet package, the MSBuild project mu88.Shared.targets
will be added to the .NET project (see here) and the target MultiArchPublish
will become available which can be called like this:
dotnet publish MyProject.csproj /t:MultiArchPublish '-p:ContainerImageTags="dev"' '-p:RuntimeIdentifiers="linux-amd64;linux-arm64"' -p:ContainerRegistry=registry.hub.docker.com
This command will do the following:
- Build the .NET project
MyProject.csproj
for botharm64
andamd64
targeting Linux. - Build and publish a Docker image per platform to the official Docker registry, i.e. there you will see two tags
dev-arm64
anddev-amd64
. - Publish a Docker image with tag
dev
using a new Docker manifest combining bothdev-arm64
anddev-amd64
.
The detour of pushing two platform-specific Docker images to the registry first and combining it afterward into a multi-platform image is a limitation of the current approach.
As the GitHub issue "Consider defining a helper "health check" utility for distroless scenarios" describes, it is not possible to use the HEALTHCHECK
instruction in a Docker scenario when using a distroless image as there is neither a shell nor a tool like curl
. To overcome this limitation, I created a minimalistic health check tool that can be used in .NET apps (inspired by this comment).
The tool is a simple HttpClient
that sends a GET
request to a specified URL and checks if the response status code is 200 OK
and the response body contains a specified string (Healthy
). If the check fails, the tool will exit with a non-zero exit code, which can be used in a Dockerfile's HEALTHCHECK
instruction.
To successfully run this tool it in a Docker container, .NET requires it to provide a runtimeconfig.json
file for the tool beside the executable. This file is included in the NuGet package as a content file and will be copied to the output directory of the consuming .NET project when building it. Thereby, it will be embedded in the Docker image and the .NET runtime can successfully run the tool.
NuGet recommends to not ship multiple executables in a single NuGet package. However, shipping the tool as a separate NuGet package would require the consuming .NET project to reference two packages, which I wanted to avoid. Therefore, I decided to include the tool in this package. Unfortunately, some ugly MSBuild logic is required to copy all the necessary bits and pieces to the output directory of the consuming .NET project. If you have a better idea on how to solve this, please let me know!
To use the health check tool in a docker-compose.yml
file, you can add the following service definition:
services:
yourservice:
container_name: yourcontainer
image: yourapp:latest
healthcheck:
test: [ "CMD", "dotnet", "/app/mu88.HealthCheck.dll", "http://localhost:8080/healthz" ]
interval: 60s
timeout: 5s
retries: 3
start_period: 30s
start_interval: 5s
As you can see, the health check tool is called with the URL of the health check endpoint of your app and instead of using bash
or curl
, the .NET runtime itself is used to run the health check tool. If the health check fails, the Docker container will be restarted after the specified number of retries.