Trying things till they work!

Back in 2017/2018 I took the Linux Foundation course for Certified Kubernetes Admin aka LFS258 and in one of the labs, the instructor did a lab where he deployed Ghost, a minimal blog site.

A few years later, K8s has caught fire, and Ghost 3 is better than ever, so it was a natural fit to run in my lab, where I could blog and host apps, serving initially as a place for me to share what I'm kludging together, but also providing me a nice playground to grow my skills.

As you can see, I have purchased a beautiful theme called NURUI by , and i look forward to exploring what I can do to push myself to try some new things this year!

I had looked at using bitnami's stable helm chart to deploy Ghost, but i couldn't get ghost to come up, so I simplified, opting for taking the Ghost docker image and configuring the standalone deploy with sqlite using manifests:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ghost
  labels:
    app: ghost
    region: milton
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ghost
      region: milton
  template:
    metadata:
      labels:
        app: ghost
        region: milton
    spec:
      containers:
        - name: ghost
          image: ghost:3.2.0-alpine
          imagePullPolicy: IfNotPresent
          env:
            - name: url
              value: https://mattymo.io/
          ports:
            - name: ghost
              containerPort: 2368
          volumeMounts:
          - mountPath: /var/lib/ghost/content/
            name: ghost-data
      volumes:
      - name: ghost-data
        persistentVolumeClaim:
          claimName: ghost-data
ghost-deployment.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: ghost-data
  labels:
    app: ghost
    region: milton 
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 50Gi
ghost-pvc.yaml
---
apiVersion: v1
kind: Service
metadata:
  name: ghost
  namespace: ghost
  labels:
    app: ghost
    region: milton
spec:
  #type: LoadBalancer
  type: ClusterIP
  ports:
    # the port that this service should serve on
  - protocol: TCP
    port: 2368 
    targetPort: 2368
  selector:
    app: ghost
    region: milton

# Expose Node Port direct to ghost
#
#---
#apiVersion: v1
#kind: Service
#metadata:
#  name: ghost 
#  labels:
#    app: ghost
#    region: milton
#spec:
#  type: NodePort 
#  selector:
#    app: ghost 
#    region: milton
#  ports:
#    - name: ghost
#      port: 2368
#      nodePort: 2368
ghost-service.yaml

Deploying Ghost was simple.

I started out serving on http and using node port to ensure the port-forward through my ISP router was working. (see commented block in ghost-service.yaml)

Then I turned my attention to deploying Nginx Ingress. Again I hoped to use the HELM easy button, but opted again for manifest approach using the "Bare Metal" docs from Nginx.

https://kubernetes.github.io/ingress-nginx/deploy/

#https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.26.2/deploy/static/provider/baremetal/service-nodeport.yaml
#
apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
  externalTrafficPolicy: Local
  type: NodePort
  ports:
    - name: http
      port: 80
      nodePort: 80
      targetPort: 80
      protocol: TCP
    - name: https
      port: 443
      nodePort: 443
      targetPort: 443
      protocol: TCP
  selector:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

I decided to update MicroK8s to set a Node Port range that would allow the Ingress to own 80 and 443, so before I could deploy the service, I had to ensure to update MicroK8s kube-apiserver start args:

# update kube-apiserver
sudo vi /var/snap/microk8s/current/args/kube-apiserver

#add the following line to set node port range
--service-node-port-range 1-9999

#restart the kube-apiserver
systemctl restart snap.microk8s.daemon-apiserver.service
kubectl -n ingress-nginx get svc
NAME            TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                 AGE
ingress-nginx   NodePort   10.152.183.23   <none>        80:80/TCP,443:443/TCP   7d12h

Once the ingress was properly deployed and was exposing http and https ports, I then prepared my SSL certs using this Nginx guide.

Once the tls secrets were loaded into MicroK8s, I deployed the ingress.

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ghost-ingress
  labels:
    app: ghost
    region: milton
  namespace: ghost
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
spec:
  tls:
  - hosts:
    - mattymo.io
    secretName: tls-secret
  rules:
  - host: mattymo.io 
    http:
      paths:
      - path: /
        backend:
          serviceName: ghost
          servicePort: 2368

A little bit of DNS magic later with Namecheap and Ghost on MicroK8s "on-prem" was live!