Curtis Z

22 posts

Someone set us up the Docker!

I love Docker. You have probably heard many things about it, from the promises it makes about streamlining the deployment process to the power it brings to developers with consistent environments. As a twenty-year veteran of Unix and Linux, it seems like the most natural way forward for computing in general. It finally abstracts away certain details of the operating environment such that your apps are manipulated as something like indepenedent service packages. Your hardware becomes a magic black box where role-based app containers go in one end, perform the services they're meant to perform and when finished, come out the other end having left almost no trace on the hardware host.

One of the first problems you're going to encounter while using docker is how to handle multiple containers efficiently. In our particular situation, we've got a few website containers that we want to run on load-balanced, commodity, dedicated hardware hosts with tons of IPs. Because we're a couple of guys running many sites in our spare time, this means we need to automate the deployment and container/service discovery process for developer efficiency. This means we don't want to use a few kludgy shell scripts listening for webhooks from Github or one of our CI/CD providers (we mostly use Shippable). We want something fire-and-forget like a Javelin missile so we can haul ass to the next target. It is our spare time, after all. Push a commit and wait a minute or so to check the live results in production: Our infrastructure handles the details.

There is basically one starting point for running multiple Docker containers on a single hardware host if you don't want to dive into one of the devops frameworks out there: This post by Jason Wilder, the primary architect of this particular method of using Nginx as a reverse proxy for your containers, as well as the creator of the image you'll want to use (jwilder/nginx-proxy). This method works by using a utility called docker-gen also written by Jason. Incidentally, he seems like a cool dude that I would like to hang out with. We seem to share a lot of the same technical interests. Anyways, there are a few others out there building on this method with helpful additional information.

This post is going to be exactly the same as eighty-five percent (or more) of the other posts on the internet about Docker running behind an Nginx reverse proxy, mostly because I used those very same articles to get my own Docker infrastructure going. And that's okay because I've got a few things to add.

Since you're still here... Run docker pull jwilder/nginx-proxy to retrieve the proxy image. You can do this from any directory, but you may need to use sudo if you haven't added your current user to the docker group in /etc/group (and generated a new login for the changes to take effect, for example by opening a new terminal window, or logging out and logging back in again, or using su username with whatever your username is).

Once you've got the image, you can immediately run the correct Docker commands to spin up a new container, but let me make a suggestion... I create a simple bash script with the proper command to start my container. I keep these bash scripts together with the Dockerfile -- if you aren't using Dockerfiles, you're a masochist -- and check them in to whichever source control you use. On a side note, if you haven't lately, take a look at Gitlab for self-hosted, private git repositories. A-mazing, easy to use, and hosted on your own hardware to keep the fascists from getting at your source with a simple twist-of-the-arm at Github. Anyways, here's a simple bash script (named nginx-proxy.sh) which is very similar to the one I use to start my Nginx reverse proxy:

#!/bin/bash
docker run -d \
  --name="nginx-proxy" \
  --restart="always" \
  -p 80:80 \
  -v /var/run/docker.sock:/tmp/docker.sock \
  jwilder/nginx-proxy

Even though a quick man docker-run will tell you what this does, let's go ahead and break this down line-by-line. The -d flag means to run in detached mode, i.e. in the background. We use --name="nginx-proxy" to assign the name nginx-proxy to the running container. This will come in handy if you need to do anything with it afterwards, so that you won't need to use those long hexadecimal IDs (e.g. ca382dc4461d). --restart="always" is important because it tells docker to restart the container on reboot. Next we use -p 80:80 to route the hardware host's tcp/80 to the container's tcp/80. The most confusing flag (-v /var/run/docker.sock:/tmp/docker.sock) is probably the most important. This is because it mounts our docker "control" socket inside the Nginx reverse proxy container, exposing it to the docker-gen utility inside the container. Finally, we're specifying that we want to run the jwilder/nginx-proxy image we pulled earlier.

Make sure you chmod +x nginx-proxy.sh and then run it. Voila! After a couple of seconds you should be able to see an Nginx 502 or 503 error when visiting your host's URL. It's okay to see these "errors" because it means the Nginx reverse proxy is running and listening for inbound connections, we just have nothing listening to answer. If you don't see Nginx answering your requests, try a quick docker logs nginx-proxy and you should see a few lines of output pointing you in the direction of the problem.

That's all for now. For my next trick, I will take you step-by-step through starting a Docker container with a very simple website to put behind your new Nginx reverse proxy. Happy New Year!

Dockerized Ghost: Es regnet.

Aloha and welcome to the first post of my developer blog! I'm dumping notes and distilled knowledge into this address and I hope that you are able to find something useful within. It's 5am on New Year's Eve and there is always just so much to do. I've been consumed by a few projects that never seem to end. But here I am, in the night, banging on a keyboard for great justice. I'll post a bit later about my adventures with Docker. But for now, time for this programmer's garbage collection!