microsoft / generator-docker

Yeoman generator for Docker
Other
346 stars 69 forks source link

Debug .NET Core with docker containers #130

Open MichaelSimons opened 7 years ago

MichaelSimons commented 7 years ago

[Copied from https://github.com/dotnet/dotnet-docker/issues/185]

What is the proper way to install the debugger on a dotnet image and get it debugging from VSCode?

Currently there are multiple (messy) ways to work with old project.json and containers like for example, that bunch of scripts generated by yo docker.

Is there any guidance on how to create a debug container for .net apps?

Thank you @galvesribeiro

MichaelSimons commented 7 years ago

For my own exercise, I went through the yo docker experience. Even though it was out of date, I was able to get it working with a few tweaks.

  1. mkdir DockerDebugApp
  2. cd DockerDebugApp
  3. dotnet new -t Console1.1
  4. yo docker (used defaults)
  5. Ignore project.json file does not exist error
  6. Open DockerDebugAdd in VSCode
  7. add the following to the csproj
    
            <ItemGroup>
        <None Update="Dockerfile">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
        </None>
        <None Update="Dockerfile.Debug">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
        </None>
        <None Update="docker-compose.yml">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
        </None>
        <None Update="docker-compose.debug.yml">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
        </None>
      </ItemGroup>
  8. Update the two Dockerfiles to reference the appropriate base images a. microsoft/dotnet:1.1-sdk-msbuild b. microsoft/dotnet:1.1-runtime
  9. Update the Dockerfile.debug to install unzip before the GetClrDbg logic
    
        RUN apt-get update \
        && apt-get install -y --no-install-recommends \
            unzip \
        && rm -rf /var/lib/apt/lists/*
  10. Set the $framework variables of the dockerTask script to match your csproj a. netcoreapp1.1
  11. dotnet restore -r debian.8-x64
  12. Build
  13. Set breakpoint in program
  14. F5
galvesribeiro commented 7 years ago

Hey @MichaelSimons

I got it to work. However there are limitations with the current approach:

  1. yo docker assumes you are working/testing a single container within a single project. Scenarios like Docker Compose(d) apps which has multiple processes/containers is not supported. 1.1 To workaround that I have to create a solution level compose file and make it spin all the containers described there. That makes the composability to work, but you have to manually attach a debugger to each container.
  2. We can't have multiple containers of the same service being debugged unless you duplicate the launch block in launch.json 2.1 e.g if you are scaling the service to more than one instance, you can't attach a debugger to the other instances. This issue I wasn't able to workaround.

I think those two painpoints are a joint work between the generator-docker and the VSCode debugger...

matthewjcooper commented 7 years ago

We also, need the the docker file to load the new 1.8 C# extension debugger. From (https://github.com/OmniSharp/omnisharp-vscode/blob/v1.8.0/CHANGELOG.md)

Remote debugging breaking changes: as part of our new architecture, there are a few breaking changes to the way remote debugging works.

vsdbg vs. clrdbg: As part of the new architecture, clrdbg has been replaced with a new executable named vsdbg. As such, the script to download vsdbg has changed to http://aka.ms/getvsdbgsh. Run curl -sSL https://aka.ms/getvsdbgsh | bash /dev/stdin -v latest -l ~/vsdbg to download. See the wiki for more information. Pseudo-tty's are no longer supported: previously we were a little more tolerant of pseudo-tty's transforming the input/output from the remote debugger. We are currently not as tolerant. If you are using ssh or plink as a pipe program, pass -T to fix this. If you have another transport and you are no longer able to connect, let us know and we can fix this for a future release. debuggerPath is now required - previously the debuggerPath property of pipeTransport was recomended but not required. As part of this change we are now requiring it.

galvesribeiro commented 7 years ago

@matthewjcooper I had the precisely same problem yesterday. Had to change everything to use vsdbg because my projects were all broken.

matthewjcooper commented 7 years ago

@galvesribeiro would you mind sending me what you did to get working, i'm having issues.

galvesribeiro commented 7 years ago

@matthewjcooper I'm not using this template anymore because it is very limited and rely on external and platform specific issues. So what I have is:

Dockerfile:

FROM microsoft/dotnet:1.1-sdk-msbuild
ENV NUGET_XMLDOC_MODE skip
WORKDIR /vsdbg
RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        unzip \
    && rm -rf /var/lib/apt/lists/* \
    && curl -sSL https://aka.ms/getvsdbgsh | bash /dev/stdin -v latest -l /vsdbg 
WORKDIR /app
ENTRYPOINT ["tail", "-f", "/dev/null"]

Solution level docker-compose.yml

version: '3.1'

services:
  YOU_PROJECT:
    image: YOUR_PROJECT:debug
    build:
      context: ./src/YOU_PROJECTDIRECTORY_HERE/bin/PublishOutput/
      dockerfile: Dockerfile.debug
    ports: 
      - "10200:10200"
    environment: 
      - ASPNETCORE_ENVIRONMENT=Development
    volumes: 
      - ./src/YOU_PROJECTDIRECTORY_HERE/bin/PublishOutput/:/app
      - ~/.nuget/packages:/root/.nuget/packages:ro

VSCode tasks.json

{
    "version": "0.1.0",
    "command": "dotnet",
    "isShellCommand": true,
    "args": [],
    "tasks": [
        {
            "taskName": "publish",
            "args": [
                "${workspaceRoot}/YOUR_SOLUTION.sln", "-c", "Debug", "-o", "bin/PublishOutput"
            ],
            "isBuildCommand": true,
            "problemMatcher": "$msCompile"
        }
    ]
}

VSCode launch.json

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "YOUR_PROJECT",
            "type": "coreclr",
            "request": "launch",
            "cwd": "/app",
            "program": "/app/YOUR_PROJECT.dll",
            "sourceFileMap": {
                "/app": "${workspaceRoot}/src/YOUR_PROJECTDIRECTORY"
            },
            "pipeTransport": {
                "debuggerPath": "/vsdbg/vsdbg",
                "pipeProgram": "/bin/bash",
                "pipeCwd": "${workspaceRoot}",
                "pipeArgs": [
                    "-c",
                    "docker exec -i YOUR_PROJECT_1 /vsdbg/vsdbg --interpreter=vscode"
                ]
            }
        }
    ]
}

To make it work:

  1. Build your project which will actually publish the files to output (I had to publish because my app requires load assemblies via reflection and the new .Net core runtime fail if I run directly from bin/debug without publish)
  2. Open a terminal to the solution directory
  3. Do a docker-compose build -> This need to be done only once if you change the container image
  4. docker-compose up -d -> This is only required once since the container will kept alive between debug sessions. Only need do it again if you tear down the container.
  5. Hit F5 on your project -> That will run the app inside the container and attach a debugger to it.

Every time you make change to the code you just need to build the project again (which will effectively publish whole solution) and run. Note that you can run multiple projects but not at same tiem. For wharever reason the VSCode setting to start multiple projects don't work while remote debugging. That doesn't prevent you to hit Start button in the second config so both debugger are running live.

Limitations: You can't use docker-compose scale yourservice=12 because you can't attach the debugger to multiple containers. This is a VSCode limitation, not docker, neither that settings.

Note that I'm not using any magical .sh or .ps1 to make things to work. Just pure VSCode config and docker. You should be able to add windows support on this config by just add that platform specific overrides on launch.json.

Let me know if you have any issues.

UPDATE: I forgot to mention that you must be on VSCode with the C# extension on version 1.8.0 or superior. If for whatever reason you need to keep pre-1.8.0, please update the example to use clrdbg instead of vsdbg.

galvesribeiro commented 7 years ago

Also, that config circumvent all limitations of the Docker extensions of VSCode since it only adds the syntax highlight so you don't need it if you don't want and is more flexible than recent released VS2017 Docker tools which is a lot more limited.

matthewjcooper commented 7 years ago

Wow, thanks for the quick reply, I’ll give this a shot!

matthewjcooper commented 7 years ago

This is working great. I also added "preLaunchTask": "publish", so I don't forget to rebuild when I make changes.

galvesribeiro commented 7 years ago

Yeah, I forgot to mention that... The preLaunchTask was intentionaly removed because we don't always need to build the project in order to run, because most of the changing code is compiled at runtime inside our project. So we preferred speed up the F5 :)

Glad it worked!

matthewjcooper commented 7 years ago

Last issue i'm having, is that the problem matcher is showing the problems, but clicking on them is not able to find the actual source file to pull up. Is there a way to change the cwd path for the problem matcher?

"problemMatcher": "$msCompile"

galvesribeiro commented 7 years ago

Hummm... No idea... Is that really related to the debugger? Because IIRC it is related to the compiler and language services, right?

matthewjcooper commented 7 years ago

You are right, not related to debugging, just how to link compile errors/warning with the vscode problems window. Nvm on that one.

hartmannr76 commented 7 years ago

It would be nice if there was a sample project setup to include these changes, or even a forked repo to demonstrate this.

I had hoped that this project would've resolved this by now, but it seems like that is not the case. Anyone willing to set something up that they have working or work on forking this? I'd be willing to chip in, but running these instructions; when attaching to debugger, it looks like VSCode just keeps doing something and sits in limbo.

PaluMacil commented 7 years ago

I'm pretty early in my understanding of Docker, as is my team, but right now we develop locally, then deploy to a local docker and then deploy to production (process simplified--there's a but more change and source control involved). We'd certainly love to do away with that first phase and simply do everything always in Docker, and despite all of us having MSDN accounts, almost all of us have moved from VS to VS Code. It's wonderful besides the debugging within Docker part. 👍

hartmannr76 commented 7 years ago

I was running into a bunch of issues with either nothing happening, or debugger kept breaking on start. My issue was actually that I was on the C# extension v1.7. VSCode was not telling me I had an updates available, but looking in the extensions view, I could see 1.7.0. I just upgraded to 1.8.0 and everything worked fine. Thanks for the solution @galvesribeiro

bialad commented 7 years ago

I've implemented @galvesribeiro workflow in a sample repo, and at first it's seems to be working correctly. But when I call the web service it does not respond.

docker-debug-dotnet

The difference I see from what I normally do is ENTRYPOINT ["tail", "-f", "/dev/null"] instead of ENTRYPOINT dotnet docker-debug-dotnet.dll. Could this make a difference for ports?

When running the docker debug workflow I get an output saying that the application is started

Hosting environment: Development
Content root path: /app
Now listening on: http://localhost:6000
Application started. Press Ctrl+C to shut down.

and docker ps tells the port is open

CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS              PORTS                    NAMES
0345816c3fd7        docker-debug-dotnet:debug   "tail -f /dev/null"      23 minutes ago      Up 23 minutes       0.0.0.0:6000->6000/tcp   docker-debug-dotnet

Does anyone have an idea why the application is not triggering on port 6000 even though it seems to be open?

galvesribeiro commented 7 years ago

@bialad you have to set the proper ports on your docker-compose file.

The reason I'm using tail -f /dev/null instead of set dotnet something.dll as entrypoint while debugging is because it is faster to keep the container up and just start/stop the application.

If you do the way you are suggesting, every time you start a debug session you need to spin up a new container.

Like I mentioned before, to make it work on windows server containers, just change the base image and the entrypoint to something similar in powershell/cmd.

So, to fix your case, just change this:

ports: 
      - "20200:20200"

To this:

ports: 
      - "6000:6000"

And it should work for you.

galvesribeiro commented 7 years ago

@hartmannr76

I was running into a bunch of issues with either nothing happening, or debugger kept breaking on start. My issue was actually that I was on the C# extension v1.7. VSCode was not telling me I had an updates available, but looking in the extensions view, I could see 1.7.0. I just upgraded to 1.8.0 and everything worked fine. Thanks for the solution @galvesribeiro

Yeah, sorry, I forgot to mention that my sample is valid on VSCode with C# Extensions 1.8.0+. Starting this version, they replaced the old clrdbg with the vsdbg as the new debugger.

However, it should work exactly same as before. You just have to change the dockerfile to pull the old clrdbg and change the launch.json accordingly.

I strongly recommend to update the extension to 1.8.0.

bialad commented 7 years ago

@galvesribeiro I figured I needed to do that, so I did. You can see my config in the repo, or in the docker-compose.yml specifically.

Keeping the container running and restarting the application is a neat trick, that's one reason I really like to get this running. :)

Do you need to do anything different in your application? I use .UseUrls("http://localhost:6000/") in Program.cs for example.

My host is running Ubuntu 16.04, docker version 17.03.0-ce, dotnet cli version 1.1, C# extension 1.8.0 if that differs from your setup.

galvesribeiro commented 7 years ago

Yeah, make sure .UseUrls("http://0.0.0.0:6000") is called to set the proper endpoint according to your needs.

bialad commented 7 years ago

That worked! Thanks @galvesribeiro

Also .UseUrls("http://*:6000") works, which I'm using in my private repo. So I'm sticking with that.

I'll update the repo if anyone's interested in the result.

alexsandro-xpt commented 7 years ago

There are no a definitive steps to get success?

galvesribeiro commented 7 years ago

@alexsandro-xpt what you mean? did you tried my example? It work perfectly for us here...

alexsandro-xpt commented 7 years ago

@galvesribeiro that example https://github.com/Microsoft/generator-docker/issues/130#issuecomment-287222915 ? I tried setup it at my project right now but your files there are a lot of words of or soluction like AdaptPOS-TMS.sln, src/AdaptPOS.TMS.API. This make a hard to replace it for my demo project, my project is a empty dotnet new webapi and a only want setup Docker features to be ready at VSCode and VS2017.

bialad commented 7 years ago

@alexsandro-xpt Try this. It's very simple and what I successfully use for several of my projects.

alexsandro-xpt commented 7 years ago

@bialad, I have tried, I not clone this repository, only just changed vscode files and docker files.

galvesribeiro commented 7 years ago

@alexsandro-xpt man, it is extremely simple to replace project name in that example. You can try @bialad repo which is the exactly same code with simpler names if you prefer...

However, its important to mention that it is for Visual Studo Code(VSCode) and not Visual Studio 2017.

VS2017 has its own extension for docker debugging but IMHO, it is very weird and dont allow you debug multiple projects neither multiple instances of the same service.

VSCode one in other hand, allow you to easily debug multiple projects easily BUT, it still need some work (which I plan to do) to debug multiple instances of the same container.

Both the cases need some work to get proper debugging on multiple instances of the same process.

alexsandro-xpt commented 7 years ago

Alright @galvesribeiro, I will try it too late, the trully is I tried set up it quickly but in another time I will try better.

Thank you and @bialad to reply!

alexsandro-xpt commented 7 years ago

Why @galvesribeiro and @bialad use microsoft/dotnet:1.1.1-sdk or microsoft/dotnet:1.1-sdk-msbuild instead of microsoft/aspnetcore:1.1 image at yours Dockerfile?

galvesribeiro commented 7 years ago

@alexsandro-xpt because all we use is .net core. I have no idea what is the difference with aspnetcore one is... In fact, it is a lot confusing...

alexsandro-xpt commented 7 years ago

@galvesribeiro I found this post https://blogs.msdn.microsoft.com/stevelasker/2016/09/29/building-optimized-docker-images-with-asp-net-core/ may it can help us to do a better work.

raRaRa commented 7 years ago

Could I change pipeProgram to cmd or powershell for Windows?

galvesribeiro commented 7 years ago

Absolutely. It work the wane way on Windows with both Powershell and CMD.

raRaRa commented 7 years ago

Hmm, I get this issue when I try to debug within VS code using cmd for pipeProgram:

Starting: "cmd" "docker exec -i dockerdev_app_1 /vsdbg/vsdbg --interpreter=vscode" "/vsdbg/vsdbg --interpreter=vscode" Error from pipe program 'cmd': 'Content-Length:' is not recognized as an internal or external command, Error from pipe program 'cmd': operable program or batch file.

Any ideas? Thanks.

rmorrin commented 7 years ago

@raRaRa I had the same issue, did you tried using powershell instead? Another thing I noticed is that the /vsdbg/vsdbg --interpreter=vscode is added automatically so you can remove this from pipeArgs in launch.json:

"pipeArgs": [
    "-c",
    "docker exec -i dockerdev_app_1"
]

Hope this helps!

daveclarke commented 7 years ago

I struggled for some time with this, trying to update the files created by the yeoman generator. I'm using a Mac and the following launch.json works for me:

    {
        "version": "0.2.0",
        "configurations": [
            {
                "name": "Launch",
                "type": "coreclr",
                "request": "launch",
                "preLaunchTask": "composeForDebug",
                "cwd": "/app",
                "program": "/app/MicroService.dll",
                "pipeTransport": {
                    "debuggerPath": "/vsdbg/vsdbg",
                    "pipeProgram": "docker",
                    "pipeCwd": "${workspaceRoot}",
                    "pipeArgs": [ "exec", "-i",  "microservice_microservice_1" ],
                    "quoteArgs": false
                }
            }
        ]
    }

Note I still use the yeoman-generated dockerTask.sh for building/composing the application.

galvesribeiro commented 7 years ago

@daveclarke yeah, it may work. I just don't want to depend on a script if I can avoid it.

daveclarke commented 7 years ago

@galvesribeiro I agree - I spent some time trying to get the yeoman generated files to work for my project with only limited success. Right now, using your template, it's working and I need to move onto something else but will definitely be coming back to refactor.

galvesribeiro commented 7 years ago

The only problem I see today is that neither VS or VSCode is able to debug multiple instances of the same container... lets say, a Service which has multiple replicas and you want to debug them in parallel...

But that is probably something that we should ask in another issue.

akay64 commented 7 years ago

Hi Guys! I just wanted to try to contribute to get this debugging and docker setup up and running faster. I created this repo -> https://github.com/akay64/aspnetcore-2.0-docker-dbg

It has clear instructions and presets on how to get this running quickly. Feel free to give your feedback :)

This project is designed to give a scaffolding inside which any .NET Core 2.0 ASP.NET application can be developed, debugged and deployed using Docker. Just follow the step by step instructions and have a ASP.NET Core 2.0 app dev enviorment ready in no time.

alexsandro-xpt commented 7 years ago

@akay64 dotnet watch run is not necessary?

evil-shrike commented 6 years ago

trying to follow advice in this issue:

            "pipeTransport": {
                "debuggerPath": "/vsdbg/vsdbg",
                "pipeProgram": "cmd",
                "pipeCwd": "${workspaceRoot}",
                "pipeArgs": ["-c",
                    "docker exec -i src_tests_mstest_1"
                ]
            }

Fails with: Starting: "cmd" -c "docker exec -i src_tests_mstest_1" "/vsdbg/vsdbg --interpreter=vscode" Error from pipe program 'cmd': 'Content-Length:' is not recognized as an internal or external command, Error from pipe program 'cmd': operable program or batch file.

Tried to call Docker directly:

            "pipeTransport": {
                "debuggerPath": "/vsdbg/vsdbg",
                "pipeProgram": "docker",
                "pipeCwd": "${workspaceRoot}",
                "pipeArgs": [
                    "exec", "-i", "src_tests_mstest_1"
                ]
            }

Error: The pipe program 'docker' exited unexpectedly with code 126.

Any ideas?

evil-shrike commented 6 years ago

This works:

            "pipeTransport": {
                "debuggerPath": "/vsdbg/vsdbg",
                "pipeProgram": "docker",
                "pipeCwd": "${workspaceRoot}",
                "pipeArgs": [
                    "exec", "-i", "my_container"
                ],
                "quoteArgs": false
            }
merovin2 commented 6 years ago

@galvesribeiro Your solution worked for me, saved my day.

kevnord commented 5 years ago

@matthewjcooper I'm not using this template anymore because it is very limited and rely on external and platform specific issues. So what I have is:

Dockerfile:

FROM microsoft/dotnet:1.1-sdk-msbuild
ENV NUGET_XMLDOC_MODE skip
WORKDIR /vsdbg
RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        unzip \
    && rm -rf /var/lib/apt/lists/* \
    && curl -sSL https://aka.ms/getvsdbgsh | bash /dev/stdin -v latest -l /vsdbg 
WORKDIR /app
ENTRYPOINT ["tail", "-f", "/dev/null"]

Solution level docker-compose.yml

version: '3.1'

services:
  YOU_PROJECT:
    image: YOUR_PROJECT:debug
    build:
      context: ./src/YOU_PROJECTDIRECTORY_HERE/bin/PublishOutput/
      dockerfile: Dockerfile.debug
    ports: 
      - "10200:10200"
    environment: 
      - ASPNETCORE_ENVIRONMENT=Development
    volumes: 
      - ./src/YOU_PROJECTDIRECTORY_HERE/bin/PublishOutput/:/app
      - ~/.nuget/packages:/root/.nuget/packages:ro

VSCode tasks.json

{
    "version": "0.1.0",
    "command": "dotnet",
    "isShellCommand": true,
    "args": [],
    "tasks": [
        {
            "taskName": "publish",
            "args": [
                "${workspaceRoot}/YOUR_SOLUTION.sln", "-c", "Debug", "-o", "bin/PublishOutput"
            ],
            "isBuildCommand": true,
            "problemMatcher": "$msCompile"
        }
    ]
}

VSCode launch.json

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "YOUR_PROJECT",
            "type": "coreclr",
            "request": "launch",
            "cwd": "/app",
            "program": "/app/YOUR_PROJECT.dll",
            "sourceFileMap": {
                "/app": "${workspaceRoot}/src/YOUR_PROJECTDIRECTORY"
            },
            "pipeTransport": {
                "debuggerPath": "/vsdbg/vsdbg",
                "pipeProgram": "/bin/bash",
                "pipeCwd": "${workspaceRoot}",
                "pipeArgs": [
                    "-c",
                    "docker exec -i YOUR_PROJECT_1 /vsdbg/vsdbg --interpreter=vscode"
                ]
            }
        }
    ]
}

To make it work:

  1. Build your project which will actually publish the files to output (I had to publish because my app requires load assemblies via reflection and the new .Net core runtime fail if I run directly from bin/debug without publish)
  2. Open a terminal to the solution directory
  3. Do a docker-compose build -> This need to be done only once if you change the container image
  4. docker-compose up -d -> This is only required once since the container will kept alive between debug sessions. Only need do it again if you tear down the container.
  5. Hit F5 on your project -> That will run the app inside the container and attach a debugger to it.

Every time you make change to the code you just need to build the project again (which will effectively publish whole solution) and run. Note that you can run multiple projects but not at same tiem. For wharever reason the VSCode setting to start multiple projects don't work while remote debugging. That doesn't prevent you to hit Start button in the second config so both debugger are running live.

Limitations: You can't use docker-compose scale yourservice=12 because you can't attach the debugger to multiple containers. This is a VSCode limitation, not docker, neither that settings.

Note that I'm not using any magical .sh or .ps1 to make things to work. Just pure VSCode config and docker. You should be able to add windows support on this config by just add that platform specific overrides on launch.json.

Let me know if you have any issues.

UPDATE: I forgot to mention that you must be on VSCode with the C# extension on version 1.8.0 or superior. If for whatever reason you need to keep pre-1.8.0, please update the example to use clrdbg instead of vsdbg.

Thank you SO much for documenting this! I got it to work and am thrilled. So helpful. Thanks again.

galvesribeiro commented 5 years ago

One thing to add to that. My sample was based on docker-compose but it would work the same way if you are using kubernetes. Just change the pipeProgram to kubectl and the parameters to the one where you run containers/services on k8s and have an interactive terminal attached to it. VSCode debugger protocol is based on console in/out buffers so it can somehow easily adapt for other debuggers.

kevnord commented 5 years ago

Good to know! Thanks. I put my configs into gist. https://gist.github.com/kevnord/e96e5a22f46d6ca1e0e7debb13b4684b