Sculpin and Docker

I've been running this blog on Sculpin for quite a while now, and I'm quite happy with how that works. We've been in a process of migrating some of our websites off a standard VPS towards a setup with Docker, Gitlab CI and Rancher. I've now migrated some websites, which is relatively easy, but most of those sites were dynamic PHP websites. Migrating a site that generates static HTML and running that is a slightly different thing. Here's how I ended up doing it.

The old setup

First, let me quickly describe my old setup. I had set up Sculpin on my webserver and put only the blogposts into a Git repository that was hosted on Github. After committing and pushing a new blogpost, I'd manually run a shell command that would generate the new static version:

ssh -t user@domain 'cd /var/www/vhosts/leftontheweb.com/site/source/_posts;git pull origin master;cd /var/www/vhosts/leftontheweb.com/site;php /var/www/vhosts/leftontheweb.com/site/bin/sculpin.phar generate --env=prod --url=http://leftontheweb.com'

This worked, but would be a bit harder with a new setup. Besides, why would I want to do it manually when I could have it all be done automatically after pushing a new blogpost?

The new setup

First, let's have a look at the global setup that we have:

  • Gitlab for Git repository hosting, we're using Gitlab pipelines to build and push changes
  • We've got droplets on Digital Ocean which contains our Docker/Rancher setup
  • Our production setup is managed by Rancher

So once we push some changes to master (I mean: merge a merge request) a Gitlab pipeline is triggered that builds the Docker container, and pushes the new container to Rancher. All of a sudden, the website is updated.

Now that we've got that basic setup described, let's have a look at how I've set this up with Sculpin.

My initial attempt

In my initial attempt I started with the base Nginx container and started working from there.

FROM nginx

I started running into some issues with installing PHP, so I decided to approach it from the opposite side.

My second attempt

I started over by using the base PHP7 container

FROM php:7.0-cli

So, first things first: I'll need to install Nginx, because eventually I need to serve my static website to anyone wanting to visit my site.

RUN apt-get update
RUN apt-get install -y nginx

For installing Sculpin I'll need Git and the Zlib library, so I'll also install that.

RUN apt-get install -y git
RUN apt-get install -y zlib1g-dev && docker-php-ext-install zip

Now, let's install Composer so I can use that for installing Sculpin.

ADD https://getcomposer.org/installer /tmp/composer-installer.php
RUN php /tmp/composer-installer.php --install-dir=/usr/local/bin --filename=composer && \
    rm /tmp/composer-installer.php

OK, all prerequisites are installed, let's install Sculpin.

RUN git clone https://github.com/sculpin/sculpin.git /usr/lib/sculpin && \
    cd /usr/lib/sculpin && \
    composer install && \
    ln -s /usr/lib/sculpin/bin/sculpin /usr/local/bin/sculpin

Now I have a basic Sculpin install in /usr/lib/sculpin. I now want to customize that installation with my own custom information. I've structured my Git repository in such a way that I can easily copy my custom configuration into this base installation. My repository layout is:

/app
/posts
/source

app/ contains a config/ directory that contains the Sculpin configuration files (sculpin_kernel.yml which contains the URL structure and sculpin_site.yml which contains some basic site information, my Google Analytics ID and the Disqus configuration).

posts/ contains all my blogposts. This is basically what I imported from my old Git repository. All the blogposts are in here with any images that may be needed for the blogposts.

source/ is my own source directory for Sculpin. It contains my theme and custom pages. As we'll see later, this is also where the contents of posts/ will end up being placed, but I wanted the blogposts to be more easily accessible, so I've seperated the posts/ directory.

So, given that structure, I can now copy the contents of those directories to my container.

COPY app /usr/lib/sculpin/app
COPY source /usr/lib/sculpin/source
COPY posts /usr/lib/sculpin/source/_posts

I copy the app/ directory to the Sculpin installation, I copy source/ to the Sculpin directory, and now I copy the posts to the _posts/ to the source/ directory. Now I've got everything I need to generate the static website using Sculpin.

RUN cd /usr/lib/sculpin && bin/sculpin generate --env=prod --url=https://leftontheweb.com

This will call Sculpin to generate the new static version of my blog. The new version is generated in the output_prod/ directory in my Sculpin installation. Of course, this is not the Nginx document root, so I need to make sure I can expose the static site using Nginx.

RUN rm -rf /var/www/html
RUN ln -s /usr/lib/sculpin/output_prod /var/www/html

Firstly, I remove the default Nginx document root. After that, I create a symlink to the output_prod/ directory. Now I can serve my static site. The only thing that is left is to ensure Nginx gets started.

CMD ["nginx", "-g", "daemon off;"]

This starts Nginx and makes sure the site is now being served. Everything is up and running!

Credits

I need to give some credits of course, because I have used some sources for inspiration. First of all, I used the gitlab runner sculpin to check some of steps in the Dockerfile in my second attempt. Also, my standard "HELP! IT NO WORK!" person Mike gave me some insights in to how to solve some of the problems I encountered along the way.