Control GPIO with Docker Swarm
If you've ever used add-ons with your Raspberry Pi before then you'll know they rely on the GPIO pins. Most GPIO libraries need elevated system privileges to access the pins and devices created by the Linux Kernel. The Docker Swarm project (codenamed SwarmKit) currently does not support running privileged containers.
In this post I outline a hack for using add-ons with Docker Swarm - without running in privileged mode using something called sysfs
.
Thanks goes to Stefan Scherer for suggesting and testing out the following method.
Demo video
In the video you can see a Docker Swarm cluster with five hosts. Each host is a Raspberry Pi Zero and is connected over a USB-ethernet dongle.
I created Docker Swarm service with my Golang progress bar animation and told the swarm to replicate one container per host. Read on for more details.
You can also learn about Swarm Services here in the Docker docs.
Using GPIO with Docker
There are three ways to use hardware with the Raspberry Pi and Docker:
- Pass
--privileged
to thedocker run
command:
$ docker run --privileged -d blinkt
This works fine with the previous Swarm offering from Docker but not the version released in June last year with 1.12. If you don't need clustering the above is the simplest way to use GPIO and Docker.
- Add the
/dev/gpiomem
device at runtime
This is another method that works with some GPIO libraries - a recent version of Raspbian added the /dev/gpiomem
device and this can be mounted into a container with docker run
.
$ docker run --device /dev/gpiomem -d blinkt
Unfortunately this still doesn't work with Swarm Services, but it may do in the future. If your add-on board uses i2c or serial then you may need to wait or use the previous Docker Swarm version.
- Use the sysfs GPIO interface
The third and last option is to use what is called the sysfs
filesystem. You can perform GPIO with user privileges by interacting with the virtual files under /sys/class/gpio
.
A drawback to this method is that not all libraries support it and it's also slower than /dev/gpio
.
Getting it working with sysfs
I recently ported Pimoroni's Python library for their Blinkt LED board over to Golang and made use of the WiringPi library. It was not hard to convert it to using sysfs
- it involved using standard syscalls such as stat/open and write.
Find a library
You need to find or create a library that uses sysfs instead of /dev/gpiomem or /dev/mem. Here's the code for my Golang Blinkt library for sysfs:
If you want to perform GPIO with a different device, then you could adapt the code above or search to find out whether someone has already used sysfs in the library you're using right now.
Build or port an application
If you're building a new application, then reference the new library and test it out on one device first. I already had a code sample so I just updated the Golang package.
- app.go - gives a progress bar-style red LED animation
Build a Dockerfile
Once your application is working you can package it into a Dockerfile. Here's my example:
Create a Swarm
You may already have a Pi swarm - if you don't then checkout these tutorials and videos.
If you have a Blinkt plugged in then you can try my Docker image right away:
$ docker service create --name sys --mount type=bind,source=/sys,destination=/sys --mode=global alexellis2/progress-blinkt:red
This image contains a single Golang binary so the total size of the image is a tiny 1.81 MB uncompressed. Even smaller on the Docker Hub where it's a 655 KB download.
Breaking this down we have two important parts:
--mount type=bind,source=/sys,destination=/sys
Mounts the virtual /sys/
filesystem from the Pi where the service is running into the container so we can access GPIO.
--mode=global
This runs the container a maximum of once per Raspberry Pi. We don't want to run this animation more than once at the same time or it will go out of sequence.
Rolling updates
You can also perform rolling image updates with Docker Swarm. This means the swarm will deploy a new version of the image into the service.
Try this out to turn the LEDs green:
$ docker service update sys --image=alexellis2/progress-blinkt:green
Have swarm deploy the blue version of the progress app:
$ docker service update sys --image=alexellis2/progress-blinkt:blue
You can even type in docker swarm update sys --rollback
to return to the original image. These features are purpose-built for web applications but also work well with GPIO.
Wrapping up:
Follow me on Twitter @alexellisuk and send your comments, questions and suggestions.
See also: