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 replaceDOCKER_OPTS="--storage-driver=overlay -D"
withDOCKER_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.
1 2 |
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)
1 2 3 4 5 |
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
:
1 2 |
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:
1 2 3 4 |
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:
Next time… setting up a discovery service…
10 comments
3 pings
Skip to comment form ↓
stefanscherer
March 21, 2015 at 9:19 pm (UTC -5) Link to this comment
That rocks! I’ll try it with myself with my smaller cluster of 3xPi2 + 1xPi1.
But I think we have to find an easier way to customize the hostname, perhaps a config file in the VFAT /boot partition…
BOK
March 22, 2015 at 6:43 am (UTC -5) Link to this comment
This is great, even with only one RPi in use!
Keep in mind that if you run from OS X and Boot2Docker, you have to execute the “docker -H” commands INSIDE the VM (enter with “boot2docker ssh”).
Dieter Reuter
March 22, 2015 at 1:19 pm (UTC -5) Link to this comment
Matt, thanks for sharing this cool tutorial.
I just built my 6x node cluster (5x Pi 2, 1x Pi B+). With your blog post it was easy to setup.
Now, we can clearly say, that HypriotOS is already supporting Docker Swarm!
The best thing is, this cluster consumes 9.5 Watts only. For me that’s the way to go, using power-efficient clusters and stepping into “Real Green IT”.
BTW: we’ll automate a few more steps, stay tuned…
Matt Williams
March 22, 2015 at 5:30 pm (UTC -5) Link to this comment
Glad to help! Thank you for all of your work as well; it made life much, much easier.
For some of the automation, maybe something like cloud-init might help….
Matt Williams
March 22, 2015 at 5:37 pm (UTC -5) Link to this comment
It would make things a bit easier. I wonder if basing it on cloud-init might work…
Matt Williams
March 23, 2015 at 1:06 am (UTC -5) Link to this comment
Makes sense — thanks for the tip.
Bargepole
March 25, 2015 at 2:37 pm (UTC -5) Link to this comment
This looks rather interesting.
Must one use a different OS, for compatibility reasons I’d like to stick to good ols Debian
Wheezy – how can one achieve this?
Matt Williams
March 25, 2015 at 3:03 pm (UTC -5) Link to this comment
Hypriot is actually wheezy, just stripped down a bit with support for Docker baked in. Stock Debian doesn’t have support for Docker without some serious hacking at which point you’re better off starting with Hypriot and then adding packages you want. To compare, the Hypriot image is about 1G whereas the Raspbian is 4G. Admittedly the Hypriot folk are building with an emphasis on a light image focused on running containers, but since they’re based on Debian it’s just a matter of ‘apt-get’ing the packages you think are missing.
goliatone
March 30, 2015 at 11:19 am (UTC -5) Link to this comment
Thanks for this series, really helpful. Initially I wanted to use zettio/weave, the idea of having a meta network to which you can plug a device anytime and have it available is really interesting. Do you have any idea how weave would integrate into this swarm architecture you are putting together?
Matt Williams
March 30, 2015 at 2:07 pm (UTC -5) Link to this comment
I’m interested in weave, too. Unfortunately as of the last I looked (the last week or so), someone reported on the Hypriot site that weave wasn’t working. I don’t remember the exact details, but I think it had something to do with the Docker port. Consequently, I’ve not given it a whole lot of thought.
I just did a little googling, however, and I see that there’s a number of folk who have used weave with the Pi. I’ll look at porting it this week and see what I get.
As far as integration, I think that a lot of it would depend on the discovery service used. Static addresses or a file containing a list of members would not work very well. Consul, etcd, or the Docker Hub token would likely work. An IP range might work with weave. I think that if you had a way of finding the “manager” node and assuming it was in the weave network then it should all work if you required that all of the addresses were weave addresses.
There is a possibility of bottlenecking, but that’s a potential issue with weave anyway.
Swarming Raspberry Pi, Part 2: Registry & Mirror » Ramblings
March 29, 2015 at 9:36 pm (UTC -5) Link to this comment
[…] Previous: Part 1 […]
350+ ???????? ????????, ???? ? ???????????? ??? ?????? ? Docker — ? ???? ??
January 14, 2016 at 4:13 am (UTC -5) Link to this comment
[…] Matthewkwilliams: ????????????? Swarm ??? Raspberry Pi; […]
?? Docker ???? - ???????
April 15, 2016 at 11:01 am (UTC -5) Link to this comment
[…] Swarming Raspberry Pi – Part 1 […]