Cross platform development with dotnet is here and it’s awesome. However, heterogeneous development environments bring their own complications. Modern dotnet development is often done with traditional tooling such as Visual Studio for windows, but the landscape is changing quickly. We recently had a project where some developers were using VS2017 on Windows, others used VS for Mac and still others used VSCode.
Adding docker support to a project in Visual Studio is as simple as a right click, but we found it a little more challenging to set up debugging in VSCode. Beyond that it was a chore to get all of these things to play well together. Once we did it was easy to maintain, but figuring out the “how” took some trial and error.
There is a lot of detail in this post, but bottom line is that our goal is to be able to debug through our simple dotnet core app, regardless of what tool or what platform we are on. All of the code in this post can be found here: https://github.com/jtoussaint/dotnet-containers
Using Visual Studio
- We wanted to move the docker compose project into a “compose” folder. We did this mainly so we could type
dotnet build
from the root folder and not be asked what project we wanted to build. - We changed the base image to use alpine linux to reduce the footprint of our containers in QA and PROD.
Docker Compose
docker-compose.override.yml
to configure settings that need to be different when debugging. The tooling uses this file when starting a debug session. We only made a couple changes here.- We changed the docker file that we were pointing at.
- We added in some environment variables and a port mapping.
services: web: image: web:debug build: dockerfile: ../Website/Dockerfile.vs.debug environment: - ASPNETCORE_ENVIRONMENT=Development - BASE_PATH=/app ports: - "5002:80"
Docker file
We started with the docker file generated by Visual Studio. It compiles the app and pushes the published content to the /app
folder. This file is very similar to the original Dockerfile that is used in our CI/CD process. The only difference is our base image. At time of this posting remote debugging on alpine linux does not work without some extra gymnastics. We still wanted to use alpine in QA/PROD so we setup a different docker file for debugging locally.
FROM microsoft/dotnet:2.1.4-aspnetcore-runtime AS base WORKDIR /app EXPOSE 80 FROM microsoft/dotnet:2.1.401-sdk AS build WORKDIR /src COPY . . RUN dotnet restore Website/Website.csproj WORKDIR /src/Website RUN dotnet build Website.csproj -c Release -o /app FROM build AS publish RUN dotnet publish Website.csproj -c Release -o /app FROM base AS final WORKDIR /app COPY --from=publish /app . ENTRYPOINT ["dotnet", "Website.dll"]
Putting it all together
At this point you can simply open up the solution file in Visual Studio (mac or windows) and press the debug button. You do need to have docker installed and configured with linux container support.
Using VSCode
Docker Compose
docker-compose.yml
and docker-compose.override.yml
. Notice how this compose file only specifies values that are specific to running and debugging with VSCode? Other values such as environment variables and port mappings can be found in the docker-compose.override.yml
file, providing a single place to configure these values between Visual Studio and VSCode
services: web: build: dockerfile: ../Website/Dockerfile.vscode.debug volumes: - ../Website/bin/pub/:/app
This compose file is used with build, start and stop scripts in the mac-cli
and win-cli
folders. As an example of our support scripts, this is what the mac-cli/start-containers.sh
script looks like. It runs a docker compose up using all three configuration files and runs in detached mode so the container will continue to run in the background.
#!/bin/bash pushd ../compose docker-compose -f docker-compose.yml \ -f docker-compose.override.yml \ -f docker-compose.vscode.debug.yml \ --project-name dotnet-containers \ up -d popd
Notice the project name of dotnet-containers
in the script above? This name is used as a prefix for the container name which can be seen by doing a docker ps
after running the start script. We will use that name in a later step.
Docker file
- It is based on the aspnet core runtime image and creates an
/app
directory. - It downloads the latest linux remote debugger and installs it.
- It exposes port 80
- It performs a `tail -f /dev/null` to keep the container running indefinitely.
FROM microsoft/dotnet:2.1.4-aspnetcore-runtime RUN mkdir app #Install debugger RUN apt-get update RUN apt-get install curl -y unzip RUN curl -sSL https://aka.ms/getvsdbgsh | bash /dev/stdin -v latest -l /vsdbg EXPOSE 80/tcp #Keep the debugger container on ENTRYPOINT ["tail", "-f", "/dev/null"]
VSCode Task
.vscode/launch.json
file and it has the following key settings.- The program value is set to
app/Website.dll
. This is because my web app is named “Website” and we are mounting to an “app” folder within the container. - We define a source file map from our
${workspaceRoot}/Website
folder to the/app
folder within the container. - We invoke the docker command line telling it to connect to our container and start the remote debugger. Note that container name is coming from a combination of the compose project name and the service name within the compose file.
{ "name": "website", "type": "coreclr", "request": "launch", "preLaunchTask": "publish", "program": "/app/Website.dll", "sourceFileMap": { "/app": "${workspaceRoot}/Website" }, "pipeTransport": { "pipeProgram": "docker", "pipeCwd": "${workspaceRoot}", "pipeArgs": [ "exec -i dotnet-containers_web_1" ], "debuggerPath": "/vsdbg/vsdbg", "quoteArgs": false } },
Putting it all together
Debugging in VSCode is a little more involved that Visual Studio, but not by much. The instructions below are for a mac, but you can follow the same steps on windows using the appropriate scripts. You will need to have docker installed and configured for linux containers.
- Go to the
mac-cli
folder and run thebuild-containers.sh
script. This will build your containers locally. Note, you will need to repeat this step if you change theDockerfile.vscode.debug
file. - Run the
start-containers.sh
script to start your container locally. Note, if you were previously debugging in Visual Studio you will need to stop those containers by hand. Both containers will try and bind to port5002
which will cause a conflict. - Open up VSCode and start debugging.
Leave a Reply