Here’s the first in a series of building a robust docker swarm of raspberry pi hosts. This installment goes through installing the Pi(s), modifiying the Docker Daemon config, and starting up a test to verify that everyone can join the cluster.
The cluster which I am using consists of
- Raspberry Pi B+ x 5
- Raspberry Pi 2 B x 5
In order to verify that the cluster is working properly, there should be ten (10) members, five (5) of which have 4 cores.
Obtaining the OS and Setup
The wonderful folks at Hypriot have produced a raspberry image based on Raspbian, but with Docker 1.5.0. The image and instructions can be found at Heavily ARMed after major upgrade: Raspberry Pi with Docker 1.5.0 – Docker Pirates ARMed with explosive stuff.
Note: The image resizes itself to fit the card upon the first boot and then reboots. This takes a little bit (minutes at most). Just don’t be surprised when it reboots.
Hostnames
In order for this to work properly, the hosts need a unique name. It’s sometimes possible to configure DHCP to do this, but I went ahead and edited /etc/hostname
myself since I wanted the most portability possible.
Like all good sysadmins, I am lazy. Doing the same thing over and over is boring. Besides, that’s what computers are for. So I wrote a script to make copies of the image and then edit the hostnames. Once that’s done I can then proceed to write the images to the SD cards.
The following script takes one argument, the location of the image you wish to modify — this should work for more than just Hypriot; it should work for just about any Rasberry Pi image. At present, it creates 10 instances and the NAME
starts with apis-rpi-
— you will likely want to change those. I may make the file a bit more generic at some point.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
#!/bin/bash IMAGE=$1 DIR=${HOME}/images/pi-swarm sudo umount /mnt 2>/dev/null mkdir -p $DIR rm -f $DIR/* for i in 1 2 3 4 5 6 7 8 9 10 do echo "Copying #${i}" NAME=`echo ${i}|awk '{printf("apis-rpi-%02d\n",$0)}'` FILE=${DIR}/${NAME}.img cp $IMAGE $FILE echo " Updating" dev=`sudo kpartx -a -v $FILE |tail -1|awk '{print $3}'` sudo mount /dev/mapper/$dev /mnt sudo echo $NAME > /mnt/etc/hostname sudo echo "127.0.1.1 $NAME" >> /mnt/etc/hosts sudo sync sudo sync sudo sync sudo umount /mnt sudo kpartx -d -v $FILE echo " Done" done |
The Pi instances all need network connectivity which can be seen by the swarm manager; a switch works well. You can either physically assign IP addresses or use DHCP.
Updating Docker
In order for swarm instances to communicate, the docker daemon needs to be bound to a TCP port.
sudo service docker stop
- Edit
/etc/default/docker
(vi is installed on the image) and replace DOCKER_OPTS="--storage-driver=overlay -D"
with DOCKER_OPTS="--storage-driver=overlay -D -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock"
sudo service docker restart
Docker should be running once again. I’ve left the unix socket enabled so that the “default” behaviour still works.
WARNING There are some security issues with this; a port is opened to anyone who can reach the host at which point they can start containers, etc.. Other than using this for play/testing on a private network, TLS needs to be configured.
To verify that the daemon is running with both the port and the socket, do docker info
and docker -H 127.0.0.1:2375
. You should see something like the following for both:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
$ docker info Containers: 2 Images: 22 Storage Driver: overlay Backing Filesystem: extfs Execution Driver: native-0.2 Kernel Version: 3.18.8-hypriotos-v7+ Operating System: Raspbian GNU/Linux 7 (wheezy) CPUs: 4 Total Memory: 925.3 MiB Name: apis-rpi-01 ID: LNUB:PB4W:KC6Z:JNHJ:LLH2:FYAY:HP4S:EWEJ:QYOQ:JRQT:XINL:4APW Debug mode (server): true Debug mode (client): false Fds: 20 Goroutines: 22 EventsListeners: 1 Init SHA1: 6f77311608d545807a397d65468a964df6a37519 Init Path: /usr/lib/docker/dockerinit Docker Root Dir: /var/lib/docker |
Obtaining Swarm
I’ve created a Raspberry Pi docker container for swarm. To install it, you can simply pull nimblestratus/rpi-swarm
. With more than a couple Raspberry Pis, this can be a bit tedious. clusterssh
makes this a lot easier — it allows you to type in the same commands to multiple servers at once.
Discovery Service
In future installments I’ll be getting a different discovery service running, but for this test the default works.
|
TOKEN=`docker run --rm swarm c` |
Starting the Nodes
On each node, execute the following: (this assumes that you’re using wired ethernet; for wireless you’ll need to change eth0 to most likely wlan0)
|
docker run --name="swarm-agent" \ -d nimblestratus/rpi-swarm join \ --addr=`ip -f inet addr show dev eth0|grep inet|awk '{print $2}'|sed -e 's#/.*##'`:2375 \ token://${TOKEN} |
It can be helpful to verify that the agent has started with docker ps
. If you don’t see it running, then there’s a problem somewhere.
Starting the Manager
I’m running the manager on my laptop; consequently I’m using the swarm
container instead of nimblestratus/rpi-swarm
:
|
docker run -d --name=swarm-manager -p 3456:2375 swarm manage token://${TOKEN} |
Once this comes back, let’s check out the swarm:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
|
matt@argentum:~$ docker run --rm swarm list token://${TOKEN} 192.168.1.102:2375 192.168.1.104:2375 192.168.1.103:2375 192.168.1.106:2375 192.168.1.108:2375 192.168.1.105:2375 192.168.1.101:2375 192.168.1.109:2375 192.168.1.110:2375 192.168.1.107:2375 matt@argentum:~$ docker -H tcp://localhost:3456 ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES bf9c29464deb nimblestratus/rpi-swarm:latest "swarm join --addr=1 6 minutes ago Up 6 minutes 2375/tcp apis-rpi-07/swarm-agent 91ad6540441c nimblestratus/rpi-swarm:latest "swarm join --addr=1 6 minutes ago Up 6 minutes 2375/tcp apis-rpi-06/swarm-agent 33e48e7d5227 nimblestratus/rpi-swarm:latest "swarm join --addr=1 6 minutes ago Up 6 minutes 2375/tcp apis-rpi-09/swarm-agent 382d2c5ce2ed nimblestratus/rpi-swarm:latest "swarm join --addr=1 6 minutes ago Up 6 minutes 2375/tcp apis-rpi-10/swarm-agent d6486e21fa17 nimblestratus/rpi-swarm:latest "swarm join --addr=1 6 minutes ago Up 6 minutes 2375/tcp apis-rpi-08/swarm-agent 2cee2e465aee nimblestratus/rpi-swarm:latest "swarm join --addr=1 6 minutes ago Up 6 minutes 2375/tcp apis-rpi-02/swarm-agent d8d755e947b7 nimblestratus/rpi-swarm:latest "swarm join --addr=1 6 minutes ago Up 6 minutes 2375/tcp apis-rpi-03/swarm-agent c3518890e96d nimblestratus/rpi-swarm:latest "swarm join --addr=1 6 minutes ago Up 6 minutes 2375/tcp apis-rpi-05/swarm-agent 33b826d6e482 nimblestratus/rpi-swarm:latest "swarm join --addr=1 6 minutes ago Up 6 minutes 2375/tcp apis-rpi-04/swarm-agent a938db42169f nimblestratus/rpi-swarm:latest "swarm join --addr=1 6 minutes ago Up 6 minutes 2375/tcp apis-rpi-01/swarm-agent matt@argentum:~$ docker -H tcp://localhost:3456 info Containers: 20 Nodes: 10 apis-rpi-02: 192.168.1.102:2375 ? Containers: 2 ? Reserved CPUs: 0 / 4 ? Reserved Memory: 0 B / 925.3 MiB apis-rpi-03: 192.168.1.103:2375 ? Containers: 2 ? Reserved CPUs: 0 / 4 ? Reserved Memory: 0 B / 925.3 MiB apis-rpi-07: 192.168.1.107:2375 ? Containers: 2 ? Reserved CPUs: 0 / 1 ? Reserved Memory: 0 B / 434.4 MiB apis-rpi-06: 192.168.1.106:2375 ? Containers: 2 ? Reserved CPUs: 0 / 1 ? Reserved Memory: 0 B / 434.4 MiB apis-rpi-09: 192.168.1.109:2375 ? Containers: 2 ? Reserved CPUs: 0 / 1 ? Reserved Memory: 0 B / 434.4 MiB apis-rpi-10: 192.168.1.110:2375 ? Containers: 2 ? Reserved CPUs: 0 / 1 ? Reserved Memory: 0 B / 434.4 MiB apis-rpi-08: 192.168.1.108:2375 ? Containers: 2 ? Reserved CPUs: 0 / 1 ? Reserved Memory: 0 B / 434.4 MiB apis-rpi-04: 192.168.1.104:2375 ? Containers: 2 ? Reserved CPUs: 0 / 4 ? Reserved Memory: 0 B / 925.3 MiB apis-rpi-01: 192.168.1.101:2375 ? Containers: 2 ? Reserved CPUs: 0 / 4 ? Reserved Memory: 0 B / 925.3 MiB apis-rpi-05: 192.168.1.105:2375 ? Containers: 2 ? Reserved CPUs: 0 / 4 ? Reserved Memory: 0 B / 925.3 MiB |
Ok, let’s test…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
matt@argentum:~$ docker -H tcp://127.0.0.1:3456 run -d -p 8080:80 hypriot/rpi-busybox-httpd c44270840d6c05c1e8dcd54bcc27598d526b5bc0d56726deeccc40d32b579732 matt@argentum:~$ docker -H tcp://localhost:3456 ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c44270840d6c hypriot/rpi-busybox-httpd:0.1.0 "/bin/busybox httpd 26 seconds ago Up 3 seconds 192.168.1.110:8080->80/tcp apis-rpi-10/sick_leakey d8d755e947b7 nimblestratus/rpi-swarm:latest "swarm join --addr=1 About an hour ago Up About an hour 2375/tcp apis-rpi-03/swarm-agent bf9c29464deb nimblestratus/rpi-swarm:latest "swarm join --addr=1 About an hour ago Up About an hour 2375/tcp apis-rpi-07/swarm-agent 91ad6540441c nimblestratus/rpi-swarm:latest "swarm join --addr=1 About an hour ago Up About an hour 2375/tcp apis-rpi-06/swarm-agent 33e48e7d5227 nimblestratus/rpi-swarm:latest "swarm join --addr=1 About an hour ago Up About an hour 2375/tcp apis-rpi-09/swarm-agent 382d2c5ce2ed nimblestratus/rpi-swarm:latest "swarm join --addr=1 About an hour ago Up About an hour 2375/tcp apis-rpi-10/swarm-agent d6486e21fa17 nimblestratus/rpi-swarm:latest "swarm join --addr=1 About an hour ago Up About an hour 2375/tcp apis-rpi-08/swarm-agent 33b826d6e482 nimblestratus/rpi-swarm:latest "swarm join --addr=1 About an hour ago Up About an hour 2375/tcp apis-rpi-04/swarm-agent a938db42169f nimblestratus/rpi-swarm:latest "swarm join --addr=1 About an hour ago Up About an hour 2375/tcp apis-rpi-01/swarm-agent c3518890e96d nimblestratus/rpi-swarm:latest "swarm join --addr=1 About an hour ago Up About an hour 2375/tcp apis-rpi-05/swarm-agent 2cee2e465aee nimblestratus/rpi-swarm:latest "swarm join --addr=1 About an hour ago Up About an hour 2375/tcp apis-rpi-02/swarm-agent |
You can find the url like this:
|
matt@argentum:/tmp$ URL="http://`docker -H tcp://localhost:3456 ps|grep httpd|awk '{print $(NF-1)}'|sed -e 's/->.*//'`" matt@argentum:/tmp$ echo $URL http://192.168.1.110:8080 |
At this point there isn’t any service discovery; that’s for the next increment….
Pop the URL into a browser and you should see:
It’s Alive!
Next time… setting up a discovery service…