I was recently setting up my development environment on a Mac for a for project that used Docker containers. My prefered editor/IDE is RubyMine, where I get a great integrated debugging tool. There were a couple of gotchas in getting all the parts working with running the debugger inside the Docker container, but at the end it's pretty straightforward.
The basic idea
The basic idea behind remote debugging is that the application runs in debug mode on a remote server (or in a Docker container in our case), while listening for debugging info on a specified port. When execution hits a specified breakpoint, the debugger sends stack frame info back to the IDE, which is listening on another specified port for this info. The key to this is that your source code exists in both environments and that there is a mapping between the two. If your Docker development environment is set up correctly, it should automatically "copy" your working directory into the container when you launch it, so you are already halfway there.
Before you start
Get the IP address for your Docker container. You'll need this later.
$ docker-machine ip default 192.168.99.100
Add "ruby-debug-ide" and "debase" gems to your Gemfile. You will have to remove "byebug" as it will interfere with debugging.
group :development, :test do gem 'ruby-debug-ide' gem 'debase' # gem 'byebug' end
Go to Run -> Edit Configurations...
Add a new "Ruby remote debug". Fill in the fields with the following:
Remote host: your docker container's ip address, in our case, 192.168.99.100
Remote port: 1234
Remote root folder: /app/yourApp (or whatever your application's root directory is in your docker container)
Local port: 26162
Local root folder: path to the folder of your project on your local machine
At the top of the dialog box is "Server command" field. Take note of this as it will contain the basic debug command and options you will run in the Docker container:
rdebug-ide --host 0.0.0.0 --port 1234 --dispatcher-port 26162 -- $COMMAND$
Click "OK" to save the new configuration.
(More info on remote debugging can be found on the JetBrains site .)
Run the debugger in the Docker container
You will need to pass through port 1234 from your local environment into the container. I like to open a bash shell into the container.
$ docker-compose run -p 1234:1234 -p 3000:3000 default bash $
Install the bundle, if necessary:
Run the debugger using the "Server command" from above as a guide. You may or may not need bundle exec depending on your setup. You will most likely need to bind the rails server to the right IP addresses to make it work. I simply use all IP addresses to make it easier.
$ bundle exec rdebug-ide --host 0.0.0.0 --port 1234 --dispatcher-port 26162 -- /app/yourApp/bin/rails s -b 0.0.0.0 -p 3000 -e development
Run remote debugging in RubyMine
Go to the "Run" menu and select the new Debug configuration that you set up.
Put a break point somewhere in your code and voilà! Integrated IDE debugging with a Docker container.
What's up with all the "Stack frame is not available" messages :(
When you hit a breakpoint, you will most likely see your current frame in the Debugger's "Frame" panel, but nearly everything else will be greyed out. If you scroll down, you'll see that some stack frames are available, but most are not. The reason is that the location for the project files is mapped between your local environment and the container environment as set in the "Ruby remote debug", BUT the locations of the gems are not. All the greyed out "not available" frames are ones that point to files in various gems. Basically, the debugger sends file location and line number info back to RubyMine, but when RubyMine looks for the file with the given path, it's not there because it is the location of the gems inside the Docker container. Unfortunately, the debugger gem and RubyMine combination does not have a way to specify separate local and remote paths for the gems. There is a short discussion related to this on the JetBrains support forum here.
One solution is to bundle the gems into vendor gems in your project, but I didn't like that one would be unecessarily including external dependencies into the project.
The solution that I've found that works pretty cleanly is to create an empty directory on your local system that matches the location of the gems in the container, and simply symlink the local gems directory to where RubyMine will be looking for them. It's not always possible to do it this way, nor is it always practical. But it does work and it's fairly easy to implement in situations where you can.
$ mkdir /same/path/as/container/gem/directory $ ln -s /path/to/your/local/gems/direcotry same/path/as/container/gem/directory
This should take care of most of the "Stack frame is not available" issues. There will be some edge cases where the stack frames will not match up due to platform differences.
And there you have it! Your beautiful RubyMine IDE is now (mostly) happily remotely debugging your rails app running in a Docker container.