Automatically build and serve your Jekyll website with Docker Cloud

This article will teach you how to:

  • Automatically build your Jekyll site into a docker-container using Docker Cloud. Including:
    • serve a Jekyll site using nginx inside a container
    • build your _site static site on Docker Cloud
    • test to verify that your build was successful

Note: For info on setting up a Jekyll site, please refer to the Jekyll docs.

For the purpose of this article we will assume the following conventions:

  • docker username: myusername
  • docker container that we are building: mycontainer

Packaging your Jekyll site for productiong

In your standard Jekyll website, add a Dockerfile:

FROM nginx:stable  
ADD ./_site /usr/share/nginx/html  

This is pretty simple:

  • We use the standard nginx public container.
  • We ADD the contents of _site to the default static content folder inside the nginx container (/usr/share/nginx/html).

Now, to get this to work locally you would run the following commands:

  1. jekyll build -> create site static contents
  2. docker build -t myusername/mycontainer:latest -> build our container
  3. docker push myusername/mycontainer:latest -> push it to the hub.

But since we're using Docker Cloud, it would be better if this could happen via an Automated Build (note: setting up Automated builds is beyond the scope of this article).

To set up an automated build in docker cloud, we would typically:

  1. connect our Github repo to docker cloud
  2. add some build rules (such as whenever I push to master, kick off a build)
  3. sit back and enjoy the magical container building.

However: there's a problem. We don't want to have to manually build _site, and we especially don't want to have to include it in our git repo. But: the Automated build only runs our Dockerfile .. and our Dockerfile assumes that _site exists.

We need some way to build _site on Docker's infrastructure as part of our automated build. So:

Build your site on docker cloud using build hooks

Build hooks to the rescue!

Docker build hooks allow you to run commands at various points in the Automated build life-cycle.

The following build hooks exist:

hooks/post_checkout  
hooks/pre_build  
hooks/post_build  
hooks/pre_test  
hooks/post_test  
hooks/pre_push (only used when executing a build rule or automated build )  
hooks/post_push (only used when executing a build rule or automated build )  

So, in our root directory, we need to create: hooks/pre_build, and then we'll use the the official Jekyll image to build our site:

hooks/pre_build:

#!/bin/bash
echo "=> Build the site using jekyll"  
mkdir -p _site  
docker run --volume=`pwd`:/srv/jekyll jekyll/jekyll jekyll build  

What's happening here:

  • We're using the jekyll/jekyll (which will give us a Jekyll ready environment)
  • We map our current directory to /srv/jekyll (where the Jekyll image expects the Jekyll site to be
  • We build our site by running jekyll build

Notes:

  • You need to create the _site directory manually (line 3: mkdir -p _site) otherwise you will get a permissions error:
Generating...  
jekyll 3.4.3 | Error: Permission denied @ dir_s_mkdir - /srv/jekyll/_site  
pre_build hook failed! (1)  
ERROR: Build failed: pre_build hook failed! (1)  

Test that Jekyll build works.

It is possible that we make a commit which has a mistake in it that causes the Jekyll build to fail. This would result in us having a broken container image. Which is bad. So: Automated tests to the rescue!

In our root directory, let's create a docker-compose.test.yml file which will run a Jekyll build. If the build succeeds, we're all good:

docker-compose.sut.yml

sut:  
  image: jekyll/jekyll
  command: jekyll build

And that's it. Now we have magical Docker CI all sorted. When we push to master the following process is kicked off:

  1. Push to master
  2. Docker Cloud kicks off an automated build
  3. It picks up our pre_build hook and using that builds the static resources that are our Jekyll site
  4. Build our website container using our Dockerfile
  5. Test that our build actually works without error
  6. Push our image and make it available.

\o/