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 so
masterand what's running aren't always in sync.
buildhappens in a
buildpackon 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
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
ADDinstruction is cached
- The flag
--build-arg DEV=1can be used with
docker buildto generate relative URLs for local testing
- This image is pretty small at 270MB considering what's packed in. Why
IPythonyou may be thinking? That's another blog post in the making...
nginx.confis very simple. It basically serves
/var/www/blogon 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.
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 - EMAILfirstname.lastname@example.org expose: - '80' image: 'interaction/letsencrypt:master'
I clicked the "Start" button and then my site was alive and kicking.
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!