Postgres in Kubernetes - Automatisierung mit dem Postgres Operator

Post image

“Persistence is hard” - als Kubernetes Administrator ist man sich dieser Tatsache bewusst. Datenbanken sind ein zentraler Bestandteil vieler Anwendungen und müssen zuverlässig und sicher betrieben werden. Gerne hätte man Unterstützung, insbesondere wenn man viele Instanzen erstellen und verwalten muss. Die Automatisierung von Datenbanken in Kubernetes ist eine Herausforderung, die mit Operatoren gelöst werden kann. In diesem Artikel zeigen wir am Beispiel des “Crunchy Data Postgres Operator” , wie das gelingen kann.

Architektur

Das Ziel des Postgres-Operators von Crunchy Data (PGO) ist es, eine schnelle und unkomplizierte Möglichkeit zu bieten, Anwendungen sowohl in Entwicklungs- als auch in Produktionsumgebungen mit PostgreSQL zu betreiben. Das klingt interessant :-)

Um besser zu verstehen, wie PGO dies ermöglicht, sollte man sich die Architektur des Operators ansehen.

PGO Architektur (Quelle: CrunchyData)
PGO Architektur (Quelle: CrunchyData)

Der Crunchy PostgreSQL Operator erweitert Kubernetes um eine höhere Abstraktionsebene für die einfachere Erstellung und Verwaltung von PostgreSQL-Clustern. Er nutzt „Custom Resources“ und definiert spezielle Custom Resource Definitions (CRDs), um sich selbst und die erzeugten PostgreSQL-Cluster zu steuern.

PGO selbst läuft als Kubernetes-Deployment mit einem einzigen Container, dem Operator. Der Operator verwendet dafür Kubernetes-Controller, die sämtliche Ereignisse auf Kubernetes-Ressourcen (Jobs, Pods) sowie auf den Custom Resources (PostgresCluster, PGUpgrade) überwachen, und den gewünschten deklarierten Zustand rekonsilieren.

Der PGO ist sehr leistungsfähig und kann hochverfügbare Cluster mit mehreren Replikas, Backup-Optionen (lokal & S3) sowie Monitoring und Connection Pooling bereitstellen.

Installation

Der Start fällt dank der sehr guten Dokumentation leicht. Der Quickstart Guide führt durch die Installation des Postgres Operators bis hin zur Bereitstellung der ersten Postgres-Datenbank.

Die einzige Voraussetzung ist ein Kubernetes-Cluster, der mit kubectl erreichbar ist.

Der Operator wird mit Helm oder kustomize installiert. Dafür sind nur wenige Schritte erforderlich:

Beispiel-Repository mit Standard-Konfigurationen klonen:

git clone https://github.com/CrunchyData/postgres-operator-examples.git
cd postgres-operator-examples

Im Beispiel-Repo sind verschiedene Beispiel-Konfigurationen für das Deployment des Operators und von Datenbanken via Helm oder Kustomize enthalten.

Installation des Operators im Namespace “postgres-operator” per Kustomize erfolgt mit:

kubectl apply -k kustomize/install/namespace
kubectl apply --server-side -k kustomize/install/default```

Die erste Postgres-Datenbank

Nach der Installation kann die erste Postgres-Datenbank erstellt werden. Dazu wird eine Custom Resource Definition (CRD) verwendet, die die Konfiguration der Datenbank beschreibt. Ein Beispiel für eine einfache Datenbank ist im Verzeichnis kustomize/postgres enthalten:

kubectl apply -k kustomize/postgres

Im Endeffekt wird nur eine Custom Resource erstellt, die die Datenbank beschreibt. Der Postgres Operator sorgt dafür, dass die Datenbank-Instanz erstellt wird und weitere dafür notwendigen Ressourcen (Backup-Pod, ConfigMaps, Secrets, Services,PVCs, …) bereitgestellt werden.

apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
  name: hippo
  namespace: mynamespace
spec:
  postgresVersion: 16
  users:
    - name: rhino
      databases:
        - zoo
  instances:
    - name: instance1
      dataVolumeClaimSpec:
        accessModes:
        - "ReadWriteOnce"
        resources:
          requests:
            storage: 1Gi
  backups:
    pgbackrest:
      repos:
      - name: repo1
        volume:
          volumeClaimSpec:
            accessModes:
            - "ReadWriteOnce"
            resources:
              requests:
                storage: 1Gi

Wenn man den Operator mit Backup-Repo betreiben möchte, dann benötigt man mindestens eine weitere Node, da der Repo-Container standardmäßig auf einer anderen Node als die Datenbank-Instanz laufen muss. Alternativ kann die topology-spread-constraints für die Backup-Repos entsprechend anpassen.

Kurz darauf sehen wir bereits die Container und Secrets in unserem Namespace “mynamespace” und können unsere Datenbank verwenden.

Zugriff!

Um auf die Datenbank zugreifen zu können, starten wir z.B. einen Container, in dem wir dann uns mit psql mit der Datenbank verbinden. Dazu mounten wir das vom Operator für unsere Datenbank-Instanz erstellte Secret hippo-pguser-rhino und mappen sämtliche Schlüssel als Umgebungsvariablen, die wir dann für das psql-Kommando verwenden können.

kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: pgclient
  namespace: mynamespace
spec:
  containers:
  - name: pgclient
    image: postgres:latest
    command: ["sleep", "infinity"]
    envFrom:
    - secretRef:
        name: hippo-pguser-rhino
    stdin: true
    tty: true
EOF

Dann verbinden wir uns mit dem Container und starten eine Shell:

kubectl exec -n mynamespace -it pgclient -- bash

Weil wir die Einträge aus dem Secret als ENV-Variablen gemountet haben, können wir bequem auf die Datenbank zugreifen:

PGPASSWORD=$password psql -U $user -d $dbname -h $host

Und los geht’s - nun können wir mit unserer Datenbank-Instanz interagieren.

bash

% User auflisten
\du+
% Datenbanken auflisten
\l

Wenn diese Kommandos funktionieren, dann kann auch eine Anwendung mit den Informationen aus dem Secret auf die Datenbank zugreifen.

Nutzung des Public Schema

Bitte beachten: Postgres empfiehlt aus Sicherheitsgründen schon seit langem, die Berechtigungen für das “public”-Schema zu entziehen. Mit Postgres 15 wurde diese Empfehlung zum Standardverhalten. Diese Änderung erhöht die Sicherheit, bereitet aber Schwierigkeiten für diejenigen, die sich als Benutzer mit der Datenbank verbinden und sofort versuchen, Tabellen zu erstellen.

Um ein Schema für den Benutzer einzurichten, kann das Feld spec.databaseInitSQL verwendet werden, um per Init-SQL-Script entsprechende Schemas zu erstellen.

Man kann aber auch die Funktion zur automatischen Erstellung von User-Schemas verwenden, die einfach per Annotation im CRD aktiviert werden kann. Weitere Informationen dazu finden sich im User / Database Management Tutorial .

metadata:
  name: hippo
  annotations:
    postgres-operator.crunchydata.com/autoCreateUserSchema: true

Damit wird direkt beim Anlegen eines Benutzers ein Schema mit dem Namen des Benutzers erstellt, das Postgres auch bevorzugt im search_path verwendet, wie folgendes Statement zeigt:

SHOW search_path;
   search_path   
-----------------
"$user", public

Konfiguration

Backup

Das Backup wird standardmäßig aktiviert und erfolgt via pgBackRest . Die Konfiguration ist im Backup Guide beschrieben. Die Sicherung erfolgt mit einem separaten Container und kann auf verschiedene Arten konfiguriert werden. So kann z.B. die Anzahl der Backups und die Aufbewahrungsdauer festgelegt werden. Auch die Verschlüsselung der Backups ist möglich. Die Konfiguration erfolgt über die Custom Resource Definition (CRD) des Operators.

Lange Zeit war es nicht möglich, z.B. für Test-Datenbanken auf ein Backup zu verzichten.

Optionales Backup

Ein relativ neues Feature der Version 5.7 ist das oft nachgefragte “Optional Backup”. Damit können Datenbanken von der Sicherung ausgeschlossen werden. Dies ist z.B. sinnvoll, wenn eine Datenbank nur temporäre Daten enthält oder nicht gesichert werden soll, z.B. eine Development-Datenbank. Die Konfiguration erfolgt ebenfalls über die CRD des Operators. Weitere Informationen finden sich im Guide Optional Backup .

Um unsere bereits deployte Beispieldatenbank (siehe oben) ohne Backup zu betreiben, löschen wir den backup-Zweig aus der Spec und fügen eine Annotation hinzu, die es dem Operator erlaubt, ein bestehendes Backup zu verwerfen. Wenn eine Datenbank neu ohne Backup erstellt wird, ist diese Annotation nicht notwendig.

apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
  name: hippo
  annotations:
    postgres-operator.crunchydata.com/authorizeBackupRemoval: true
spec:
  postgresVersion: 16
  users:
    - name: rhino
      databases:
        - zoo
  instances:
    - name: instance1
      dataVolumeClaimSpec:
        accessModes:
          - "ReadWriteOnce"
        resources:
          requests:
            storage: 1Gi

Day Two Operations

Natürlich ist das Betreiben einer Datenbank wesentlich mehr als nur das Erstellen und Löschen von Datenbanken. Der Operator bietet viele Funktionen, die den täglichen Betrieb von Datenbanken in Kubernetes erleichtern.

Dazu gehören:

  • Überwachung
  • Cluster-Management
  • Skalierung/Clustering
  • Konfigurationsänderungen
  • Versions-Upgrade
  • Migration

CrunchyData hat für diese Themen Tutorials für Day Two Operations erstellt und typische Fragestellungen mit zahlreichen Guides dokumentiert. Dadurch zeigt sich die eigentliche Stärke und Reife der Lösung.

Kubernetes - Helm Deployment

Neben der Möglichkeit, den Postgres Operator mittels kustomize zu installieren, bietet CrunchyData auch ein Helm-Chart an. Das Chart ist sehr flexibel und bietet viele Konfigurationsmöglichkeiten.

Die Installation des Operators mit Helm ist etwas umständlich, da die Charts (nicht mehr?) in einem Repository verfügbar sind. Stattdessen muss das Chart heruntergeladen und z.B. als Dependency in das eigene Chart eingebunden werden. Die Standard-Values sind für den Einstieg gut geeignet und lassen sich bei speziellen Anforderungen leicht anpassen.

CrunchyData stellt auch ein Helm-Chart für das Deployment von Datenbank-Instanzen bereit, das in die Deployments der Anwendungen, die Postgres-Datenbanken benötigen, eingebunden werden kann.

Bei der Installation des Operators mit Helm via ArgoCD kann beim Synchronisieren der Custom Resource Definitions (CRDs) der Fehler “Too long: must have at most 262144 bytes” auftreten. Dies liegt daran, dass Kubernetes eine Größenbeschränkung von 256 KB für Annotations hat. Standardmäßig verwendet Argo CD Client-Side Apply, wodurch die Annotation last-applied-configuration hinzugefügt wird, was bei umfangreichen CRDs dazu führen kann, dass diese Grenze überschritten wird. Die empfohlene Lösung ist die Aktivierung von Server-Side Apply, das diese Annotation nicht hinzufügt. Dies kann in Argo CD ab Version 2.5 durch Setzen der Sync-Option ServerSideApply=true erreicht werden. Für frühere Versionen kann kubectl replace als Alternative verwendet werden, jedoch mit Vorsicht, da es zu unerwarteten Ergebnissen führen kann, wenn mehrere Clients ein Objekt ändern. In diesem Artikel wird das Problem und die Lösung sehr gut beschrieben.

Fazit

Der Crunchy Data Postgres Operator ist ein ausgereiftes System, um Postgres-Datenbanken in Kubernetes zu betreiben. Die Installation ist einfach und die Konfiguration erfolgt über Custom Resource Definitions (CRDs). Der Operator bietet viele Funktionen, die das Betreiben von Datenbanken in Kubernetes erleichtern. Die Dokumentation ist sehr gut und enthält viele Beispiele und Tutorials. Der Operator ist eine gute Wahl für die Automatisierung von Postgres-Datenbanken in Kubernetes.

Der Operator bietet Unterstützung für die Administration von Postgres-Datenbanken in Kubernetes und verbirgt einen Großteil der Komplexität. Dennoch bleibt der stabile Betrieb einer (oder vieler!) Datenbank/en eine Herausforderung und erfordert ein gewisses Maß an Erfahrung und Wissen.

Und es gilt: kein Backup, kein Mitleid!

You May Also Like

Agent Smith - übernehmen Sie?

Agent Smith - übernehmen Sie?

Welche Auswirkungen hat die KI auf die Arbeit agiler Teams in der Software-Entwicklung? Werden wir durch Tools ergänzt oder übernimmt Agent Smith? Wir verschaffen uns einen Überblick über die aktuelle Entwicklung und Trends in der KI und wie agentenbasierten Workflows unsere Arbeit verändern könnten.