My First Docker
I’ve been told I should check out Docker for over a year. Chris Chang and Noah Seger at the Tribune were both big proponents. They got excited enough I always felt like I was missing something since I didn’t get it, but I haven’t had the time to really dig into it until the last few weeks.
After my initial glance at it, I couldn’t see how it was better/different than using Vagrant and a virtual machine. Over the last few weeks I’ve started dipping my toes in the Docker waters and now I’m starting to understand what the big deal is about.
Docker versus VM
I’ve been a longtime fan of Vagrant as a way to quickly orchestrate virtual machines. That fits my brain. It’s a server that’s run like any other box, just not on existing hardware. Docker goes a different route by being more about applications, regardless of the underlying OS. For example, let’s talk about my npm-cache.
Using this blog post as a base, I wanted to create an easily deployable nginx instance that would serve as a cache for npmjs.org. The normal route for this is to get nginx installed on a server and set it up with the right configuration. You could also add it to an existing nginx server if you have one running.
Docker views something like this npm-cache less as the pieces of that infrastructure (nginx and the server its on) and more as an application unto itself with an endpoint that you need to hit. Its a subtle shift, but important in a service-oriented world.
Docker has been described as Git for deployment, and there’s a reason. Each step of a deployment is a commit unto itself that can be shared and re-orchestrated into something bigger. For example, to start my npm-cache, I started by using the official nginx container.
The nginx container can be configured by extending it and providing your own configuration. I used in the configuration from yammer, created a few empty directories that are needed for the cache to work, then I was almost ready to go. The configuration needed to know how to handle rewriting the responses to point to the caching server.
Parameterizing a Container
This is where things got a little tricky for me as a Docker newbie. nginx
rewrites the responses from npm and replaces
registry.npmjs.org with your
own host information. Starting the container I would know that information,
but inside the running container, where the information was needed, I wouldn’t
know unless I had a way to pass it in.
I managed this by creating a simple script called
runner that checks for two
environment variables to be passed in: the required
PORT and the optional
HOST is optional because I know what it is for
boot2docker (what I use locally).
PORT is required because you have to
tell Docker to bind to a specific port so you can control what nginx uses.
runner script outputs information about whether those values are
available, exiting if
PORT isn’t, modifies the
/etc/nginx.conf file, then
nginx. The whole thing is less than 20 lines of code and could
probably be made shorter.
Deploying with Docker
I got all of this running locally, but then the thought occurred to me that this shouldn’t be that hard to get running in the cloud. We use Digital Ocean a lot at Continuum, so I decided to see what support they have for Docker out-of-the-box. Turns out, you can launch a server with Docker already configured and ready to run.
With that, deploying is ridiculously easy. I started a small box with Docker
installed, then used
ssh to connect to the box, and ran the following
docker pull tswicegood/npm-cache export PORT=8080 docker run -d -e HOST=<my server's IP> -e PORT=$PORT -p $PORT:80 tswicegood/npm-cache
That’s it! Including network IO downloading the npm-cache, I spent less than five minutes from start to finish to get this deployed on a remote server. The best part, I can now use that server to deploy other infrastructure too!
Making deployment of a piece of infrastructure this easy is not a simple problem. I’m sure there are all sorts of edge cases that I haven’t hit yet, but kudos to the Docker team for making this so easy.