topics: docker (post)

Github Pages Alpine

Overview

Following on from an init blog post where I got up and running with the Github Pages Gem using a local Docker environment, I found I wanted to start blogging from my 12" macbook. The macbook has a 500GB SSD and with the Starefossen docker-github-pages Dockerfile building to an image over 845MB in size, I decided to author a new Dockerfile, minimising the image size as much as I could.

Creating the Dockerfile

The Starefossen Dockerfile I used previously is descended from Debian Jessie which has an uncompressed image size of 51.4 MB. I decided to try using Alpine Linux as a base, weighing in at only 5MB. Keen to avoid reinventing the wheel if possible, I found madducci docker-github-pages, pulled it down and tried it out. Whilst the madduci alpine-github-pages Dockerfile proved functionally sufficient, the built image weighed in at 186.4MB uncompressed. I decided to use it as a reference and see if there were any optimisations I could add.

Firstly I added an environment variable declaring the version of the Github Pages Gem I want to use. Reviewing the list of Github Pages dependencies and versions I noticed that the Starefossen image I was using previously runs an out of date version of the Github Pages gem.

ENV GITHUB_GEM_VERSION 111

I then moved all dependencies required to build the Github Pages Ruby Gem into an environment variable, to facilitate easier reading and tidier apk add and apk del statements. Apk provides a namespacing option that allows to install packages under a namespace. This namespace mechanism also allows for all packages in a namespace to be removed with just the namespace as an argument, e.g:

apk add --virtual .build-deps gcc g++ make ...
apk del .build-deps

On this occasion the environment variable approach looked quicker and easier to read to me.

ENV BUILD_DEPS \
    gcc \
    g++ \
    make \
    curl \
    bison \
    ca-certificates \
    tzdata \
    ruby-dev \
    glib-dev \
    libc-dev \
    ruby-bundler \
    ruby-irb

Wrapping the commands necessary to install the Github Pages Gem (which pulls in Jekyll as a dependency) in a single RUN instruction limits the number of layers in the final image and so aids in minimising image size.

RUN apk add --no-cache $BUILD_DEPS ruby && \
    echo 'gem: --no-document' > /etc/gemrc && \
    gem install github-pages:${GITHUB_GEM_VERSION} && \
    apk del $BUILD_DEPS && \
    rm -rf /usr/lib/ruby/gems/2.3.0/cache && \
    mkdir -p /usr/src/app

Apk includes a new –no-cache option as of Aline Linux 3.3. The –no-cache option removes the need to run an apk update and remove any apk cache files. Gem cache files are removed as I do not expect to want to use Gem in the container after the image is built.

Finally I decided on running Jekyll as I would ordinarily as a convenience. The Docker CMD reference states “The main purpose of a CMD is to provide defaults for an executing container” so this seemed appropriate.

CMD jekyll serve -d /_site --incremental -H 0.0.0.0 -P 4000

The Result

richardjennings/github-pages-alpine at 21MB compressed and 57.99MB uncompressed. That seems a little more reasonable.