In last week's post, we talked about how to create a 2-container application with Docker.

Now we want to use a brand new project called Serf (from the makers of Vagrant) to have the containers learn about each other automatically. Serf uses gossip-based membership to create a decentralized solution for service discovery and orchestration that is lightweight, highly available, and fault tolerant. Sounds awesome, but how do you use it with Docker?

Configure Serf for Service Discovery

First, let's setup serf agent on a Docker container.

$ SERF_ID=$(docker run -d --name serf_1 -p 7946 -p 7373 ctlc/serf /run.sh)

Well that was easy! Now let's try to connect to our serf agent from our Docker host machine to make sure it is working.

$ wget https://dl.bintray.com/mitchellh/serf/0.5.0_linux_amd64.zip
$ unzip 0.5.0_linux_amd64.zip
$ sudo mv serf /usr/bin/
$ serf agent &
$ serf members
packer-virtualbox    10.0.2.15:7946     alive

Now that we have serf available on your host, you can connect to your Docker container's serf agent to see it in action.

$ serf join $(docker port $SERF_ID 7946)
Successfully joined cluster by contacting 1 nodes.
$ serf members
packer-virtualbox    10.0.2.15:7946     alive    
b62e43c286c8         172.17.0.76:7946   alive    serf-agent

Sweet. We now have a decentralized solution for Docker containers with a mini two-system serf cluster. The next step is to launch a MySQL database Docker container with serf built-in. Conveniently, you can find a Trusted Image on the Docker Index called ctlc/mysql-serf.

$ MYSQL_ID=$(docker run -d --name mysql --link serf_1:serf_1 -p 3306 ctlc/mysql-serf /run.sh)
$ docker logs $MYSQL_ID
========================================================================
You can now connect to this MySQL Server using:

    mysql -uadmin -p8gmn0Pc7cWJt -h -P

Please remember to change the above password as soon as possible!
MySQL user 'root' has no password but only allows local connections
========================================================================

$ DB_PASSWORD=8gmn0Pc7cWJt
$ mysql -uadmin -p8gmn0Pc7cWJt -h 0.0.0.0 -P $(docker port $MYSQL_ID 3306 | cut -d":" -f2) -e "create database  wordpress;"
$ serf members
packer-virtualbox    10.0.2.15:7946     alive    
b62e43c286c8         172.17.0.76:7946   alive    serf-agent
356dc2b8c86c         172.17.0.77:7946   alive    mysql

The final step is to launch a WordPress Docker container that has serf built-in. Luckily, I have built one already called ctlc/wordpress-serf and link it to the serf container we already named "serf".

$ WORDPRESS_ID=$(docker run -d --name wordpress --link serf_1:serf_1 -e="DB_PASSWORD=$DB_PASSWORD" -p 80 ctlc/wordpress-serf /run.sh)
$ serf members
packer-virtualbox    10.0.2.15:7946     alive    
b62e43c286c8         172.17.0.76:7946   alive    serf-agent
356dc2b8c86c         172.17.0.77:7946   alive    mysql
56b4d5f7580d         172.17.0.78:7946   alive    wordpress

Congratulations! We now have created a distributed Docker container system that is self-aware. Every container can run docker members and get a list of the other members in the system and what their roles are. We can even see WordPress in action by curling the website.

$ curl --location http://$(docker port $WORDPRESS_ID 80)/

How does this work? By a special line in wp-config.php that makes the magic happen.

/** MySQL hostname */
define('DB_HOST', exec("/usr/bin/serf members -tag role=mysql | awk {'print $2'} | cut -d':' -f1"));

Notice that the serf members -tag role=mysql call will automatically find the correct IP address for MySQL. Before we finish this week's blog post, let's start scaling out our WordPress by adding more instances of the ctlc/wordpress-serf container.

$ docker run -d --name wordpress2 --link serf_1:serf_1 -e="DB_PASSWORD=$DB_PASSWORD" -p 80 ctlc/wordpress-serf /run.sh
$ docker run -d --name wordpress3 --link serf_1:serf_1 -e="DB_PASSWORD=$DB_PASSWORD" -p 80 ctlc/wordpress-serf /run.sh
$ docker run -d --name wordpress4 --link serf_1:serf_1 -e="DB_PASSWORD=$DB_PASSWORD" -p 80 ctlc/wordpress-serf /run.sh
$ serf members
packer-virtualbox    10.0.2.15:7946      alive    
b62e43c286c8         172.17.0.76:7946    alive    serf-agent
356dc2b8c86c         172.17.0.77:7946    alive    mysql
44f00d42908a         172.17.0.81:7946    alive    wordpress
37fd075fac46         172.17.0.82:7946    alive    wordpress
32431bfcf1ca         172.17.0.80:7946    alive    wordpress
211467741536         172.17.0.83:7946    alive    wordpress

Conclusion

You can see how easy it is to create a distributed application with Docker and Serf together, but this does not yet show the power of Serf. In next week's column, we will add an Nginx Docker container that will automatically detect when a new WordPress container comes into existence and HUP nginx to take advantage of it using serf event hooks.