Docker-Jekyll
20 Feb 2020Backstory
This page will cover the (1) use and (2) creation of a Docker image for generating websites with Jekyll. Why would I do such a thing?
Well, the story is as follows. I maintain two websites, this one, and another one, that are generated with Jekyll. Every time I tried to update the other one (not amytabb.com), things were broken, I would have to fight with Ruby – something I know very little about – and a planned 5-minute update ended up consuming an inordinate amount of time. Since I update from more than one machine, my thinking was that I could freeze the Ruby version once and for all, and just run Jekyll from a container.
Getting a Docker image set up for amytabb.com was easy. Getting a Docker image set up for the other website never worked – and finally, I realized I would need to start with a template with fewer dependencies. Since I have very little on that other website (I am purposefully not linking to it …), this is not a big deal and will save me time and angst in the long run.
In the meantime, I had gotten a container working, so why not write a blog post? If you’ve been having a hard time getting Jekyll working, or there are incompatibilities with versions, this method should work no matter what OS you have. Technically, since Jekyll will be running from the container, even though I use an Ubuntu image, it should run.
Here goes!
Install Docker
If you don’t have Docker already, you’ll need to install it. Here’s the link to install for Linux, and I also endorse uninstalling old versions if you have them floating around.
Build image or pull from Docker hub
The image is currently at Docker hub, amytabb/jekyll-basic. To pull to your local disk,
docker pull amytabb/jekyll-basic
Alternatively, you can build the Docker image on your local disk yourself. The Dockerfile is in a Github repository, amy-tabb/docker-jekyll-basic
git clone https://github.com/amy-tabb/docker-jekyll-basic.git
cd
into the directory, and then build:
sudo docker build -t jekyll-image .
Run Jekyll from container
Now it is time to run Jekyll from the container. We need a lot of stuff here. Assuming that jekyll-image
is the image name (it would be amytabb/jekyll-basic:latest
if you pulled it from Docker hub),
sudo docker run --network host -v /home/atabb/git/amy-tabb.github.io:/write_directory -it jekyll-image jekyll serve
is one concrete example. I’m going to put in another example, because I myself use this post a lot.
Case where you are in the directory, and you are using the container from the Docker hub:
sudo docker run --network host -v $PWD:/write_directory -it amytabb/jekyll-basic:latest jekyll serve
Substituting in the generics:
sudo docker run --network host -v /full/file/path/of/web/site:/write_directory -it jekyll-image jekyll-commands
Let’s start at the back – the jekyll commands.
For some websites, the commands are bundle exec jekyll serve
. The template I am using is generated with jekyll serve
, so I used that.
jekyll-image
is the name of the docker image, again, you would use whatever name you selected when you built the image locally, or amytabb/jekyll-basic:latest
if you pulled it from Docker hub.
-v /full/file/path/of/web/site:/write_directory
means to bind the first path on the host machine, to the second path in the image (separated by a :
). I set up the Dockerfile such that the working directory is /write_directory
, so that will not change. -v
indicates a bind mount (a type of mount in Docker).
--network host
means to allow the container to access the host’s network. From the docs, this is only available in the Linux Docker version. But I really don’t know what the status is for other OS’s. I have complained about Docker’s docs before.
run
: we’re running the image to get a container.
Details of the Dockerfile, extensions
FROM ubuntu:18.04
RUN apt-get update
RUN apt-get -y upgrade
RUN apt-get -y install ruby-full build-essential zlib1g-dev
RUN apt-get -y install ruby-bundler
RUN useradd jekylluser
RUN mkdir /home/jekylluser
RUN mkdir /write_directory
RUN chown jekylluser /home/jekylluser
USER jekylluser
WORKDIR /home/jekylluser/
ENV HOME "/home/jekylluser"
ENV GEM_HOME "$HOME/gems"
ENV PATH "$HOME/gems/bin:$PATH"
RUN gem install github-pages
RUN gem install bundler
WORKDIR /write_directory/
I’ll explain each part. For more in-depth details about using Docker for research purposes, I have a tutorial that concerns using Docker to package code for those not on your OS – which in my case, is C++ with Linux.
FROM
creates a layer from theubuntu:18.04
image. This image is already created on Docker hub.RUN ...
runs instructions as we would from a bash shell, in this case:- updating the packages
update
, - and upgrading them to the most recent versions
upgrade
. Is this necessary? Maybe not for this particular project, but for code, it is. - install the relevant ruby libraries for Jekyll. From here, you will see many similarities to Linux installation for Jekyll.
useradd jekylluser
: adds a new user, as in Docker we only have a root/superuser by default.mkdir /home/jekylluser
: we create a home directory for the new user.mkdir /write_directory
: we create a working directory for Docker, while we still have root access.chown jekylluser /home/jekylluser
: give the new user access to their home directories.
- updating the packages
USER jekylluser
: switch user to the new user.WORKDIR /home/jekylluser/
switch to the new user’s home directory. I learned the hard waycd
is not your friend in Docker: Docker: What’s the deal with WORKDIR?.ENV
: set environment variables, which is similar to the instructions relevant to./bashrc
from the Linux installation for Jekyll page.RUN..
: I install thegithub-pages
gem, and then install thebundler
gem. You could add more gems if needed.WORKDIR /write_directory/
: set the working directory to/write_directory/
. This is important as we’ll bind a directory on the host machine to this particular directory in the Docker image, so we need to make sure that the finalWORKDIR
is the second argument of-v /full/path/to/host/dir:/our/workdir/in/docker/image
.
Final note: beware of relative and full paths in Docker. There are many ways to get things wrong. Docker: What’s the deal with WORKDIR? has a full explanation of these phenomenon, and to eliminate problems, I use full paths (starting with /
). In short, if there’s a way to get it wrong, I’ve probably done it!
Extensions
Hopefully, you can see there’s plenty of ways you can alter this basic Dockerfile for custom cases, to install more gems or to extend functionality.