Kubernetes einfach mit Rancher: Ghost CMS (Part 6)
Kubernetes einfach mit Rancher: Ghost CMS ist der sechste und somit der letzte Part der Kubernetes einfach mit Rancher Serie. In diesem Beitrag beschreibe ich die Installation und die Inbetriebnahme von Ghost.
In Kubernetes einfach mit Rancher: Ghost CMS lernen wir nicht nur die Inbetriebnahme per Helm. Ich demonstriere in diesem Artikel auch ein einfaches Deployment per YAML.
Was ist Ghost
Ghost ist eine moderne Publikationsplattform. Den Anfang machte Ghost mit einer Kickstarter-Kampagne und bewirbte sich als einfache Open-Source Blogging-Plattform-Alternative. Mittlerweile ist man zwar immer noch weit von der Komplexität von WordPress entfernt, aber man ist auch nicht mehr die reine Blogging-Plattform.
Ghost hat sich zu einer interessanten Alternative für verschiedene Zwecke entwickelt. Selbst schmückt man sich mit der Aussage, dass man die Nummer 1 im Headless-NodeJS-CMS-Markt ist – wozu ich überhaupt nichts sagen kann.
Ich verwende Ghost als reine Blogging-Plattform, und das nicht einmal im Team. Daher kann ich zu allem, was Ghost darüber hinaus verspricht, kein bisschen meinen Senf abgeben. Als Blogging-Plattform bin ich mit Ghost sehr zufrieden und kann es ohne Bedenken weiterempfehlen.
So, dieser Artikel soll nur am Rande Ghost behandeln. Das Thema ist die Inbetriebnahme innerhalb von einem Rancher Kubernetes Cluster.
Rancher, Kubernetes, check! Ghost installieren?
In diesem Artikel – Kubernetes einfach mit Rancher: Ghost CMS – installieren wir gemeinsam Ghost einmal über den Katalog (Helm Chart) und zum anderen mit einem Deployment.
Die Variante mit Helm erstellt 2 Pods, einmal für Ghost und zum anderen für die Datenbank (MySQL oder MariaDB). Die Standard-Deployment-Variante dahingegen nur einen Pod.
Requirements
Der aufmerksame Leser sollte bis hier alle vorhergehenden 5 Teile gelesen und umgesetzt haben. Wir brauchen einen RKE Cluster, Let’s Encrypt Cert Manager, Storage und Lust.
Anstatt einer Storage-Lösung wie Gluster, OpenEBS, Longhorn, Ceph etc. kann natürlich – und sollte meiner Meinung nach auch – Managed Storage von Azure etc. verwendet werden. Wir werden ein Volume brauchen.
Ghost bietet die Möglichkeit, E-Mails zu verschicken, um z.B. Team-Mitglieder einzuladen. Sofern diese Funktionalität gewünscht ist, sollten die Mail-Postfach-Anmeldeinformationen vorliegen.
Die Funktionalitäten… Was möchte ich?
Das ist ganz einfach erklärt. Folgende Punkte sind für mich wichtig:
- Automatische Zertifikats-Anforderung
- Ausgehende E-Mails
- Upload-Größe von 50 MB pro Datei
- Betrieb mit SQLite
Mit der Bereitstellung über Helm und den darauf folgenden Nacharbeiten konnte ich die ersten 3 Punkte lösen. Leider war es mir nicht möglich, in dieser Variante auf die SQL-Datenbank zu verzichten. Der Chart hat diese fest eingebunden. Weshalb ich hier eine zweite Variante mit einem Standard-Deployment vorstellen werde.
Ghost in RKE mit Helm
Ghost über Helm bereitzustellen war jedenfalls keine einfache Aufgabe. Nach mehreren Anläufen gelang es mir doch, nur um dann festzustellen, dass ich noch hier und da Anpassungen vornehmen muss.
Nach dieser Erfahrung habe ich alle wichtigen Einstellungen in einer values.yml-Datei festgehalten.
Mit dieser Form der Bereitstellung stellen wir per Helm-Chart einen Pod mit zwei Containern bereit: zum einen unsere Ghost-Software und zum anderen der Datenbank-Container – bei diesem Chart ist es der MariaDB-Container.
Bereitstellung mit Helm
Die Installation kann ganz einfach in Rancher ausgeführt werden. Hierfür reicht es aus, dass der gewiefte Benutzer/Admin in das vorgesehene Projekt wechselt und durch Auswahl von „Apps” zu den Katalog-Apps wechselt und auf „Launch” klickt.
In dieser Ansicht werden alle Charts angezeigt, um die Anzeige einzuschränken kann rechts oben entweder gefiltert oder über die Searchbox die Ergebnismenge eingeschränkt werden.
Wir möchten in die Ghost-Chart, also tippen wir „Ghost” in die Searchbox und klicken auf „View Details”, womit wir in die Chart-Maske wechseln.
In dem „Configuration Options”-Abschnitt gibt man den Namen sowie den vorgesehenen Namespace an. Die Chart-Einstellungen können entweder als Key-Value in der Maske eingegeben oder per YAML bereitgestellt werden.
In diesem Beispiel verwenden wir eine YAML-Datei. Der aufmerksame Benutzer muss die Werte vor dem Anwenden anpassen.
ghostHost: "<blog_url>"
ghostUsername: "<user>"
ghostPassword: "<pass>"
ghostEmail: "<user_mail>"
ghostBlogTitle: "<blog title>"
allowEmptyPassword: "no"
mariadb:
rootUser:
password: "<mariadb_pass>"
master:
persistence:
enabled: "true"
storageClass: "<storage_class>"
accessMode: "ReadWriteOnce"
size: "<mariadb_größe>Gi"
persistence:
storageClass: "<storage_class>"
size: "<storage_größe>Gi"
resources:
requests:
memory: "512Mi"
cpu: "300m"
ingress:
enabled: "true"
certManager: "true"
annotations: [{kubernetes.io/ingress.class: "nginx"}, {certmanager.k8s.io/cluster-issuer: "letsencrypt-prod"}]
hosts:
-
name: "<blog_url>"
path: "/"
tls: "true"
tlsSecret: "<lets_encrypt_cert_secret_name>"
Da ich Beispiele liebe, findet sich unten ein Beispiel, um die Placeholder beispielhaft zu befüllen:
ghostHost: "ghost.ak8s.de"
ghostUsername: "admin"
ghostPassword: "eins2Drei4@Ausrufezeichen"
ghostEmail: "aytac@kirmizi.online"
ghostBlogTitle: "smart blog"
allowEmptyPassword: "no"
mariadb:
rootUser:
password: "40302010"
master:
persistence:
enabled: "true"
storageClass: "longhorn"
accessMode: "ReadWriteOnce"
size: "2Gi"
persistence:
storageClass: "longhorn"
size: "2Gi"
resources:
requests:
memory: "512Mi"
cpu: "300m"
ingress:
enabled: "true"
certManager: "true"
annotations: [{kubernetes.io/ingress.class: "nginx"}, {certmanager.k8s.io/cluster-issuer: "letsencrypt-prod"}]
hosts:
-
name: "ghost.ak8s.de"
path: "/"
tls: "true"
tlsSecret: "ghost-ak8s-de-crt"
Durch Klicken auf „Read from File” kann nun der Inhalt in die Konsole kopiert werden.
Durch Bestätigen – weiter unten – wird nun die Bereitstellung gestartet.
Nacharbeiten… Zertifikat und Routing
Da nun Ghost per Helm installiert ist, können wir gleich loslegen… Nope, können wir nicht. Der Loadbalancer über Helm ist, sagen wir mal, nicht für einen produktiven Einsatz geeignet. Routing funktioniert zwar, aber ohne SSL-Verschlüsselung – was heutzutage Pflicht ist. Die Stellen, an denen wir Hand anlegen müssen, sind nicht viele und können über die Oberfläche getätigt werden.
Neuer Ingress – Layer 7
Die Vorgehensweise für dieses Ergebnis habe ich bereits in einem anderen Beitrag festgehalten: Rancher 2x und Let’s Encrypt. Das Verfahren hier ist analog zu den Informationen in dem verlinkten Beitrag.
Für die Erstellung eines neuen Ingress-Eintrags wechseln wir in der Oberfläche auf „Load Balancing” – achtet bitte darauf, dass hier im selben Projekt gearbeitet wird. Nachdem wir auf „Add Ingress” geklickt haben, erwartet uns die Eingabeoberfläche.
Die Eingaben sollten selbsterklärend sein. Tatsächlich gibt es hier nur einen Punkt zu erwähnen. Das vorhandene Target-Backend löschen wir durch das Klicken auf das Minus-Symbol. Danach klicken wir auf „Add Service” und wählen den Ghost-Service aus. Das war es auch schon.
Nachdem wir diesen Ingress abgespeichert haben, müssen wir nun die zertifikat-relevanten Einträge setzen. Dafür wechseln wir wieder in unseren Ingress – über Edit – und erweitern jeweils SSL/TLS Certificates und Labels & Annotations.
Als nächstes müssen wir das YAML von unserem Ingress bearbeiten und den secretName hinzufügen.
Fertig! Somit wären wir mit der Inbetriebnahme über Helm fertig… nicht wirklich.
Die maximale Upload-Größe
Spätestens wenn wir ein neues Template für unsere Ghost-Umgebung verwenden möchten, laufen wir in diesen Fehler.
Das ist keine Einschränkung von Ghost, sondern kommt von Ingress selber und genau dort müssen wir auch Hand anlegen. Dafür wechseln wir wieder in unsere Load-Balancing-Ansicht und editieren den Ingress.
Die Upload-Größe beeinflussen wir durch eine weitere Annotation. An dieser Stelle legen wir 50 Megabyte als maximale Upload-Größe fest:
nginx.ingress.kubernetes.io/proxy-body-size: 50m
Natürlich hätte man diese Eingabe auch im Kapitel davor – Let’s Encrypt Zertifikats-Anforderung – tätigen können, wobei die Dramatik dahinter untergegangen wäre. Sind wir nun fertig? Leider nein, die ausgehenden E-Mails funktionieren leider nicht automatisch – auch wenn diese in den Values angegeben wurden. Um diese Funktion zu gewährleisten, müssen wir nun unseren Workload editieren. Hierfür wechseln wir in die Workloads-Auflistung und klicken unseren Workload an.
Innerhalb der Workload-Detail-Ansicht müssen wir zuerst die drei Böbberl anwählen und „Edit” klicken. Nachdem die Seite neu geladen hat, können wir unterhalb von „Environment Variables” die benötigten Informationen pflegen. Anschließend bestätigen wir durch das Klicken auf „Upgrade” – weiter unten auf der Seite.
Jetzt sind wir fertig! :).
Ghost in RKE mit Standard-Deployment-Verfahren
In der von mir verwendeten Helm-Chart ist es nicht möglich, Ghost ohne einen Datenbank-Container oder externe Datenbank zu betreiben. Für einen „Ein-Mann-Blog” reicht die Installation mit SQLite vollkommen aus. Wir sparen uns damit einen Container, ein Volume und eine Software, die gewartet werden muss.
Die YAML-Dateien
Die Bereitstellung besteht aus mehreren YAML-Dateien: einem Service, einem Ingress, einem PersistentVolumeClaim und einem Deployment. Unten findet sich ein Beispiel.
apiVersion: v1
kind: Service
metadata:
name: test-ko-ghost
labels:
app: ghost
spec:
ports:
- name: ghost
port: 2368
protocol: TCP
targetPort: 2368
selector:
app: ghost
sessionAffinity: None
type: ClusterIP
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
certmanager.k8s.io/cluster-issuer: letsencrypt-prod
kubernetes.io/tls-acme: "true"
nginx.ingress.kubernetes.io/proxy-body-size: 50m
generation: 4
name: test-ko-ghost
spec:
rules:
- host: ghost.ak8s.de
http:
paths:
- backend:
serviceName: test-ko-ghost
servicePort: 2368
path: /
tls:
- hosts:
- ghost.ak8s.de
secretName: ghost-ak8s-de-crt
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-ko-ghost-volume
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
storageClassName: longhorn
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: test-ko-ghost
labels:
app: ghost
spec:
selector:
matchLabels:
app: ghost
strategy:
type: RollingUpdate
template:
metadata:
labels:
app: ghost
spec:
containers:
- env:
- name: ALLOW_EMPTY_PASSWORD
value: "no"
- name: GHOST_EMAIL
value: aytac@kirmizi.online
- name: GHOST_HOST
value: ghost.ak8s.de
- name: GHOST_PORT_NUMBER
value: "80"
- name: GHOST_PROTOCOL
value: http
- name: GHOST_USERNAME
value: EinUser
- name: mail__from
value: aytac_blog@kirmizi.online
- name: mail__options__auth__pass
value: "40302010"
- name: mail__options__auth__user
value: aytac_blog@kirmizi.online
- name: mail__options__host
value: mail.kirmizi.online
- name: mail__options__port
value: "587"
- name: mail__transport
value: SMTP
- name: url
value: https://ghost.ak8s.de
image: ghost:latest
imagePullPolicy: Always
name: test-ko-ghost
ports:
- containerPort: 2368
name: ghost
protocol: TCP
volumeMounts:
- mountPath: /var/lib/ghost/content
name: test-ko-ghost-volume
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
terminationGracePeriodSeconds: 30
volumes:
- name: test-ko-ghost-volume
persistentVolumeClaim:
claimName: test-ko-ghost-volume
Die Bereitstellung
Die Bereitstellung ist ganz einfach. In dem Ziel-Projekt navigieren wir in die Workloads-Auflistung. An dieser Stelle klicken wir oben auf „Import YAML” und kopieren den Inhalt unserer YAML-Datei hinein.