In dieser Anleitung erfahren Sie, wie Sie mit dem Zalando-Postgres-Operator Postgres-Cluster in Google Kubernetes Engine (GKE) bereitstellen.
PostgreSQL ist ein leistungsstarkes, objektrelationales Open-Source-Datenbanksystem, das über mehrere Jahrzehnte hinweg aktiv entwickelt wurde und sich einen guten Ruf für Zuverlässigkeit, Robustheit von Features und Leistung verdient hat.
Dieser Leitfaden richtet sich an Plattformadministratoren, Cloud-Architekten und Betriebsexperten, die PostgreSQL als Datenbankanwendung in GKE ausführen möchten, anstatt Cloud SQL für PostgreSQL zu verwenden.
Umgebung einrichten
So richten Sie Ihre Umgebung ein:
- Legen Sie Umgebungsvariablen fest: - export PROJECT_ID=PROJECT_ID export KUBERNETES_CLUSTER_PREFIX=postgres export REGION=us-central1- Ersetzen Sie - PROJECT_IDdurch Ihre Google Cloud Projekt-ID.
- Klonen Sie das GitHub-Repository: - git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
- Wechseln Sie in das Arbeitsverzeichnis: - cd kubernetes-engine-samples/databases/postgres-zalando
Clusterinfrastruktur erstellen
In diesem Abschnitt führen Sie ein Terraform-Skript aus, um einen privaten, hochverfügbaren regionalen GKE-Cluster zu erstellen.
Sie können den Operator mit einem Standard- oder Autopilot-Cluster installieren.
Standard
Das folgende Diagramm zeigt einen privaten regionalen Standard-GKE-Cluster, der in drei verschiedenen Zonen bereitgestellt wird:
Stellen Sie diese Infrastruktur bereit:
export GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token)
terraform -chdir=terraform/gke-standard init
terraform -chdir=terraform/gke-standard apply \
  -var project_id=${PROJECT_ID} \
  -var region=${REGION} \
  -var cluster_prefix=${KUBERNETES_CLUSTER_PREFIX}
Geben Sie bei Aufforderung yes ein. Es kann einige Minuten dauern, bis dieser Befehl abgeschlossen ist und der Cluster den Status „Bereit“ anzeigt.
Terraform erstellt die folgenden Ressourcen:
- Ein VPC-Netzwerk und ein privates Subnetz für die Kubernetes-Knoten
- Ein Router für den Zugriff auf das Internet über NAT
- Ein privater GKE-Cluster in der Region us-central1
- Knotenpools mit aktiviertem Autoscaling (ein bis zwei Knoten pro Zone, mindestens ein Knoten pro Zone)
- Ein ServiceAccountmit Logging- und Monitoring-Berechtigungen
- Backup for GKE zur Notfallwiederherstellung
- Google Cloud Managed Service for Prometheus für das Clustermonitoring
Die Ausgabe sieht in etwa so aus:
...
Apply complete! Resources: 14 added, 0 changed, 0 destroyed.
...
Autopilot
Das folgende Diagramm zeigt einen privaten regionalen Autopilot-GKE-Cluster:
Stellen Sie die Infrastruktur bereit:
export GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token)
terraform -chdir=terraform/gke-autopilot init
terraform -chdir=terraform/gke-autopilot apply \
  -var project_id=${PROJECT_ID} \
  -var region=${REGION} \
  -var cluster_prefix=${KUBERNETES_CLUSTER_PREFIX}
Geben Sie bei Aufforderung yes ein. Es kann einige Minuten dauern, bis dieser Befehl abgeschlossen ist und der Cluster den Status „Bereit“ anzeigt.
Terraform erstellt die folgenden Ressourcen:
- Ein VPC-Netzwerk und ein privates Subnetz für die Kubernetes-Knoten
- Ein Router für den Zugriff auf das Internet über NAT
- Ein privater GKE-Cluster in der Region us-central1
- Ein ServiceAccountmit Logging- und Monitoring-Berechtigung
- Google Cloud Managed Service for Prometheus für das Clustermonitoring
Die Ausgabe sieht in etwa so aus:
...
Apply complete! Resources: 12 added, 0 changed, 0 destroyed.
...
Mit dem Cluster verbinden
Konfigurieren Sie kubectl für die Kommunikation mit dem Cluster:
gcloud container clusters get-credentials ${KUBERNETES_CLUSTER_PREFIX}-cluster --location ${REGION}
Zalando-Operator in Ihrem Cluster bereitstellen
Stellen Sie den Zalando-Operator mithilfe eines Helm-Diagramms in Ihrem Kubernetes-Cluster bereit.
- Fügen Sie das Helm-Diagramm-Repository für den Zalando-Operator hinzu: - helm repo add postgres-operator-charts https://opensource.zalando.com/postgres-operator/charts/postgres-operator
- Erstellen Sie einen Namespace für den Zalando-Operator und den Postgres-Cluster: - kubectl create ns postgres kubectl create ns zalando
- Stellen Sie den Zalando-Operator mit dem Helm-Befehlszeilentool bereit: - helm install postgres-operator postgres-operator-charts/postgres-operator -n zalando \ --set configKubernetes.enable_pod_antiaffinity=true \ --set configKubernetes.pod_antiaffinity_preferred_during_scheduling=true \ --set configKubernetes.pod_antiaffinity_topology_key="topology.kubernetes.io/zone" \ --set configKubernetes.spilo_fsgroup="103"- Sie können die Einstellungen von - podAntiAffinitynicht direkt für die benutzerdefinierte Ressource konfigurieren, die den Postgres-Cluster darstellt. Legen Sie stattdessen die- podAntiAffinity-Einstellungen global für alle Postgres-Cluster in den Operatoreinstellungen fest.
- Prüfen Sie den Bereitstellungsstatus des Zalando-Operators mit Helm: - helm ls -n zalando- Die Ausgabe sieht in etwa so aus: - NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION postgres-operator zalando 1 2023-10-13 16:04:13.945614 +0200 CEST deployed postgres-operator-1.10.1 1.10.1
Postgres bereitstellen
Die grundlegende Konfiguration für die Postgres-Clusterinstanz umfasst die folgenden Komponenten:
- Drei Postgres-Replikate: ein Leader und zwei Standby-Replikate.
- CPU-Ressourcenzuweisung: eine CPU-Anfrage und zwei CPU-Limits mit 4 GB Speicheranfragen und -Limits.
- Toleranzen, nodeAffinitiesundtopologySpreadConstraints, die für jede Arbeitslast konfiguriert sind und eine ordnungsgemäße Verteilung auf Kubernetes-Knoten mithilfe ihrer jeweiligen Knotenpools und verschiedenen Verfügbarkeitszonen gewährleisten.
Diese Konfiguration stellt die minimale Einrichtung dar, die zum Erstellen eines produktionsfertigen Postgres-Clusters erforderlich ist.
Das folgende Manifest beschreibt einen Postgres-Cluster:
Dieses Manifest hat die folgenden Felder:
- spec.teamIdist ein Präfix für die von Ihnen ausgewählten Clusterobjekte
- spec.numberOfInstancesist die Gesamtzahl der Instanzen für einen Cluster
- spec.usersist die Nutzerliste mit Berechtigungen
- spec.databasesist die Datenbankliste im Format- dbname: ownername
- spec.postgresqlsind die Postgres-Parameter
- spec.volumesind die Parameter für den nichtflüchtigen Speicher
- spec.tolerationsist die Toleranz-Pod-Vorlage, mit der Cluster-Pods auf- pool-postgres-Knoten geplant werden können
- spec.nodeAffinityist die Pod-Vorlage- nodeAffinity, die GKE mitteilt, dass Cluster-Pods lieber auf- pool-postgres-Knoten geplant werden sollen.
- spec.resourcessind Anfragen und Limits für Cluster-Pods
- spec.sidecarsist eine Liste der Sidecar-Container, die- postgres-exporterenthält
Weitere Informationen finden Sie in der Referenz zu Clustermanifesten in der Postgres-Dokumentation.
Einfachen Postgres-Cluster erstellen
- Erstellen Sie mithilfe der grundlegenden Konfiguration einen neuen Postgres-Cluster: - kubectl apply -n postgres -f manifests/01-basic-cluster/my-cluster.yaml- Mit diesem Befehl wird eine benutzerdefinierte PostgreSQL-Ressource des Zalando-Betreibers mit folgenden Eigenschaften erstellt: - CPU- und Speicheranforderungen und -limits
- Markierungen und Affinitäten, um die bereitgestellten Pod-Replikate auf GKE-Knoten zu verteilen.
- Eine Datenbank
- Zwei Nutzer mit Datenbankinhaberberechtigungen
- Ein Nutzer ohne Berechtigungen
 
- Warten Sie, bis GKE die erforderlichen Arbeitslasten gestartet hat: - kubectl wait pods -l cluster-name=my-cluster --for condition=Ready --timeout=300s -n postgres- Die Verarbeitung dieses Befehls kann einige Minuten dauern. 
- Prüfen Sie, ob GKE die Postgres-Arbeitslasten erstellt hat: - kubectl get pod,svc,statefulset,deploy,pdb,secret -n postgres- Die Ausgabe sieht in etwa so aus: - NAME READY STATUS RESTARTS AGE pod/my-cluster-0 1/1 Running 0 6m41s pod/my-cluster-1 1/1 Running 0 5m56s pod/my-cluster-2 1/1 Running 0 5m16s pod/postgres-operator-db9667d4d-rgcs8 1/1 Running 0 12m NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/my-cluster ClusterIP 10.52.12.109 <none> 5432/TCP 6m43s service/my-cluster-config ClusterIP None <none> <none> 5m55s service/my-cluster-repl ClusterIP 10.52.6.152 <none> 5432/TCP 6m43s service/postgres-operator ClusterIP 10.52.8.176 <none> 8080/TCP 12m NAME READY AGE statefulset.apps/my-cluster 3/3 6m43s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/postgres-operator 1/1 1 1 12m NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE poddisruptionbudget.policy/postgres-my-cluster-pdb 1 N/A 0 6m44s NAME TYPE DATA AGE secret/my-user.my-cluster.credentials.postgresql.acid.zalan.do Opaque 2 6m45s secret/postgres.my-cluster.credentials.postgresql.acid.zalan.do Opaque 2 6m44s secret/sh.helm.release.v1.postgres-operator.v1 helm.sh/release.v1 1 12m secret/standby.my-cluster.credentials.postgresql.acid.zalan.do Opaque 2 6m44s secret/zalando.my-cluster.credentials.postgresql.acid.zalan.do Opaque 2 6m44s
Der Operator erstellt die folgenden Ressourcen:
- Ein Postgres-StatefulSet, das drei Pod-Replikate für Postgres steuert
- Einen PodDisruptionBudgets, wodurch mindestens ein verfügbares Replikat garantiert wird
- Den my-cluster-Dienst, der nur auf das Leader-Replikat abzielt
- Der Dienst my-cluster-repl, der den Postgres-Port für eingehende Verbindungen und für die Replikation zwischen Postgres-Replikaten freigibt
- Den monitorlosen Dienst my-cluster-configzum Abrufen der Liste der laufenden Postgres-Pod-Replikate
- Secrets mit Nutzeranmeldedaten für den Zugriff auf die Datenbank und die Replikation zwischen Postgres-Knoten.
Bei Postgres authentifizieren
Sie können Postgres-Nutzer erstellen und ihnen Datenbankberechtigungen zuweisen. Das folgende Manifest beschreibt beispielsweise eine benutzerdefinierte Ressource, die Nutzern Rollen zuweist:
apiVersion: "acid.zalan.do/v1"
kind: postgresql
metadata:
  name: my-cluster
spec:
  ...
  users:
    mydatabaseowner:
    - superuser
    - createdb
    myuser: []
  databases:
    mydatabase: mydatabaseowner
In diesem Manifest:
- Der mydatabaseowner-Nutzer hat die RollenSUPERUSERundCREATEDB, die uneingeschränkte Administratorrechte gewähren (Verwaltung der Postgres-Konfiguration, neue Datenbanken, Tabellen und Nutzer erstellen, usw). Sie sollten diesen Nutzer nicht mit Kunden teilen. In Cloud SQL können Kunden beispielsweise nicht auf Nutzer mit der RolleSUPERUSERzugreifen.
- Dem Nutzer myusersind keine Rollen zugewiesen. Dies entspricht der Best Practice, mit derSUPERUSERNutzer mit den geringsten Berechtigungen zu erstellen. Detaillierte Rechte werdenmyuservonmydatabaseownergewährt. Aus Sicherheitsgründen sollten Siemyuser-Anmeldedaten nur für Clientanwendungen freigeben.
Passwörter speichern
Sie sollten die scram-sha-256
empfohlene Methode zum Speichern von Passwörtern verwenden. Das folgende Manifest beschreibt beispielsweise eine benutzerdefinierte Ressource, die die scram-sha-256-Verschlüsselung mithilfe des Felds postgresql.parameters.password_encryption angibt:
apiVersion: "acid.zalan.do/v1"
kind: postgresql
metadata:
  name: my-cluster
spec:
  ...
  postgresql:
    parameters:
      password_encryption: scram-sha-256
Nutzeranmeldedaten rotieren
Sie können Nutzer-Anmeldedaten rotieren, die in Kubernetes-Secrets mit Zalando gespeichert sind. Das folgende Manifest beschreibt beispielsweise eine benutzerdefinierte Ressource, die die Rotation der Nutzeranmeldedaten mithilfe des Felds usersWithSecretRotation definiert:
apiVersion: "acid.zalan.do/v1"
kind: postgresql
metadata:
  name: my-cluster
spec:
  ...
  usersWithSecretRotation:
  - myuser
  - myanotheruser
  - ...
Beispiel für die Authentifizierung: Mit Postgres verbinden
In diesem Abschnitt erfahren Sie, wie Sie einen Postgres-Beispielclient bereitstellen und mit dem Passwort aus einem Kubernetes-Secret eine Verbindung zur Datenbank herstellen.
- Führen Sie den Client-Pod aus, um mit Ihrem Postgres-Cluster zu interagieren: - kubectl apply -n postgres -f manifests/02-auth/client-pod.yaml- Die Anmeldedaten der Nutzer - myuserund- mydatabaseownerwerden aus den zugehörigen Secrets übernommen und als Umgebungsvariablen im Pod bereitgestellt.
- Stellen Sie eine Verbindung zum Pod her, wenn er bereit ist: - kubectl wait pod postgres-client --for=condition=Ready --timeout=300s -n postgres kubectl exec -it postgres-client -n postgres -- /bin/bash
- Stellen Sie eine Verbindung zu Postgres her und versuchen Sie, mit - myuser-Anmeldedaten eine neue Tabelle zu erstellen:- PGPASSWORD=$CLIENTPASSWORD psql \ -h my-cluster \ -U $CLIENTUSERNAME \ -d mydatabase \ -c "CREATE TABLE test (id serial PRIMARY KEY, randomdata VARCHAR ( 50 ) NOT NULL);"- Der Befehl sollte mit einem Fehler wie dem folgenden fehlschlagen: - ERROR: permission denied for schema public LINE 1: CREATE TABLE test (id serial PRIMARY KEY, randomdata VARCHAR...- Der Befehl schlägt fehl, weil Nutzer ohne zugewiesene Berechtigungen sich standardmäßig nur in Postgres anmelden und Datenbanken auflisten können. 
- Erstellen Sie eine Tabelle mit den Anmeldedaten - mydatabaseownerund gewähren Sie- myuseralle Berechtigungen für die Tabelle:- PGPASSWORD=$OWNERPASSWORD psql \ -h my-cluster \ -U $OWNERUSERNAME \ -d mydatabase \ -c "CREATE TABLE test (id serial PRIMARY KEY, randomdata VARCHAR ( 50 ) NOT NULL);GRANT ALL ON test TO myuser;GRANT ALL ON SEQUENCE test_id_seq TO myuser;"- Die Ausgabe sieht in etwa so aus: - CREATE TABLE GRANT GRANT
- Fügen Sie mithilfe von - myuser-Anmeldedaten zufällige Daten in die Tabelle ein:- for i in {1..10}; do DATA=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 13) PGPASSWORD=$CLIENTPASSWORD psql \ -h my-cluster \ -U $CLIENTUSERNAME \ -d mydatabase \ -c "INSERT INTO test(randomdata) VALUES ('$DATA');" done- Die Ausgabe sieht in etwa so aus: - INSERT 0 1 INSERT 0 1 INSERT 0 1 INSERT 0 1 INSERT 0 1 INSERT 0 1 INSERT 0 1 INSERT 0 1 INSERT 0 1 INSERT 0 1
- Rufen Sie die von Ihnen eingefügten Werte ab: - PGPASSWORD=$CLIENTPASSWORD psql \ -h my-cluster \ -U $CLIENTUSERNAME \ -d mydatabase \ -c "SELECT * FROM test;"- Die Ausgabe sieht in etwa so aus: - id | randomdata ----+--------------- 1 | jup9HYsAjwtW4 2 | 9rLAyBlcpLgNT 3 | wcXSqxb5Yz75g 4 | KoDRSrx3muD6T 5 | b9atC7RPai7En 6 | 20d7kC8E6Vt1V 7 | GmgNxaWbkevGq 8 | BkTwFWH6hWC7r 9 | nkLXHclkaqkqy 10 | HEebZ9Lp71Nm3 (10 rows)
- Beenden Sie die Pod-Shell: - exit
Informationen zum Erfassen von Messwerten für den Postgres-Cluster durch Prometheus
Das folgende Diagramm zeigt, wie die Erfassung von Prometheus-Messwerten funktioniert:
Im Diagramm enthält ein privater GKE-Cluster Folgendes:
- Einen Postgres-Pod, der Messwerte für den Pfad /und den Port9187erfasst.
- Prometheus-basierte Collectors, die die Messwerte aus dem Postgres-Pod verarbeiten.
- Eine PodMonitoring-Ressource, die Messwerte an Cloud Monitoring sendet
Google Cloud Managed Service for Prometheus unterstützt die Messwerterfassung im Prometheus-Format. Cloud Monitoring verwendet ein integriertes Dashboard für Postgres-Messwerte.
Zalando stellt Clustermesswerte im Prometheus-Format bereit. Dabei wird die postgres_exporter-Komponente als Sidecar-Container verwendet.
- Erstellen Sie die - PodMonitoring-Ressource, um Messwerte nach- labelSelectorzu extrahieren:- kubectl apply -n postgres -f manifests/03-prometheus-metrics/pod-monitoring.yaml
- Rufen Sie in der Google Cloud Console die Seite GKE-Cluster auf. - Im Dashboard wird eine Datenaufnahmerate ungleich null angezeigt. 
- Rufen Sie in der Google Cloud Console die Seite Dashboards auf. 
- Öffnen Sie das PostgreSQL Prometheus-Übersichts-Dashboard. Im Dashboard wird die Anzahl der abgerufenen Zeilen angezeigt. Es kann einige Minuten dauern, bis das Dashboard automatisch bereitgestellt wird. 
- Stellen Sie eine Verbindung zum Client-Pod her: - kubectl exec -it postgres-client -n postgres -- /bin/bash
- Zufällige Daten einfügen: - for i in {1..100}; do DATA=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 13) PGPASSWORD=$CLIENTPASSWORD psql \ -h my-cluster \ -U $CLIENTUSERNAME \ -d mydatabase \ -c "INSERT INTO test(randomdata) VALUES ('$DATA');" done
- Aktualisieren Sie die Seite. Die Diagramme Zeilen und Blöcke werden aktualisiert, um den tatsächlichen Datenbankstatus anzuzeigen. 
- Beenden Sie die Pod-Shell: - exit