Using Docker to reduce deployment friction
Many years ago, I was moving for a new job. Once I arrived at my new place, I struggled to find things that were still in boxes from the move. It gave me an idea to create a small application that could help me track physical things hidden away in boxes. I created Boxmeup.
That was over 4 years ago.
Fast forwards to today, and Boxmeup has quite a bit of usage with very little maintenance requirements other than paying for a VPS server. I decided I wanted to move the application to a cheaper box/host, so I chose to go with a very cheap DigitalOcean instance. Now, I had the headache of having to move all the services that Boxmeup is using over to the new provider (MySQL, PHP/Apache, and Sphinxsearch). The tool I chose to do the migration was Docker.
Boxmeup serves as a project that allows me to experiment with different technologies. Whether it be different concepts, libraries, or tools, it is a big enough project that involves typical application features that allows for end-to-end experimentation on a vast array of things.
Over the evolution of the project, it went from very manual deployment and configuration, to Vagrant, and now finally to Docker.
Some of the reasons that I decided on Docker (I will go into details below):
- Versions of software (PHP/MySQL, etc) that I wanted to use are easy to setup.
- docker-compose essentially replaced Vagrant as a provisioner for server software.
- Builds of the application are immutable.
Easy software version with container images
Being able to control exactly what version of software or dependency you run is very challenging. Having to install a specific version of PHP with extensions often requires compiling from source as most linux package managers are usually very out of date. The beauty of Docker (really it’s ecosystem) is that anyone can publish images of preinstalled software. In Vagrant, you have to worry about using some provisioner like Ansible or Chef, but with the docker images, they are simple bash scripts that have layers of configuration to get exactly what you need and build the same way each time.
In the case of Docker, I can simply reference the php-apache:5.6 image and that gets me a stable version of a docker image that I can further customize to my liking.
Another challenge with modern applications is the orchestration of all the various services they need to function. In this case: a datastore and a search index. Docker has a utility called docker-compose which allows multiple docker containers to be built and configured. There’s no need any longer to have Chef cookbooks that install MySQL and Sphinxsearch as these are now replaced with immutable images that I can simply reference and use.
Immutability with building Docker images
The core concept of Docker images is the fact that they are immutable; that is, once an image is constructed it can’t be changed. This property guarantees certain things:
- Production parity — The run-time environment and code that I run in my local and staging environment is exactly the same as what runs in production (aside from different environment variables for configuration).
- I can be assured that the unit tests run against the image means that their test results are valid for what will go to production.
The big move to Digitalocean
After I “dockerized” my application, I was ready to move the services over to the new Digitalocean “droplet”. The transition was simplified drastically since there is a deployment option of an instance that already had Docker installed.
I then simply pulled docker images of all the services (including my application), and run them (with the help of a docker-compose configuration). Very simple and my application was up and running.
A note on continuous integration
Boxmeup is built and tested on Travis CI, and part of that build includes (once merged into the main branch) an automatic docker image build of the application and a push of the image up to Docker Hub. The latest tag (what is built by travis, is meant for a testing environment (local/staging), and when it is deemed ready to release, I can tag it with a specific version and pull that image down on the production box.
There are so many advantages to using Docker for development and deployment that it’s difficult to narrow them down. I didn’t even touch on the added security of the application container being “separated” from the host machine (like a VM without the performance hit). With many providers supporting Docker out of the box (AWS ECS, Google Container Service, etc), it seems that this is the path forward in Devops…well at least until something better comes along.
Boxmeup is open source software, feel free to look at how I use docker with this application.