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 \ -e DOCKER_TLS_VERIFY=$DOCKER_TLS_VERIFY \ -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.
#!/bin/bash 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 EOD # This is the Fig orchestration configuration cat <<EOF > fig.yml mysql: environment: MYSQL_ROOT_PASSWORD: supersecret MYSQL_DATABASE: figgydata MYSQL_USER: figgyuser MYSQL_PASSWORD: password ports: - "3306:3306" image: mysql:latest figgypudding: environment: RAILS_ENV: development DATABASE_URL: mysql2://figgyuser:email@example.com:3306/figgydata links: - mysql ports: - "3000:3000" build: . command: bash -xc 'bundle exec rake db:migrate && bundle exec rails server' EOF # 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 http://192.168.59.103:3000/ 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
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.