UPDATED: Running the Rails debugger in a Docker container using RubyMine on a Mac

note from Mon Feb 6, 2017

This is a follow-up post to my original post about setting up Docker and RubyMine. It is basically updated to use the newer and better Docker for Mac, rather than the older Docker Toolbox setup that required docker-machine and VirtualBox.

[UPDATE 4/16/2017] Some people seem to be having problems with the container not being able to reach out to the dispatcher IP address on the host. I have not experienced this issue until today with a new installation on a different Mac. AFAIK the configuration should be the same as the other computer and I do not see any meaningful difference with the docker setup. I will investigate and post what I find shortly.

Some develoment projects that I have been working on recently use Docker technology to manage a consistent envornments across development and deployed environments. Although not right for every project, using Docker containers offers many benefits, including shorter developer onboarding times and reliable and predictable behavior across environments. 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.

 

Configuration

I had neglected in my last post to include some information about my platform and configuration info. I am running macOS Sierra on a MacBook Pro (a 2013 model, I think), Docker for Mac, and RubyMine 2016. The project uses docker-compose to manage multiple containers that provide various services.

 

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.

 

Project configuration

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

 

RubyMine configuration

Go to Run -> Edit Configurations...

Add a new "Ruby remote debug". Fill in the fields with the following:

Remote host: localhost (use your docker container's IP address if localhost does not work, but it should)

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:

$ bundle

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.

 

Voilà!

And there you have it! Your beautiful RubyMine IDE is now (mostly) happily remotely debugging your rails app running in a Docker container.

tags: #docker #dockercontainer #rubyonrails #ruby-debug-ide #debugging #docker-compose #mac

Docker Containers

I'm still relatively new to the world of Docker containers. Here are two O'Reilly books that have lots of useful info.

 

Docker is constantly improving, evolving and changing. Here is a book that has some more up-to-date info, as well as a concise step-by-step tutorial to get up and running.

 

Rails books

This is the grandaddy of the Ruby on Rails books. Most Rails developers I know (including myself) started with this one.

I also like the Rails Recipes book from the same publisher, as it gives real world problems that I can relate to and then sample solutions. There doesn't seem to be a Rails 4 edition yet. Here is the most recent one that I've found.