This is second article regarding ghost blog.
As annoying as is upgrading Ghost Blog is a must due to security patches and functional upgrades. This is especially true if you're self hosting it.
In previous article I was describing how to setup Ghost with Kubernetes on DigitalOcean. This time we're upgrading from SQLLite 3 to MYSQL 8, still running on single node. Although this configuration could easily be upgraded to multiple replicas.
I paid my price :) Starting from 0 members on 02/01/2023. I did backup the whole folder with SQLite DB but I'd have to go in with my bare hands and pluck that data out. I'm cutting my losses in favor of my time.
If you're setting up Ghost for the first time check this blog post first: https://igor.technology/installing-ghost-on-digitalocean-with-kubernetes/
If you'd like to setup newsletter emailing with Mailgun check this article: https://igor.technology/ghost-blog-setting-up-environment-variables-for-email-signup/
The process is as follows:
- Backup and manual backup instructions. I recommend you do both or at minimal manual backup
- Create mysql kubernetes deployment files and deploy to your cluster.
- Update deployment file of ghost kubernetes to utilize the mysql 8 from now on.
- Import all the exported content (if you have a new PersistentVolumeClaim restore the folders and files from your backup).
I'd also recommend to tar the complete
content folder. The way to access the pod from kubectl is:
kubectl exec -ti ghost-blog-podname /bin/bash cd /var/lib/ghost tar cvf ghost_backup.tar.gz content/
Then copy the file to local computer:
kubectl cp ghost-blog-podname:/var/lib/ghost/ghost_backup.tar.gz .
Prerequisite: Create Kubernetes
secret.yaml and deploy to kubernetes:
apiVersion: v1 kind: Secret metadata: name: ghost-secrets namespace: default type: Opaque stringData: password: mypass url: https://mydomain.com mail_from: My Name <firstname.lastname@example.org> mailgun_username: email@example.com mailgun_password: mypassword
kubectl create -f secret.yaml
MySQL8 Kubernetes deployment
The mysql8 creates a new service, volume and deployment.
apiVersion: v1 kind: Service metadata: name: ghost-mysql namespace: default labels: app: ghost spec: ports: - port: 3306 selector: app: ghost tier: mysql clusterIP: None --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: blog-mysql namespace: default spec: accessModes: - ReadWriteOnce resources: requests: storage: 10Gi storageClassName: do-block-storage --- apiVersion: apps/v1 kind: Deployment metadata: name: ghost-mysql namespace: default labels: app: ghost spec: selector: matchLabels: app: ghost tier: mysql strategy: type: Recreate template: metadata: labels: app: ghost tier: mysql spec: containers: - image: mysql:8.0.31-debian name: mysql env: - name: MYSQL_DATABASE value: "ghost" - name: MYSQL_USER value: "ghost" - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: ghost-secrets key: password ports: - containerPort: 3306 name: mysql volumeMounts: - name: mysql-persistent-storage mountPath: /var/lib/mysql volumes: - name: mysql-persistent-storage persistentVolumeClaim: claimName: blog-mysql
Deploy MySQL8 onto your cluster:
kubectl apply -f ghost-mysql.yaml
Check if everything is working by entering pods console:
kubectl exec -ti ghost-mysql-xxxx /bin/bash. Login to with mysql client:
mysql -u root -p mypassword
Modify Ghost Blog deployment
piVersion: apps/v1 kind: Deployment metadata: name: blog labels: app: blog spec: replicas: 1 selector: matchLabels: app: blog template: metadata: labels: app: blog spec: containers: - name: blog image: ghost:5.26.4 imagePullPolicy: Always ports: - containerPort: 2368 env: - name: url valueFrom: secretKeyRef: name: ghost-secrets key: url - name: database__client value: mysql - name: database__connection__host value: ghost-mysql - name: database__connection__user value: root - name: database__connection__password valueFrom: secretKeyRef: name: ghost-secrets key: password - name: database__connection__database value: ghost - name: mail__transport value: SMTP - name: mail__from valueFrom: secretKeyRef: name: ghost-secrets key: mail_from - name: mail__options__service value: Mailgun - name: mail__options__auth__user valueFrom: secretKeyRef: name: ghost-secrets key: mailgun_username - name: mail__options__auth__pass valueFrom: secretKeyRef: name: ghost-secrets key: mailgun_password volumeMounts: - mountPath: /var/lib/ghost/content name: content volumes: - name: content persistentVolumeClaim: claimName: blog-content
The Ghost Blog deployment assumes you've already had a PersistentVolumeClaim where all the ghost blog files live. So I'm excluding it from here.
Importing the data
After all is done importing the exported JSON should restore all prior written articles back. All we have left to do is to re-import the design and we're done.
In case there are no pictures/files/media in the articles you'd have to copy backed up files under folder