It's 2017 and my blog was starting to look a little dated so I decided it was time for a face-lift. While I was at it, I overhauled the way I deploy my blog too.
The Old Stack
My old stack was based on Pelican and deployed using Dokku. Pelican is a static site generator written in Python with a vibrant community of themes and plugins - if you aren't using it already you should check it out. Dokku allows you to have a Heroku-like workflow but using Docker. In principle, this sounds good... but after a few years of using it I have to say that I'm not a fan. My main issues are:
-
You publish by
git push
-ing directly to your server somaster
and what's running aren't always in sync. -
All the
build
happens in abuildpack
on the server-side. There is no way to test what it actually does and it failed catastrophically for me a few times. -
Everything was being served over plain old HTTP
The New Stack
Being a container aficionado, I decided that everything should be containerized and running on Docker Cloud. I also wanted HTTPS so I was in need of a certificate. Let's Encrypt to the rescue!
All I needed now was to replace the buildpack
with a Dockerfile
.
Here is what I came up with:
FROM python:3-alpine
RUN apk add --no-cache nginx
RUN pip install pelican markdown "ipython=2.4.1"
ADD . /var/www/blog
WORKDIR /var/www/blog
ARG DEV
RUN pelican content -o output -s pelicanconf.py
RUN cp -f nginx.conf /etc/nginx/nginx.conf
CMD nginx
EXPOSE 80
There are a couple of smart things I'd like to point out here.
- Rebuilds are quick as everything up until the
ADD
instruction is cached - The flag
--build-arg DEV=1
can be used withdocker build
to generate relative URLs for local testing - This image is pretty small at 270MB considering what's packed in. Why
IPython
you may be thinking? That's another blog post in the making... - The
nginx.conf
is very simple. It basically serves/var/www/blog
on port 80. - "Why port 80? That's not HTTPS..." Don't worry. All will be explained in time...
So with image, means of obtaining a certificate, and Docker ID in hand I logged on to Docker Cloud.
Docker Cloud
First thing I had to do was link the private GitHub repo that contains my blog and set up automatic builds. This was really easy to do by simply following the wizard.
I then created a Node Cluster and "brought my own node" to Docker Cloud as I use Mythic Beasts for all of my hosting - they are awesome. I update my DNS to make sure that my blog dtucker.co.uk
pointed to the IP address of my Docker Cloud node. Once this was completed, all I needed to do was define a Stack!
This whole Let's Encrypt thing is pretty complicated, especially when containers are concerned. As certificates expire every 90 days, we need some means of requesting a new one without interaction especially as my mean time to blog post is usually > 90 days. It also requires a magical directory to be served on port 80 for domain validation. This means I'm going to need:
- A container to deal with getting certificates from Let's Encrypt
- A container to serve my blog
- A proxy container so network connections go to the right container!
It just so happens that some smart cookie has solved this proxy problem already and it's really easy to use!
Here is my completed Stackfile:
blog:
autoredeploy: true
environment:
- FORCE_SSL=yes
- 'VIRTUAL_HOST=*,https://*'
image: 'davetucker/blog:latest'
haproxy:
image: 'interaction/haproxy:master'
links:
- blog
- letsencrypt
ports:
- '80:80'
- '443:443'
roles:
- global
volumes_from:
- letsencrypt
letsencrypt:
environment:
- DOMAINS=dtucker.co.uk
- [email protected]
expose:
- '80'
image: 'interaction/letsencrypt:master'
I clicked the "Start" button and then my site was alive and kicking.
Conclusion
When I write a new blog post, I now just git push
and Docker Cloud takes care of the rest.
It builds the blog
container and automatically re-deploys when the new version is created!
Everything is secured with HTTPS courtesy of a Let's Encrypt certificate which is magically renewed so it should never expire.
I also have a great writing/test workflow too!
If you want more content like this, want to know what IPython was for or want to say "Hi!" please leave a comment below or ping me on the social network of your choosing!
@dave_tucker