Docker III - Making incredibly small container images with Alpine Base
The Basics
Creating Docker files and containers is fairly easy. We started by exploring the debian based distro because it's a known quantity and quite easy, but even the lovable debian can grow if you have many images or they have many layers (don't forget transfer time if you deploy rapidly).
A popular alternative is 'alpine linux' which is ALOT smaller than most other distros (it uses musl instead of glibc). Caveats are that it may not have the compatibility that glibc does of course have, but many of our popular packages like Nginx, Python, PHP all work well!
Prerequisite:
You should be familiar enough with Docker ang Nginx/Python to appreciate the subtleties or rationale of what we're attempting to do here.
Rationale:
Size does matter you say? Let's take a look at the installed images on a docker instance running on Debian-8.4
with Docker-1.12.3
.
Application | Debian 8.4 | Alpine 3.4 |
---|---|---|
Linux | 125.1 mb | 4.803 mb |
Nginx-11 | 181.5 mb | 54.23 mb |
Python-2.7 | 673.3 mb | 71.37 mb |
PHP-7.0 | 362.9 mb | 58.66 mb |
Or as a pretty graph here
As you can see, not only is the difference in the base linux images huge, so is the compiled applications.
Obviously there are trade-offs here and it requires the use of being compiled with MUSL, and this isn't always compatible, but for space-concerned docker images, it's quite amazing.
Let's dive in
TL; DR; You could just use 2.7-alpine from python's official docker library
We're going to create a basic python2.7 image that contains UWSGI & NGINX
Starting Point
We're going to use the official Nginx image with the alpine tag, as NGINX can be involved to setup especially with alpine, we'll trust that the official Nginx package is maintained and optimised better than we could hope to achieve realistically.
Whoa there, no apt-get?
That's right, this is alpine, so it uses the apyly named apk, such as
apk add widget
However we'll specify --no-cache
as we don't want the cache sticking around in our image.
We also have a few extra files, such as templates for NGINX, UWSGI and supervisord (which arbitrates both the aforementioned programs running and can report/restart them, as there's no rc-daemon here).
These are available on my repo here.
Here's the complete Dockerfile.
Our Dockerfile:
FROM nginx:alpine
RUN apk add --no-cache uwsgi && \
apk add --no-cache uwsgi-python && \
apk add --no-cache supervisor && \
python -m ensurepip && \
rm -r /usr/lib/python*/ensurepip && \
pip install --upgrade pip setuptools && \
pip install bottle && \
rm -r /root/.cache && \
echo "daemon off;" >> /etc/nginx/nginx.conf
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY supervisord.conf uwsgi.ini /etc/
COPY ./app /app
WORKDIR /app
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisord.conf"]
As you can see at the end, we (re)set the CMD (foreground process Docker managers) to be supervisord.
Now want to run it?
docker run -d -p 8081:80 mitchins/alpine-bottle
links http://localhost:8081/hello/world
Of course you could just run http://localhost:8081/hello/world
in a graphical browser
If all goes according to plan you'll be greeted with :
Hello world!
Now how big is the image?
root@linux2:~# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mitchins/alpine-bottle latest 32ef6937eb9d 6 days ago 106.3 MB
Not so bad, it's 106.3mb, which since it contains both Nginx, python, and UWSGI is about expected.
Conclusion
Well that about sums it up, Alpine Linux can provide docker images at a fraction of the size that appear to work just as well when the packages are available and performant.
Your milage may of course vary, and it's worth pointing out Python3 and UWSGI don't quite work well due to a missing plugin in current Alpine package/build-source. Feel free to take my image for a spin.