Close modal

Blog Post

Docker III - Making incredibly small container images with Alpine Base

Development
Tue 15 November 2016
0 Comments


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

graph

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.