PostgreSQL の DR 対策を 30 分で試してみる

こんにちは！インサイトテクノロジーの松尾です。

本記事では、Oracle Database SE / SQL Server / PostgreSQL をサポートする DR ソリューションである Dbvisit StandbyMP のトライアル(test drive)環境を使い、PostgreSQL を対象に実際に災害発生時にDBが切り替わる様子を体験してみます。環境の用意から実際に試すまで30分程度で無料で試せますので、データベースの DR 製品を検討中の方、理解したい方、ぜひ体験してみてください。

## はじめに：PostgreSQL の DR 対策とは

データベースの DR (Disaster Recovery, ディザスタリカバリ, 災対対策) は、万一の災害やトラブルに備えて本番環境データベース(プライマリ)のコピーを物理的に離れた場所に保持(スタンバイ)しておき、災害などが発生した時にデータベースを切り替えて使用できるようにしておく仕組みです。PostgreSQL ではストリーミングレプリケーションやWALファイル転送で構成することが多いと思います。標準の機能として組み込まれている反面、利用するにあたっては設定ファイルの変更やコマンドラインからの処理など、基本はテキストベースのオペレーションにならざるを得ません。PostgreSQL に熟練しているエンジニアであれば問題ないと思いますが、常にそういったエンジニアが災害に備えて待機しているわけにもいかないと思います。障害が発生して本番環境が使用できなくなった場合など多くの環境に対して対応しなければならいことを想像してみてください。DR ソフトウェアを使うことで DR 構成の管理や確認、自動でスタンバイ環境に切り替えることなど操作を容易に行うことができます。

https://www.fujitsu.com/jp/products/software/resources/feature-stories/postgres/article-index/disasterrecovery/

https://medium.com/@PinkOwl/postgresql-and-me-log-shipping-replication-6bc945757822

## DR ソフトウェアを体験してみよう！

本記事では、Oracle Database SE / SQL Server / PostgreSQL 向けに提供されている DR ソフトウェア Dbvisit StandbyMP の試用環境 (Test drive) を使用し、セットアップから、スイッチオーバー (プライマリとスタンバイの切り替え) やフェイルオーバー (障害発生時のスタンバイへの自動切換え) を PostgreSQL を題材にして実際に体験してみます。Test drive には体験用の PostgreSQL が用意されていますので、簡単に体験可能です。

https://www.insight-tec.com/products/dbvisit/

https://dbvisit.com/solutions/disaster-recovery-for-postgresql

### Test drive の申し込みと起動

ではさっそく Test drive をはじめましょう。https://testdrive.dbvisit.com/ にアクセスすると、以下の画面が表示されます。

https://testdrive.dbvisit.com/

![00testdrive.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/e12dea5d-ef98-9029-a756-9bc9b447dc47.png)

入力は名前とメールアドレスのみです。早速入力してみましょう。

![01testdrive_config.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/a22ca9af-88aa-a75b-8f84-b9169064e4b4.png)

Test drive の構築が始まります。2分間程度で終わるので、その間、製品紹介動画でも見ていましょう♪構築が終わると、アクセスに必要な情報等が表示されます。

![02testdrive_starthere.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/e2bd66d8-cfb7-1593-29fe-7e6f20477227.png)

「START HERE」のところに記載のある「CONTROL CENTER HOMEPAGE」に記載されている URL へアクセスすると、ログイン画面が表示されます。

![03login.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/c341370d-eef2-73ec-3511-24f70f55cb48.png)

ログイン画面で、同じく「START HERE」のところに記載のあるユーザー名とパスワードを使ってログインします。ログイン後、左のメニューの「SETTING」から、言語を日本語に変更することができます。変更すると、再度ログイン画面に戻り、日本語のログイン画面が表示されます。 

![04login_ja.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/df394843-1550-596b-bac4-a2d1d785d176.png)

### DR の構成と操作

では早速 PostgreSQL を登録していきます。「Start with PostgreSQL」をクリックします。クリックすると、登録済み(DbvisitのAgentプログラムがインストール済み)のデータベースを選択する画面が出てきます。

![05new_config.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/feefa403-9da6-038a-d4bd-546fca9ac110.png)
![06select_host.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/b321f0d8-1125-56d7-835b-179785165a34.png)

「psql1～」と記載のあるデータベースを選択します。Standby 側にも同様に「psql2～」のデータベースを選択します。

![07license.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/9d460e60-b943-6ce9-1471-fa6dd1e54508.png)

「Create Configuration」をクリックします。ダッシュボードに PostgreSQL の DR 構成が登録されます。

![08before.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/20efc018-7223-2bc8-bda1-91a2471dff57.png)

この段階では DR 構成が登録されているのみなので、「今すぐ設定しますか？」をクリックします。ここでは Dbvisit StandbyMP がサポートしているレプリケーション方法を選択できますが、特に要件がなければ、継続的に PostgreSQL のログ(WAL)を連携する WAL Streaming を選択します。また、Hot Standby か Warm Standby かも選択が可能です。Hot Standby は読み取りアクセス可能、Warm Standby は読み取りアクセスもできない環境です。

さいごに「Standby Database の作成」をクリックし、スタンバイ DB を作成します。

![09replication_test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/ff8d42e2-6038-03d5-f01e-9aaa77d65158.png)
![10processing.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/02a40e6b-0d1b-d210-ca61-94b865e91661.png)

スタンバイ DB が作成できると、ダッシュボード上のスタンバイ DB が「ホットSTANDBY」となります。

![11configured.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/9c5ead0f-3f87-7e29-51fe-138a6e73be0b.png)

さて、PostgreSQL はどういう状態になっているでしょうか？

ssh で PostgreSQL がセットアップされているマシンにログインしてみます。psql1、psql2 でそれぞれ見てみましょう。なお ssh ログインに必要な情報は、デプロイ後に表示される情報に含まれています。選択した WAL Streaming 方式では、PostgreSQL の機能であるストリーミングレプリケーション機能を使用しているようです。そのため、レプリケーション状態を確認するコマンドで確認してみましょう。

Primary側では
```bash
postgres@psql1:~$ psql -U postgres
psql (14.9 (Ubuntu 14.9-0ubuntu0.22.04.1))
Type "help" for help.

postgres=# select * from pg_stat_replication;
 pid  | usesysid | usename  | application_name |  client_addr   | client_hostname | client_port |         backend_start         | backend_xmin |   state   | sent_lsn  | write_lsn | flush_lsn | replay_lsn | write_lag | flush_lag | replay_lag | sync_priority | sync_state |          reply_time
------+----------+----------+------------------+----------------+-----------------+-------------+-------------------------------+--------------+-----------+-----------+-----------+-----------+------------+-----------+-----------+------------+---------------+------------+-------------------------------
 1595 |       10 | postgres | 14/main          | 13.211.163.199 |                 |       48354 | 2023-10-11 15:16:06.005274+00 |              | streaming | 4/6000148 | 4/6000148 | 4/6000148 | 4/6000148  |           |           |            |             0 | async      | 2023-10-11 15:24:14.632479+00
(1 row)
```

Standby側では
```bash
postgres=# select * from pg_stat_wal_receiver;
 pid  |  status   | receive_start_lsn | receive_start_tli | written_lsn | flushed_lsn | received_tli |      last_msg_send_time      |     last_msg_receipt_time     | latest_end_lsn |        latest_end_time        | slot_name |              sender_host              | sender_port |                                                                                                                                                 conninfo
------+-----------+-------------------+-------------------+-------------+-------------+--------------+------------------------------+-------------------------------+----------------+-------------------------------+-----------+---------------------------------------+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 1977 | streaming | 4/6000000         |                 1 | 4/6000148   | 4/6000148   |            1 | 2023-10-11 15:29:15.07852+00 | 2023-10-11 15:29:15.078463+00 | 4/6000148      | 2023-10-11 15:19:44.239284+00 | dbvisit_1 | psql1-43E255BE16DD4A02.mp-standby.com |        5432 | user=postgres password=******** channel_binding=prefer dbname=replication host=psql1-43E255BE16DD4A02.mp-standby.com port=5432 fallback_application_name=14/main sslmode=prefer sslcompression=0 sslsni=1 ssl_min_protocol_version=TLSv1.2 gssencmode=prefer krbsrvname=postgres target_session_attrs=any
(1 row)
```

適当なテーブルを作っても確認してみても、レプリケートされていることが確認できると思います。たとえば、

Primary側で
```bash
postgres=# create table table1 (col1 int);
CREATE TABLE
postgres=# insert into table1 values (1);
INSERT 0 1
```

Standby側で
```bash
postgres=# \dt
         List of relations
 Schema |  Name  | Type  |  Owner
--------+--------+-------+----------
 public | table1 | table | postgres
(1 row)

postgres=# select * from table1;
 col1
------
    1
(1 row)

postgres=# insert into table1 values(2);
ERROR:  cannot execute INSERT in a read-only transaction
```

### スイッチオーバー

スイッチオーバーとは、双方のデータベースが稼働状態にある状態で、プライマリとスタンバイを切り替える作業です。Dbvisit StandbyMP ではデータベースの種類によらず同じオペレーションでスイッチオーバーを行うことができます。

スイッチオーバーを実行するには、ダッシュボードにある対象の構成で「処理」をクリックします。右側に表示されるメニューから「スイッチオーバー」を選択します。

![12switchover.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/d5e2a2cb-423c-7f25-7e85-3f4c0e5b5401.png)

確認画面が出るので、そこからスイッチオーバーを実行します。

![13switchover_confirm.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/39d288b3-321e-c2a0-4f9a-6e27b9754859.png)

ダッシュボード画面を見ると、データベースが入れ替わっていることが確認できると思います。

データベースの状態を確認してみると、、、役割が確かに入れ替わっていますね。

psql1(Standby)側では
```bash
postgres=# select * from pg_stat_wal_receiver;
 pid  |  status   | receive_start_lsn | receive_start_tli | written_lsn | flushed_lsn | received_tli |      last_msg_send_time       |     last_msg_receipt_time     | latest_end_lsn |       latest_end_time        | slot_name |              sender_host              | sender_port |                                                                                                                                                 conninfo
------+-----------+-------------------+-------------------+-------------+-------------+--------------+-------------------------------+-------------------------------+----------------+------------------------------+-----------+---------------------------------------+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 2112 | streaming | 4/8000000         |                 1 | 4/9000158   | 4/9000158   |            1 | 2023-10-11 15:38:46.041232+00 | 2023-10-11 15:38:46.041319+00 | 4/9000158      | 2023-10-11 15:38:15.99787+00 | dbvisit_1 | psql2-43E255BE16DD4A02.mp-standby.com |        5432 | user=postgres password=******** channel_binding=prefer dbname=replication host=psql2-43E255BE16DD4A02.mp-standby.com port=5432 fallback_application_name=14/main sslmode=prefer sslcompression=0 sslsni=1 ssl_min_protocol_version=TLSv1.2 gssencmode=prefer krbsrvname=postgres target_session_attrs=any
(1 row)
```

psql2(Primary)側では
```bash
postgres=# select * from pg_stat_replication;
 pid  | usesysid | usename  | application_name |  client_addr  | client_hostname | client_port |         backend_start         | backend_xmin |   state   | sent_lsn  | write_lsn | flush_lsn | replay_lsn | write_lag | flush_lag | replay_lag | sync_priority | sync_state |          reply_time
------+----------+----------+------------------+---------------+-----------------+-------------+-------------------------------+--------------+-----------+-----------+-----------+-----------+------------+-----------+-----------+------------+---------------+------------+-------------------------------
 2242 |       10 | postgres | 14/main          | 54.253.33.159 |                 |       38038 | 2023-10-11 15:37:03.772446+00 |              | streaming | 4/9000060 | 4/9000060 | 4/9000060 | 4/9000060  |           |           |            |             0 | async      | 2023-10-11 15:37:36.615552+00
(1 row)
```

### 自動フェイルオーバー

さて、次は自動フェイルオーバーを試してみます。自動フェイルオーバーはプライマリで障害が発生した際に、自動でスタンバイ側をプライマリに変更する機能です。

まず、Dbvisit StandbyMP ではデフォルトでは自動フェイルオーバーが設定されていないので、まず自動フェイルオーバーの設定を行います。右のメニューから自動フェールオーバーを選択します。

![14failover_menu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/68e823b4-e8c6-89d9-cc9f-48661ada2976.png)

「緊急時のアクション」で「自動フェイルオーバーを実行する」を選択して設定を保存します。

![15failover_confirm.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/bc14853f-3a89-59ad-0f07-93e7119d52b5.png)

さて、では自動フェイルオーバーを試してみましょう。

早速、プライマリデータベースを使えない状態にします。ここでは、PostgreSQL のプロセスを強制終了してみます。ssh で psql2 (今はスイッチオーバー実行後のため psql2 がプライマリ) にログインし、プロセス ID を確認したうえでプロセスを強制終了します。

```bash
postgres@psql2:~$ ps aux | grep postgres
postgres    1162  0.0  0.2 1468732 42056 ?       Ssl  01:16   0:01 /PSQL_DATA/dbvisit/standbymp/bin/dbvagentmanager service run
postgres    1919  0.0  0.2 1464904 33460 ?       Sl   01:20   0:02 /PSQL_DATA/dbvisit/standbymp/bin/dbvhelper -agentManagerId m0aaqak8xqg5 -directorId 3snp568w9w3ia -hostAddress psql2-7B921FA345BF47AC.mp-standby.com -natsAddress controlcenter-7b921fa345bf47ac.mp-standby.com -natsPort 5533 -configurationType PostgreSQL -helperProcessKey postgresql:m0aaqak8xqg5
postgres    2063  0.0  0.1 218672 30080 ?        Ss   02:10   0:00 /usr/lib/postgresql/14/bin/postgres -D /var/lib/postgresql/14/main -c config_file=/etc/postgresql/14/main/postgresql.conf
postgres    2064  0.0  0.0  73208  6944 ?        Ss   02:10   0:00 postgres: 14/main: logger
postgres    2066  0.0  0.0 218672  7364 ?        Ss   02:10   0:00 postgres: 14/main: checkpointer
postgres    2067  0.0  0.0 218672  8516 ?        Ss   02:10   0:00 postgres: 14/main: background writer
postgres    2068  0.0  0.0 218672 11588 ?        Ss   02:10   0:00 postgres: 14/main: walwriter
postgres    2069  0.0  0.0 219344  9924 ?        Ss   02:10   0:00 postgres: 14/main: autovacuum launcher
postgres    2070  0.0  0.0  73440  7108 ?        Ss   02:10   0:00 postgres: 14/main: stats collector
postgres    2071  0.0  0.0 219088  9156 ?        Ss   02:10   0:00 postgres: 14/main: logical replication launcher
postgres    2083  0.0  0.0 219964 14532 ?        Ss   02:10   0:00 postgres: 14/main: walsender postgres 52.64.9.21(38302) streaming 4/7000060
root        2097  0.1  0.0  17164 10672 ?        Ss   02:11   0:00 sshd: postgres [priv]
postgres    2117  0.1  0.0  17036  9472 ?        Ss   02:11   0:00 /lib/systemd/systemd --user
postgres    2118  0.0  0.0 170552  6708 ?        S    02:11   0:00 (sd-pam)
postgres    2180  0.0  0.0  17300  7820 ?        S    02:11   0:00 sshd: postgres@pts/0
postgres    2181  0.0  0.0   9100  4992 pts/0    Ss   02:11   0:00 -bash
postgres    2198  0.0  0.0  10460  3200 pts/0    R+   02:12   0:00 ps aux
postgres    2199  0.0  0.0   7004  2304 pts/0    R+   02:12   0:00 grep postgres
```

プロセス ID が 2063 のものを KILL しましょう。
```bash
postgres@psql2:~$ kill -KILL 2063
postgres@psql2:~$ ps aux | grep postgres
postgres    1162  0.0  0.2 1468732 35624 ?       Ssl  01:16   0:01 /PSQL_DATA/dbvisit/standbymp/bin/dbvagentmanager service run
postgres    1919  0.0  0.2 1464904 33332 ?       Sl   01:20   0:02 /PSQL_DATA/dbvisit/standbymp/bin/dbvhelper -agentManagerId m0aaqak8xqg5 -directorId 3snp568w9w3ia -hostAddress psql2-7B921FA345BF47AC.mp-standby.com -natsAddress controlcenter-7b921fa345bf47ac.mp-standby.com -natsPort 5533 -configurationType PostgreSQL -helperProcessKey postgresql:m0aaqak8xqg5
root        2097  0.0  0.0  17164 10672 ?        Ss   02:11   0:00 sshd: postgres [priv]
postgres    2117  0.0  0.0  17036  9472 ?        Ss   02:11   0:00 /lib/systemd/systemd --user
postgres    2118  0.0  0.0 170552  6708 ?        S    02:11   0:00 (sd-pam)
postgres    2180  0.0  0.0  17300  7820 ?        S    02:11   0:00 sshd: postgres@pts/0
postgres    2181  0.0  0.0   9100  4992 pts/0    Ss   02:11   0:00 -bash
postgres    2212  0.0  0.0  10460  3200 pts/0    R+   02:13   0:00 ps aux
postgres    2213  0.0  0.0   7004  2304 pts/0    R+   02:13   0:00 grep postgres
postgres@psql2:~$
```

ストリーミングが切断されたこと、そして、Primaryの監視が失敗したことが確認されます。

![16disconnected.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/99de9a11-f85b-19e8-c435-783ec167f7c1.png)
![17fail1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/8d47e47e-3b5e-e730-52af-c6a9f9a51d25.png)

設定どおり 3 回確認に失敗すると、フェールオーバーが開始され、psql1 がオンライン状態に設定されることが確認できました。

![18failovered.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3480312/2d03a0cc-c514-028e-f4f6-3f875ae9fab0.png)

## おわりに

Dbvisit StandbyMP を用いて、PostgreSQL のスイッチオーバー (DR 環境との切り替え操作) や、自動フェイルオーバー (プライマリ環境がダウンしたときの自動切り替え) を体験してみました。

Dbvisit StandbyMP はここで紹介した PostgreSQL 以外に Oracle Database と SQL Server をサポートしています。以下のページもご参考になればと思います。

https://qiita.com/takumats/items/9727cec7f57753708271

https://qiita.com/takumats/items/1a0f70ec304a0daa460b

このようなソフトを使わない場合に、災害発生時にバックアップから復旧させるなどの作業を行うのは、かなりしっかりとした手順を構築しておかないと骨の折れる作業だと思います。繰り返しになりますが、障害が発生して本番環境が使用できなくなった場合など多くの環境に対して対応しなければならいことを想像してみてください。DR 製品を活用し、災害発生時にも事業継続できるよう備えておきましょう。