Debugging an ASP.NET Core Docker Container in Windows AND Linux

There is no question that .NET Core has exploded in popularity over the last couple of years. The new cross-platform successor to the .NET Framework has opened many new doors to developers. Using tools such as Docker have allowed developers to deploy their solutions in very repeatable and reliable ways.

Docker is an revolutionary tool that has far too many benefits to list in this blog post. Instead, we will be walking through the tooling that can be leveraged when you create and debug your next ASP.NET Core Docker application.

Taking it a step further, what would be a better way to highlight the cross platform nature of .NET Core than exploring this on Windows AND Linux!

Visual Studio Tools for Docker

On the Windows side of the house, I have been impressed by the tooling that exists natively in Visual Studio. Using Visual Studio Tools for Docker makes working with containers a breeze. You can see this firsthand when creating a new ASP.NET Core project and selecting the Enable Docker Support checkbox.

Once complete, you will notice a new Dockerfile is created at the root of the project (shown below). The Dockerfile will run your application inside of a container however, that’s not all. To guarantee consistency across development machines, you can see the Dockerfile actually restores and publishes the project inside a build container. Say goodbye to those “it builds on my machine” excuses!

FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src
COPY ["JrTech.Docker.Web/JrTech.Docker.Web.csproj", "JrTech.Docker.Web/"]
RUN dotnet restore "JrTech.Docker.Web/JrTech.Docker.Web.csproj"
COPY . .
WORKDIR "/src/JrTech.Docker.Web"
RUN dotnet build "JrTech.Docker.Web.csproj" -c Release -o /app

FROM build AS publish
RUN dotnet publish "JrTech.Docker.Web.csproj" -c Release -o /app

FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "JrTech.Docker.Web.dll"]

Auto generating a Dockerfile is a great first step however, your development process is going to be rocky if you cannot debug your code. Luckily Visual Studio does some magic for us to allow just that. If you run the application, the following output will be shown in the debug window. This highlights how Visual Studio is running the Docker container with the remote debugger attached.

docker run -dt -v "C:\Users\jason\vsdbg\vs2017u5:/remote_debugger:rw" -v "C:\Jason\Repositories\gc\ahbc_dotnet_201810\JrTech.Docker.Web\JrTech.Docker.Web:/app" -v "C:\Users\jason\AppData\Roaming\ASP.NET\Https:/root/.aspnet/https:ro" -v "C:\Users\jason\AppData\Roaming\Microsoft\UserSecrets:/root/.microsoft/usersecrets:ro" -v "C:\Users\jason\.nuget\packages\:/root/.nuget/fallbackpackages2" -v "C:\Program Files\dotnet\sdk\NuGetFallbackFolder:/root/.nuget/fallbackpackages" -e "DOTNET_USE_POLLING_FILE_WATCHER=1" -e "ASPNETCORE_ENVIRONMENT=Development" -e "ASPNETCORE_URLS=https://+:443;http://+:80" -e "ASPNETCORE_HTTPS_PORT=44372" -e "NUGET_PACKAGES=/root/.nuget/fallbackpackages2" -e "NUGET_FALLBACK_PACKAGES=/root/.nuget/fallbackpackages;/root/.nuget/fallbackpackages2" -p 56567:80 -p 44372:443 --entrypoint tail jrtechdockerweb:dev -f /dev/null

We can now set breakpoints and debug the application while it is running. Many of us may take this for granted however, we can see Visual Studio had to do a little bit of work to put this together for us.

I have to say, I’ve been impressed with how easy it to create an ASP.NET Core Docker container with Visual Studio. What could make it even better? Doing the same on a Linux OS! Next we will go through the same exercise with VSCode on Linux.

Visual Studio Code on Linux

One of the best things about .NET Core is that it is completely cross platform. It’s been a couple years but, it still feels weird to say‚Ķ we can develop, build, and run ASP.NET Core on the operating system of our choice. Visual Studio Code is also cross platform and can be run on Windows, Mac, or Linux.

To get started, we need to install the following components.

Docker Visual Studio Code Extension

Visual Studio Code has a great extension subsystem so, it is no surprise that there is Docker extension readily available.


Once we have the extension installed, we can get started by creating our new project. Visual Studio Code is designed for working with all kinds of languages and frameworks so, it is no surprise that we don’t have a “new project template” available. No need to worry, .NET Core has an awesome command line interface!

Using the dotnet new command, we can create a new ASP.NET Core MVC project.

jason@Laptop-ln:~/Source$ dotnet new mvc -n JrTech.Docker.Web -o JrTech.Docker.Web
The template "ASP.NET Core Web App (Model-View-Controller)" was created successfully.
This template contains technologies from parties other than Microsoft, see https://aka.ms/aspnetcore-template-3pn-210 for details.

Processing post-creation actions...
Running 'dotnet restore' on JrTech.Docker.Web/JrTech.Docker.Web.csproj...
  Restoring packages for /home/jason/Source/JrTech.Docker.Web/JrTech.Docker.Web.csproj...
  Generating MSBuild file /home/jason/Source/JrTech.Docker.Web/obj/JrTech.Docker.Web.csproj.nuget.g.props.
  Generating MSBuild file /home/jason/Source/JrTech.Docker.Web/obj/JrTech.Docker.Web.csproj.nuget.g.targets.
  Restore completed in 531.14 ms for /home/jason/Source/JrTech.Docker.Web/JrTech.Docker.Web.csproj.

Restore succeeded.

jason@Laptop-ln:~/Source$ code .

When Visual Studio Code launches, you will see the following popup on the bottom right corner of the IDE. Selecting ‘Yes’ will create a tasks.json file with the required build steps.

We should now see a new web project similar to the one we created with Visual Studio. There is one key difference though, we do not have a Dockerfile yet. This is where the Docker extension helps us out. We can add a Dockerfile using the Add Docker Files to Workspace command. For a default file, select ASP.NET Core as the application platform, Linux as the operating system, and port 80 as the default port.

Looking at it newly created Dockerfile, we can see a familiar file format. In essence, this is the same multistage Dockerfile we had with Visual Studio.

FROM microsoft/dotnet:2.2-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 80

FROM microsoft/dotnet:2.2-sdk AS build
WORKDIR /src
COPY ["JrTech.Docker.Web.csproj", "./"]
RUN dotnet restore "./JrTech.Docker.Web.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "JrTech.Docker.Web.csproj" -c Release -o /app

FROM build AS publish
RUN dotnet publish "JrTech.Docker.Web.csproj" -c Release -o /app

FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "JrTech.Docker.Web.dll"]

The next step is setting up our debug configuration so we can debug our application while it is running in a container. From the Debug pane, we can select Add Configuration from the drop-down to add our new configuration.

We have a ton of options to choose from but, in our case, we want to add the Docker: Launch .NET Core (Preview) configuration.

Depending on our containers requirements we can apply specific configurations here. With a newly created project, using the defaults works fine.

With our new configuration selected, clicking the play button will build and run our container with the debugger attached. As we did with Visual Studio, we can now add breakpoints and step through our application.

Summary

As I’ve said previously, Docker is a very powerful technology, especially when combined with an orchestrator like Kubernetes. It is great to see the developer tooling we get out of the box with ASP.NET Core. I’ve been on a bit of a Linux kick lately so, it’s encouraging that we can replicate this behavior on multiple operating systems.

Happy coding!