Ansible を使って PostgreSQL のクラスタ構成を構築しよう

PostgreSQL には様々なクラスタ構成があります。その都度、構築するのもたいへんなので、個人的に Ansible で自動化しています。せっかくプレイブックを作ったので、GitHub で公開することにしました。この記事では、そのプレイブック、Ansible Playbooks for PostgreSQL Clusters について紹介します。

# Ansible Playbooks for PostgreSQL Clusters とは

Ansible Playbooks for PostgreSQL Clusters は、PostgreSQL の様々なクラスタ構成を構築するための Ansible プレイブックです。ライセンスは BSD です。リポジトリは GitHub で公開しています。

__Ansible Playbooks for PostgreSQL Clusters__
https://github.com/tom-sato/ansible-postgresql-cluster

プレイブックは 2021 年 2 月時点で以下のクラスタ構成に対応しています。

* [ストリーミングレプリケーション](https://www.postgresql.jp/document/current/html/warm-standby.html#STREAMING-REPLICATION)
* [Pgpool-II](https://www.pgpool.net/mediawiki/jp/)
* [Pacemaker](https://clusterlabs.org/pacemaker/) + [DRBD](https://www.linbit.com/drbd/)
* Pacemaker + ストリーミングレプリケーション
* [PostgreSQL Automated Failover (PAF)](https://clusterlabs.github.io/PAF/)
* [repmgr (Replication Manager for PostgreSQL clusters)](https://repmgr.org/)

今後、論理レプリケーションや Patroni など、対応するクラスタ構成は増やしていきたいです。

# 必要なソフトウェア

Ansible Playbooks for PostgreSQL Clusters を使うには、以下のソフトウェアが必要です。

* VirtualBox 6.0.x
* Vagrant 2.2.x
* Vagrant ボックス [centos/8](https://app.vagrantup.com/centos/boxes/8) または [centos/7](https://app.vagrantup.com/centos/boxes/8)
* Ansible 2.9.x

各ソフトウェアのバージョンは上記より新しい分には問題ないでしょう。Vagrant ボックスも RedHat/CentOS 系列であれば、ほかのボックスでも問題ないでしょう。ただ、上記のバージョン以外では動作を確認していないので、何かしら問題があるかもしれません。

# 使い方

Ansible Playbooks for PostgreSQL Clusters の使い方は以下のとおりです。

1. リポジトリをクローンします。

    ```shell-session
    $ git clone https://github.com/tom-sato/ansible-postgresql-cluster.git
    Cloning into 'ansible-postgresql-cluster'...
    remote: Enumerating objects: 145, done.
    remote: Counting objects: 100% (145/145), done.
    remote: Compressing objects: 100% (79/79), done.
    remote: Total 145 (delta 44), reused 135 (delta 34), pack-reused 0
    Receiving objects: 100% (145/145), 29.67 KiB | 5.93 MiB/s, done.
    Resolving deltas: 100% (44/44), done.
    ```

1. リポジトリのディレクトリに移動します。

    ```shell-session
    $ cd ansible-postgresql-cluster
    ```

1. 仮想マシンを起動し、プロビジョニングを行います。

    ```shell-session
    $ PLAYBOOK=pgpool2 vagrant up --provosion
    Bringing machine 'node-1' up with 'virtualbox' provider...
    Bringing machine 'node-2' up with 'virtualbox' provider...
    Bringing machine 'node-3' up with 'virtualbox' provider...
    (省略)
    PLAY RECAP *********************************************************************
    node-1                     : ok=64   changed=47   unreachable=0    failed=0    skipped=28   rescued=0    ignored=0
    node-2                     : ok=61   changed=44   unreachable=0    failed=0    skipped=31   rescued=0    ignored=0
    node-3                     : ok=61   changed=44   unreachable=0    failed=0    skipped=31   rescued=0    ignored=0
    ```

    `PLAYBOOK=pgpool2` は環境変数でプレイブックを指定しています。これだと、CentOS 8 で 3 ノードからなる Pgpool-II によるクラスタ構成が構築されます。指定できる環境変数は [カスタマイズのしかた](#カスタマイズのしかた) を見てください。

1. 仮想マシンに接続し、動作を確認してみましょう。

    ```shell-session
    $ PLAYBOOK=pgpool2 vagrant ssh node-1
    ```

    ホスト名は `node-1`、`node-2`、... になります。

    Pgpool-II によるクラスタ構成であれば、例えば、`postgres` ユーザに切り替わって、Pgpool-II 管理下のノード一覧を確認したり、

    ```shell-session
    $ sudo su - postgres
    $ psql -h vip-1 -p 9999 -c "SHOW pool_nodes"
     node_id | hostname | port | status | lb_weight |  role   | select_cnt | load_balance_node | replication_delay | replication_state | replication_sync_state | last_status_change
    ---------+----------+------+--------+-----------+---------+------------+-------------------+-------------------+-------------------+------------------------+---------------------
     0       | node-1   | 5432 | up     | 0.333333  | primary | 0          | false             | 0                 |                   |                        | 2021-02-10 10:52:45
     1       | node-2   | 5432 | up     | 0.333333  | standby | 0          | false             | 0                 | streaming         | async                  | 2021-02-10 10:52:45
     2       | node-3   | 5432 | up     | 0.333333  | standby | 0          | true              | 0                 | streaming         | async                  | 2021-02-10 10:52:45
    (3 行)
    ```

    プライマリを停止し、しばらく待って、フェイルオーバされるのを確認したりしてもよいでしょう。

    ```shell-session
    $ /usr/pgsql-13/bin/pg_ctl stop -m i
    サーバ停止処理の完了を待っています....完了
    サーバは停止しました
    (しばらく待つ)
    $ psql -h vip-1 -p 9999 -c "show pool_nodes"
     node_id | hostname | port | status | lb_weight |  role   | select_cnt | load_balance_node | replication_delay | replication_state | replication_sync_state | last_status_change
    ---------+----------+------+--------+-----------+---------+------------+-------------------+-------------------+-------------------+------------------------+---------------------
     0       | node-1   | 5432 | down   | 0.333333  | standby | 0          | false             | 0                 |                   |                        | 2021-02-10 11:00:34
     1       | node-2   | 5432 | up     | 0.333333  | primary | 0          | true              | 0                 |                   |                        | 2021-02-10 11:00:34
     2       | node-3   | 5432 | up     | 0.333333  | standby | 0          | false             | 0                 |                   |                        | 2021-02-10 11:00:36
    (3 行)
    ```

# カスタマイズのしかた

プレイブックなどは `Vagrantfile` ファイルを直接編集しなくても、環境変数で指定できるようになっています。指定できる環境変数は以下のとおりです。

* `PLAYBOOK` - プレイブックを指定します。デフォルトは `postgresql` です。
* `NUM_NODES` - ノード数を指定します。デフォルトは 3 ノードで、DRBD のみ 2 ノードです。
* `BOX` - ボックスを指定します。デフォルトは `centos/8` です。
* `PROXY` - 仮想マシンに設定するプロキシを指定します。デフォルトは空文字列です。事前に [vagrant-proxyconf](https://github.com/tmatilai/vagrant-proxyconf) プラグインのインストールが必要です。

例えば、CentOS 7 でノード 2 台からなるストリーミングレプリケーションによるクラスタ構成を構築するのであれば、以下のように環境変数を指定します。

```shell-session
$ NUM_NODES=2 BOX=centos/7 vagrant up
```

なお、すでにプロビジョニンズ済みだと、うまく構築できないかもしれません。その場合、`vagrant destroy -f` で仮想マシンを破棄した上で行うとよいでしょう。

指定できるプレイブックはリポジトリの [トップディレクトリ](https://github.com/tom-sato/ansible-postgresql-cluster) 内の `*.yml` ファイルから拡張子を取り除いたものです。

* `postgresql` - ストリーミングレプリケーションによるクラスタ構成を構築します。
* `pgpool2` - Pgpool-II によるクラスタ構成を構築します。クラスタリングモードはストリーミングレプリケーション、ネイティブレプリケーション、スナップショットアイソレーション (Pgpool-II 4.2 以降)、raw に対応しています。
* `pacemaker-drbd` - Pacemaker + DRBD によるクラスタ構成を構築します。
* `pacemaker-replication` - Pacemaker + ストリーミングレプリケーションによるクラスタ構成を構築します。PostgreSQL 12 以降には対応していません。
* `pacemaker-paf` PAF によるクラスタ構成を構築します。CentOS 8 には対応していません。
* `repmgr` - repmgr によるクラスタ構成を構築します。実験的です。

PostgreSQL のバージョンやデータディレクトリなど、そのほかのソフトウェアごとの設定はロール変数で指定できます。指定できるすべてのロール変数は [README.md](https://github.com/tom-sato/ansible-postgresql-cluster) に書いてあります。どうやって指定するのがよいか分かってないのですが、とりあえずプレイブック内の `vars:` に書いておけばよいでしょう。

例えば、Pgpool-II によるクラスタ構成で Pgpool-II のバージョンを 4.1、クラスタリングモードをネイティブレプリケーション、PostgreSQL のバージョン 12 に指定するのであれば、以下のように `pgpool2.yml` ファイルの `vars:` にロール変数を指定します。`postgresql_extra_config_parameters` 以降はプレイブックで上書きが必要なロール変数です。

```yml:pgpool2.yml
---
- hosts: all
  become: yes
  vars:
    pgpool2_version: 4.1
    pgpool2_backend_clustering_mode: native_replication
    postgresql_version: 12
    postgresql_extra_config_parameters: |
      {% if pgpool2_backend_clustering_mode == 'snapshot_isolation' %}
      default_transaction_isolation = 'repeatable read'
      {% endif %}
      restart_after_crash = off
    postgresql_control_as_service: no
    postgresql_setup_stage: "{{ (pgpool2_backend_clustering_mode == 'streaming_replication') | ternary('write_recovery_conf', 'basebackup') }}"
  roles:
    - common
    - hosts
    - locale
    - postgresql
    - pgpool2
```