Screen Shot 2014-03-17 at 12.25.19 PM

Docker is fundamentally changing the way people do infrastructure in the cloud. However many developers I know are still confused about how to use it to do anything beyond a simple “Hello World” or a WordPress-all-in-one container with everything bundled into a single container, Apache, MySQL, nginx, etc.

The power of Docker is in being able to ship and scale individual components or “containers”. However it is still a bit of a black art about how that works. There are a few awesome projects being started to make it easier to build multi-container apps like CoreOSSerfFlynn, Maestro, Deis, and Dokku. But what if you want to do it without any fancy tools? How do you do it from scratch?

I am not going to say this is the best way, but I would like to walk you through how I recently built a 2-Container app (WordPress in one container and MySQL in the other). In future articles, we will go into more complex setups and walk you through how to use some of the cool Open Source tools out there.

This tutorial will guide you through 5 somewhat easy steps (CTL-C is a new project we are working on to make this kind of tutorial unnecessary, you will be able to stitch together Docker containers in a simple web UI).

Step 1) Setup Docker Locally

First, setup docker on your machine. If you are on a Mac, this is a simple 3-step process:

  1. Install VirtualBox
  2. Install Vagrant
  3. Install docker-osx (I can’t yet get the new version, boot2docker working for me even though docker-osx is supposedly deprecated, docker-osx works great)

docker-osx will create a VirtualBox virtual machine running docker automatically and allow you to run docker on your OS X command line as if it were running locally (proxied transparently to your VirtualBox docker).

$ export DOCKER_HOST="tcp://"
$ echo 'export DOCKER_HOST="tcp://"' >> ~/.profile
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

Step 2) Pull an Existing Container to Start With

Now that you have docker running on your development machine, let’s do something interesting. Let’s create your first single-container application.

First, you go to the public Docker Index to find a container you want to use. Let’s search for WordPress. You will find about 30 results, but you only want to use a Trusted Repository.

NOTE: Most of the docker containers in the Docker Index are unsafe to use as-is. You have no idea how they were built, so you can’t be sure there isn’t malware on the images. If you want to use any public Docker images as a starting point, make sure to use a “Trusted Repository” (read more about trusted builds) and make sure to audit it’s Dockerfile before you use it.

One of the good trusted WordPress containers out there is managed by Tutum, a Docker-based hosting company that lets you run Docker containers and provides private docker registries and private docker images. The WordPress container you want to use is called tutum/wordpress and you can use it quickly and easily by doing the following:

$ docker run -d -p 80 tutum/wordpress /
Unable to find image 'tutum/wordpress' (tag: latest) locally
Pulling repository tutum/wordpress
ef6168ed7674: Download complete 
27cf78414709: Download complete 
[... more of the same ...]
d713116991b8: Download complete 
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
567a9d28bb37        tutum/wordpress:latest                       /                13 seconds ago      Up 12 seconds>80/tcp, 3306/tcp          suspicious_heisenberg

Congratulations! You are now running WordPress in a single container with MySQL on the same container. Easy right?

Want to see WordPress running for yourself? Run docker-osx ssh, and then run curl

Now how do you get MySQL to run in its own container?

Step 3) Modifying an Existing Container

Now that you can use existing “Trusted Images”, let me show you how to modify them. Instead of running in daemon mode with the -d flag, use the interactive and TTY flags (-i -t) and specify bash to get into the container as root.

$ docker run -i -t tutum/wordpress bash

Every wonder what / did in step 2′s run of the docker command? Let’s find out.

root@dfa24049c264:/# cat
if [ ! -f /.mysql_admin_created ]; then
exec supervisord -n

Now that we don’t want MySQL running on the same container as WordPress, let’s modify this container to not run MySQL.

root@dfa24049c264:/# rm /etc/supervisor/conf.d/supervisord-mysqld.conf
root@dfa24049c264:/# touch /.mysql_admin_created 

In a new Terminal window, run docker ps again to find the container id of your interactive container.

$ docker ps
CONTAINER ID        IMAGE                                        COMMAND                CREATED             STATUS              PORTS                             NAMES
dfa24049c264        tutum/wordpress:latest                       bash                   9 minutes ago       Up 9 minutes        3306/tcp, 80/tcp                  condescending_albattani            
110631a70e76        tutum/wordpress:latest                       /                13 minutes ago      Up 13 minutes>80/tcp, 3306/tcp   cocky_einstein                     

Now that we have the container id (dfa24049c264) we can commit our changes to the filesystem.

$ docker commit dfa24049c264 myuser/wordpress

Now we have our own version of tutum’s WordPress with MySQL disabled. Let’s try it out. First we will stop our other containers and then we will start a new one with our newly committed image, myuser/wordpress.

$ docker stop dfa24049c264
$ docker stop 110631a70e76
$ docker run -d -p 80 myuser/wordpress /
$ docker ps
CONTAINER ID        IMAGE                                        COMMAND                CREATED             STATUS              PORTS                     NAMES
f1bc5c3bd28a        myuser/wordpress:latest                      /                7 seconds ago       Up 6 seconds>80/tcp     jovial_nobel                       

Notice that the 3306 port does not show as open in the new container when it did in the previous one, but that you can still curl the url curl

Step 4) Create Your MySQL Container

Just like WordPress, you can get yourself a MySQL container from the Docker Index.

$ docker run -d -p 3306 tutum/mysql /
$ docker ps
CONTAINER ID        IMAGE                                        COMMAND                CREATED             STATUS              PORTS                     NAMES
63a974062480        tutum/mysql:latest                           /                18 seconds ago      Up 17 seconds>3306/tcp   cranky_babbage                     
f1bc5c3bd28a        myuser/wordpress:latest                      /                5 minutes ago       Up 5 minutes>80/tcp     jovial_nobel                       

Again, let’s go in and check out what does.

$ docker run -i -t tutum/mysql bash
root@a6fd073d27bb:/# cat
if [ ! -f /.mysql_admin_created ]; then
exec supervisord -n

Look familiar? It should, the same tutum folks made this image. We don’t need to remove any components out of this container, but we do want to make sure that the root username and password do not change. If you go back to your OS X terminal, we can find out what username and password were generated and commit the new container locally.

$ docker run -d -p 3306 tutum/mysql /
$ docker ps
CONTAINER ID        IMAGE                                        COMMAND                CREATED             STATUS              PORTS                     NAMES
63a974062480        tutum/mysql:latest                           /                18 seconds ago      Up 17 seconds>3306/tcp   cranky_babbage                     
f1bc5c3bd28a        myuser/wordpress:latest                      /                5 minutes ago       Up 5 minutes>80/tcp     jovial_nobel                       
$ docker logs 63a974062480
=> Creating MySQL admin user with random password
=> Done!
You can now connect to this MySQL Server using:

    mysql -uadmin -pqa1N76pWAri9 -h -P

Please remember to change the above password as soon as possible!
MySQL user 'root' has no password but only allows local connections
2014-01-20 21:56:10,100 CRIT Supervisor running as root (no user in config file)
2014-01-20 21:56:10,111 WARN Included extra file "/etc/supervisor/conf.d/supervisord-mysqld.conf" during parsing
2014-01-20 21:56:10,330 INFO RPC interface 'supervisor' initialized
2014-01-20 21:56:10,330 WARN cElementTree not installed, using slower XML parser for XML-RPC
2014-01-20 21:56:10,330 CRIT Server 'unix_http_server' running without any HTTP authentication checking
2014-01-20 21:56:10,330 INFO supervisord started with pid 1
2014-01-20 21:56:11,333 INFO spawned: 'mysqld' with pid 385
2014-01-20 21:56:12,536 INFO success: mysqld entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
$ mysql -uadmin -pqa1N76pWAri9 -h -P 49165 -e 'create database wordpress;'
$ docker commit 63a974062480 myuser/mysql

Ok now we know what the username and password are and have created our own container with a static root username and password. Let’s stop the generic container and start our new custom MySQL server container.

$ docker stop 63a974062480
$ docker run -d -p 3306 myuser/mysql /
$ docker ps
CONTAINER ID        IMAGE                                        COMMAND                CREATED             STATUS              PORTS                     NAMES
5a840e255282        myuser/mysql:latest                          /                1 seconds ago       Up 1 seconds>3306/tcp   angry_bardeen                      
f1bc5c3bd28a        myuser/wordpress:latest                      /                23 minutes ago      Up 23 minutes>80/tcp     jovial_nobel                       

Step 5) Binding Your WordPress Container to your MySQL Container

Congratulations, you have reached the last step! All you have to do now is bind the two containers together. In order for the two containers to know about each other, we will need to link them using the docker -link flag. Before we do that, we will need to make another modification to our WordPress container so that it can pull credentials from environment variables.

$ docker run -i -t myuser/wordpress bash
root@f74cc171d7d5:/# apt-get install curl
root@f74cc171d7d5:/# curl -L > /app/wp-config.php

Now you go back to another OS X terminal (keeping the bash container running) and commit these changes to the container.

$ docker commit f74cc171d7d5 myuser/wordpress
$ docker stop f74cc171d7d5

Finally, it is time to start the myuser/wordpress container with a special -link flag and the environmental variable for the MySQL DB password.

$ export DB_PASSWORD=qa1N76pWAri9
$ docker ps
CONTAINER ID        IMAGE                                        COMMAND                CREATED             STATUS              PORTS                     NAMES
5a840e255282        myuser/mysql:latest                          /                21 minutes ago      Up 21 minutes>3306/tcp   angry_bardeen                      
$ docker run -e="DB_PASSWORD=$DB_PASSWORD" -link angry_bardeen:db -d -p 80 myuser/wordpress /
$ docker ps
CONTAINER ID        IMAGE                                        COMMAND                CREATED             STATUS              PORTS                     NAMES
2fb827d3c3a7        myuser/wordpress:latest                      /                2 seconds ago       Up 1 seconds>80/tcp     ecstatic_thompson                    
5a840e255282        myuser/mysql:latest                          /                22 minutes ago      Up 22 minutes>3306/tcp   angry_bardeen,ecstatic_thompson/db   

We now have a 2-container setup that are bound together. To see it in action, go to:

$ curl


We can now create a 2-container application in Docker without even having to touch a Dockerfile. We can tie the containers together in a loosely coupled way. You can create multiple instances of the WordPress container that all talk to the same MySQL database.

Note that this is not a production environment yet. The MySQL container’s data is ephemeral and we did not cover how to persist it yet (hint: look at the docker run -v flag).

Also note that in production we would want to docker push our myuser/wordpress and myuser/mysql images to a private Docker registry. We will show you how to run your own registry in an upcoming tutorial.

Next Week

In the next installment next week, we will talk about adding an nginx container to load balance and using Serf to automatically make all of the containers aware of each other. To automatically get an email when the next installment comes out, subscribe to our weekly newsletter that has Docker tips and news.