Ghost Blog on Azure Kubernetes Service (AKS)
This site (cmcrowell.com) is a ghost blog running on Azure Kubernetes Service (AKS). I think there are less expensive ways of hosting a website, but that's a story for another time 😉
One of the niceties of running this way, is that updating ghost is just a matter of changing the image in the deployment manifest. Here's the TL:DR:
alias k=kubectl
k edit deploy ghost
# replace ghost:4.41.0-alpine with ghost:5.2.2-alpine
The more convoluted part I tend to do is building my own image and pushing that image to Azure Container Registry (ACR). Why do I do this? Well, for two reasons. First, it allows me to customize my ghost blog to include the casper theme and add the ability to comment below each article (scroll down and you can see for yourself). Second, it allows me to control the version and release, instead of just taking the latest tag (which you should never do), and automatically updating to the current release. Nonetheless, here's how I do it:
Building Your Image
The first gotcha is that when you clone down the repo for ghost at https://github.com/TryGhost/Ghost, they have symlinked the ghost/core/content/themes/casper
directory to another tagged release, which creates a problem. The problem comes when you build your image via dockerfile, the build process fails because the files don't exist (hence, the symlinks).
For this, I simply click on the link in GitHub (picture above), taking me to the proper tagged release of the casper theme. Then I change to the cloned directory and clone the casper release inside there. It looks something like this:
We can see that this directory now contains the files needed to build our image.
In the themes
directory, I added two things before creating my Dockerfile
and building my image. First, in the casper/post.hbs
file, I added the following in order to allow comments at the end of each post (like this one):
<div id="disqus_thread"></div>
<script>
var disqus_config = function () {
this.page.url = "{{url absolute="true"}}";
this.page.identifier = "ghost-{{comment_id}}"
};
(function() {
var d = document, s = d.createElement('script');
s.src = 'https://atxlinux.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
Also in the themes
directory, I created a file named config.production.json
and it contained the following:
{
"url": "http://localhost:2368",
"server": {
"port": 2368,
"host": "0.0.0.0"
},
"database": {
"client": "sqlite3",
"connection": {
"filename": "/var/lib/ghost/content/data/ghost.db"
}
},
"mail": {
"from": "'Chad Crowell' <chad@mg.cmcrowell.com>",
"transport": "SMTP",
"options": {
"host": "smtp.mailgun.org",
"port": 587,
"auth": {
"user": "postmaster@mg.cmcrowell.com",
"pass": "e451ea22f95-2a9a428a-e8281288"
}
}
},
"logging": {
"transports": [
"file",
"stdout"
]
},
"process": "systemd",
"paths": {
"contentPath": "/var/lib/ghost/content"
}
}
The addition of the mail configuration allows Ghost to send transactional emails such as user invitations, password resets, member signup, and member login links. Read more about custom configuration files like this one here: https://ghost.org/docs/config/
Now it's time to create our Dockerfile
!! This is a pretty simple Dockerfile, in that it takes the entire casper
directory (in the current directory) and copies it to /var/lib/ghost/current/content/themese/casper
inside the container. It also takes config.production.json
and copies it into /var/lib/ghost/config.production.json
inside the container. files from your current directory and copies them into the container. It looks like this:
FROM ghost:5.60-alpine
# copy themes/config to container
COPY casper /var/lib/ghost/current/content/themes/casper
COPY config.production.json /var/lib/ghost/config.production.json
Finally, we can build our image and push to our Azure Container Registry. If you don't already have a container registry, create one using the following commands, assuming you have azure-cli installed.
# set a few environment variables
# e.g. name your resource group "container-rg"
# e.g. name your container registry atxlinux
ACR_RESOURCE_GROUP=container-rg
ACR_NAME=atxlinux
# create a resource group in the east US region
az group create --name $ACR_RESOURCE_GROUP --location eastus
# create a container registry
az acr create --resource-group $ACR_RESOURCE_GROUP --name ACR_NAME --sku Basic --admin-enabled true
Learn more about creating a private azure container registry here.
Luckily, we can build our image and push to our registry in one single command by using Azure ACR tasks.
az acr build --image atxlinux-ghost:v1 --registry $ACR_NAME --file Dockerfile .
Congratulations! 🎉 Now you've built and pushed your new customized ghost image to ACR (Azure Container Registry).
Just like in the TL:DR section above, we can edit our deployment and change the image tag.
Let's recap!
- Clone the ghost github repo
- Clone the casper theme github repo inside of the themes folder
- Modify the
post.hbs
file - Create the
config.production.json
file - Create a
Dockerfile
- Build and push your new image to ACR
- Edit the ghost deployment in AKS
If you liked this article, please let me know by commenting below. If you would like to see how I built my ghost blog on AKS, please let me know as well.
Thanks for reading! 🤠