Compute Engine での PostgreSQL データベースの PITR の実行


このチュートリアルでは、アーカイブ プロセスを設定する方法と、Compute Engine で実行されている PostgreSQL データベースのポイントインタイム リカバリ(PITR)を実行する方法について説明します。

このチュートリアルでは、デモ用のデータベースを作成し、アプリケーション ワークロードを実行します。次に、アーカイブ プロセスとバックアップ プロセスを構成します。その後、バックアップ、アーカイブ、復元のプロセスを確認する方法について説明します。最後に、データベースを特定の時点まで復元する方法について説明します。

このチュートリアルは、PostgreSQL データベースのバックアップと復元の戦略に関心があるデータベース管理者、システム オペレータ、DevOps 専門家、クラウド アーキテクトを対象としています。

このチュートリアルでは、読者が Docker コンテナに精通しており、Linux コマンド、PostgreSQL データベース エンジン、Compute Engine を使い慣れていることを前提としています。

目標

  • バックアップとアーカイブのプロセスを設定する。
  • PITR を実行する。
  • バックアップをモニタリングします。
  • 復元を確認します。

費用

このドキュメントでは、Google Cloud の次の課金対象のコンポーネントを使用します。

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。 新しい Google Cloud ユーザーは無料トライアルをご利用いただける場合があります。

このドキュメントに記載されているタスクの完了後、作成したリソースを削除すると、それ以上の請求は発生しません。詳細については、クリーンアップをご覧ください。

始める前に

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. Make sure that billing is enabled for your Google Cloud project.

  4. Enable the Compute Engine and Cloud Storage APIs.

    Enable the APIs

  5. Install the Google Cloud CLI.
  6. To initialize the gcloud CLI, run the following command:

    gcloud init
  7. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  8. Make sure that billing is enabled for your Google Cloud project.

  9. Enable the Compute Engine and Cloud Storage APIs.

    Enable the APIs

  10. Install the Google Cloud CLI.
  11. To initialize the gcloud CLI, run the following command:

    gcloud init
  12. In the Google Cloud console, activate Cloud Shell.

    Activate Cloud Shell

    At the bottom of the Google Cloud console, a Cloud Shell session starts and displays a command-line prompt. Cloud Shell is a shell environment with the Google Cloud CLI already installed and with values already set for your current project. It can take a few seconds for the session to initialize.

コンセプト

チュートリアルを開始する前に、PostgreSQL の次のコンセプトを確認してください。

  • 継続的なアーカイブ。データベースが継続的に順次トランザクションをファイルに保存することを指します。
  • ログ先行書き込み(WAL)。データファイルへの変更は、ファイルに適用される前に WAL に記録されます。
  • WAL レコード。データベースに適用される各トランザクションは、WAL レコードとしてフォーマットされて、保存されます。
  • セグメント ファイル。セグメント ファイルのファイル名は単調に増加し、可能な限り多くの WAL レコードが含まれます。ファイルサイズは構成可能で、デフォルトは 16 MiB です。大きなサイズまたは多数のトランザクションが予想される場合は、生成されるセグメント ファイルの合計数を減らし、ファイル管理の負担を軽減するために、このファイルサイズを大きくできます。

詳細については、信頼性とログ先行書き込みをご覧ください。

次の図は、WAL が 2 つのステージでどのように保持されるかを示しています。

永続的な WAL の 2 つのステージ。

上の図では、永続的な WAL の最初のステージは、テーブルへの書き込みと同時に、WAL バッファ内の書き込みトランザクションを記録するデータベース エンジンで構成されています。トランザクションが commit されると、2 番目のステージで WAL バッファがディスクに書き込まれ(フラッシュされ)、WAL セグメント ファイルに追加されます。

PITR を選択する

PITR は、次のような場合に適しています。

  • リカバリ ポイント目標(RPO)を最小限に抑える。 RPO とは、許容されるデータ損失の最大時間です。この時間を超えるとビジネス プロセスに重大な影響がおよびます。バックアップ スナップショット間ですべてのトランザクションを WAL に保存すると、最後の完全バックアップ以降のトランザクションがデータベースに適用されるため、失われるデータの量が大幅に減少します。
  • リカバリ時間目標(RTO)を最小限に抑える。RTO は、破壊的なイベントが発生した場合にデータベースを復元するために必要な時間です。バイナリ バックアップとログアーカイブを設定すると、データベースの復元に必要な時間を最小限に抑えることができます。
  • データ破損のバグや管理上の誤りを修正する。 コードのリリースによって壊滅的なデータの破損が発生した場合や、定期メンテナンス中に回復不能なミスをした場合、その前の状態に復元できます。

マイクロサービス アーキテクチャなどの一部のアプリケーション アーキテクチャには、独立した復元が必要な並列データベースが存在する場合があります。たとえば、小売アプリケーションで顧客データが 1 つのデータベースに格納され、小売注文の詳細と在庫情報が他のデータベースに格納されている場合などです。データの全体的な状態に応じて、1 つ、2 つ、またはすべてのデータベースを並行して復元する必要があります。

以下の状況では、PITR は適切でありません。

  • RPO が大きい。障害復旧ポリシーにより、最近のスナップショットの後に受信したトランザクションの損失を許容できる場合は、追加の手順を行わずに、データの復元時間を短縮することに集中できます。
  • データベースを完全に復元する必要がある。直近のトランザクションに復元することが目標である場合、復元のターゲットは最後に保存されたトランザクションのタイムスタンプになります。このシナリオは PITR の特殊なケースですが、意味的にこの目標は「完全復旧」と呼ばれます。

パフォーマンスに関する注意事項

アーカイブ プロセスにより、データベース サーバーに追加の I/O 負荷がかかります。追加の負荷は、トランザクションの書き込み、更新、削除の量に比例するため、ワークロードの特性によって異なります。

WAL アーカイブのアクティビティによってプライマリ データベースに及ぼされる I/O の影響を減らすには、読み取り専用レプリカを使用して定期的に WAL アーカイブを実行します。

この構成により、WAL ファイルの転送に関連するバッチ指向の I/O アクティビティからプライマリ データベースが分離されます。読み取り専用レプリカを宛先とするトランザクションは、プライマリ データベースから一定のストリームで送信されるため、安定したスループットに対する影響ははるかに小さくなります。

さらに、本番環境のデータベース トポロジにすでに読み取り専用のレプリカが含まれている場合、この構成によって管理や価格などの負担が増えることはありません。

リファレンス アーキテクチャ

次の図は、このチュートリアルで実装するアーキテクチャを示しています。

Compute Engine と Cloud Storage を使用した、PITR のクラウド インフラストラクチャ。

このチュートリアルでは、クラウド インフラストラクチャを作成して、次のコンポーネントを使用する PITR を確認します。

  • Compute Engine で実行される PostgreSQL データベース サーバー。
  • スナップショットとトランザクション ログを保存するための Cloud Storage。

次の図は、PostgreSQL データベース仮想マシン(VM)で起動される 2 つの Docker コンテナを示しています。関心の分離として、データベース サーバーはコンテナの 1 つで実行され、WAL アーカイバは別のコンテナで実行されます。

データベース サーバーと WAL アーカイバ用の Docker コンテナ。

この図は、各コンテナの Docker ボリュームが、ホスト VM の永続ディスク マウント ポイントにどのようにマッピングされるかを示しています。

環境変数を設定する

このチュートリアルで使用するスクリプトとコマンドは、シェル環境変数に依存します。

  1. Cloud Shell で、プロジェクト、インスタンス名、デモ用の PostgreSQL データベースの環境変数を設定します。

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

    次のように置き換えます。

    • your-gcp-project: このチュートリアル用に作成したプロジェクトの名前。
    • PasswordIsThis: PostgreSQL データベースの安全なパスワード。
  2. Google Cloud ゾーンの環境変数を設定します。choose-an-appropriate-zoneGoogle Cloud ゾーンに置き換えます。

    export ZONE=choose-an-appropriate-zone
    export REGION=${ZONE%-[a-z]}
    
  3. ゾーンのリージョンのデフォルトの Virtual Private Cloud(VPC)サブネットの環境変数を設定します。

    export SUBNETWORK_URI=$(gcloud compute networks subnets \
        describe default --format=json --region=$REGION | \
        jq --raw-output '.ipCidrRange')
    
  4. Cloud Storage バケットの環境変数を設定します。archive-bucket は、WAL が保存されている Cloud Storage バケットの一意の名前に置き換えます。

    export ARCHIVE_BUCKET=archive-bucket
    

Cloud Storage バケットの作成

  • Cloud Storage バケットを作成して、PostgreSQL データベースから WAL ファイルをアーカイブします。

    gcloud storage buckets create gs://${ARCHIVE_BUCKET}
    

プライベート IP アドレスのインスタンスにアクセスできるようにする

このチュートリアルで使用するインスタンスの場合、多くの本番環境のユースケースと同様に、VM インスタンスでパブリック IP アドレスを取得する必要はありません。ただし、インスタンスはパブリック インターネットにアクセスしてサンプル コンテナ イメージを pull する必要があり、セキュアシェルを使用して接続するための権限が必要です。ネットワーク アドレス変換(NAT)ゲートウェイを構成し、TCP 転送用の Identity-Aware Proxy(IAP)を構成します。

NAT ゲートウェイを作成する

作成する VM インスタンスにはパブリック IP アドレスがないため、インスタンスが Docker Hub からコンテナ イメージを pull できるように NAT ゲートウェイを作成します。

  1. Cloud Shell で、Cloud Router を作成します。

    export CLOUD_ROUTER_NAME=${PROJECT_ID}-nat-router
    gloud compute routers create $CLOUD_ROUTER_NAME \
        --network=default --region=$REGION
    
  2. NAT ゲートウェイを作成します。

    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
    

TCP 転送用に IAP を構成する

IAP は、Google Cloud で実行されているクラウド アプリケーションと VM へのアクセスを制御します。IAP は、リクエストのユーザー ID とコンテキストを確認して、ユーザーが VM にアクセスできるかどうかを判断します。

  1. Cloud Shell で、TCP 転送ネットブロックからプロジェクト内のインスタンスへのトラフィックを許可します。

    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. TCP 転送トンネルを使用して接続するには、Identity and Access Management(IAM)ポリシー バインディングを追加します。your-email-address は、Google Cloud コンソールへのログインに使用するメールアドレスに置き換えます。

    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 データベース インフラストラクチャを作成する

  1. Cloud Shell で、構成スクリプトを含むソース リポジトリのクローンを作成し、シェル コンテキストをローカル リポジトリに変更します。

    git clone https://github.com/GoogleCloudPlatform/gcs-postgresql-recovery-tutorial
    cd gcs-postgresql-recovery-tutorial
    
  2. データベース VM インスタンスを作成して構成するには、次のスクリプトを実行します。

    cd bin
    ./create_postgres_instance.sh
    

    このチュートリアルのスクリプトでは、コンテナ用に最適化されたオペレーティング システムと 2 つの新たに追加された永続ディスクを使用して、選択したゾーンで VM インスタンスを起動します。この場合、スクリプトによって小さな永続ディスクが作成されるため、API から返される I/O パフォーマンスの低下に関する警告メッセージは無視してかまいません。

cloud-init 構成を確認する

cloud-init は、クラウド インスタンスを初期化するマルチ配布パッケージです。

次の cloud-init コードサンプルを確認します。

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

このチュートリアルでは、cloud-init を使用して次のことを行います。

  1. 2 つの永続ディスク ブロック ストレージ デバイスを作成します。
  2. 2 つのデバイスにファイル システムを作成します。1 つはデータ用で、もう 1 つはアーカイブログ用です。
  3. Docker コンテナと共有されている VM インスタンスの論理マウント ポイントにデバイスをマウントします。
  4. systemd サービス(postgres.service)を作成して開始します。これにより、次とともに、PostgreSQL Docker コンテナが開始します。
    • ボリュームとしてマウントされた永続ディスク。
    • VM ホストに公開されている PostgreSQL ポート(5432)。
  5. /var/tmp/docker-entrypoint-initdb.d/init-pitr-demo-db.sql ファイルを作成して、デモ用のデータベースとスキーマに簡単なテーブルセットを作成します。
  6. WAL ディスクをボリュームとしてマウントして、Google Cloud CLI の Docker コンテナを実行する 2 つ目の systemd サービス(wal_archive.service)を作成して起動します。このサービスにより、アーカイブされた WAL ファイルが Cloud Storage にバックアップされます。
  7. wal_archive.service を定期的に実行する systemd タイマー(wal_archive.timer)を作成して有効にし、開始します。
  8. トランザクション生成ツールがデータベース ポートに到達できるように、VPC サブネットの PostgreSQL ポート(5432)が開いていることを確認します。

データベース インスタンス構成を変更する

データベース サーバーは稼働していますが、ネットワーク アクセスを構成し、WAL アーカイブ プロセスを開始する必要があります。

データベース VM インスタンスに接続する

  1. Google Cloud コンソールで [VM インスタンス] ページに移動します。

    VM インスタンスに移動

  2. ターミナル シェルを開くには、作成した instance-pg-pitr インスタンスの横にある [SSH] をクリックします。

  3. ターミナル シェルで、Docker コンテナが起動したことを確認します。docker ps

    出力は次のようになります。

    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
    

    コンテナがまだ実行されていない場合は、しばらく待ってから、同じコマンドを使用してもう一度確認します。

データベースへの受信ネットワーク接続を許可する

  1. instance-pg-pitr インスタンスのターミナル シェルで、PostgreSQL ホストベースの認証構成ファイルを開いて編集します。

    sudoedit /mnt/disks/data/pgdata/pg_hba.conf
    
  2. データベースへのデフォルトのすべての IP アドレス アクセスを削除するには、行の先頭に # を追加してファイルの末尾から次の行をコメントアウトします。ファイル内の行は次のようになります。

    #host all all all md5
    
  3. 10.0.0.0/8 CIDR ブロックでホストからのパスワードで保護された接続を許可するには、次の行をファイルの末尾に追加します。

    host    all             all             10.0.0.0/8               md5
    

    このエントリにより、後でトランザクション生成ツールが作成される VPC サブネットからの接続が有効になります。

  4. ファイルを保存して閉じます。

WAL アーカイブを構成する

  1. instance-pg-pitr インスタンスのターミナル シェルで、postgresql.conf ファイルを編集します。

    sudoedit /mnt/disks/data/pgdata/postgresql.conf
    
  2. コメントアウトされた既存の archive_modearchive_commandarchive_timeout 行を次のように置き換えます。

    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
    

    変更したファイルの行を置き換えると、次のコード スニペットのようになります。

    
    .... 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. ファイルを保存して閉じます。

構成の変更を適用して確認する

  1. instance-pg-pitr インスタンスのターミナル シェルで、コンテナを再起動して変更を適用します。

    sudo systemctl restart postgres
    
  2. WAL セグメント ファイルを確認します。

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

    出力は次のようになります。

    total 16388
    -rw------- 1 postgres 70 16777216 Sep  5 23:07 000000010000000000000001
    drwx------ 2 postgres 70     4096 Sep  5 23:05 archive_status
    
  3. データベースへのネットワーク接続を確認します。

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

    出力は次のようになります。

    ?column?
    ----------
           1
    (1 row)
    
  4. インスタンスに対する SSH 接続を閉じます。

トランザクション生成ツールを起動してデータベースにデータを入力する

次の手順では、このチュートリアルのトランザクションを生成する Go プログラムを起動します。このプログラムは VM インスタンス上のコンテナ内で実行されます。

コンテナのイメージは、一般公開されている Container Registry を使用してすでにビルドされ、プロジェクトでホストされています。

  1. Cloud Shell で、トランザクション生成ツールのディレクトリに移動します。

    cd ~/gcs-postgresql-recovery-tutorial/bin
    
  2. 環境変数を設定します。

    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. トランザクション生成ツールを実行するには、インスタンスを起動します。

    ./run_trans_gen_instance.sh
    

    I/O パフォーマンスの低下に関する警告メッセージは無視してください。

  4. しばらく待ってから、トランザクションが PostgreSQL データベースに到達していることを確認します。

    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;'"
    

    トランザクション生成ツールによってデータベースにレコードが追加されると、出力には 0 よりも大きい数値が含まれます。

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

バイナリ スナップショットのバックアップ スケジュールを構成する

スケジュールに従って永続ディスクをバックアップし、リソース ポリシーで定義された期間保持できます。

スナップショット スケジュールを作成する

  1. Cloud Shell で、環境変数を設定します。

    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
    

    zone-of-your-instance は、以前にデータベース VM を起動した Google Cloud ゾーンに置き換えます。

  2. スナップショット スケジュールを作成します。

    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
    

スナップショット スケジュールをディスクに接続する

インスタンスを作成するスクリプトを実行したときに、2 つの独立した永続ディスクとしてデータと WAL ボリュームが作成されました。定義されたスケジュールに従って永続ディスクのスナップショットを作成するには、各永続ディスクにリソース ポリシーを関連付けます。この場合、ディスク スナップショットを同時に実行する必要があるため、Compute Engine VM に接続された両方の永続ディスクに同じポリシーを使用します。

  1. Cloud Shell で、環境変数を設定します。

    export SNAPSHOT_SCHEDULE_NAME=pgdata-disk-schedule
    export PG_INSTANCE_NAME=instance-pg-pitr
    export ZONE=zone-of-your-instance
    
  2. 永続データディスクにスケジュール ポリシーを接続します。

    gcloud beta compute disks add-resource-policies ${PG_INSTANCE_NAME}-data \
        --resource-policies $SNAPSHOT_SCHEDULE_NAME \
        --zone $ZONE
    
  3. 永続 WAL ディスクにスケジュール ポリシーを接続します。

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

スナップショットを手動で実行する

(省略可)スケジュールされたスナップショットはスケジュール設定された期間内で実行されるため、スケジュールを作成したときにスナップショットがすぐに作成されることはほどんどありません。そこで、スケジュールされたスナップショットを待たずに、最初のスナップショットを手動で実行する手順を以下に示します。

  1. Cloud Shell で、環境変数を設定します。

    export ZONE=zone-of-your-instance
    export PG_INSTANCE_NAME=instance-pg-pitr
    export REGION=${ZONE%-[a-z]}
    
  2. 2 つの PostgreSQL インスタンス永続ディスクのスナップショットを作成します。

    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. 作成したスナップショットを表示します。

    gcloud compute snapshots list
    

    出力は次のようになります。

    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 を実行する

多くの場合、PITR は運用上またはプログラム上の誤った手順によって失われたデータを復元するために実行されます。

チュートリアルのこのセクションでは、データベースの更新を実行して、データの壊滅的な損失をシミュレートします。次に、パニック状態のレスポンスをシミュレートした後、誤ったコマンドが発行されたときよりも前の時点への復旧を開始します。

PITR を使用できるようにする

PITR を実行する前に、次を実行するための十分な時間を確保しておく必要があります。

  • バイナリのバックアップ(ディスク スナップショット)
  • WAL のアーカイブ

このチュートリアルでは、頻繁に WAL ファイルをローテーションするために、標準的ではない 120 秒という低い値に archive_timeout を設定しました。少なくとも 1 つのスケジュールされたディスク スナップショットが実行されるまで待つか、手動でディスクのスナップショットを作成する必要があります。

  1. 少なくとも 1 つのスナップショットが作成されたことを確認します。

    1. Google Cloud コンソールで、[スナップショット] ページに移動します。

      [スナップショット] ページに移動

    2. 少なくとも 2 つのスナップショットがあることを確認します。1 つはデータ ボリューム用で、もう 1 つは WAL ボリューム用です(例: instance-pg-pitr--us-central1-a-20190805023535-i3hpw7kn)。

  2. セグメント ファイルが Cloud Storage にアーカイブされていることを確認します。

    1. Google Cloud コンソールで、Cloud Storage ブラウザページに移動します。

      [Cloud Storage ブラウザ] ページに移動

    2. [archive-bucket] をクリックします。

      オブジェクトを含む Cloud Storage バケット。

データを破損する

壊滅的なデータ損失をシミュレートするには、PostgreSQL データベースに対してコマンドライン シェルを開き、トランザクション生成ツールによって入力されたテーブルのデータを破損させます。

  1. Google Cloud コンソールで [VM インスタンス] ページに移動します。

    [VM インスタンス] ページに移動

  2. instance-pg-pitr インスタンスの [SSH] をクリックします。

  3. SSH ターミナルで、Docker コンテナの PostgreSQL ターミナルベースのフロントエンドを実行します。

    docker exec -it postgres-db psql --dbname=pitr_demo
    
  4. 顧客テーブルの行を変更するには、PostgreSQL シェルで意図的な誤字を含む SQL DML ステートメントを送信します。

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

    出力は次のようになります。

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

    WHERE 句の前に余分なセミコロンが挿入されたため、エラーが発生しました。データベース内のすべての行が更新されました。これで、PITR を実行して、誤ったステートメントによって変更された行を復元できます。

復元の目標時間を決定する

PITR の最初のステップは、復元の目標時間を決定することです。この時間は、データを調べて、データ破損イベントの少し前の時点を特定することで決まります。

  1. instance-pg-pitr インスタンスのターミナル シェルで、破損した行の最大タイムスタンプを取得します。

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

    出力は次のようになります。

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

    本番環境のデータベースでは、特に影響を受けるテーブルが大きく、指標列がインデックスに登録されていない場合、復元ターゲットを決定するクエリは複雑になります。

  2. 結果をコピーします。このクエリで返された値を次のステップで使用します。

データベースを復元する

このチュートリアルでは、復元スクリプトによって PITR が自動化されます。データベースを復元する自動プロセスを用意し、このプロセスを定期的にテストすることをおすすめします。

  1. Cloud Shell で、現在の作業ディレクトリを復元スクリプトの場所に変更します。

    cd ~/gcs-postgresql-recovery-tutorial/bin
    
  2. スクリプトに必要な環境変数を設定します。YYYY-MM-DD HH:MM:SS.999999+00 は、以前にコピーしたクエリ出力に置き換えます。

    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. 復元スクリプトを実行します。

    ./recover_to_point_in_time.sh
    

復元スクリプトを理解する

このセクションでは、入力パラメータとスクリプトで実行される手順について詳しく説明します。

このスクリプトでは、次の環境変数を設定する必要があります。

  • PIT_RECOVERY_TARGET: 復元のターゲット時間。
  • PROJECT_ID: PG_INSTANCE_NAMEインスタンスが配置されているプロジェクト。
  • ZONE: PG_INSTANCE_NAME インスタンスが配置されているゾーン。
  • PG_INSTANCE_NAME: 本番環境の PostgreSQL インスタンスが実行されているインスタンス。
  • RECOVER_BUCKET: WAL セグメント ファイルがアーカイブされる Cloud Storage バケット。
  • POSTGRES_PASSWORD: PostgreSQL データベース ユーザーに使用するパスワード。

スクリプトによって、次の手順が実行されます。

  1. 復元ターゲットの日時に基づいて、最新のディスク スナップショットを判別します。
  2. cloud-init.yaml ファイルを作成します。このファイルは、PITR データベースを実行するコンテナ用に最適化されたストレージ VM に提供されます。cloud-init.yaml ファイルは構成ファイルを作成し、いくつかのシステム コマンドを実行して次の環境を確立します。

    • WAL セグメント ファイルのアーカイブ バケットをボリュームとしてマウントする gcsfuse コンテナ。これは、Docker バインド マウントでホストに公開されます。
    • データベース エンジンが次を実行する postgres-db コンテナ。

      • 永続ディスクがボリュームとして接続されているホスト ファイル システム。
      • Cloud Storage バケットがボリュームとして接続されているホスト ファイル システム。
    • PostgreSQL データ ディレクトリにある recovery.conf リカバリ ファイル。次の情報が含まれています。

      • ターゲットの日付。
      • restore コマンド。データベースが必要に応じてアーカイブ ファイル システムから WAL セグメント ファイルをコピーするために使用するパラメータ化されたコピーコマンド。%f はセグメント ファイルであり、%p はデータベースが復元中にファイルを処理するために使用するパスです。
    • archive_ の設定は、WAL アーカイブ ディレクトリが破損しないように、postgresql.conf 設定ファイルからコメントアウトされます。

  3. 次の情報を使用して、PITR インスタンスを起動します。

    • $PG_INSTANCE_NAME 環境変数と、$PIT_RECOVERY_TARGET 環境変数の英数字の値を組み合わせて作成された名前。
    • 以前に識別されたディスク スナップショットから作成された永続ディスク。

recovery.conf ファイルの例を次に示します。

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

復元を検証する

  1. Google Cloud コンソールで [VM インスタンス] ページに移動します。

    [VM インスタンス] ページに移動

  2. instance-pg-pitr-YYYYMMDDHHMMSS インスタンスの [SSH] をクリックします。

  3. SSH ターミナルで、Docker コンテナの PostgreSQL ターミナルベースのフロントエンドを実行します。

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

    次のエラーが発生した場合は、PostgreSQL コンテナが起動するまで待ち、コマンドを再実行します。

    Error: No such container: postgres-db
    
  4. 顧客テーブルのデータを確認します。

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

    出力は次のようになります。

       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
    

    名前は、トランザクション生成ツールによって作成された値を示します。最後の行のタイムスタンプは、復元ターゲット(環境変数で復元スクリプトに指定したもの)よりも前に設定されています。復元するレコードの数によっては、すべての行が更新されるまでしばらく待つ必要があります。

クリーンアップ

課金を停止する最も簡単な方法は、チュートリアル用に作成した Google Cloud プロジェクトを削除することです。また、リソースを個別に削除することもできます。

プロジェクトを削除する

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

次のステップ