.NET and Kubernetes: Collect dumps on crash

Somebody who likes to code
Search for a command to run...

Somebody who likes to code
No comments yet. Be the first to comment.
Google processes tens of thousands of code changes every day across a codebase of hundreds of millions of lines. The secret isn't magic tooling — it's a documented, principled approach to code review

Amazon Simple Notification Service (SNS) looks straightforward on the surface — publish a message, deliver it to subscribers — but the gap between a topic that "works in dev" and one that is productio

Amazon Simple Queue Service (SQS) is deceptively simple to get started with, but surprisingly easy to misconfigure in ways that cause silent message loss, runaway costs, duplicate processing storms, o

HTTP API is the right default choice for the majority of .NET serverless workloads. At 70% less cost than REST API, lower latency, and native JWT authorization, it covers most production requirements

AWS Lambda has become a cornerstone of modern serverless architectures, but writing a function that "just works" is very different from writing one that is performant, cost-effective, secure, and read

Collecting dumps after a crash is an invaluable practice for understanding why it occurred. For .NET applications, this can be achieved simply by specifying environment variables. However, how do we accomplish this when hosting our application in a Kubernetes cluster? It's particularly challenging when the dump is generated inside the container, which will be destroyed shortly after the crash. In this article, we will explore how Persistent Volumes can help address this challenge.
Install Docker Desktop
Enable Kubernetes (the standalone version included in Docker Desktop)
Run the following command to create a simple .NET API:
dotnet new web -o CrashApi
dotnet new sln -n CrashApi
dotnet sln add --in-root CrashApi
Navigate to the Program.cs file and update it as follows:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
throw new Exception("Boom!!");
app.Run();
This application will crash as soon as it starts.
At the project level, we need a Dockerfile to containerize our application. It should include the following content:
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
COPY ["CrashApi/CrashApi.csproj", "CrashApi/"]
RUN dotnet restore "CrashApi/CrashApi.csproj"
COPY . .
WORKDIR "/CrashApi"
RUN dotnet build "CrashApi.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "CrashApi.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "CrashApi.dll"]
At the solution level, execute the following command to create the container image:
docker build -t raulnq/crashapi:1.0 -f .\CrashApi\Dockerfile .
In the persistent volume, we will mount the local directory C:\Temp. Create a persistentvolume.yaml file at the solution level with the following content:
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-persistent-volume
labels:
type: local
spec:
storageClassName: hostpath
capacity:
storage: 256Mi
accessModes:
- ReadWriteMany
hostPath:
path: "/run/desktop/mnt/host/c/Temp"
persistentVolumeReclaimPolicy: Retain
Since we are using Docker Desktop for Windows, the storageClassName is set to hostpath, and the path is configured as /run/desktop/mnt/host/c/Temp. Generally, the path follows the pattern /run/desktop/mnt/host/MY_LOCAL_PATH. To create the Persistent Volume Claim, create a persistentvolumeclaim.yaml file with the following content:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-persistent-volume-claim
spec:
storageClassName: hostpath
accessModes:
- ReadWriteMany
resources:
requests:
storage: 128Mi
Create a deployment.yaml file with the following content:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployments
spec:
replicas: 1
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- name: my-container
image: raulnq/crashapi:1.0
imagePullPolicy: IfNotPresent
env:
- name: DOTNET_DbgEnableMiniDump
value: "1"
- name: DOTNET_DbgMiniDumpType
value: "1"
- name: DOTNET_DbgMiniDumpName
value: "/dumps/coredump.%p.dmp"
volumeMounts:
- mountPath: /dumps
name: my-volume
volumes:
- name: my-volume
persistentVolumeClaim:
claimName: my-persistent-volume-claim
Here, we mounted the previously created Persistent Volume Claim and set the following environment variables:
COMPlus_DbgEnableMiniDump or DOTNET_DbgEnableMiniDump: If set to 1, this enables core dump generation.
COMPlus_DbgMiniDumpType or DOTNET_DbgMiniDumpType: Specifies the type of dump to be collected (Mini, Heap, Triage, and Full).
COMPlus_DbgMiniDumpName or DOTNET_DbgMiniDumpName: Path to the file where the dump will be written. The %p acts as a wildcard for the PID of the dumped process.
The prefix DOTNET_ becomes functional, starting with .NET 7 on all platforms. Execute the following commands to deploy the application to the cluster:
kubectl apply -f .\persistentvolume.yaml
kubectl apply -f .\persistentvolumeclaim.yaml
kubectl apply -f .\deployment.yaml
The command kubectl get pods will display our application:
NAME READY STATUS RESTARTS AGE
my-deployments-5884c58f88-mgxvw 0/1 CrashLoopBackOff 4 (84s ago) 2m59s
Check the pod logs with the command kubectl logs my-deployments-5884c58f88-mgxvw:
Unhandled exception. System.Exception: Boom!!
at Program.<Main>$(String[] args) in /CrashApi/Program.cs:line 4
To analyze the dump, we need a tool like dotnet-dump. Run the following command to install it:
dotnet tool install --global dotnet-dump
Finally, run dotnet-dump analyze C:\Temp\coredump.1.dmp to begin the analysis (you might need to delete our deployment first):
Loading core dump: C:\Temp\coredump.1.dmp ...
Ready to process analysis commands. Type 'help' to list available commands or 'help [command]' to get detailed help on a command.
Type 'quit' or 'exit' to exit the session.
As a final note, in a real environment, the persistent volume should be replaced with an appropriate type like csi. The final code can be found here. Thank you, and happy coding.