I’m getting into the habit of building everything inside containers.
Initially, Docker’s most attractive feature was removing the ever-present
fear of hosing my entire setup with a misplaced make install
. More recently I’ve
come to appreciate the time it saves when revisiting old projects.
Without containers, running old code means a hold-your-breath moment as you wait to see what’s broken. You wrote this against Elastic 6? Well, too bad you’ve since installed 7. An hour of today will be figuring out how to roll it back and an hour of tomorrow will be rolling it forward.
That’s not a productive use of your time.
Containers fulfil the promise of entirely reproducible environments and the end of “works on my machine”, even when the “my machine” in question is “this machine, but last January”.
Which brings me to rebooting this site on Jekyll, but containerized.
Bootstrapping Jekyll without installing Jekyll
First off, we install Jekyll on a throwaway container to create a skeleton site.
--rm
delete the container once it stops, we’re not going to use it again-it
broadly speaking; make the container respond to interactive commands. Any deeper explanation starts with “When mainframes had physical terminals…”-v
mount the current directory as/app
on the container. This replicates any changes we make in the container back to our machine.
A simple Dockerfile for development
With the basic site created, we add a Dockerfile to make the whole thing reproducible.
That’s a decent first pass, but there’s a problem applicable to any Ruby container. Let’s time
how long it takes to build our image
About two and a half minutes with the majority of time spent on 4/6
installing gems.
A second build of the image is instantaneous because Docker has cached each step as an intermediate layer. These are reused on subsequent builds until an altered, uncached step is encountered.
But if we make the slightest change to our site we’re back to several minutes
For COPY
, the contents of each file is considered: changing them will bust the cache.
If we add a post, change the stylesheet or delete an image from Jekyll, the next build have to rewind to the COPY
step and
subsequently rebundle from scratch two steps later.
This wasted time will quickly mount up, especially when using gems with native compilation, like sassc
or nokogiri
.
Better Gem management
The solution is to separate the steps where we COPY
files that require changes to gems.
Docker’s diff is smart. It will identify which step individual files are on copied on and preserves the cache up to that step. With this Dockerfile most builds will be instant.
And with that, we have a working Jekyll image that’s only dependent on Docker.