Why and How To Use Docker for Development
Docker is not just for deployment, it’s great for development too
We’re heavily invested in Docker at Iron.io, where we’re using it for the deployment of all our APIs and apps, running other people’s code on our IronWorker infrastructure, local testing for our users, and delivering our new IronMQ v3 on-premise product. We also use it for development, here’s why and how.
Why Use Docker for Development?
Here are a few of the reasons why you could/should use Docker for development.
- Consistent development environments for your entire team. All developers use the same OS, the same system libraries, and the same language runtime, no matter what host OS they’re using (even Windows, if you can believe it).
- The development environment is the exact same as the production environment. Meaning you can deploy and it will “just work”.
- If you’re having a hard time building something (by build, I mean compile), build it inside Docker. This primarily applies to developers using MacOS and Windows.
- You only need Docker to develop. You don’t need to install a bunch of language environments on your machine. Want to run a Ruby script but don’t have Ruby installed? Run it in a Ruby Docker image.
- You can use multiple language versions without having to resort to all the hackarounds for your language (python, python, ruby, ruby, java, node). Want to run your Python program in Python 3, but only have Python 2 installed? Run it using Python 3 image. Want to compile your Java program with Java 1.6 instead of the 1.7 that’s installed on your machine? Compile it in a Java 1.6 Docker image.
- Deployment is easy. If it runs in your container, it will run on your server just the same. Just package up your code and deploy it on a server with the same image or push a new Docker image with your code in it and run that new image.
- Can still use your favorite editor/IDE as you normally do. No need for running a VM in VirtualBox and SSHing in and developing from the shell just so you can build/run on a Linux box.
How To Use Docker for Development
It’s not much different than what you normally do for development, except for two things:
- Ensure all your dependencies are in your working directory, along with your code.
- Change your build and run commands to run inside your Docker container.
I’ll show how to do this with a few different languages, starting with a long explanation in Ruby, then quick run-throughs in other languages.
Let’s start with a very simple Hello World example in Ruby. You don’t even need Ruby installed to run this.
Copy the code below and put it in a file called hello.rb.
Now run it:
It should print “Hello Ruby!”. Easy peasy. This is the really the magic command you’ll use for almost everything. Now let’s add a dependency just to show how vendoring your dependencies works so you can run it inside the container easily without having to install the dependencies into the container. Copy this into a file called Gemfile:
And change hello.rb to this:
If you run bundle install to install the gems, then run ruby hello.rb, this will work — but if you run it inside the Docker container it will fail because the iron_mq gem isn’t installed inside the container. We could install it inside the container, but then you have to manage the container, remember the state of the container and how you got it to that state before deploying your code to another server, etc. I like my containers ephemeral. You’re much better off vendoring your dependencies and keeping all your code and dependencies all together in a nice little package. So instead, let’s run this:
This will install your dependencies into the working directory and, as a bonus, if there is anything that needs compiling (native extensions), they will be built on the right architecture!
Now one last change to hello.rb to use this new bundle, notice the require_relative line at the top:
Now let’s try running it again with our new dependency:
All good! How about running it on a different version of Ruby, like Ruby 1.9? Just run it in a different container:
If you’re making a web app and need to open ports, just pass the -p PORT:PORT flag to docker run. Here’s an example web app to try it out.
Copy this into webapp.rb:
Add gem ‘sinatra’ to your Gemfile, run the bundle install command above again. Then run this command:
And go to http://localhost:8080 to check it out.
That’s about it! Most of these things apply to every language, with some minor differences. There are a few more language examples below, without all the details, and here’s the full source code for all of these.
Similar to the Ruby example, get your dependencies (jar files) into your working directory, then compile your program in the container and run it. The example repo has the dependencies for this example.
Copy this into Hello.java:
Compile it in a container:
Run it in a container:
Node is pretty nice since npm install defaults to installing dependencies in the working directory. First, install our dependency:
Now copy the program into a file called
And run it:
Copy this code into a file called
Now run the following lines to get dependencies, build it and run it:
One Final Tip
In all the previous examples, the container is deleted right after it runs ( — rm) flag. To keep the container for whatever reason, maybe you a dependency that takes a while to build, like RocksDB, you can use this trick:
This will do a docker run the first time you start the container and docker start on the existing container after the first run.
If you tried any of the examples above, you can see that it doesn’t change your development process too much and can provide many benefits as listed at the start of this post. And obviously, all of this can be applied to other languages as well. Give it a try — would love to hear what you think about it.