深夜2時の呼び出し:シングルノードのPostgreSQLが限界を迎えるとき
午前2時14分。ナイトスタンドの上でスマートフォンが震えています。Nagiosが「Database connection limit reached(データベース接続上限に達しました)」と叫んでいます。またか、と思いました。アプリケーションの同時セッション数が1,200に達し、デフォルトの100接続という制限を押し潰したのです。CPU使用率は99%に張り付き、新しいリクエストはことごとく拒否されていました。
この悪夢のような瞬間こそ、すべてのDBAがスタンドアロン構成のデータベースは「時限爆弾」であると悟る時です。私はその拡張性の高さからPostgreSQLを選ぶことが多いですが、スケーリングには堅牢なミドルウェア層が必要です。そこで登場するのがPgpool-IIです。ただ夜を乗り切るだけでなく、実際に正しく機能することで、アラートの鳴らない静かなシステムを構築したいものです。
アーキテクチャ:単なるプロキシ以上の存在
エンジニアからはよく「PgBouncerがあるのに、なぜPgpool-IIを使うのか?」という質問を受けます。どちらもコネクションプーリングを処理しますが、Pgpool-IIはより洗練されたツールです。これはPostgreSQLプロトコルを理解するインテリジェントなプロキシとして動作し、SQLクエリの内容に基づいてルーティングを判断します。
コネクションプーリング vs ロードバランシング
コネクションプーリングはオーバーヘッドを大幅に削減します。クエリごとに新しいプロセスを生成して30ミリ秒を費やす代わりに、Pgpool-IIは既存のプロセスを再利用します。しかし、真の力はロードバランシングにあります。アプリが SELECT クエリを送信すると、Pgpool-IIはそれをスタンバイサーバーにルーティングします。これにより、プライマリサーバーは INSERT、UPDATE、DELETE 操作の処理に専念できるようになります。実務上、アプリケーションのコードを一行も変更することなく、リードスループットを3倍に高めることができます。
Watchdogによる高可用性
プロキシはオンラインであり続けてこそ価値があります。もしPgpool-IIがクラッシュすれば、スタック全体が停止してしまいます。Pgpool-IIはこの問題を**Watchdog**で解決します。複数のインスタンスをクラスター化し、ノード同士がハートビートを監視し合います。リーダーが故障すると、フォロワーが即座に仮想IP(VIP)を引き継ぎます。アプリケーションは切り替えに気づくことさえありません。
ハンズオン:復元力の高いクラスターの構築
実際のセットアップを見てみましょう。今回は3台のサーバーを使用します:
- ノード 1 (192.168.1.10): プライマリ PostgreSQL
- ノード 2 (192.168.1.11): スタンバイ PostgreSQL (ストリーミング・レプリケーション)
- ノード 3 (192.168.1.12): Pgpool-II
ステップ 1: インストール
Ubuntuの場合、バイナリの取得は簡単です。重要なのは設定ファイルの中にあります。
sudo apt-get update
sudo apt-get install pgpool2
ステップ 2: pgpool.conf の設定
/etc/pgpool2/pgpool.conf を開きます。バックエンドノードを定義し、ロードバランシングモードを有効にする必要があります。
# ネットワーク設定
listen_addresses = '*'
port = 9999
# ロードバランシングを有効化
load_balance_mode = on
master_slave_mode = on
master_slave_sub_mode = 'stream'
# バックエンドノードの設定
backend_hostname0 = '192.168.1.10'
backend_port0 = 5432
backend_weight0 = 1
backend_flag0 = 'ALLOW_TO_FAILOVER'
backend_hostname1 = '192.168.1.11'
backend_port1 = 5432
backend_weight1 = 1.5
backend_flag1 = 'ALLOW_TO_FAILOVER'
backend_weight を割り当てることで、より高性能なハードウェアを優先できます。例えばノード2のRAMが64GBで、ノード1が32GBの場合、ノード2の重みを1.5に設定すれば、より能力の高いサーバーに重い処理を任せることができます。
ステップ 3: 自動フェイルオーバー
手動での介入はアップタイムの敵です。数秒ごとにバックエンドを調査するヘルスチェックを設定する必要があります。
health_check_period = 10
health_check_timeout = 5
health_check_user = 'pgpool_check'
health_check_password = 'your_password'
# フェイルオーバースクリプト
failover_command = '/etc/pgpool2/failover.sh %d %h %p %D %m %M %H %P'
failover_command は、作成したシェルスクリプトをトリガーします。ノード1がダウンすると、Pgpool-IIはこのスクリプトを実行してノード2で pg_ctl promote を実行します。これにより、5秒足らずでスタンバイがプライマリに昇格します。
セットアップのテスト:手動での停止
意図的に壊してみたことのない設定を信用してはいけません。私はいつもステージング環境でハードクラッシュをシミュレートします。プライマリでPostgreSQLプロセスを停止し、遷移を観察してください。
# ノード 1 にて
sudo systemctl stop postgresql
# Pgpool-II ノードにて、昇格を監視
tail -f /var/log/pgpool2/pgpool.log
Pgpool-IIがノード0を「down」とマークし、フェイルオーバースクリプトをトリガーするのが確認できるはずです。アプリケーションには2秒ほどの瞬き(フリッカー)が見えるかもしれませんが、スタンバイが昇格すれば自動的に動作を再開します。
現場からの教訓
以前、「Permission Denied(アクセス拒否)」エラーのデバッグに4時間費やしたことがありますが、結局は pool_hba.conf の単純な不一致が原因でした。PostgreSQLの pg_hba.conf と同様に、Pgpool-IIにも独自のアクセス制御リストがあります。これらは常に同期させておきましょう。また、pcp.conf にはMD5ハッシュ化したパスワードを設定してください。これにより、プロキシ全体を再起動することなく、pcp_attach_node を使って復旧したノードを再参加させることができます。
スケーリングとは、単一のサーバーにRAMを投入し続けることではありません。それはオーケストレーション(統合的な管理)のことです。Pgpool-IIは、負荷を分散し、データや睡眠時間を犠牲にすることなくハードウェア障害を乗り切るための「テコ」をあなたに与えてくれます。

