Docker & PHP: beyond virtual machines

Docker is currently one of the hottest technologies around, because it solves a very specific problem: the ability to easily package and deploy a (self contained) application, without the overhead of traditional virtualization solutions.

In this post you’ll learn how to build, run and host Docker containers, integrate with other containers, and see how Vagrant interacts with Docker.

Background

The Linux kernel contains a number of containment features that enable resource (CPU, network, memory, etc.) & process (user id’s, file systems, process trees) isolation on the same host, without the need for a virtual machine: cgroups and namespaces.

This operating system-level virtualization imposes far less overhead. User space instances (also called containers) run within the host OS, so a hypervisor and guest OS are not needed. This results in lightweight images and incredibly fast start/boot times: think seconds, not minutes. Containers are like vm’s, without the associated weight.

Docker is a toolkit built around those containment features. Before version 0.9, Docker relied on LXC (LinuX Containers): a (userspace) interface to the kernel containment features. As of version 0.9, Docker has its own interface layer called libcontainer.

Installing Docker

Installing Docker is easy if you are using Ubuntu. To install the official package (may not be the absolute latest version):

Should you really want the latest version:

Note: because Docker relies on Linux-specific kernel features, it is not natively supported on OSX or Windows.
There are some workarounds: use Vagrant (see below), or refer to the Docker installation instructions ( https://docs.docker.com/installation/windows/ and https://docs.docker.com/installation/mac/) for details.

Creating and starting a container

Let’s start with a small PHP app, which we will package and deploy as a Docker container. The app will expose a simple socket server on a port (1337), and output a string to connections on that port. To build the app we’ll be using React, a library that adds event-driven, non-blocking IO to PHP.

First, install composer (http://getcomposer.org), then run the following command:

Then save the following code as “app.php”:

If we then run the following command:

we should see something like this:

Open your browser, point it to http://localhost:1337/, and you should see something like:

React screenshot

Now, to get our app to run inside a Docker container, we first need to build an image. The instructions that define how to build the image are saved in a Dockerfile, in the form of commands:

In this Dockerfile we’ve used the following commands:

  • FROM: an existing image that our image should be based on
  • ADD: a tar file or existing directory pointing to our app
  • CMD: command to run when starting the container
  • EXPOSE: ports that we want accessible outside of the container

The full list of commands is available .

Now that our Dockerfile is finished, let’s build the image. Run the following command:

This builds a new image using the Dockerfile in the current directory, and will tag
the resulting image with the string “our_app_image”. The output of the command should look like this:

The next step is creating and running a new container that uses our freshly baked image.

The container will be named “our_app_container”, and will run in the background (the ‘-d’ option). Docker will also proxy port 1337 on the host to port 1337 in the container. After starting the container, ‘docker ps’ shows the list of running containers and their attributes, something like:

To verify that the app is running and available, we can use ‘docker logs app’ to retrieve the stdin/stderr streams of the running container.

Dependencies: linking containers

Let’s extend our app with an external dependency. The requirement: increase a counter with every request to port 1337. The counter will live inside a Redis instance.

First, add predis-async to the list of composer dependencies:

Then, insert the following lines in app.php:

Now, how to add a Redis server to our app? We can connect directly to a specific hostname, or assume that whoever works on our app has Redis installed and running. Both are somewhat fragile, and make setting up a development environment more difficult. There is a third option: installing Redis directly to the Docker image of our app. However, this would polute the image and make it less flexible.

Enter Fig, a tool to quickly build and start (isolated) development environments. Fig requires a single configuration file (default: fig.yml):

The above file defines two containers: app (which contains our php application), and redis. The first container depends on the latter through the “links” section. Fig will make sure that, within the app container, “redis” resolves and points to that container.

Save the file, and run:

This should produce the following output:

By default, Fig will start the containers in the foreground, blocking the terminal. To start in the background, add ‘-d’ to the command line.

Docker & Vagrant

As of version 1.6, Vagrant directly supports Docker and Docker containers through a new provider. Additionally, on platforms where Docker is not natively supported (Mac, Windows), Vagrant automatically starts a Linux-based (VirtualBox) vm which can then run a Docker container.

Let’s create a Vagrantfile for our app. We can re-use the existing Dockerfile, and let Vagrant use that:

Save the file, and run ‘vagrant up’:

And once again, our app is available on port 1337, proxied to the container that Vagrant built.

Conclusion

If you’ve made it this far, you now know how to start a Docker container that runs your app, and link that container to other dependencies. Ofcourse this is but a little intro into the exciting world of Docker, which is rapidly changing and growing, and has much more to offer!

Michiel Rook is a Java/PHP/Scala contractor and consultant from the Netherlands. He loves coaching teams to develop better software and implement continuous deployment. He is a co-founder of Make.io and a member of the Dutch Web Alliance. He is an Army reservist and enjoys music, cars, sports and movies.

Dutch Web Alliance