I have recently been redeploying all my services running on a mixture of docker and LXD with Kubernetes based deployments. This post will take you through replacing your wordpress install with Jekyll. Compared to wordpress, my blog is now much simpler to maintain, easier to backup, faster and looks better.
You should end up with:
- Your blog will be stored within git
- Commit new posts to the git project
- Within the kubernetes deployment
- A container that checks for new commits in the git project and pulls them down
- A container running jekyll that checks for changes and rebuilds the site
- A container running nginx that serves the built directory
Install Jekyll
The first step is to install Jekyll; the official documentation is pretty good. This can be found here for Ubuntu.
Basically:
1
sudo apt-get install ruby-full build-essential zlib1g-dev
1
2
3
4
echo '# Install Ruby Gems to ~/gems' >> ~/.bashrc
echo 'export GEM_HOME="$HOME/gems"' >> ~/.bashrc
echo 'export PATH="$HOME/gems/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
1
gem install jekyll bundler
Chirpy Theme
I really wanted to use the chirpy theme github. The documentation is focused on using github actions with the developers other project chirpy-starter. I wanted to deploy my blog locally on my own Kubernetes cluster which wasn’t as obvious.
Firstly fork and clone the main github project.
Note: I am mirring it to my local git instance so my URL’s will be different to yours.
1
git clone https://hamgit.monotok.org/hammer/blog-chirpy-jekyll.git -b master --single-branch
I would create a new branch, this is not necessary but it makes it a bit easier to keep in sync with upstream.
1
git checkout -b myblog
Now run the initialise script to remove some directories and files, ready for your own content.
1
bash tools/init.sh
Now we want to update some settings in the _config.ym
. It should be obvious which things you need to change. For example the URL. After you make any changes you want, you can run the blog locally.
1
bash tools/run.sh
This will build the site which generates the static content and copies it to the _site
directory, this is then served on port 4000
. While the script is running it will detect any changes to the files and rebuild for you. This is useful while writing new content.
Wordpress Exporter Plugin
Now we need to install the additional Gems for the official wordpress exporter (Official Docs).
The mysql2
gem needs client package to be installed.
1
sudo apt install mariadb-client
Open the Gemfile and add at the bottom.
1
2
3
4
5
6
#For import from wordpress
gem "safe_yaml"
gem "sequel"
gem "unidecode"
gem "jekyll-import"
gem "mysql2"
To actually install the official wordpress exporter plugin.
1
bundle install
This plugin connects to the database and creates the posts as html pages. The images are not downloaded, however they are saved as links, referencing the original wordpress blog.
So to actually generate the posts, run the import command. You might want to clear your cache before running the below command, see ‘Exporter Issues’ below.
1
jekyll import wordpress --dbname <database name> --host <host ip or dns name> --port <port> --user <username> --password <password> --comments --categories --tags
Exporter Issues
Oembed Cache
The export ran successfully except I got quite a few oembed_cache pages generated in the _posts
folder. I cleared the cache from wordpress via the DB. See clearing oembed_cache. I then ran the above command again which fixed the problem.
Images not downloaded
The exporter plugin does not download the images but the images will still display when you run the blog locally as they are referenced back to the original wordpress site.
Unfortunately it was a manual process to fix, if I had more posts then I would have automated it. Basically download the images into a folder within this project, I chose assets/img/posts/date/
. Then replace the references to the original wordpress site with the local image; for example in html:
1
<p><a href="/assets/img/posts/2017/Screenshot_C_Pointers.png"><img class="size-large wp-image-612" src="/assets/img/posts/2017/Screenshot_C_Pointers.png" alt="" width="1024" height="751"></a></p>
Now you should commit the changes in git. Next we need to look at deployment.
Deployment via Kubernetes
This wasn’t as straightforward as I had hoped, although it works pretty well now. The deployment will use multiple containers with a pod:
- Git-sync will check git for changes and sync to a shared volume
- A custom jekyll container will continuously watch for changes on the shared volume and rebuild if detected
- The last container is standard nginx and will serve the site from the generated content
git-sync
Unfortunately I couldn’t see a prebuilt container for git-sync, so I had to build and publish the image to my own repository. This is pretty simple to do.
You need docker-buildx (You could probably build it without but their standard build uses it). Docker buildx github. I downloaded the binary and placed it in my ~/.docker/cli-plugins
directory.
Clone git-sync project and checkout the latest tag eg: git checkout v4.0.0-rc5
. You then tag the image with that specific version.
1
git clone https://github.com/kubernetes/git-sync.git
Inside the project run.
The registry can be anything that your K8’s cluster is able to access. Eg: Docker Registry or something like gittea.
1
2
make container REGISTRY=<docker public or your own registry> VERSION=4.0.0-rc5
make push REGISTRY=<same as above> VERSION=4.0.0-rc5
Custom Jekyll Container
Now we need a container for jeykll to watch for changes and rebuild the site. Once again this needs to be pushed to a repository that your K8’s cluster can reach. Create a new file in the chirpy git project at the top level.
touch Dockerfile
Add the following contents.
Note: You will need to change the git clone URL to reflect your fork.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
FROM ubuntu:22.04
RUN apt update && \
apt-get install -y ruby-full build-essential zlib1g-dev git libmariadb-dev
RUN useradd -rm -d /home/ubuntu -s /bin/bash -g root -G sudo -u 1000 ubuntu
USER ubuntu
WORKDIR /home/ubuntu
ENV GEM_HOME="/home/ubuntu/gems"
ENV PATH="/home/ubuntu/gems/bin:$PATH"
RUN gem install jekyll bundler
RUN git clone https://hamgit.monotok.org/hammer/blog-chirpy-jekyll.git -b myblog --single-branch
WORKDIR /home/ubuntu/blog-chirpy-jekyll
RUN bundle install
RUN rm -rf /home/ubuntu/blog-chirpy-jekyll
Now build the dockerfile. Once again you will need to change the tag to your repository.
1
2
docker build -t <docker repo>blog-chirpy-jekyll:u2204 .
docker push -t <docker repo>blog-chirpy-jekyll:u2204 .
Kubernetes Deployment Files
I am going to assume you know how to deploy things to Kubernetes etc. This can be in the same git project if you wish, but I have my deployments for K8s in it’s own project.
I did try to get this working with a ‘emptyDir’ storage type however I couldn’t reliably get the second container to read the data git-sync populated it with.
The deployment file.
Apply as usual with kubectl apply -f file
.
You might have to run the git-sync container by itself the first time by commenting out the others to allow it to do an initial git sync. If you don’t then the jekyll container will probably crash as the directories don’t exist.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
apiVersion: apps/v1
kind: Deployment
metadata:
name: blog
spec:
replicas: 1
selector:
matchLabels:
name: blog
template:
metadata:
labels:
name: blog
spec:
containers:
- name: git-sync
image: <your repo>git-sync/git-sync:4.0.0-rc5__linux_amd64
volumeMounts:
- name: tmp-git
mountPath: /git
args:
- --repo=https://<your fork>blog-chirpy-jekyll.git
- --depth=1
- --period=60s
- --link=current
- --root=/git
- --http-metrics
- --http-bind=:9090
- --ref=myblog #your branch you created for your blog
env:
- name: GITSYNC_PASSWORD
valueFrom:
secretKeyRef:
key: PASSWORD
name: gitsync-secrets
- name: GITSYNC_USERNAME
valueFrom:
secretKeyRef:
key: USERNAME
name: gitsync-secrets
securityContext:
runAsUser: 1000
runAsGroup: 1000
- name: jekyll
image: <your repo>/blog-chirpy-jekyll:u2204
workingDir: /git/current
volumeMounts:
- name: tmp-git
mountPath: /git
- name: html
mountPath: /dest
env:
- name: JEKYLL_ENV
value: production
command:
- "bundle"
- "exec"
- "jekyll"
- "build"
args:
- --destination=/dest
- --watch
securityContext:
runAsUser: 1000
runAsGroup: 1000
- name: nginx
image: nginx:1.25.1
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
readOnly: true
readinessProbe:
initialDelaySeconds: 10
periodSeconds: 10
exec:
command:
- ls
- /usr/share/nginx/html/index.html
volumes:
- name: tmp-git
persistentVolumeClaim:
claimName: blog-git-sync
- name: html
emptyDir: {}
The secret:
Encode a string into base64 like this:
echo 'my string' | openssl base64 -A
1
2
3
4
5
6
7
8
9
apiVersion: v1
kind: Secret
metadata:
name: gitsync-secrets
type: Opaque
data:
PASSWORD: base64 encoded password
USERNAME: base64 encoded password
The PVC. The storage-class depends on your cluster distribution. I am running RKE2.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
labels:
application.name: blog
name: blog-git-sync
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path
resources:
requests:
storage: 2G
status: {}
The service.
1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Service
metadata:
name: blog-service
spec:
type: ClusterIP
ports:
- port: 80
selector:
name: blog
The ingress. You will need to adjust accordingly.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
ingress.cert-manager.io/issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
nginx.ingress.kubernetes.io/proxy-body-size: "30M"
name: blog
spec:
ingressClassName: nginx
rules:
- host: blog.monotok.org
http:
paths:
- backend:
service:
name: blog-service
port:
number: 80
path: /
pathType: Prefix
tls:
- hosts:
- blog.monotok.org
- monotok.org
secretName: blog-tls-cert
status:
loadBalancer: {}
Publish a new post
You can publish new posts by simply following the documentation here. Once you have made your changes, commit them in git and push. It should then magically update.
Final Notes
As you have forked the project then you can easily make modifications to the default theme. For example I have:
- Added extra pages down the left side
- Added links to the right side
Hopefully this has been useful.
Comments powered by Disqus.