One of the easiest ways to build applications programmatically into containers through Docker is to use a Dockerfile. Dockerfiles are the Makefiles of the Docker world. A ton of blog posts and tutorials have sprung up over the last few months about how to set up Docker, or how to set up a LAMP stack and even a LEMP stack in Docker.
So, yep, that's great. Now I know how to build a fresh Docker image with a brand new LAMP stack in it. My company doesn't run on fresh LAMP stacks though, it runs on established and customized setups.
The hardest thing about writing Dockerfiles is knowing what to put in them. If you have an older system that's been sitting around for a while, it might be tricky and time consuming to manually discover what you have, what dependencies you require and what configs you need. UpGuard is the surfactant that makes liquifying your infrastructure with Docker much smoother.
For this blog post I have used an example bookstore website that is built with a LAMP stack. I've decided on a LAMP stack because it's sort of familiar to both the enterprise and startup types. Open source enough for trendy enterprise engineers to use (yes, you are trendy, you're reading a blog post on Linux Containers), but just distantly hipster enough for the modern startup developer to at least have some experience in.
The application is a simple webpage that shows a list of books that the bookstore currently sells. There is also a basic HTML form at the bottom of the page for adding new books to the bookstore's database. When data is submitted from the form it is handled by a second PHP script that saves the new record to the database and returns the user back to the main webpage that displays the list of books. The database consists of one table, a books table, with 3 columns: title, author and price. The entire app and DB are running on an Ubuntu 12.04 LTS box.
I want to containerize my app. I know how to set up a fresh LAMP stack using containers, but I don't have a full grasp on all the modifications I've made to my existing app relative to a default installation. I took a UpGuard scan of my app to figure out what I had and compared it a baseline scan of an Ubuntu 12.04 box with the basic LAMP stack installed. Here is a general overview of the differences between my app's server and a baseline server:
Even though there are evidently a fair number of differences between these two machines, I'm going to ignore most of the general differences. For example, the Inventory differences are mostly differing IP addresses and MAC addresses, and many of the local Ports that are open are dynamic ports. I know I'm looking particularly at how my Apache, MySQL and PHP setups differ. If I focus on file differences in Apache's files, for example, a number of files differ by only a few lines. Here's an example of one of the simpler file differences, where PHP script execution is re-enabled in user directories.
After I had worked out that I'd need to install the Apache2, PHP5, MySQL and the various Apache mod packages for PHP5 and MySQL from the UpGuard scan, I also had the customizations highlighted for me. I selected each of these items and created a policy in UpGuard. Having the infrastructure requirements in one place allowed me to then export to a Dockerfile skeleton. I commented out the "mysql-service" package from the exported file as I wanted to keep the existing DB separate and just containerize the app at this stage. Below are the first few lines of the generated Dockerfile showing some of the more familiar elements.
# # Dockerfile template generated by UpGuard # To build: # 1) Place this file in a folder (e.g. ~/docker/bookstore/) and rename to just 'Dockerfile' # 2) Run: # $ docker build -t="bookstore" . (<--- don't forget the dot) # For details on running this container, see: # http://docs.docker.io/en/latest/commandline/cli # --------------------------------- # Account: UpGuard # Package: BookStore # Downloaded: 2014-02-16 11:47:53 -0800 FROM ubuntu MAINTAINER Generated by UpGuard for Steve Cossell "email@example.com" # Just once before doing one or more apt-get install commands... # (If your package is not in the main Ubuntu repo, uncomment the following line) # RUN echo 'deb http://archive.ubuntu.com/ubuntu precise main restricted universe' >> /etc/apt/sources.list RUN apt-get update # The package apache2 should be installed. RUN apt-get install apache2 # The package libapache2-mod-php5 should be installed. RUN apt-get install libapache2-mod-php5 # The package libapache2-mod-auth-mysql should be installed. RUN apt-get install libapache2-mod-auth-mysql # The package php5-mysql should be installed. RUN apt-get install php5-mysql # The file /etc/apache2/mods-available/php5.conf should have te defined properties # -- Please symlink the following file to the location of your Dockerfile # -- $ cd /dir/your/DockerFile/lives/in/ # -- $ ln -s /etc/apache2/mods-available/php5.conf # -- Or, change the file's source to a URL # -- (For more information, see: http://docs.docker.io/en/latest/use/builder/#add) ADD php5.conf /etc/apache2/mods-available/php5.conf RUN chown 0 /etc/apache2/mods-available/php5.conf RUN chgrp 0 /etc/apache2/mods-available/php5.conf RUN chmod 644 /etc/apache2/mods-available/php5.conf # The local TCP port 80 should be awaiting connections or able to receive packets EXPOSE 80 . . .
Once the container was built and run I could then send the original policy down to validate that the container was packed correctly. I had just successfully ported my web app from a raw OS to a Linux Container without really all that much effort and had a set of auto generated tests that could validate my container at will. So if the goal of DevOps is to go fast(er), then with this approach I can do that while having confidence that we have full visibility and control of the containers and contents within those containers.
There is a strong tendency to correlate automation with DevOps. I absolutely believe that automating as much of the release management process as possible is critical to being responsive to today's business demands. However, as an industry we need to pause our automation tendencies and first get end-to-end visibility and control of what we have and create a test-driven approach to build, test and deploy next-gen apps. Leveraging UpGuard and Docker provides a practical, simplified approach to managing the all of this and can help developers and operations practice test-driven development and automation with favorable results.