For a little while now I've been using the excellent shairport-sync package by Mike Brady on a Raspberry Pi 2B+ to stream music in my kitchen. I'd originally tried this out on FreeBSD around a year ago, but hit a wall due to only ALSA support for the package at the time, and a lack of ALSA support on FreeBSD for ARM-based architectures.
At the time, I threw in the towel, loaded up Raspbian Jessie, and it mostly just worked. There were occasional issues over the year where it seemed like connection sessions with the Avahi agent would get stuck and the Shairport daemon would fail to kick off defunct streaming clients, but I paid it little mind.
Now I haven't been working on QBSD much over the last few weeks, mostly due to family trips (it's that time of the year, after all). To get back on the wagon I decided to take a stab at replacing the Shairport-Sync's manually built system with a Docker container, because a little bit of experience with how Docker actually works in practice will help me plan out my project (codename: Condominium) for FreeBSD. There's little good coding or configuration I can do without being led by good design, so it would be pretty dumb to plow ahead without at least checking out how the issues of packaged service deployment were solved by others first.
First thing I noticed returning to Linux is the immense sea of somewhat conflicting or competing guides and advice for running Docker in the first place. I'd heard that Debian now offered a package, so of course I first try installing that. Seems to work; installs with no complaints. However, the only version available for Jessie in the standard repository is 1.3.3, which was so out-of-date that I couldn't pull any images to run. Instead I get this maddening nonsense:
me@kitchen:~ $ sudo docker pull orbsmiv/shairport-sync-rpi Pulling repository orbsmiv/shairport-sync-rpi 2018/08/19 08:49:06 Could not reach any registry endpoint
So the hunt began.
The first guide that actually worked on the Pi was this random github 4-step guide. The Hypriot guys seem ok, and to be perfectly fair I didn't follow their own guid, but umiddelb's, in order to run the Hypriot install script as listed ... and this fetched version 1.10.3-1. A little poking and prodding revealed the consistent early problem that for some reason, systemd refused to start the service due to "masking", whatever that means. Apparently all I had to do was this:
me@kitchen:~ $ sudo systemctl unmask docker.service me@kitchen:~ $ sudo systemctl unmask docker.socket me@kitchen:~ $ sudo systemctl start docker
... and those magic words finally started my dockerd-hypriot .deb package. After that, it was just a matter of pulling the same image, and telling it to run with the default config. Nice.
But wait; what if I want to reboot the Pi in case that stuck session issue crops up again? After a quick reboot test I realize I have to run the
docker run command again on the same image, which seems a bit... nonsense. After poking around online, it seems like the issue was resolved in version 1.11 where the --restart flag was introduced.
Ok, so this seems easy enough: go to the Hypriot website and note that there's a single minor-version upgrade available to 1.11.1 as a debian package (Hypriot, I note, has a fully-loaded image with Docker v1.18 supported, but that would require I pull the damn thing down off my cabinet, reflash the system, and plug it into my currently-occupied TV to set up the networking again correctly). So I try to install the newer version.
me@kitchen:~ $ sudo apt-get install docker-hypriot=1.11.1-1 Reading package lists... Done Building dependency tree Reading state information... Done docker-hypriot is already the newest version. 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. 1 not fully installed or removed. After this operation, 0 B of additional disk space will be used. Do you want to continue? [Y/n] Setting up docker-hypriot (1.11.1-1) ... Job for docker.service failed. See 'systemctl status docker.service' and 'journalctl -xn' for details. invoke-rc.d: initscript docker, action "start" failed. dpkg: error processing package docker-hypriot (--configure): subprocess installed post-installation script returned error exit status 1 Errors were encountered while processing: docker-hypriot E: Sub-process /usr/bin/dpkg returned an error code (1)
... is this the same error? Let's try that magic unmasking again.
me@kitchen:~ $ sudo systemctl unmask docker.service me@kitchen:~ $ sudo systemctl unmask docker.socket me@kitchen:~ $ sudo systemctl start docker Job for docker.service failed. See 'systemctl status docker.service' and 'journalctl -xn' for details. me@kitchen:~ $ journalctl -xn No journal files were found. me@kitchen:~ $ systemctl status docker.service ● docker.service - Docker Application Container Engine Loaded: loaded (/lib/systemd/system/docker.service; enabled) Active: failed (Result: exit-code) since Sun 2018-08-19 11:55:51 UTC; 16s ago Docs: https://docs.docker.com Process: 3002 ExecStart=/usr/bin/docker daemon -H fd:// (code=exited, status=1/FAILURE) Main PID: 3002 (code=exited, status=1/FAILURE)
So what the hell is going on now? First stop was looking at alternative installation methods, because if 1.11 is the latest Raspbian package available for Docker something must be seriously wrong. This leads me back to Alex Ellis's blog on running v1.12, and a short instruction set on how to do this using a more standard supply chain. But it's another script to enable fetching from a different repository. So first I purge the special docker-hypriot package; second I run Ellis's recommendation.
me@kitchen:~ $ curl -sSL https://get.docker.com | sh # Executing docker install script, commit: 36b78b2 + sudo -E sh -c apt-get update -qq >/dev/null + sudo -E sh -c apt-get install -y -qq apt-transport-https ca-certificates curl >/dev/null + sudo -E sh -c curl -fsSL "https://download.docker.com/linux/raspbian/gpg" | apt-key add -qq - >/dev/null + sudo -E sh -c echo "deb [arch=armhf] https://download.docker.com/linux/raspbian jessie edge" > /etc/apt/sources.list.d/docker.list + [ raspbian = debian ] + sudo -E sh -c apt-get update -qq >/dev/null + sudo -E sh -c apt-get install -y -qq --no-install-recommends docker-ce >/dev/null The mail frontend needs a installed 'sendmail', using pager E: Sub-process /usr/bin/dpkg returned an error code (1)
Now what? Like a dummy, I say ok, let's install sendmail. This ends similarly:
Processing triggers for systemd (215-17+deb8u7) ... Errors were encountered while processing: docker-ce E: Sub-process /usr/bin/dpkg returned an error code (1)
Feeling a little defeated, I start looking around for hits regarding the actual error message returned by the
systemctl status docker.service as listed. Turns out there's an unlisted action that the purge script failed to invoke: removing /var/lib/docker.
So I do that, remove sendmail, purge docker-ce, apt-get autoremove and apt-get clean the remaining packages just for good measure, and try the install script again.
me@kitchen:~ $ curl -sSL https://get.docker.com | sh # Executing docker install script, commit: 36b78b2 + sudo -E sh -c apt-get update -qq >/dev/null + sudo -E sh -c apt-get install -y -qq apt-transport-https ca-certificates curl >/dev/null + sudo -E sh -c curl -fsSL "https://download.docker.com/linux/raspbian/gpg" | apt-key add -qq - >/dev/null + sudo -E sh -c echo "deb [arch=armhf] https://download.docker.com/linux/raspbian jessie edge" > /etc/apt/sources.list.d/docker.list + [ raspbian = debian ] + sudo -E sh -c apt-get update -qq >/dev/null + sudo -E sh -c apt-get install -y -qq --no-install-recommends docker-ce >/dev/null + sudo -E sh -c docker version Client: Version: 18.06.0-ce API version: 1.38 Go version: go1.10.3 Git commit: 0ffa825 Built: Wed Jul 18 19:24:36 2018 OS/Arch: linux/arm Experimental: false Server: Engine: Version: 18.06.0-ce API version: 1.38 (minimum version 1.12) Go version: go1.10.3 Git commit: 0ffa825 Built: Wed Jul 18 19:20:24 2018 OS/Arch: linux/arm Experimental: false If you would like to use Docker as a non-root user, you should now consider adding your user to the "docker" group with something like: sudo usermod -aG docker me Remember that you will have to log out and back in for this to take effect! WARNING: Adding a user to the "docker" group will grant the ability to run containers which can be used to obtain root privileges on the docker host. Refer to https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface for more information.
Success! And a much more recent version of Docker now running on my previously configured rpi.
Let's restart that docker pull-and-run procedure, now with --restart=always enabled.
me@kitchen:~ $ sudo docker pull orbsmiv/shairport-sync-rpi Using default tag: latest latest: Pulling from orbsmiv/shairport-sync-rpi 95d54dd4bdad: Pull complete ## [snipped content] Digest: sha256:50e00fca643ec68cf1e648ba9fc08c77485438fb77ea7bd6026d7ff8fde7d5cb Status: Downloaded newer image for orbsmiv/shairport-sync-rpi:latest rdain@kitchen:~ $ docker run -d --net host --restart=always --device /dev/snd -e AIRPLAY_NAME=ktchen_ap orbsmiv/shairport-sync-rpi
I note there's no "masking" at issue any more. Waiting a bit now for the service to come up...
And there it is, after I finally get tired of waiting and try to run
systemctl status docker.service again. Time to reboot and see how long it takes to come up.
Cool, it's back. And in no time at all, really. Looks like the former AirPlay connection left in my iPhone's list isn't stale, either.
So my experience so far is that Docker is extremely easy to use ... once it's running. Updating the daemon itself, or updating the running software service shairport-sync isn't clear or straightforward; I'll need to unpack how to get the version information of the installed shairport-sync software out of the container, and then either rebuild it myself the next time Mike Brady updates it, or I can just wait for orbsmiv to update the thing. That may be where I'll find room for improvement.