Some while ago I got a new wifi-capable camera. Of course, it has some awful proprietary system for actually transferring images to a real computer. Fortunately, it’s all based on a needlessly complex HTTP interface which can fairly easily be driven by any moderately capable HTTP client. I played around with FlashAero a bit first but it doesn’t do quite what I want out of the box and the code is a country mile from anything I’d like to hack on. It did serve as a decent resource for the HTTP interface to go alongside the official reference which I didn’t find until later.
Fast forward a bit and I’ve got txflashair doing basically what I want - essentially, synchronizing the contents of the camera to a local directory. Great. Now I just need to deploy this such that it will run all the time and I can stop thinking about this mundane task forever. Time to bust out Docker, right? It is 2017 after all.
This afternoon I took the Dockerfile I’d managed to cobble together in the last hack session:
FROM python:2-alpine
COPY . /src
RUN apk add --no-cache python-dev
RUN apk add --no-cache openssl-dev
RUN apk add --no-cache libffi-dev
RUN apk add --no-cache build-base
RUN pip install /src
VOLUME /data
ENTRYPOINT ["txflashair-sync"]
CMD ["--device-root", "/DCIM", "--local-root", "/data", "--include", "IMG_*.JPG"]
and turn it into something halfway to decent and that produces something actually working to boot:
FROM python:2-alpine
RUN apk add --no-cache python-dev
RUN apk add --no-cache openssl-dev
RUN apk add --no-cache libffi-dev
RUN apk add --no-cache build-base
RUN apk add --no-cache py-virtualenv
RUN apk add --no-cache linux-headers
RUN virtualenv /app/env
COPY requirements.txt /src/requirements.txt
RUN /app/env/bin/pip install -r /src/requirements.txt
COPY . /src
RUN /app/env/bin/pip install /src
FROM python:2-alpine
RUN apk add --no-cache py-virtualenv
COPY --from=0 /app/env /app/env
VOLUME /data
ENTRYPOINT ["/app/env/bin/txflashair-sync"]
CMD ["--device-root", "/DCIM", "--local-root", "/data", "--include", "IMG_*.JPG"]
So, what have I done exactly? The change to make the thing work is basically just to install the missing py-virtualenv
. It took a few minutes to track this down. netifaces
has this as a build dependency. I couldn’t find an apk
equivalent to apt-get build-dep
but I did finally track down its APKBUILD file and found that linux-headers
was probably what I was missing. Et voila, it was.
<>Perhaps more interesting, though, are the changes to reduce the image size. I began using the new-ish Docker feature of multi-stage builds. From the beginning of the file down to the 2nd FROM
line defines a Docker image as usual. However, the second FROM
line starts a new image which is allowed to copy some of the contents of the first image. I merely copy the entire virtualenv that was created in the first image into the second one, leaving all of the overhead of the build environment behind to be discarded.
The result is an image that only has about 50MiB of deltas (compressed, I guess; Docker CLI presentation of image/layer sizes seems ambiguous and/or version dependent) from the stock Alphine Python 2 image. That’s still pretty big for what’s going on but it’s not crazy big - like including all of gcc, etc.
The other changes involving virtualenv are in support of using the multi-stage build feature. Putting the software in a virtualenv is not a bad idea in general but in this case it also provides a directory containing all of the necessary txflashair bits that can easily be copied to the new image. Note that py-virtualenv
is also copied to the second image because a virtualenv does not work without virtualenv itself being installed, strangely.
Like this kind of thing? Check out Supporing Open Source on the right.