This time we want to enable line by line debugging of our microservice inside a running container. Normally I would advise to first do the debugging while running locally and only use this method as last resort, if something really does not work while running inside a container and apparently works when running the microservice directly on the host.
As always we want to provide an experience as friction free as possible. Thus we will once more use our beloved tool
Using a code editor such as VS Code it is easy to debug a .NET Core application that is running natively on the host. But that is not the point of this post. We want to run our microservice inside a container and debug inside there. For this we need to install a debugger in the container where the microservice will be running. Since the debugger shall never be part of the final Docker image that will be used in production we have to create a special debug image that will be exclusively used in case we need to line-by-line debug our service.
Creating a special Docker Image for Debugging
Let’s thus start crafting that Dockerfile. We will call this Dockerfile
Dockerfile-debug to distinguish it from the default one. We can (and should) use the default Dockerfile as a template for this special one. Here is my version:
Create a file
Dockerfile-debug in the
api folder and add the above code snippet to it.
On line 1 of the Dockerfile we are using the same base image as in our standard image. Then in the run command on lines 2 through 4 we use the Linux package manager
apt to install the
zip tool which we will then use on line 4 to decompress the debugger
vsdbg that we download from Microsoft (
Subsequently we define the working directory inside the container (line 5), copy the content of the
publish folder from the host to the
app folder in the container (line 6) and define the container start command (line 7).
With this approach we are relying on the fact that the application or microservice has been published on the host first, using
Authoring a Docker Compose File for Debugging
Usually we want to debug the microservice when it is running together with its external dependencies such as the database. Thus we will now craft a special Docker Compose file that will allow us to run the service and the Postgres DB that we have used in all our previous examples. Add a file called
docker-compose-debug.yml to the project folder
demo-project. To this file add the following code snippet:
We do not have to discuss lines 1 through 13 since that is unchanged compared to the other Docker Compose files we used so far. The interesting part is on lines 15 through 19. On line 16 we define an image name for this special debugging case (
acme/api-debug:1.0). On lines 17 through 19 we define how the image shall be built when running this application. We are providing the context (subfolder
api) and the name of the Dockerfile to use (which must be part of the context, that is, in our case inside the
api folder. More is not needed at this time.
Let’s run the application with the command:
docker-compose -f docker-compose-debug.yml up --build
--build argument which instructs Docker Compose to build any image needed before running. In our case this is the image for the
So far this was the easy part. Now we need to configure our editor VS Code to execute debugger in the
api service container. We also need to tell the debugger to which process running inside the container it should attach. We can do this by defining a launch configuration in the launch.json file residing in subfolder
Configuring VS Code for Debugging
Open VS Code from within the project folder
demo-project. Create a subfolder
.vscode in the project folder
demo-project (if it doesn’t already exist) and add a file
launch.json to it. Add the following code snippet to it:
On line 5 we define the name of the launch configuration that will appear in the drop down on the debug view of VS Code. The type of the configuration is
coreclr (line 6) and we want to
attach (to a process; line 7). The process we want to have launched in the
pickRemoteProcess. This will open a drop down in VS Code when you start debugging showing a list of available processes. But we need to specify what processes we want to see, and that is done via the
pipeTransport part of the configuration. We want the pick process command to use the following command to run the debugger inside the container:
docker-compose -f docker-compose-debug.yml \
exec -T api /vsdbg/vsdbg
This is a combination of the information found on lines 10 through 12.
In VS Code switch to the debugging view, make sure you have the configuration
.NET Code Docker Attach (Compose) selected and then hit the green run button:
as soon as you have clicked the debug run button the the process selection popup will open and present the list of all processes running inside the
Select the process
dotnet api.dll as the one the debugger shall attach to. You will see output generated in the
DEBUG CONSOLE tab of VS Code:
Add a break point e.g. on line 17 of the
ValuesController of the
api microservice. Use
curl to access the API of the microservice:
and observe how the debugger stops at the break point. You can now analyze variables and watches as well as the call stack. You can also start walking through the code line by line.
In this 3rd post of the series A Docker Workflow for .NET Developers I have shown how we can use Docker Compose to run a microservice and its dependencies such as a database, where the microservice is ready to be debugged inside its container. Our code editor VS Code can then be configured to launch the debugger in the microservices’ container and attach it to the correct process. Once the debugger is attached we can line-by-line debug the microservice exactly the same way as if it would run natively on the host machine.
The code accompanying this series can be found on GitHub.