Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debug Container #56

Closed
galvesribeiro opened this issue Jan 28, 2017 · 22 comments
Closed

Debug Container #56

galvesribeiro opened this issue Jan 28, 2017 · 22 comments

Comments

@galvesribeiro
Copy link

Hello guys,

I would like to know if this extension would accept a PR adding debugging capabilities for a container...

Initially the idea is to make it debug .Net Core applications that are hosted in (multiple) containers and the debugger could be eventually extended to support other types of applications like node.js for example...

If that is the case, I can work on the .Net Core part initially and later on extend it for other techs.

The idea is to make something similar to the experience we have on VS2017 and docker tooling there where you can add the initial docker files (this extension does the very basic already), create the whole infra from the docker (compose) files and then attach a debugger to it.

How does it sound?

Thanks

@lostintangent
Copy link
Member

@galvesribeiro PRs are always welcome! I'm not too familiar with the VS2017 experience, so any feedback/thoughts you have on how to improve this extension would be awesome.

@galvesribeiro
Copy link
Author

Hello @lostintangent, thanks for the promptly reply.

Have a look on this post https://blogs.msdn.microsoft.com/webdev/2016/11/16/new-docker-tools-for-visual-studio/

It show how it work on Visual Studio. Basically you right click a project or solution and add Docker support to it. Once it is added, just hit F5 and all the "magic" happens and you have a debugger attached to your target container. On VS2017 it even support multiple containers which are running on docker-compose scenarios so you can debug a bunch of containers part of same compose file at same time...

@chrisdias
Copy link
Member

For Node, the extension generates a docker-compose.debug.yml file that will open both the site and debugger ports and run node with the debug switch. you could automate the debug process by running docker-compose up on the yml file as a pre launch task and then VS Code should be able to attach to it.

For C#, the same pattern could be used... generate the right task file, docker-compose.debug.yml, and then connecting them in launch.json.

@spboyer
Copy link
Member

spboyer commented Feb 8, 2017

@lostintangent look at my repo here: https://github.com/spboyer/dockerdebugapp
This has the base images and proper debug compose images to attach the debugger to VS Code. There are changes needed to the extension to generate these files for .NET Core.

@chrisdias
Copy link
Member

Cool!

@galvesribeiro
Copy link
Author

Guys I just illustrated the docker-compose as an example. Your approaches work well for a single project, without a solution and that has a single container to attach. The idea is to press F5 and attach a debugger to all services inside docker-compose file.

After multiple try and error, I found a better way that doesn't rely on scripts/hacks to suppress the lack of the feature in VSCode.

This is a sample docker-compose.yml at the solution level:

version: '3.1'

services:
  repository-api:
    image: repository-api:debug
    build:
      context: ./src/QUBIX.Repository.API/bin/PublishOutput/
      dockerfile: Dockerfile.debug
    ports: 
      - "10200:10200"
    environment: 
      - ASPNETCORE_ENVIRONMENT=Development
    volumes: 
      - ./src/QUBIX.Repository.API/bin/PublishOutput/:/app
      - ~/.nuget/packages:/root/.nuget/packages:ro
    depends_on: 
      - repository-silo
  repository-silo:
    image: repository-silo:debug
    build:
      context: ./src/QUBIX.Repository.Silo/bin/PublishOutput/
      dockerfile: Dockerfile.debug
    environment: 
      - QUBIX_REPOSITORY_MBR_CS
    volumes: 
      - ./src/QUBIX.Repository.Silo/bin/PublishOutput/:/app
      - ~/.nuget/packages:/root/.nuget/packages:ro
  repository-reader-silo:
    image: repository-reader-silo:debug
    build:
      context: ./src/QUBIX.Repository.Reader.Silo/bin/PublishOutput/
      dockerfile: Dockerfile.debug
    environment: 
      - QUBIX_REPOSITORY_READER_MBR_CS
      - QUBIX_REPOSITORY_READER_DOCDB_EP
      - QUBIX_REPOSITORY_READER_DOCDB_KEY
    volumes: 
      - ./src/QUBIX.Repository.Reader.Silo/bin/PublishOutput/:/app
      - ~/.nuget/packages:/root/.nuget/packages:ro

This is the Dockerfile.debug:

FROM microsoft/dotnet:1.1-sdk-msbuild
ENV NUGET_XMLDOC_MODE=skip 
ARG CLRDBG_VERSION=VS2015U2
WORKDIR /clrdbg
RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        unzip \
    && rm -rf /var/lib/apt/lists/* \
    && curl -SL https://raw.githubusercontent.com/Microsoft/MIEngine/getclrdbg-release/scripts/GetClrDbg.sh --output GetClrDbg.sh \
    && chmod 700 GetClrDbg.sh \
    && ./GetClrDbg.sh $CLRDBG_VERSION \
    && rm GetClrDbg.sh
WORKDIR /app
ENTRYPOINT ["tail", "-f", "/dev/null"]

Note that I have multiple services in it but none of them has the debugger port open. Note also that the DockerFile.debug basically start the container and the entrypoint just keep it alive without exit. It also download the getclrdbg.sh in order to install the debugger (yes, I think both windows and linux containers for .net core SDK should contain the clrdbg) . A volume is mapped to the project output folder in /app and is passed on docker-compose.

Once in a day, when I start working on the project, I just open a terminal into the solution directory and docker-compose up -d. I don't ever need to turn the container down, unless I need to change something in the container itself like ENV vars, the image, etc. Otherwise, you should have the whole compose project up all the time. (If this is the first time, do a docker-compose build to build the images).

Once you have all the containers up and running, you need to configure your VSCode to get it to debug the containers.

This is my tasks.json:

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

Note that instead of build, I'm using publish. The reason for that is because .Net Core builds doesn't put all your dependencies at the output folder. So inside the container clrdbg will not be able to find the referenced assemblies when requested.

Now this is my launch.json:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Repository-Silo",
            "type": "coreclr",
            "request": "launch",
            "cwd": "/app",
            "program": "/app/QUBIX.Repository.Silo.dll",
            "sourceFileMap": {
                "/app": "${workspaceRoot}/src/QUBIX.Repository.Silo"
            },
            "pipeTransport": {
                "pipeProgram": "/bin/bash",
                "pipeCwd": "${workspaceRoot}",
                "pipeArgs": [
                    "-c",
                    "docker exec -i qubixrepository_repository-silo_1 /clrdbg/clrdbg --interpreter=mi"
                ]
            }
        },
        {
            "name": "Repository-API",
            "type": "coreclr",
            "request": "launch",
            "cwd": "/app",
            "program": "/app/QUBIX.Repository.API.dll",
            "sourceFileMap": {
                "/app": "${workspaceRoot}/src/QUBIX.Repository.API"
            },
            "pipeTransport": {
                "pipeProgram": "/bin/bash",
                "pipeCwd": "${workspaceRoot}",
                "pipeArgs": [
                    "-c",
                    "docker exec -i qubixrepository_repository-api_1 /clrdbg/clrdbg --interpreter=mi"
                ]
            }
        },
        {
            "name": "Repository-Reader-Silo",
            "type": "coreclr",
            "request": "launch",
            "cwd": "/app",
            "program": "/app/QUBIX.Repository.Reader.Silo.dll",
            "sourceFileMap": {
                "/app": "${workspaceRoot}/src/QUBIX.Repository.Reader.Silo"
            },
            "pipeTransport": {
                "pipeProgram": "/bin/bash",
                "pipeCwd": "${workspaceRoot}",
                "pipeArgs": [
                    "-c",
                    "docker exec -i qubixrepository_repository-reader-silo_1 /clrdbg/clrdbg --interpreter=mi"
                ]
            }
        },
        {
            "name": "Repository-Reader-API",
            "type": "coreclr",
            "request": "launch",
            "cwd": "/app",
            "program": "/app/QUBIX.Repository.Reader.API.dll",
            "sourceFileMap": {
                "/app": "${workspaceRoot}/src/QUBIX.Repository.Reader.API"
            },
            "pipeTransport": {
                "pipeProgram": "/bin/bash",
                "pipeCwd": "${workspaceRoot}",
                "pipeArgs": [
                    "-c",
                    "docker exec -i qubixrepository_repository-reader-api_1 /clrdbg/clrdbg --interpreter=mi"
                ]
            }
        }
    ]
}

Now what is happening here? Remember that our publishOutput is mapped to /app into the container? So, for each service/project that I have at the docker-compose.yml, I have a launch configuration. What this launch configuration does, is to call docker exec into the container for that given service and call clrdbg. That will use it to start the remote debugger inside the container and the application to be debugger (which is mapped in /app)and makes .Net Core debugger on you machine to attach to it. Since the dlls are remotely deployed (inside the debugger), the source map parameters tell the debugger where the source is.

This way, when you press stop in a debugger session, the command that exit is the docker exec which started the remote debugger and the application and not the whole container itself. That makes iteration A LOT faster than keep rebuilding the container every time you press F5.

Note that there is no external script or extension at all. All you need is VSCode, C# extension and Docker installed.

That works perfectly! 💃 :shipit:

Now the downside 😞

  1. Note that in the launch configuration I'm using docker exec to target directly into a specific container within the service (for example qubixrepository_repository-reader-api_1 in the last entry).
  2. You are not able to scale your composition to more than one instance and debug multiple instances of the same service.
  3. You are not able to hit F5 and attach the debugger to all services. You have to start one by one from VSCode debugger section. (the compound launch configuration just don't work and even if it does, it does not respect the depends_on which declare the docker-compose dependency chain so one service can start before its dependencies).
  4. Use docker-compose as a pre-launch task, slow down a lot the build process and the debugger experience so I removed it.

The desired experience:

  1. I create a docker-compose.yml file, describe the images and services as usual.
  2. Press F5 to run the project
  3. dotnet build happens at solution level
  4. docker-compose check if there is update, if is not and the services are up, just ignore and don't run it. Otherwise, docker-compose build and docker-compose up are invoked.
  5. docker-compose ps to get all the containers for all the configured projects and attach the debugger to each one of them.
  6. When you stop the debugger on VSCode, you just kill the remote debugger process but the containers are still running.
  7. While the debug session is running, you can attach the debugger for the same project multiple times in different containers (i.e qubixrepository_repository-reader-api_1, qubixrepository_repository-reader-api_2, qubixrepository_repository-reader-api_N) so you can debug distributed application

Does it make sense guys? How can we achieved that experience?

Thanks!

@Shawn-Fan
Copy link

@galvesribeiro
It's very helpful in debugging multi-services, do you have example code?

@galvesribeiro
Copy link
Author

@Shawn-Fan multi-services is something not supported by the debugger out-of-the-box unfortunately. Neither is multi-instance of the same service as well (very useful in truly distributed applications).

If you follow my steps in this issue, you can start all the debug containers from the docker-compose up -d and then run each debugger from the debug section on VS for each service.

I still think MSFT must allow us to attach the debugger to multiple services and multiple instances out-of-the-box. What I did was a hack to workaround the debugger limitation.

@Shawn-Fan
Copy link

Shawn-Fan commented Aug 16, 2017

@galvesribeiro
Thanks for your help very much. I'll try your way late......
Although VS 2017 for windows works pretty nice, I am trying to use Visual studio code in Mac to work in Docker(Hopefully Visual studio for Mac works well as VS 2017).

@galvesribeiro
Copy link
Author

Both VS2017 and VS for Mac have the same limitation regarding Docker debugging experience as VSCode...

@ferantivero
Copy link

first of all thanks to @galvesribeiro for the detailed information he shared here. It was really helpful.

For people who is looking to debug aspnetcore 2.0 from vscode I had to do some minimal tweaks to the Dockerfile + launch.json:

  1. change the FROM to microsoft/aspnetcore-build:1.0-2.0 (because my app, maybe you can just go with 2)
  2. use vsdbg instead of clrdbg, since it seems with the latest C# extensions crl is not supported anymore. For detailed information on this please refer to this Dockerfile.debug
  3. changed a little bit the launch.json because point#2 and also included the preLaunchTask for publish:

Important: everything between <> needs to be filled with data from your app

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "<Just_Another_Name_For_Your_Launch_Conf>",
            "type": "coreclr",
            "request": "launch",
            "preLaunchTask": "publish",
            "cwd": "/app",
            "program": "/app/<Your_AppName_Here>.dll",
            "sourceFileMap": {
                "/app": "${workspaceRoot}/<your_project_folder_here>"
            },
            "pipeTransport": {
                "pipeProgram": "docker",
                "pipeCwd": "${workspaceRoot}",
                "pipeArgs": [
                    "exec -i <your_containername_here>"
                ],
                "quoteArgs": false,
                "debuggerPath": "/vsdbg/vsdbg"
            }
        }
    ]
}

Happy debugging!

@mmacneil
Copy link

@galvesribeiro - great work, thanks for sharing. Do you, by chance have an example Dockerfile from one of your service projects - ie. repository-api

Thanks
Mark

@galvesribeiro
Copy link
Author

@mmacneil thanks. The debug image is the one I posted here. The runtime image is essentially an empty image inheriting from a microsoft/dotnet runtime-only image and the entrypoint is dotnet myProject.dll without any fancy stuff in it.

@mmacneil
Copy link

Thank you @galvesribeiro, that makes sense. I was thinking somehow you were merging Dockerfile.debug and the runtime image as I have seen that mentioned elsewhere but I see in this case there are 2 separate containers for debug and runtime.

@philliphoff
Copy link
Contributor

#500 added preview support for (single project) ASP.NET .NET Core debugging (a la VS). I'd love to know if this gets you closer to what you want and, more importantly, if not, what else is needed.

@galvesribeiro
Copy link
Author

@philliphoff was it released to VSCode?

@philliphoff
Copy link
Contributor

@galvesribeiro Sorry, perhaps I got a little ahead of myself. The PR is merged to master, but it's not yet been released.

@galvesribeiro
Copy link
Author

galvesribeiro commented Oct 19, 2018 via email

@PrashanthCorp
Copy link
Contributor

@galvesribeiro , we're closing this bug based on the PR #500. File a new bug if you run into any issues. :-)

@galvesribeiro
Copy link
Author

Cool @PrashanthCorp I didn't had time myself to test it as I promised. Did it found its way to the release build of VSCode? Is there are doc/guide on how to proper use it and what should we expect from it?

Thanks!

@StephenWeatherford
Copy link
Contributor

@galvesribeiro
Copy link
Author

Thanks @StephenWeatherford

Just saw that... However, it still struggle in a single-container debug :( No compose or kubernetes support...

@vscodebot vscodebot bot locked and limited conversation to collaborators Apr 21, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants