It’s not rocket science!

Have Docker, Will Swarm

Jacob Blain Christen
On Docker
3 min readMay 13, 2016

--

This nifty little project (GitHub link) is the direct result of a discussion with Jeff Nickoloff on the PhoenixDocker #Meetup Slack channel. The composition presented here is a derivative of his work.

The Why

Have you ever found yourself in need of a “dummy” Docker daemon? Something that you could stand-up and abuse via remote API calls and then throw away when you were done with it? Recently, Jeff Nickoloff came to the Phoenix Docker Slack channel looking for just such a thing. I come from the Maven world and tend to think of such requirements as “build-local” run-times to be stood up, tested, and then torn down during `mvn verify` aka `pre-integration-test -> integration-test -> post-integration-test` and so I suggested he create a composition to orchestrate standing up, executing, and tearing down a local Swarm cluster.

The What

So, cool story bro. And I don’t think that he was entirely convinced but he went head-down for nine hours or so and came up to share the below composition (I’ve included some of my small tweaks but this is 99% Jeff Nickoloff):

sm:
command:
["manage", "-H", "tcp://0.0.0.0:3376", "nodes://sn1:2375,sn2:2375,sn3:2375"]
image: swarm
links:
- sn1:sn1
- sn2:sn2
- sn3:sn3
ports:
- "3375:3376"

sn1:
image:
docker:dind
hostname: node-1
privileged: true

sn2:
image:
docker:dind
hostname: node-2
privileged: true

sn3:
image:
docker:dind
hostname: node-3
privileged: true

sn1lc:
command:
docker run -d --label 'launched-by=sn1lc' --label 'scheduled-by=sn1' alpine sh -c 'echo NODE1; sleep 36000'
image: docker
links:
- sn1:docker

sn2lc:
command:
docker run -d --label 'launched-by=sn2lc' --label 'scheduled-by=sn2' alpine sh -c 'echo NODE2; sleep 36000'
image: docker
links:
- sn2:docker

sn3lc:
command:
docker run -d --label 'launched-by=sn3lc' --label 'scheduled-by=sn3' alpine sh -c 'echo NODE3; sleep 36000'
image: docker
links:
- sn3:docker

smlc:
command:
docker run -d --label 'launched-by=smlc' --label 'scheduled-by=sm' alpine sh -c 'echo SWARM; sleep 36000'
environment:
- "DOCKER_HOST=tcp://swarm:3376"
image: docker
links:
- sm:swarm

“Wait”, you say, “that’s it?!” Yes, yes it is. And it is beautiful in its simplicity.

This is a single composition for running a three-node Docker Swarm on your local Docker bridge network!

The How Does This Even Work?!

First, a very simple Swarm definition that leverages the static discovery mechanism:

sm:
command:
["manage", "-H", "tcp://0.0.0.0:3376", "nodes://sn1:2375,sn2:2375,sn3:2375"]
image: swarm
links:
- sn1:sn1
- sn2:sn2
- sn3:sn3
ports:
- "3375:3376"
sn1:
image:
docker:dind
hostname: node-1
privileged: true

sn2:
image:
docker:dind
hostname: node-2
privileged: true

sn3:
image:
docker:dind
hostname: node-3
privileged: true

Next, a container pointing at the Swarm manager that launches dummy containers:

smlc:
command:
docker run -d --label 'launched-by=smlc' --label 'scheduled-by=sm' alpine sh -c 'echo SWARM; sleep 36000'
environment:
- "DOCKER_HOST=tcp://swarm:3376"
image: docker
links:
- sm:swarm

But hold up, that wasn’t next! This was next:

sn1lc:
command:
docker run -d --label 'launched-by=sn1lc' --label 'scheduled-by=sn1' alpine sh -c 'echo NODE1; sleep 36000'
image: docker
links:
- sn1:docker

sn2lc:
command:
docker run -d --label 'launched-by=sn2lc' --label 'scheduled-by=sn2' alpine sh -c 'echo NODE2; sleep 36000'
image: docker
links:
- sn2:docker

sn3lc:
command:
docker run -d --label 'launched-by=sn3lc' --label 'scheduled-by=sn3' alpine sh -c 'echo NODE3; sleep 36000'
image: docker
links:
- sn3:docker

And I chose to skip over it because there is some wizardry afoot! This looks like a block of three containers for directly scheduling containers on the Swarm nodes, bypassing the Swarm scheduler. But how is that actually working? I don’t see a DOCKER_HOST, do you? No, but you will see that for each launching container it has aliased, via Compose links, the target node with the name ‘docker and this is where the magic happens. Taking a look at the source for the Docker container entry point:

#!/bin/sh
set -e

if [ "${1:0:1}" = '-' ]; then
set -- docker "$@"
fi

# if our command is a valid Docker subcommand, let's invoke it through Docker instead
# (this allows for "docker run docker ps", etc)
if docker "$1" --help > /dev/null 2>&1; then
set -- docker "$@"
fi

# if we have "--link some-docker:docker" and not DOCKER_HOST, let's set DOCKER_HOST automatically
if [ -z "$DOCKER_HOST" -a "$DOCKER_PORT_2375_TCP" ]; then
export DOCKER_HOST='tcp://docker:2375'
fi

exec "$@"

And there it is.

--

--