Update - Ruby Gem Dockerfile with Alpine Linux
This post was originally published on my previous blog jer-k.github.io
Published on 2018-09-19
An update to my post on adding a testing environment to a gem. After doing some recent updates to our Docker images at work, I realized that we are always using Ruby Alpine images, and not the base Ruby image. I can't remember why I built the gem's Dockerfile using the base Ruby image, perhaps I had just overlooked the fact that we used Ruby Alpine, but I wanted to standardize the Dockerfiles I had written at work and here for the blog so I decided to look into what it would take to do so.
First, why choose an Alpine image? Many other developers have covered this topic in their blog posts and I think it's best not wander down that path again. Instead, we'll look at a couple interesting snippets and move onto implementation details.
"Alpine Linux is a very tiny Linux distribution. It’s built on BusyBox, and it includes only the minimum files needed to boot and run the operating system."
from Ilija Eftimov's Build a Minimal Docker Container for Ruby Apps blog post, which is a great in-depth overview about going building a Ruby application from scratch with Docker and Alpine Linux.
"Debian based base images may be easier to start with but it comes with the cost of image size (Image 2). It is almost six times bigger than image based on Alpine Linux."
from Lauri Nevala's Dockerizing Ruby Application blog post, which details the different base images that are available for Ruby and goes through an example of building a Ruby application with Docker and Alpine Linux.
First let's look at the updated Dockerfile.
And let's dive into the changes as seen in the commit where I made this conversion.
To start I changed the image to ruby:2.5.0-alpine
to use the Ruby Alpine image. Next, I'm using apk to run
apk --update add --no-cache --virtual run-dependencies
. Let's break down the flags I passed to this command.
--update
:
Interestingly enough the --update
flag does not seem to be documented anywhere in the Wiki, but I learned about it from
a Gliderlabs' post on Docker Alpine Usage. The description they give is
"The --update flag fetches the current package index before adding the package.
We don't ship the image with a package index (since that can go stale fairly quickly)."
It appears to be shorthand for doing apk update && apk add
.
add
:
This is pretty straight forward. From the docs.
"Use add to install packages from a repository. Any necessary dependencies are also installed.
If you have multiple repositories, the add command installs the newest package."
--no-cache
:
The apk --help
description for --no-cache
is "--no-cache Do not use any local cache path". However, I think the
Gliderlabs article did a better job of describing the functionality.
"It allows users to install packages with an index that is updated and used on-the-fly and not cached locally."
--virtual run-dependencies
: The apk add --help
description for --virtual
is
"-t, --virtual NAME Instead of adding all the packages to 'world', create a new virtual package with the listed dependencies and add that to 'world';
the actions of the command are easily reverted by deleting the virtual package."
The Gliderlabs article gives a good example of using --virtual
to install build-dependencies which can then be
removed after building the image is complete. We've named our packages run-dependencies
because they are needed at
runtime and should not be removed.
Now lets go through the packages that we add
bash is added so that we can execute our wait_for_pg.sh script when we use the entry_point in our
docker-compose
file. Also, we are able to run a shell inside the container via docker-compose run app /bin/bash
.
This is actually a great way to play around with apk
if you want to try it out!
build-base adds the applications needed to compile our application for use, like make
and gcc
. Below you
can see everything that is added.
postgresql-client is installed for access to psql
, which we use in wait_for_pg.sh to ensure that the
database is ready before we execute the tests.
postgresql-dev adds the needed libraries to be able to install the pg
gem.
git
is used in the autogenerated portion of the gemspec file for the ls-files command.
That's it! Let's run our bin/ci.sh
script and ensure everything is still working.
A final note: Gliderlabs maintain the Docker Alpine image on GitHub if you're interested in looking the source code!