Fri Nov 7, 2014 in docker , fig , development , orchestration using tags

A common devops problem when developing Docker containers is managing the orchestration of multiple containers in a development environment.

There are a number of orchestration harnesses for Docker available:

There are a number of hosted service offerings now as well:

There are also RAFT/GOSSIP clustering solutions like:

My coreos-vagrant-kitchen-sink github project submits cloud-init units via a YAML file when booting member nodes. It’s a good model for production, but it’s a bit heavy for development.

Docker is currently working on Docker Clustering, but it is presently just a proof-of-concept and is now under a total re-write.

They are also implementing docker composition which provides Fig like functionality using upcoming docker “groups”.

That influence of Fig makes sense, as Docker bought Orchard.

Internally, Docker developers use Fig.

Docker’s website also directs everyone to Boot2Docker, as that is the tool Docker developers use as their docker baseline environment.

Boot2Docker spawns a VirtualBox based VM as well as a native docker client runtime on the developer’s host machine, and provides the DOCKER_HOST and related enviroments necessary for the client to talk to the VM.

This allows a developer’s Windows or OS/X machine to have a docker command that behaves as if the docker containers are running natively on their host machine.

While Fig is easy to install under OS/X as it has native Python support (“pip install fig”), installing Fig on a Windows developer workstation would normally require Python support be installed separately.

Rather than do that, I’ve built a new ianblenke/fig-docker docker Hub image, which is auto-built from ianblenke/docker-fig-docker on github.

This allows running fig inside a docker container using:

docker run -v $(pwd):/app \
           -v $DOCKER_CERT_PATH:/certs \
           -e DOCKER_CERT_PATH=/certs \
           -e DOCKER_HOST=$DOCKER_HOST \
           -ti --rm ianblenke/fig-docker fig --help

Alternatively, a developer can alias it:

alias fig="docker run -v $(pwd):/app \
                      -v $DOCKER_CERT_PATH:/certs
                      -e DOCKER_CERT_PATH=/certs \
                      -e DOCKER_HOST=$DOCKER_HOST \
                      -e DOCKER_TLS_VERIFY=$DOCKER_TLS_VERIFY \
                      -ti --rm ianblenke/fig-docker fig"

Now the developer can run fig as if it is running on their development host, continuing the boot2docker illusion.

In the above examples, the current directory $(pwd) is being mounted as /app inside the docker container.

On a boot2docker install, the boot2docker VM is the actual source of that volume path.

That means you would actually have to have the current path inside the boot2docker VM as well.

To do that, on a Mac, do this:

boot2docker down
VBoxManage sharedfolder add boot2docker-vm -name home -hostpath /Users
boot2docker up

From this point forward, until the next boot2docker init, your boot2docker VM should have your home directory mounted as /Users and the path should be the same.

A similar trick happens for Windows hosts, providing the same path inside the boot2docker VM as a developer would use.

This allows a normalized docker/fig interface for developers to begin their foray into docker orchestration.

Let’s setup a very quick Ruby on Rails application from scratch, and then add a Dockerfile and fig.yml that spins up a mysql service for it to talk to.

Here’s a quick script that does just that. The only requirement is a functional docker command able to spin up containers.

set -ex

# Source the boot2docker environment variables
eval $(boot2docker shellinit 2>/dev/null)

# Use a rails container to create a new rails project in the current directory called figgypudding
docker run -it --rm -v $(pwd):/app rails:latest bash -c 'rails new figgypudding; cp -a /figgypudding /app'

cd figgypudding

# Create the Dockerfile used to build the figgypudding_web:latest image used by the figgypudding_web_1 container
cat <<EOD > Dockerfile
FROM rails:onbuild
ENV HOME /usr/src/app

# This is the Fig orchestration configuration
cat <<EOF > fig.yml
    MYSQL_ROOT_PASSWORD: supersecret
    MYSQL_DATABASE: figgydata
    MYSQL_USER: figgyuser
    MYSQL_PASSWORD: password
    - "3306:3306"
  image: mysql:latest
    RAILS_ENV: development
    DATABASE_URL: mysql2://figgyuser:password@
    - mysql
    - "3000:3000"
  build: .
  command: bash -xc 'bundle exec rake db:migrate && bundle exec rails server'

# Rails defaults to sqlite, convert it to use mysql
sed -i -e 's/sqlite3/mysql2/' Gemfile

# Update the Gemfile.lock using the rails container we referenced earlier
docker run --rm -v $(pwd):/usr/src/app -w /usr/src/app rails:latest bundle update

# Use the fig command from my fig-docker container to fire up the Fig formation
docker run -v $(pwd):/app -v $DOCKER_CERT_PATH:/certs \
                          -e DOCKER_CERT_PATH=/certs \
                          -e DOCKER_HOST=$DOCKER_HOST \
                          -e DOCKER_TLS_VERIFY=$DOCKER_TLS_VERIFY \
                          -ti --rm ianblenke/fig-docker fig up

After running that, there should now be a web server running on the boot2docker VM, which should generally be as that seems to be the common boot2docker default IP.

This is fig, distilled to its essence.

Beyond this point, a developer can “fig build ; fig up” and see the latest result of their work. This is something ideally added as a git post-commit hook or a iteration harness like Guard.

While it may not appear pretty at first glance, realize that only cat, and sed were used on the host here (and very well could also themselves have also been avoided). No additional software was installed on the host, yet a rails app was created and deployed in docker containers, talking to a mysql server.

And therein lies the elegance of dockerizing application deployment: simple, clean, repeatable units of software. Orchestrated.

Have fun!