WordPress ist das meistgenutzte Content-Management-System im Internet. Warum du es benutzen und - nach Möglichkeit - selbst hosten solltest, kannst du hier nachlesen.

In diesem Tutorial lernst du, wie du WordPress auf deinem Azure Kubernetes Service Cluster manuell zum Laufen bringst. Alternativ zu dieser manuellen Methode kannst du natürlich auch das Helm Chart von Bitnami verwenden um WordPress automatisch zu deployen.

Viel Spaß!

Schritt #1 vorbereiten des Cluster

Den Kubernetes Cluster sowie das Tool kubectl solltest du schon eingerichtet haben. Schau dir dazu am besten den Beitrag über das Anlegen eines AKS Clusters an.

Ferner gehe ich in diesem Beitrag davon aus, dass du bereits einen Ingress Controller mit Certificate Issuer einsetzt. Was das ist, kannst du hier nachlesen.

Um deinen Cluster gut zu strukturieren, solltest du für dieses Projekt einen Namespace anlegen. In diesem Fall bietet sich der Name “wordpress-ns” an. Dazu legst du eine neue Datei “namespace.yaml” an.

apiVersion: v1
kind: Namespace
metadata:
  name: wordpress-ns

Schritt #2 Bereitstellen eines persistenten Volumes

Um ein persistentes Volume im Deployment verwenden zu können musst du es zunächst anlegen. Das Erstellen von persistenten Volumes ist auf Kubernetes Clustern nicht ganz einfach da hierfür mehreren Komponenten angelegt werden müssen.

Für dieses Projekt reicht es, wenn du zwei Ansprüche auf persistente Volumes auf einer managed Disk erzeugst. Das eine für den WordPress Container und das andere für den MySQL Container.

Lege eine neue Datei “persistentVolumeclaims.yaml” an und kopiere folgenden Inhalt hinein.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: wordpress-pv-cm
  labels:
    app: wordpress
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
  storageClassName: default
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-cm
  labels:
    app: wordpress
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
  storageClassName: default

Ich habe die Speicherklasse default mit 20 Gigabyte verwendet. Dies ist eine spezielle Speicherklasse für AKS.

Damit du besser verstehst, was das bedeutet und um einen kurzen Überblick zu geben, gehe ich noch auf die einzelnen Komponenten von persistenten Volumes bei Kubernetes ein. Dieses Hintergrundwissen kann sinnvoll sein, wenn du darüber nachdenkst auf einen anderen Cloudanbieter zu wechseln.

Wenn dich das nicht interessiert dann mach einfach bei Schritt #3 weiter.

Was ist eine Speicherklasse (storage class)

In der Speicherklasse wird die Art des Volumes definiert. Kubernetes weiß anhand der Speicherklasse wie es das jeweilige Volume Claim handhaben soll. Typische Speicherklassen sind der lokale Speicher, ISCI, Cloudproviderspezifische Storages oder einfache Netzwerkfreigaben über NFS.

Glücklicherweise bietet Microsoft auf dem AKS Cluster vordefinierte Speicherklassen an mit denen Kubernetes das Anlegen der jeweiligen Azure Ressourcen übernimmt. Diese umfassen aktuell die managed Disks (default / managed-premium) sowie Azure Storage per SMB Schnittstelle (azurefile / azurefile-premium).

Dabei ist anzumerken, dass bei diesen 4 vordefinierten Speicherklassen die Storages von Kubernetes zwar angelegt, aber nicht wieder gelöscht werden. Da dies Fluch und Segen zugleich sein kann, solltest du es auf jeden Fall im Hinterkopf behalten.

Mehr zu dem Thema Speicherklassen auf Azure findest du in den Microsoft Docs. Informationen über Speicherklassen im Allgemeinen findest du in der offiziellen Kubernetes Dokumentation.

Was ist ein persistentes Volume (persistent volume)

Persistente Volumes sind dazu da, um Daten zwischen Pods auszutauschen. Sie bleiben bestehen auch, wenn der dazugehörige Pod gelöscht wird. Ähnlich wie Nodes sind sie Clusterressourcen.

Du kannst ein persistentes Volume manuell erzeugen oder - wie oben - über eine Storage Klasse dynamisch erzeugen lassen. Beim dynamischen Erzeugen übernimmt Kubernetes die Details der Implementation direkt von der Storage Class.

Was ist ein Anspruch auf ein persistentes Volume (persistent volume claim)

Mit Ansprüchen auf persistenten Volumes grenzt Kubernetes in abstrakter Form Storage Ressourcen ab. Du kannst dir das so vorstellen, dass sich der Anspruch zu einem persistenten Volume so verhält wie ein Ordner zu einem Dateisystem.

Dieser Aufbau hat den Hintergrund, dass Appentwickler nicht unbedingt wissen müssen wie ein Volume im Hintergrund implementiert ist. Es reicht, wenn sie einen Anspruch auf ein persistentes Volume stellen und sich darauf verlassen können, dass es funktioniert.

Schritt #3 Anlegen der Deployments

In diesem Projekt brauchst du nur zwei Container. Einen WordPress Container für das Frontend und einen MySQL Container für das Backend.

Erstellen des MySQL Deployments

Ich verwende hier MySQL 5.7. Du kannst natürlich die jeweils aktuellste Version benutzen. Achte aber darauf, dass nicht alle WordPress Versionen mit allen MySQL Versionen funktionieren. Genauere Informationen hierzu findest du bei WordPress.com.

Für das MySQL Deployment legst du eine neue Datei “mysqldeployment.yaml” an.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress-mysql
  labels:
    app: wordpress
spec:
  selector:
    matchLabels:
      app: wordpress
      tier: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: wordpress
        tier: mysql
    spec:
      containers:
        - image: mysql:5.7
          name: mysql
          args:
            - "--ignore-db-dir"
            - "lost+found"
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: wordpresssecret
                  key: MYSQL_ROOT_PASSWORD
          ports:
            - containerPort: 3306
              name: mysql
          volumeMounts:
            - name: mysql-persistent-store
              mountPath: "/var/lib/mysql"
      volumes:
        - name: mysql-persistent-store
          persistentVolumeClaim:
            claimName: mysql-pv-cm

Dieses Deployment verwendet das eben erstellte Persistent Volume Claim mysql-pv-cm sowie ein Secret das du in Schritt #6 erstellen wirst.

Erstellen des WordPress Deployments

Der WordPress Container greift ebenfalls auf das Secret zu und bekommt das andere Persistente Volume Claim zugewiesen. Falls du noch weitere Veränderungen am Container vornimmst, solltest du darauf achten, dass in diesem Beispiel nur der Ordner “/var/www/html” persistent gespeichert wird.

Erstelle eine Datei mit dem Namen “wordpressdeployment.yaml”.

apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  selector:
    matchLabels:
      app: wordpress
      tier: frontend
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: wordpress
        tier: frontend
    spec:
      containers:
        - image: wordpress:5.7
          name: wordpress
          env:
            - name: WORDPRESS_DB_HOST
              value: mysqlservice:3306
            - name: WORDPRESS_DB_USER
              value: root
            - name: WORDPRESS_DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: wordpresssecret
                  key: MYSQL_ROOT_PASSWORD
          ports:
            - containerPort: 80
              name: wordpress
          volumeMounts:
            - name: wordpress-persistent-storage
              mountPath: "/var/www/html"
      volumes:
        - name: wordpress-persistent-storage
          persistentVolumeClaim:
            claimName: wordpress-pv-cm

Schritt #4 Anlegen der Kubernetes Services

In diesem Projekt benötigst du zwei Services. Der Erste für MySQL auf Port 3306. Dieser braucht keine ClusterIP da du ihn mit dem Label ansprichst. Der Zweite für WordPress mit Port 443 und Port 80. Diesen Service benötigst du im nächsten Schritt beim Ingress Regelsatz.

Um die Services anzulegen, erstellst du eine Datei mit dem Namen “services.yaml”.

apiVersion: v1
kind: Service
metadata:
  name: mysqlservice
  labels:
    app: wordpress
    tier: mysql
spec:
  ports:
    - port: 3306
  selector:
    app: wordpress
  clusterIP: None
---
apiVersion: v1
kind: Service
metadata:
  name: wordpress
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: 80
      name: http
      protocol: TCP
    - port: 443
      targetPort: 443
      name: https
      protocol: TCP
  selector:
    app: wordpress
    tier: frontend

Schritt #5 Anlegen von Ingressregeln

Der Ingress Controller benutzt Ingressregeln, um einkommende Datenströme weiterzuleiten.

Folgende Regeln leiten alle Anfragen die auf diese URL gehen zum Service wordpress den du im vorherigen Schritt konfiguriert hast. Vor dem Weiterleiten wird das SSL Handling vom Ingress Controller übernommen, sodass der WordPress Container nur auf Port 80 lauschen muss.

Anstelle von ENTERURLHERE musst du natürlich deine eigene URL eingeben.

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: wordpress-ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /$1
    nginx.ingress.kubernetes.io/use-regex: "true"
    cert-manager.io/cluster-issuer: letsencrypt
spec:
  tls:
  - hosts:
    - ENTERURLHERE
    - www.ENTERURLHERE
    secretName: tls-secret
  rules:
  - host: ENTERURLHERE
    http:
      paths:
      - backend:
          serviceName: wordpress
          servicePort: 80
        path: /(.*)
  - host: www.ENTERURLHERE
    http:
      paths:
      - backend:
          serviceName: wordpress
          servicePort: 80
        path: /(.*)

Weil ich die Adresse sowohl mit als auch ohne “www” benutzen möchte verwende ich hier 2 Regeln. Eine mit www und eine ohne. WordPress leitet in beiden Fällen auf die gewünschte Variante selbstständig um.

Schritt #6 Deployen der ganzen App

Jetzt fehlt nur noch eine Passwortdatei für den MySQL Container. Erstelle dazu die Datei “.password.txt”.

MYSQL_ROOT_PASSWORD=HIERPASSWORTEINGEBEN

Hast du die ganzen Dateien erstellt müssen sie nur noch in den Cluster Deployt werden. Dazu kannst du folgendes Windows Bat Script verwenden.

kubectl apply -f .\namespace.yaml
for /f "delims=" %%a in (.password.txt) do set %%a
kubectl delete secret wordpresssecret --ignore-not-found -n wordpress-ns
kubectl create secret generic wordpresssecret --from-literal=MYSQL_ROOT_PASSWORD=%MYSQL_ROOT_PASSWORD% -n wordpress-ns
kubectl apply -f .\storageclass.yaml -n wordpress-ns
kubectl apply -f .\persistentvolumeclaims.yaml -n wordpress-ns
kubectl apply -f .\services.yaml -n wordpress-ns
kubectl apply -f .\mysqldeployment.yaml -n wordpress-ns
kubectl apply -f .\wordpressdeployment.yaml -n wordpress-ns
kubectl apply -f .\wordpressingress.yaml --namespace wordpress-ns


Konnte ich helfen? Ich freue mich über einen Drink! 💙