The Alpine Mistake

Feb 25 2016

Alpine Linux is the new darling of the containerati. This is no big surprise. I too have bought into the story that Alpine's small footprint makes it the perfect fit for the Docker world. But it's not. Alpine is a short play that puts containerization in danger of losing the long game.

Trailer Homes

Imagine for a moment that Linux distributions are homes.

Ubuntu is the McMansion, ready to be filled with shiny toys. RHEL is the Pullman town house. Debian is a Prepper home. And between these are vast swaths of very decent single family houses.

Then there's Alpine. It's on the low end of the spectrum. The walls are thin. The fixtures are cheap. And words like "laminate" and "adhesive" collide with "cozy" and "compact". Welcome to the world of container austerity.

The Cost of Small

Alpine was designed for embedded systems. From libc (the heart of all modern UNIX systems) to the core libraries to the shell utilities, all of Alpine is built on the same premise: tininess trumps functionality.

Just like the cramped flush toilet in the mobile home, or that cupboard of a kitchen, Alpine's compact but cheap furnishings work. But you're going to have to adjust your lifestyle.

You'll be tweaking build flags, working around networking limitations, and rolling your shell scripts back to their 1980s versions. In some cases, you may find yourself rewriting the sort of functionality you used to "just get" with other systems. More often, you'll start installing plenty of supporting packages, ballooning your image size right back up. And sometimes you'll find (after a frustrating quest) that you need to just bite the bullet and compile your own tools.

You'll experience the pressure of trying to wedge your nice things into a trailer park world, all the while telling yourself that "really, this is just as good as when I had that nice house in the suburbs."

Did We Take The Wrong Exit?

The venerable glibc has been around for decades, and has driven everything from phones to enterprise server clusters. We have a rich array of production hardened core libraries for things like networking. And from C Shell and Bourne shell, we've seen a litany of increasingly powerful base layers.

Now we're excited about just barely functional.

How did we get here? And why are we so happy to arrive?

The answer comes down to our revised expectations about how containerized apps ought to work:

  1. We want containers that work like a full operating system
  2. We want to push our containers around the Internet
  3. And we're stingy and impatient

For #1, we are loathe to transform our commitment to containers into a commitment to rewrite all of our applications as stand-alone monolithic executables. This, I think, is a good thing. Doing otherwise would truly throw us back a few decades.

But with #2, things start to get interesting. When it came to machine virtualization, Amazon and other public cloud providers trained us to think about machine images as a thing that is stored over there. We don't push those behemoth multi-gig images back and forth unless we absolutely have to.

Yet containerized images are approximately the same size as machine images. After all, they too contain most of what we refer to as an operating system (minus the kernel and a few other low-level chunks).

Docker has us trained differently than Amazon did, though. Containers, says Docker dogma, should be pushed back and forth across the network. In fact, it's a Good Thing (TM) for us to push all of our images into the DockerHub registry, even if we're mainly running the images locally.

Combining #2 with #3 is what forces us out of our McMansions and into the trailer park: We don't want to keep a bunch of big giant images hanging around on our hard drives, and we really don't want to wait while those images get pushed back and forth across slow Internet links.

That leads us to the trade off that will come back to haunt us: We skimp on the plumbing, the framing, and the foundation. We opt for 25MB of minimalist base libraries and hope that our apps will work like they do in our more spacious and versitile development environments.

The Solution is a Bungalow

Realistically, containerized apps don't need McMansions. But what they do need is solid foundations, sturdy framing, and quality plumbing.

  • We need a better base image. It'll be bigger, but also sturdier
  • We must develop better development habits.
    • We need to get better about minimizing dependencies
    • We need to leverage the tooling for building smaller binaries (UPX, stripping, and so on)
  • In the long(er) run, we need to build better container runtimes. Not only is it silly that every container must bring its own base environment, it's also a security nightmare.

Alpine is just a little too far to the austere extreme. And we too readily accept the story that this is just the way containers have to work.

Read On

I am not arguing that Alpine (or, for that matter, trailer homes) have no place in our world. Alpine's a good tool that is being mis-used.

Alpine uses musl. This is the musl author's comparison of that libc to others. Ignore the color coding, and just read the comparisons -- and keep in mind that the author is biased.

Busybox provides a partial implementation of a few dozen UNIX tools. Pick a command, like tar, and compare it to the GNU equivalent. Tar is a great example: I personally have had to write utilities to do part of what GNU tar does so that I can run things in Alpine.

Finally, spend some time reading up on Alpine itself. It is a great tool if you really want to favor smallness over functionality (such as when doing IoT). I'm simply claiming that we don't want that trade-off in our containers.