PITR einer PostgreSQL-Datenbank in Compute Engine ausführen


In dieser Anleitung wird gezeigt, wie Sie den Archivierungsprozess einrichten und dann eine Wiederherstellung zu einem bestimmten Zeitpunkt (Point-in-time Recovery – PITR) einer PostgreSQL-Datenbank ausführen, die auf Compute Engine ausgeführt wird.

In dieser Anleitung erstellen Sie eine Demonstrationsdatenbank und führen eine Anwendungsarbeitslast aus. Anschließend konfigurieren Sie die Archivierungs- und Sicherungsprozesse. Als Nächstes erfahren Sie, wie Sie die Sicherungs-, Archivierungs- und Wiederherstellungsprozesse prüfen. Abschließend erfahren Sie, wie Sie die Datenbank zu einem bestimmten Zeitpunkt wiederherstellen.

Diese Anleitung richtet sich an Datenbankadministratoren, Systembetreiber, DevOps-Experten und Cloud-Architekten, die eine Sicherungs- und Wiederherstellungsstrategie für PostgreSQL-Datenbanken konfigurieren möchten.

In dieser Anleitung wird davon ausgegangen, dass Sie mit Docker-Containern sowie mit Linux-Befehlen, PostgreSQL-Datenbankmodulen und Compute Engine vertraut sind.

Ziele

  • Einen Sicherungs- und Archivierungsprozess einrichten.
  • Eine PITR ausführen.
  • Ihre Datensicherung überwachen.
  • Eine Wiederherstellung prüfen.

Kosten

In diesem Dokument verwenden Sie die folgenden kostenpflichtigen Komponenten von Google Cloud:

Mit dem Preisrechner können Sie eine Kostenschätzung für Ihre voraussichtliche Nutzung vornehmen. Neuen Google Cloud-Nutzern steht möglicherweise eine kostenlose Testversion zur Verfügung.

Nach Abschluss der in diesem Dokument beschriebenen Aufgaben können Sie weitere Kosten vermeiden, indem Sie die erstellten Ressourcen löschen. Weitere Informationen finden Sie unter Bereinigen.

Vorbereitung

  1. Melden Sie sich bei Ihrem Google Cloud-Konto an. Wenn Sie mit Google Cloud noch nicht vertraut sind, erstellen Sie ein Konto, um die Leistungsfähigkeit unserer Produkte in der Praxis sehen und bewerten zu können. Neukunden erhalten außerdem ein Guthaben von 300 $, um Arbeitslasten auszuführen, zu testen und bereitzustellen.
  2. Wählen Sie in der Google Cloud Console auf der Seite der Projektauswahl ein Google Cloud-Projekt aus oder erstellen Sie eines.

    Zur Projektauswahl

  3. Die Abrechnung für das Google Cloud-Projekt muss aktiviert sein.

  4. Compute Engine and Cloud Storage APIs aktivieren.

    Aktivieren Sie die APIs

  5. Installieren Sie die Google Cloud CLI.
  6. Führen Sie folgenden Befehl aus, um die gcloud CLI zu initialisieren:

    gcloud init
  7. Wählen Sie in der Google Cloud Console auf der Seite der Projektauswahl ein Google Cloud-Projekt aus oder erstellen Sie eines.

    Zur Projektauswahl

  8. Die Abrechnung für das Google Cloud-Projekt muss aktiviert sein.

  9. Compute Engine and Cloud Storage APIs aktivieren.

    Aktivieren Sie die APIs

  10. Installieren Sie die Google Cloud CLI.
  11. Führen Sie folgenden Befehl aus, um die gcloud CLI zu initialisieren:

    gcloud init
  12. Aktivieren Sie Cloud Shell in der Google Cloud Console.

    Cloud Shell aktivieren

    Unten in der Google Cloud Console wird eine Cloud Shell-Sitzung gestartet und eine Eingabeaufforderung angezeigt. Cloud Shell ist eine Shell-Umgebung, in der das Google Cloud CLI bereits installiert ist und Werte für Ihr aktuelles Projekt bereits festgelegt sind. Das Initialisieren der Sitzung kann einige Sekunden dauern.

Konzepte

Lesen Sie die folgenden PostgreSQL-Konzepte, bevor Sie mit der Anleitung beginnen:

  • Kontinuierliche Archivierung: Wenn die Datenbank kontinuierlich sequenzielle Transaktionen in einer Datei speichert.
  • Write-Ahead-Log (WAL): Änderungen an Datendateien werden im WAL aufgezeichnet, bevor sie an der Datei vorgenommen werden.
  • WAL-Eintrag: Jede auf die Datenbank angewendete Transaktion wird formatiert und als WAL-Eintrag gespeichert.
  • Segmentdateien: Segmentdateien haben kontinuierlich ansteigende Dateinamen und enthalten so viele WAL-Datensätze wie möglich. Die Dateigröße ist konfigurierbar und beträgt standardmäßig 16 MiB. Sie können eine höhere Größe auswählen, wenn Sie umfangreiche Transaktionen oder eine große Anzahl von Transaktionen erwarten, um die Gesamtzahl der erstellten Segmentdateien zu verringern und die Dateiverwaltung zu reduzieren.

Weitere Informationen finden Sie unter Reliability and the Write-Ahead Log.

Das folgende Diagramm zeigt, wie WALs in zwei Phasen beibehalten werden.

Zwei Phasen von persistenten WALs.

Im vorherigen Diagramm besteht die erste Phase von persistenten WALs darin, dass das Datenbankmodul Schreibvorgänge im WAL-Puffer gleichzeitig mit dem Schreibvorgang in eine Tabelle aufzeichnet. Wenn die Transaktion festgelegt wird, wird der WAL-Puffer in der zweiten Phase auf das Laufwerk geschrieben (geleert), wobei ein Anfügen an die WAL-Segmentdatei erfolgt.

PITR auswählen

Eine PITR eignet sich für folgende Szenarien:

  • Recovery Point Objective (RPO) minimieren. Das RPO ist die maximale Zeit des Datenverlusts, die geduldet wird, bevor er sich erheblich auf die Geschäftsprozesse auswirkt. Durch das Speichern aller Transaktionen in den WALs zwischen Sicherungs-Snapshots wird die Menge der verlorenen Daten drastisch reduziert, da die Transaktionen seit der letzten vollständigen Sicherung auf die Datenbank angewendet werden.
  • Recovery Time Objective (RTO) minimieren. Das RTO ist die Zeit, die für die Wiederherstellung einer Datenbank erforderlich ist, wenn eine Datei beschädigt wird. Nachdem Sie binäre Sicherungen und die Logarchivierung eingerichtet haben, kann die Wiederherstellungszeit der Datenbank minimal sein.
  • Datenkorruptionsfehler oder administrativen Fehler beheben. Wenn ein Code-Release eine schwerwiegende Datenbeschädigung verursacht oder bei der Routinewartung ein nicht behebbarer Fehler auftritt, können Sie die Wiederherstellung bis zu diesem Zeitpunkt durchführen.

In einigen Anwendungsarchitekturen wie einer Mikrodienstarchitektur können parallele Datenbanken vorhanden sein, die unabhängige Wiederherstellungen erfordern. Eine Einzelhandelsanwendung kann beispielsweise Kundendaten in einer Datenbank und Details zu Einzelhandelsaufträgen und Inventardaten in anderen Datenbanken enthalten. Abhängig vom Gesamtstatus der Daten müssen eine, zwei oder alle Datenbanken parallel wiederhergestellt werden.

Eine PITR ist in folgenden Szenarien nicht geeignet:

  • RPO ist groß. Wenn Ihre Richtlinie zur Notfallwiederherstellung den Verlust von Transaktionen, die nach dem letzten Snapshot eingegangen sind, zulässt, können Sie zusätzliche Schritte vermeiden und sich auf die Wiederherstellung Ihrer Daten konzentrieren.
  • Eine vollständige Datenbankwiederherstellung ist erforderlich. Wenn Sie die letzte Transaktion wiederherstellen möchten, ist Ihr Wiederherstellungsziel der Zeitstempel der letzten persistenten Transaktion. Dieses Szenario ist ein spezifischer Fall von PITR. Dieses Ziel wird jedoch semantisch als vollständige Wiederherstellung bezeichnet.

Hinweise zur Leistung

Durch die Archivierung wird Ihr Datenbankserver zusätzlich mit E/A belastet. Die zusätzliche Last hängt von den Eigenschaften Ihrer Arbeitslast ab, da sie dem Transaktionsvolumen beim Schreiben, Aktualisieren und Löschen entspricht.

Sie Können die E/A-Auswirkungen reduzieren, die die WAL-Archivaktivität auf Ihre primäre Datenbank haben kann. Führen Sie dazu die regelmäßigen WAL-Archive mit einem schreibgeschützten Replikat aus.

Durch diese Konfiguration wird die primäre Datenbank von den Batch-orientierten E/A-Aktivitäten für die Übertragung der WAL-Dateien isoliert. Transaktionen, die für das schreibgeschützte Replikat bestimmt sind, werden in einem konstanten Stream von der primären Datenbank übertragen, was sich deutlich auf den Durchsatz im stationären Zustand auswirkt.

Wenn Ihre Produktionsdatenbanktopologie bereits ein schreibgeschütztes Replikat enthält, verursacht diese Konfiguration keine zusätzliche Belastung für Verwaltung, Preis oder andere.

Referenzarchitektur

Das folgende Diagramm zeigt die Architektur, die Sie in dieser Anleitung implementieren.

Cloud-Infrastruktur von PITR mit Compute Engine und Cloud Storage

In dieser Anleitung erstellen Sie eine Cloud-Infrastruktur, um eine PITR zu beobachten, die die folgenden Komponenten verwendet:

  • Ein PostgreSQL-Datenbankserver, der auf Compute Engine ausgeführt wird.
  • Cloud Storage zum Speichern von Snapshots und Transaktionslogs.

Das folgende Diagramm zeigt die beiden Docker-Container, die auf der PostgreSQL-Datenbank-VM gestartet werden. Als Trennung von Belangen wird der Datenbankserver in einem der Container ausgeführt und die WAL-Archivierung im anderen Container.

Docker-Container für den Datenbankserver und die WAL-Archivierung

Dieses Diagramm zeigt, wie die Docker-Volumes in jedem Container den Bereitstellungspunkten von Persistent Disk auf der Host-VM zugeordnet werden.

Umgebungsvariablen einrichten

Die in dieser Anleitung verwendeten Skripts und Befehle basieren auf Shell-Umgebungsvariablen.

  1. Legen Sie in Cloud Shell Umgebungsvariablen für Ihr Projekt, den Instanznamen und die PostgreSQL-Demonstrationsdatenbank fest.

    export PROJECT_ID=your-gcp-project
    export PG_INSTANCE_NAME=instance-pg-pitr
    export POSTGRES_PASSWORD=PasswordIsThis
    export POSTGRES_PITR_DEMO_DBNAME=pitr_demo
    

    Dabei gilt:

    • your-gcp-project: Der Name des Projekts, das Sie für diese Anleitung erstellt haben.
    • PasswordIsThis: Ein sicheres Passwort für die PostgreSQL-Datenbank.
  2. Legen Sie die Umgebungsvariable für die Google Cloud-Zone fest. Ersetzen Sie choose-an-appropriate-zone durch eine Google Cloud-Zone.

    export ZONE=choose-an-appropriate-zone
    export REGION=${ZONE%-[a-z]}
    
  3. Legen Sie die Umgebungsvariable für das Standardsubnetz Virtual Private Cloud (VPC) für die Region Ihrer Zone fest:

    export SUBNETWORK_URI=$(gcloud compute networks subnets \
        describe default --format=json --region=$REGION | \
        jq --raw-output '.ipCidrRange')
    
  4. Legen Sie die Umgebungsvariable für den Cloud Storage-Bucket fest. Ersetzen Sie archive-bucket durch einen eindeutigen Namen für den Cloud Storage-Bucket, in dem WALs gespeichert werden.

    export ARCHIVE_BUCKET=archive-bucket
    

Cloud Storage-Bucket erstellen

  • Erstellen Sie einen Cloud Storage-Bucket, um die WAL-Dateien aus der PostgreSQL-Datenbank zu archivieren:

    gsutil mb gs://${ARCHIVE_BUCKET}
    

Zugriff auf private IP-Adressinstanzen zulassen

Für die in dieser Anleitung verwendeten Instanzen müssen die VM-Instanzen wie in vielen Produktionsanwendungsfällen keine öffentlichen IP-Adressen abrufen. Die Instanzen benötigen jedoch Zugriff auf das öffentliche Internet, um die Beispiel-Container-Images abzurufen. Sie benötigen auch Zugriff, um eine Verbindung über eine sichere Shell herzustellen. Sie konfigurieren ein NAT-Gateway (Network Address Translation) und den Identity-Aware Proxy (IAP) für die TCP-Weiterleitung.

NAT-Gateway erstellen

Da die von Ihnen erstellten VM-Instanzen keine öffentlichen IP-Adressen haben, erstellen Sie ein NAT-Gateway. Damit können die Instanzen Container-Images aus dem Docker Hub abrufen.

  1. Erstellen Sie in Cloud Shell einen Cloud Router:

    export CLOUD_ROUTER_NAME=${PROJECT_ID}-nat-router
    gloud compute routers create $CLOUD_ROUTER_NAME \
        --network=default --region=$REGION
    
  2. Erstellen Sie das NAT-Gateway:

    gcloud compute routers nats create ${PROJECT_ID}-nat-gateway \
        --region=$REGION \
        --router=$CLOUD_ROUTER_NAME \
        --auto-allocate-nat-external-ips \
        --nat-all-subnet-ip-ranges
    

IAP für die TCP-Weiterleitung konfigurieren

IAP steuert den Zugriff auf Ihre Cloud-Anwendungen und VMs, die in Google Cloud ausgeführt werden. IAP prüft die Nutzeridentität und den Kontext der Anfrage, um zu bestimmen, ob ein Nutzer auf eine VM zugreifen darf.

  1. Lassen Sie in Cloud Shell Traffic vom TCP-Weiterleitungsnetzblock zu den Instanzen in Ihrem Projekt zu:

    export IAP_FORWARDING_CIDR=35.235.240.0/20
    gcloud compute --project=$PROJECT_ID firewall-rules create \
        cloud-iap-tcp-forwarding --direction=INGRESS  \
        --priority=1000 --network=default \
        --action=ALLOW --rules=all  \
        --source-ranges=$IAP_FORWARDING_CIDR
    
  2. Wenn Sie mithilfe eines TCP-Weiterleitungstunnels eine Verbindung herstellen möchten, fügen Sie eine Richtlinienbindung für Identity and Access Management (IAM) hinzu. Ersetzen Sie your-email-address durch die E-Mail-Adresse, mit der Sie sich bei der Google Cloud Console anmelden.

    export GRANT_EMAIL_ADDRESS=your-email-address
    gcloud projects add-iam-policy-binding $PROJECT_ID \
       --member=user:$GRANT_EMAIL_ADDRESS \
       --role=roles/iap.tunnelResourceAccessor
    

PostgreSQL-Datenbankinfrastruktur erstellen

  1. Klonen Sie in Cloud Shell das Quell-Repository, das die Konfigurationsskripts enthält, und ändern Sie den Shell-Kontext in das lokale Repository:

    git clone https://github.com/GoogleCloudPlatform/gcs-postgresql-recovery-tutorial
    cd gcs-postgresql-recovery-tutorial
    
  2. Führen Sie das folgende Skript aus, um die Datenbank-VM-Instanz zu erstellen und zu konfigurieren:

    cd bin
    ./create_postgres_instance.sh
    

    Für diese Anleitung startet dieses Skript eine VM-Instanz in der ausgewählten Zone mit dem Container-Optimized OS und zwei neuen angehängten nichtflüchtigen Speichern. In diesem Fall können Sie die von der API zurückgegebene Warnmeldung zu schlechter E/A-Leistung ignorieren, da die Skripts kleine nichtflüchtige Speicher erstellen.

Cloud-init-Konfiguration prüfen

Cloud-init ist ein Multi-Distributionspaket, das eine Cloud-Instanz initialisiert.

Sehen Sie sich das folgende cloud-init-Codebeispiel an:

write_files:
- path: /var/tmp/docker-entrypoint-initdb.d/init-pitr-demo-db.sql
  permissions: 0644
  owner: root
  content: |
    CREATE DATABASE ${POSTGRES_PITR_DEMO_DBNAME};

    \c ${POSTGRES_PITR_DEMO_DBNAME}

    CREATE SCHEMA pitr_db_schema;

    CREATE TABLE pitr_db_schema.customer
       (id SERIAL NOT NULL,
        name VARCHAR(255),
        create_timestamp TIMESTAMP DEFAULT current_timestamp,
        PRIMARY KEY (id));

    CREATE TABLE pitr_db_schema.invoice
       (id SERIAL NOT NULL,
        customer_id INTEGER
          REFERENCES pitr_db_schema.customer(id),
        description VARCHAR(1000),
        create_timestamp TIMESTAMP DEFAULT current_timestamp,
        PRIMARY KEY (customer_id, id));

- path: /etc/systemd/system/postgres.service
  permissions: 0644
  owner: root
  content: |
    [Unit]
    Requires=docker.service
    After=docker.service
    Description=postgres docker container

    [Service]
    TimeoutStartSec=0
    KillMode=none
    Restart=always
    RestartSec=5s
    ExecStartPre=-/usr/bin/docker kill postgres-db
    ExecStartPre=-/usr/bin/docker rm -v postgres-db
    ExecStart=/usr/bin/docker run -u postgres --name postgres-db \
                                  -v /var/tmp/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d \
                                  -v /mnt/disks/data:/var/lib/postgresql/data \
                                  -v /mnt/disks/wal:/var/lib/postgresql/wal \
                                  -e PGDATA=/var/lib/postgresql/data/pgdata \
                                  -e POSTGRES_PASSWORD=${POSTGRES_PASSWORD} \
                                  -e POSTGRES_INITDB_WALDIR=/var/lib/postgresql/wal/pg_wal \
                                  -p ${POSTGRES_PORT}:${POSTGRES_PORT} \
                               postgres:11-alpine
    ExecStop=-/usr/bin/docker stop postgres-db
    ExecStopPost=-/usr/bin/docker rm postgres-db

- path: /etc/systemd/system/wal_archive.service
  permissions: 0644
  owner: root
  content: |
    [Unit]
    Requires=docker.service postgres.service
    After=docker.service postgres.service
    Description=WAL archive docker container

    [Service]
    TimeoutStartSec=10min
    Type=oneshot
    ExecStart=/usr/bin/docker run --name wal-archive \
                                  -v /mnt/disks/wal/pg_wal_archive:/mnt/wal_archive \
                               google/cloud-sdk:slim gsutil mv /mnt/wal_archive/[0-9A-F]*[0-9A-F] gs://${ARCHIVE_BUCKET}
    ExecStopPost=-/usr/bin/docker rm wal-archive

- path: /etc/systemd/system/wal_archive.timer
  permissions: 0644
  owner: root
  content: |
    [Unit]
    Description=Archive WAL to GCS (every 5 minutes)

    [Timer]
    OnBootSec=5min
    OnUnitInactiveSec=5min
    OnUnitActiveSec=5min

    [Install]
    WantedBy=timers.target

In dieser Anleitung wird "cloud-init" für Folgendes verwendet:

  1. Erstellen von zwei Blockspeichergeräten für Persistent Disk.
  2. Erstellen der Dateisysteme auf den beiden Geräten: eines für die Daten und eines für die Archivlogs.
  3. Bereitstellen der Geräte an logischen Bereitstellungspunkten auf der VM-Instanz, die für die Docker-Container freigegeben sind.
  4. Erstellen und Starten eines systemd-Dienstes (postgres.service), der einen PostgreSQL-Docker-Container so startet:
    • Die nichtflüchtigen Speicher, die als Volumes bereitgestellt wurden.
    • Der PostgreSQL-Port (5432), der auf dem VM-Host veröffentlicht wurde.
  5. Erstellen einer /var/tmp/docker-entrypoint-initdb.d/init-pitr-demo-db.sql-Datei, um einen einfachen Satz von Tabellen in einer Demonstrationsdatenbank und einem Schema zu erstellen.
  6. Erstellen und Starten eines zweiten systemd-Dienstes (wal_archive.service), der einen Docker-Container des Google Cloud CLI mit den als Volume bereitgestellten WAL-Laufwerken ausführt. Dieser Dienst sichert archivierte WAL-Dateien in Cloud Storage.
  7. Erstellen, Aktivieren und Starten eines systemd-Timers (wal_archive.timer), der den wal_archive.service regelmäßig ausführt.
  8. Darauf achten, dass der PostgreSQL-Port (5432) für das VPC-Subnetz geöffnet ist, damit der Transaktionsgenerator den Datenbankport erreichen kann.

Konfiguration der Datenbankinstanz ändern

Der Datenbankserver wird ausgeführt, aber Sie müssen den Netzwerkzugriff konfigurieren und den WAL-Archivierungsprozess starten.

Verbindung zu Datenbank-VM-Instanz herstellen

  1. Rufen Sie in der Google Cloud Console die Seite VM-Instanzen auf.

    Zur VM-Instanz

  2. Klicken Sie zum Öffnen einer Terminal-Shell neben der von Ihnen erstellten instance-pg-pitr-Instanz auf SSH.

  3. Prüfen Sie in der Terminal-Shell, ob der Docker-Container gestartet wurde: docker ps

    Die Ausgabe sieht etwa so aus:

    CONTAINER ID   IMAGE                COMMAND                  CREATED              STATUS              PORTS   NAMES
    8bb65d8c1197   postgres:11-alpine   "docker-entrypoint.s…"   About a minute ago   Up About a minute           postgres-db
    

    Wenn der Container noch nicht ausgeführt wird, warten Sie einen Moment. Prüfen Sie die Ausführung dann mit demselben Befehl noch einmal.

Eingehende Netzwerkverbindungen zur Datenbank zulassen

  1. Öffnen Sie in der Terminal-Shell der instance-pg-pitr-Instanz die Konfigurationsdatei für die hostbasierte PostgreSQL-Authentifizierung zur Bearbeitung:

    sudoedit /mnt/disks/data/pgdata/pg_hba.conf
    
  2. Zum Entfernen des Standardzugriffs für alle IP-Adressen auf die Datenbank kommentieren Sie die folgende Zeile am Ende der Datei aus. Fügen Sie dazu am Anfang der Zeile # hinzu. Die Zeile in der Datei sieht in etwa so aus:

    #host all all all md5
    
  3. Fügen Sie die folgende Zeile am Ende der Datei hinzu, um passwortgeschützte Verbindungen von Hosts im CIDR-Block 10.0.0.0/8 zuzulassen:

    host    all             all             10.0.0.0/8               md5
    

    Dieser Eintrag aktiviert die Konnektivität vom VPC-Subnetz, in dem später der Transaktionsgenerator erstellt wird.

  4. Speichern und schließen Sie die Datei.

WAL-Archivierung konfigurieren

  1. Bearbeiten Sie in der Terminal-Shell der instance-pg-pitr-Instanz die Datei postgresql.conf:

    sudoedit /mnt/disks/data/pgdata/postgresql.conf
    
  2. Ersetzen Sie die vorhandenen auskommentierten Zeilen archive_mode, archive_command und archive_timeout durch folgende:

    archive_mode=on
    archive_command = '( ARCHIVE_PATH=/var/lib/postgresql/wal/pg_wal_archive;
    test ! -f $ARCHIVE_PATH/%f && cp %p $ARCHIVE_PATH/%f.cp && mv
    $ARCHIVE_PATH/%f.cp $ARCHIVE_PATH/%f ) '
    archive_timeout = 120
    

    Wenn Sie die Zeilen in der geänderten Datei ersetzen, ähnelt sie dem folgenden Code-Snippet:

    
    .... illustrative snippet start ....
    
    # - Archiving -
    archive_mode=on
    archive_command = '( ARCHIVE_PATH=/var/lib/postgresql/wal/pg_wal_archive;  test ! -f $ARCHIVE_PATH/%f && cp %p $ARCHIVE_PATH/%f.cp && mv $ARCHIVE_PATH/%f.cp $ARCHIVE_PATH/%f ) '
    archive_timeout = 120
    #------------------------------------------------------------------------------
    # REPLICATION
    #------------------------------------------------------------------------------
    
    .... illustrative snippet end ....
    
    
  3. Speichern und schließen Sie die Datei.

Konfigurationsänderungen anwenden und prüfen

  1. Starten Sie den Container in der Terminal-Shell der instance-pg-pitr-Instanz neu, um die Änderungen zu übernehmen:

    sudo systemctl restart postgres
    
  2. Prüfen Sie die WAL-Segmentdateien:

    sudo ls -l /mnt/disks/wal/pg_wal
    

    Die Ausgabe sieht etwa so aus:

    total 16388
    -rw------- 1 postgres 70 16777216 Sep  5 23:07 000000010000000000000001
    drwx------ 2 postgres 70     4096 Sep  5 23:05 archive_status
    
  3. Prüfen Sie die Netzwerkverbindung zur Datenbank:

    export LOCAL_IP=127.0.0.1
    docker exec postgres-db psql -w --host=$LOCAL_IP \
          --command='SELECT 1'
    

    Die Ausgabe sieht etwa so aus:

    ?column?
    ----------
           1
    (1 row)
    
  4. Trennen Sie die SSH-Verbindung zur Instanz.

Transaktionsgenerator zum Füllen der Datenbank starten

Mit den folgenden Schritten wird ein Go-Programm gestartet, das Transaktionen für diese Anleitung erstellt. Das Programm wird in einem Container auf einer VM-Instanz ausgeführt.

Das Image für den Container wurde bereits in einem Projekt mit einer öffentlichen Container Registry erstellt und gehostet.

  1. Wechseln Sie in Cloud Shell zum Verzeichnis des Transaktionsgenerators:

    cd ~/gcs-postgresql-recovery-tutorial/bin
    
  2. Legen Sie die Umgebungsvariablen fest:

    export TRANS_GEN_INSTANCE_NAME=instance-trans-gen
    export POSTGRES_HOST_IP=$(gcloud compute instances describe  \
        --format=json --zone=${ZONE} ${PG_INSTANCE_NAME} | \
        jq --raw-output '.networkInterfaces[0].networkIP')
    
  3. Starten Sie die Instanz, um den Transaktionsgenerator auszuführen:

    ./run_trans_gen_instance.sh
    

    Ignorieren Sie die Warnmeldung zu schlechter E/A-Leistung.

  4. Warten Sie einen Moment und prüfen Sie, ob die Transaktionen die PostgreSQL-Datenbank erreichen:

    gcloud compute ssh $PG_INSTANCE_NAME \
       --tunnel-through-iap \
       --zone=$ZONE \
       --command="docker exec postgres-db psql \
       --dbname=$POSTGRES_PITR_DEMO_DBNAME \
       --command='SELECT COUNT(*) FROM pitr_db_schema.customer;'"
    

    Die Ausgabe enthält eine Anzahl größer als 0, wenn Datensätze vom Transaktionsgenerator zur Datenbank hinzugefügt werden:

     count
    -------
       413
    (1 row)
    

Sicherungszeitplan für binäre Snapshots konfigurieren

Sie können nichtflüchtige Speicher nach einem Zeitplan sichern und für eine in der Ressourcenrichtlinie definierte Zeit aufbewahren.

Snapshot-Zeitplan erstellen

  1. Legen Sie in Cloud Shell Umgebungsvariablen fest:

    export ZONE=zone-of-your-instance
    export SNAPSHOT_SCHEDULE_NAME=pg-disk-schedule
    export REGION=${ZONE%-[a-z]}
    export SNAPSHOT_WINDOW_START=$(TZ=":GMT" date "+%H:00")
    export SNAPSHOT_RETENTION_DAYS=2
    export SNAPSHOT_FREQUENCY_HOURS=1
    

    Ersetzen Sie zone-of-your-instance durch die Google Cloud-Zone, in der Sie zuvor die Datenbank-VM gestartet haben.

  2. Erstellen Sie den Snapshot-Zeitplan:

    gcloud compute resource-policies create snapshot-schedule \
        $SNAPSHOT_SCHEDULE_NAME \
        --region=$REGION \
        --max-retention-days=$SNAPSHOT_RETENTION_DAYS \
        --on-source-disk-delete=apply-retention-policy \
        --hourly-schedule=$SNAPSHOT_FREQUENCY_HOURS \
        --start-time=$SNAPSHOT_WINDOW_START \
        --storage-location=$REGION
    

Snapshot-Zeitplan an Laufwerke anhängen

Wenn Sie das Skript zum Erstellen einer Instanz ausgeführt haben, wurden die Daten- und WAL-Volumes als zwei unabhängige nichtflüchtige Speicher erstellt. Zum Erstellen von Snapshots nichtflüchtiger Speicher nach einem definierten Zeitplan weisen Sie jedem nichtflüchtigen Speicher eine Ressourcenrichtlinie zu. In diesem Fall möchten Sie, dass die Laufwerk-Snapshots gleichzeitig erstellt werden, sodass Sie für beide nichtflüchtigen Speicher, die an die Compute Engine-VM angehängt sind, dieselbe Richtlinie verwenden.

  1. Legen Sie in Cloud Shell Umgebungsvariablen fest:

    export SNAPSHOT_SCHEDULE_NAME=pgdata-disk-schedule
    export PG_INSTANCE_NAME=instance-pg-pitr
    export ZONE=zone-of-your-instance
    
  2. Hängen Sie die Zeitplanrichtlinie an den nichtflüchtigen Speicher an:

    gcloud beta compute disks add-resource-policies ${PG_INSTANCE_NAME}-data \
        --resource-policies $SNAPSHOT_SCHEDULE_NAME \
        --zone $ZONE
    
  3. Hängen Sie die Zeitplanrichtlinie an den nichtflüchtigen WAL-Speicher an:

    gcloud beta compute disks add-resource-policies ${PG_INSTANCE_NAME}-wal \
        --resource-policies $SNAPSHOT_SCHEDULE_NAME \
        --zone $ZONE
    

Snapshot manuell ausführen

(Optional) Geplante Snapshots werden innerhalb des Zeitplanfensters erstellt. Daher ist es unwahrscheinlich, dass ein Snapshot sofort erstellt wird, wenn Sie den Zeitplan erstellt haben. Wenn Sie nicht auf den geplanten Snapshot warten möchten, können Sie den ersten Snapshot manuell ausführen.

  1. Legen Sie in Cloud Shell Umgebungsvariablen fest:

    export ZONE=zone-of-your-instance
    export PG_INSTANCE_NAME=instance-pg-pitr
    export REGION=${ZONE%-[a-z]}
    
  2. Erstellen Sie einen Snapshot der beiden nichtflüchtigen Speicher der PostgreSQL-Instanz:

    gcloud compute disks snapshot \
        ${PG_INSTANCE_NAME}-data ${PG_INSTANCE_NAME}-wal \
        --snapshot-names=${PG_INSTANCE_NAME}-data-`date+%s`,${PG_INSTANCE_NAME}-wal-`date +%s` \
        --zone=$ZONE --storage-location=$REGION
    
  3. Rufen Sie die von Ihnen erstellten Snapshots auf:

    gcloud compute snapshots list
    

    Die Ausgabe sieht etwa so aus:

    NAME                              DISK_SIZE_GB  SRC_DISK                                   STATUS
    instance-pg-pitr-data-1578339767  200           us-central1-f/disks/instance-pg-pitr-data  READY
    instance-pg-pitr-wal-1578339767   100           us-central1-f/disks/instance-pg-pitr-wal   READY
    

PITR ausführen

Eine PITR wird häufig verwendet, um Daten wiederherzustellen, die aufgrund eines betrieblichen oder programmatischen Fehlers verloren gegangen sind.

In diesem Abschnitt der Anleitung führen Sie eine Datenbankaktualisierung durch, um einen schwerwiegenden Datenverlust zu simulieren. Anschließend simulieren Sie eine Panikantwort, bevor Sie eine Wiederherstellung für den Zeitpunkt starten, bevor der fehlerhafte Befehl ausgegeben wurde.

Durchführung einer PITR gewährleisten

Bevor Sie eine PITR ausführen, müssen Sie genügend Zeit für die Ausführung folgender Aktionen einplanen:

  • Die binären Sicherungen (Laufwerk-Snapshots)
  • WAL-Archivierung

In dieser Anleitung wurde für archive_timeout ein ungewöhnlich niedriger Wert von 120 Sekunden festgelegt, um eine häufige Rotation der WAL-Dateien zu erzwingen. Sie müssen auch warten, bis mindestens ein geplanter Laufwerk-Snapshot erstellt wurde, oder diesen manuell erstellen.

  1. Prüfen Sie, ob mindestens ein Snapshot erstellt wurde:

    1. Rufen Sie in der Google Cloud Console die Seite Snapshots auf.

      Zur Seite "Snapshots"

    2. Prüfen Sie, ob mindestens zwei Snapshots vorhanden sind: einer für das Daten-Volume und einer für das WAL-Volume, z. B. instance-pg-pitr--us-central1-a-20190805023535-i3hpw7kn.

  2. Prüfen Sie, ob die Segmentdateien in Cloud Storage archiviert sind:

    1. Wechseln Sie in der Google Cloud Console zum Cloud Storage-Browser.

      Zum Cloud Storage-Browser

    2. Klicken Sie auf archive-bucket.

      Cloud Storage-Bucket mit Objekten

Daten beschädigen

Um einen schwerwiegenden Datenverlust zu simulieren, öffnen Sie eine Befehlszeilen-Shell für die PostgreSQL-Datenbank und beschädigen Sie die Daten in der vom Transaktionsgenerator gefüllten Tabelle.

  1. Rufen Sie in der Google Cloud Console die Seite VM-Instanzen auf.

    Zur Seite "VM-Instanzen"

  2. Klicken Sie für die Instanz instance-pg-pitr auf SSH.

  3. Führen Sie im SSH-Terminal das Terminal-basierte PostgreSQL-Frontend im Docker-Container aus:

    docker exec -it postgres-db psql --dbname=pitr_demo
    
  4. Zum Ändern einer Zeile in der Kundentabelle senden Sie eine SQL-DML-Anweisung mit einem absichtlichen Tippfehler in der PostgreSQL-Shell:

    UPDATE pitr_db_schema.customer
    SET name = 'New Name for customer id=1';
    WHERE id = 1;
    

    Die Ausgabe sieht etwa so aus:

    UPDATE 999
    pitr_demo=#  WHERE id = 1;
    ERROR:  syntax error at or near "WHERE"
    LINE 1: WHERE id = 1;
            ^
     

    Der Fehler wurde erzeugt, weil vor der WHERE-Klausel ein zusätzliches Semikolon eingefügt wurde. Alle Zeilen in der Datenbank wurden aktualisiert. Sie können jetzt eine PITR verwenden, um die Zeilen wiederherzustellen, die von der falschen Anweisung geändert wurden.

Zielzeit für die Wiederherstellung festlegen

Der erste Schritt in einer PITR besteht darin, die Zielzeit für die Wiederherstellung zu bestimmen. Diese Zeit wird ermittelt, indem Ihre Daten durchsucht werden, um einen Zeitpunkt kurz vor dem schädlichen Ereignis zu identifizieren.

  1. Rufen Sie in der Terminal-Shell der instance-pg-pitr-Instanz den maximalen Zeitstempel der beschädigten Zeilen ab:

    SELECT MAX(create_timestamp)::timestamptz
      FROM pitr_db_schema.customer
    WHERE name = 'New Name for customer id=1';
    

    Die Ausgabe sieht etwa so aus:

                 max              .
    -------------------------------
    2019-08-05 18:14:58.226511+00
    (1 row)
    

    In einer Produktionsdatenbank ist die Abfrage zum Bestimmen des Wiederherstellungsziels komplexer, insbesondere in Fällen, in denen die betroffene Tabelle groß ist und die indikative Spalte nicht indexiert ist.

  2. Kopieren Sie das Ergebnis. Sie verwenden im nächsten Schritt den von dieser Abfrage zurückgegebenen Wert.

Datenbank wiederherstellen

In dieser Anleitung automatisiert ein Wiederherstellungsskript die PITR. Wir empfehlen Ihnen, einen automatisierten Prozess zur Wiederherstellung der Datenbank zu verwenden und diesen Prozess regelmäßig zu testen.

  1. Ändern Sie in Cloud Shell das aktuelle Arbeitsverzeichnis in den Speicherort des Wiederherstellungsskripts:

    cd ~/gcs-postgresql-recovery-tutorial/bin
    
  2. Legen Sie die erforderlichen Umgebungsvariablen für das Skript fest. Ersetzen Sie YYYY-MM-DD HH:MM:SS.999999+00 durch die zuvor kopierte Abfrageausgabe.

    export PROJECT_ID=$(gcloud config get-value project)
    export PG_INSTANCE_NAME=instance-pg-pitr
    export POSTGRES_PASSWORD=PasswordIsThis
    export PG_INSTANCE_NAME=instance-pg-pitr
    export RECOVER_BUCKET=archive-bucket
    export PIT_RECOVERY_TARGET="YYYY-MM-DD HH:MM:SS.999999+00"
    export ZONE=zone-of-your-instance
    
  3. Führen Sie das Wiederherstellungsskript aus:

    ./recover_to_point_in_time.sh
    

Informationen zum Wiederherstellungsskript

Dieser Abschnitt enthält einige Details zu den Eingabeparametern und den vom Skript ausgeführten Schritten.

Für das Skript müssen die folgenden Umgebungsvariablen festgelegt sein:

  • PIT_RECOVERY_TARGET: Die Zielzeit für die Wiederherstellung.
  • PROJECT_ID: Das Projekt, in dem sich die PG_INSTANCE_NAME-Instanz befindet.
  • ZONE: Die Zone, in der sich die PG_INSTANCE_NAME-Instanz befindet.
  • PG_INSTANCE_NAME: Die Instanz, auf der die PostgreSQL-Instanz production ausgeführt wird.
  • RECOVER_BUCKET: Der Cloud Storage-Bucket, in dem WAL-Segmentdateien archiviert werden.
  • POSTGRES_PASSWORD: Das Passwort für den PostgreSQL-Datenbanknutzer.

Das Skript führt die folgenden Schritte aus:

  1. Bestimmt die neuesten Laufwerk-Snapshots anhand des Datums und der Uhrzeit des Wiederherstellungsziels.
  2. Erstellt eine cloud-init.yaml-Datei, die für eine containeroptimierte Storage-VM bereitgestellt wird, auf der die PITR-Datenbank ausgeführt wird. Die Datei cloud-init.yaml erstellt Konfigurationsdateien und führt mehrere Systembefehle aus, um die folgende Umgebung einzurichten:

    • Ein gcsfuse-Container, der den WAL-Segment-Dateiarchiv-Bucket als Volume bereitstellt, das dann dem Host mit einem Docker-Bind-Mount bereitgestellt wird.
    • Ein postgres-db-Container, in dem das Datenbankmodul Folgendes ausführt:

      • Das Hostdateisystem, an das die nichtflüchtigen Speicher als Volumes angehängt sind.
      • Das Hostdateisystem, an das der Cloud Storage-Bucket als Volume angehängt ist.
    • Eine recovery.conf-Wiederherstellungsdatei im PostgreSQL-Datenverzeichnis mit den folgenden Informationen:

      • Dem Zieldatum.
      • Dem Befehl restore: ein parametrisierter Kopierbefehl, mit dem die Datenbank WAL-Segmentdateien nach Bedarf aus dem Archivdateisystem kopiert. %f ist die Segmentdatei und %p der Pfad, den die Datenbank während der Wiederherstellung zur Verarbeitung von Dateien verwendet.
    • Die archive_-Einstellungen werden aus der Einstellungsdatei postgresql.conf auskommentiert, um das WAL-Archivverzeichnis nicht zu beschädigen.

  3. Startet die PITR-Instanz mit den folgenden Informationen:

    • Einem Namen, der durch Kombination der Umgebungsvariablen $PG_INSTANCE_NAME und der alphanumerischen Werte der Umgebungsvariablen $PIT_RECOVERY_TARGET erstellt wird.
    • Nichtflüchtige Speicher, die aus den zuvor identifizierten Laufwerk-Snapshots erstellt wurden.

Hier sehen Sie eine recovery.conf-Beispieldatei:

restore_command = '(test -d /var/lib/postgresql/wal/pg_wal_recover && cp /var/lib/postgresql/wal/pg_wal_recover/%f %p ) '
recovery_target_time='YYYY-MM-DD HH:MM:SS UTC'
recovery_target_inclusive=true

Wiederherstellung prüfen

  1. Rufen Sie in der Google Cloud Console die Seite VM-Instanzen auf.

    Zur Seite "VM-Instanzen"

  2. Klicken Sie für die Instanz instance-pg-pitr-YYYYMMDDHHMMSS auf SSH.

  3. Führen Sie in den SSH-Terminals das Terminal-basierte Frontend von PostgreSQL im Docker-Container aus:

    docker exec -it postgres-db psql --dbname=pitr_demo
    

    Wenn Sie die folgende Fehlermeldung erhalten, warten Sie einen Moment, bis der PostgreSQL-Container gestartet ist. Führen Sie den Befehl dann noch einmal aus:

    Error: No such container: postgres-db
    
  4. Prüfen Sie die Daten in der Kundentabelle:

    SELECT * FROM pitr_db_schema.customer
    WHERE id > (SELECT MAX(id)-10 FROM pitr_db_schema.customer);
    

    Die Ausgabe sieht etwa so aus:

       id  |           name            |      create_timestamp
    ------+---------------------------+----------------------------
      711 | customer_name_from_golang | 2019-12-06 18:03:51.229444
      712 | customer_name_from_golang | 2019-12-06 18:03:52.531755
      713 | customer_name_from_golang | 2019-12-06 18:03:53.555441
      714 | customer_name_from_golang | 2019-12-06 18:03:54.581872
      715 | customer_name_from_golang | 2019-12-06 18:03:55.607459
      716 | customer_name_from_golang | 2019-12-06 18:03:56.633362
      717 | customer_name_from_golang | 2019-12-06 18:03:57.658523
      718 | customer_name_from_golang | 2019-12-06 18:03:58.685469
      719 | customer_name_from_golang | 2019-12-06 18:03:59.706939
    

    Der Name gibt den vom Transaktionsgenerator erstellten Wert an. Die letzte Zeile enthält einen Zeitstempel, der vor dem Wiederherstellungsziel liegt, das Sie dem Wiederherstellungsskript in einer Umgebungsvariablen bereitgestellt haben. Je nach Anzahl der wiederherzustellenden Datensätze müssen Sie möglicherweise einige Sekunden warten, bis alle Zeilen aktualisiert wurden.

Bereinigen

Am einfachsten können Sie die Abrechnung deaktivieren, wenn Sie das Google Cloud-Projekt löschen, das Sie für die Anleitung erstellt haben. Alternativ haben Sie die Möglichkeit, die einzelnen Ressourcen zu löschen.

Projekt löschen

  1. Wechseln Sie in der Google Cloud Console zur Seite Ressourcen verwalten.

    Zur Seite „Ressourcen verwalten“

  2. Wählen Sie in der Projektliste das Projekt aus, das Sie löschen möchten, und klicken Sie dann auf Löschen.
  3. Geben Sie im Dialogfeld die Projekt-ID ein und klicken Sie auf Shut down (Beenden), um das Projekt zu löschen.

Nächste Schritte