Moving a MiddleMan blog from Vagrant to Docker

21 Feb 2015   docker, middleman

Like a lot of people I’ve been following Docker with interest. At Fifty we usually deploy our Rails apps to AWS using Elastic Beanstalk. This means we have to use Amazon Linux in production. But Amazon Linux can’t be run outside of AWS so we use CentOS for development. This is less than ideal and moving to Docker would allow us to run identical containers in all environments.

In production there are also the performance benefits from removing the VM layer. I also like the idea of combining Docker with auto scaling to launch new containers much faster than VMs.

However Docker and the ecosystem around it are still evolving. For AWS we could use Docker with Elastic Beanstalk, the new Elastic Container Service or use EC2 with a specialist host OS like CoreOS. Until these deployment options settle a bit more we’ll continue using VMs for our existing projects. But for new projects it’s likely we’ll use Docker. As long term switching from VMs to containers makes sense for us.

Barcelona on Rails

After reading a lot about Docker I’ve been keen to try it out. On Thursday this week I went to my first Barcelona on Rails meet-up. The talk by Jorge Dias from Xing was Docker basics with code examples. So the subject was perfect for me and it was an excellent talk by Jorge.

Following on from the talk I decided to replace the Vagrant VM I use for this blog with a Docker container.

Moving MiddleMan

This blog is a static site hosted on S3 which is generated using the MiddleMan static site generator. To generate the blog I use a Vagrant VM. This uses Ubuntu as the guest OS. The Vagrantfile has some simple provisioning steps which install Ruby using rbenv. Its a simple setup especially since MiddleMan doesn’t use a database. So it was a good first candidate for replacing Vagrant with Docker.

boot2docker

I use OS X as my host operating system. Since Docker is built on top of technologies such as LXC (Linux Containers) it only runs on Linux. This is solved by using boot2docker which is a lightweight Linux distribution designed for running Docker containers in development.

I used HomeBrew to install both Docker and boot2docker on my Mac. The boot2docker VM runs on VirtualBox but I already had it installed from using Vagrant.

brew install docker
brew install boot2docker

boot2docker init
boot2docker up

On running boot2docker up it will tell you the 3 environment variables you need to set to allow your docker client to connect to the boot2docker server. It makes sense to add these to your .profile so they are set when your shell starts.

Dockerfile

Moving from Vagrant to Docker involved replacing the existing Vagrantfile with a Dockerfile. The 2 files have a very similar purpose. For Vagrant it specifies the guest VM, network setup and the provisioning steps. For Docker it has the same purpose except you specify the Docker base image rather than a guest VM and it doesn’t include the network settings.

FROM ruby:2.1.5

RUN apt-get update

RUN usr/sbin/useradd --create-home --home-dir /app --shell /bin/bash blog

WORKDIR /app

COPY Gemfile* /app/

RUN bundle install

ADD . /app

RUN chown -R blog:blog /app

USER blog

CMD ["middleman", "server"]

I’m using the Ruby 2.1.5 image from the Docker public registry. This is a lightweight image based on Debian with Ruby pre-installed meaning that I could remove the rbenv provisioning steps and Chef Solo. The only provisioning steps needed for Docker are to create a blog user and do a bundle install for the MiddleMan gem, extensions and their dependencies. Creating a user is recommended as otherwise your containers will run as root.

The build command creates a Docker image from the Dockerfile. This includes downloading the base image and running the provisioning commands.

docker build -t blog .

The run command with the rm option starts the container but doesn’t persist any changes when the container is destroyed. The -p option means the middleman port is forwarded to the boot2docker host.

docker run --rm -p 4567:4567 -t blog

The run command with the -i option launches a container with an interactive session. For middleman this lets me build the site and deploy it to S3.

docker run -i --rm -t blog /bin/bash
blog@6972429a614b:~$ bundle exec middleman build

Conclusion

Overall I’m impressed with Docker so far and the move from Vagrant was very straightforward. Although this is a very simple example and it seems that a lot of the complexity with Docker is in managing storage and deciding what data is included in the container. We’ll face this challenge in development for our Rails apps and running the development and test databases. However in production this should be less of an issue as we’ll continue to use RDS Postgres from AWS.

At the moment I’m keen to use Docker directly to get a good understanding of the commands. However then I think I’ll move to Fig which acts as a wrapper for Docker and provides a workflow that is similar to Vagrant.

comments powered by Disqus