「午前3時のデータベースダウン」という悪夢
想像してみてください。午前3時、スマートフォンが激しく振動します。メインデータベース(Primary)がダウンし、上司からの電話は鳴り止まず、あらゆるチャネルで顧客からの苦情が殺到しています。駆け出しの頃、私はサーバーにSSHでログインし、どのノードが生きているかを確認するだけで1時間も費やしたことがありました。その後はリスクの高い手動操作の連続です。SELECT pg_promote(); を打ち込み、新しいIPを指すようにアプリケーションの設定を急いで修正します。わずか一瞬のミスで、データが消失してしまうという恐怖の中にいました。
もしあなたが PostgreSQL Streaming Replication をマスターしているなら、データのコピーはすでに持っているでしょう。しかし、レプリケーションだけでは不十分です。障害が発生した際に、エラーを自動で検知したり、切り替え(フェイルオーバー)を行ったりすることはできないからです。そこで、これらすべての重労働とストレスを引き受けるために誕生したのが Patroni です。
高可用性(HA)を実現する3つの一般的なアプローチ
Patroni を選ぶ前に、Primary が「戦死」した際に技術者が通常どのような手段を取るのか振り返ってみましょう:
1. 手動切り替え(Manual Failover)
- 手法: 管理者が直接介入し、サブノードを昇格させる。
- メリット: 完全にコントロールでき、誤ったノード昇格を防げる。
- デメリット: 遅すぎる。ダウンタイムの長さは、管理者がいかに早く起きられるかにかかっている。
2. 自作スクリプト
- 手法: Bash や Python を使い、Primary を常に ping で監視して自動的に Standby を昇格させる。
- メリット: 人間よりも反応が早い。
- デメリット: スプリットブレイン(Split-brain)という致命的なエラーのリスクがある。2つのノードが同時に自分を Primary だと思い込み、データを書き込み始めると、データベースは修復不可能な状態になります。
3. Patroni – 現在の黄金律(標準構成)
- 手法: 「共通の台帳」(ETCD や Consul などの DCS)を通じて集中管理する。
- メリット: 完全に自動化されており、合意形成アルゴリズムによってスプリットブレインを防止。洗練された REST API もサポート。
- デメリット: 初期設定の学習に多少の時間が必要。
なぜ Patroni が大規模プロジェクトで選ばれるのか?
私は以前、同時アクセス数が15,000人を超えるECサイトのシステムを構築したことがあります。要件は非常に厳しく、「ダウンタイムは30秒以内」というものでした。Repmgr と Patroni を比較検討した結果、分散構成ストア(DCS: Distributed Configuration Store) メカニズムを備えた Patroni を採用しました。
ETCD を強力な審判だと考えてください。各ノードの Patroni は、審判に対して「私は生きています」という信号(ハートビート)を送り続けます。もし Primary が10秒以上沈黙した場合、審判は残りのノードに対して、直ちに新しいリーダー(Leader)を選出するよう命じます。このプロセスは透過的かつ正確に行われ、人間の介入を一切必要としません。
Patroni クラスターの標準的なアーキテクチャ
私が実戦でよくセットアップするモデルは以下の通りです:
- PostgreSQL: データを保存する主役。
- Patroni: 各 PostgreSQL インスタンスに付随して動作する「ガードマン」。
- ETCD: クラスター全体の生存状態(Cluster state)を保持する場所。
- HAProxy & Keepalived: 単一の仮想IP(Virtual IP)を提供。アプリケーションはこのIPに接続するだけで、HAProxy が現在の Primary ノードを自動的に判別して接続を振り分けます。
詳細な導入手順
3台の Ubuntu サーバーがあることを想定します。システムの骨組みとなる Patroni と ETCD のインストールに焦点を当てます。
ステップ 1: ETCD(状態ストア)の設定
3つのノードすべてに etcd をインストールします:
sudo apt-get update
sudo apt-get install etcd -y
/etc/default/etcd を編集して、ノード同士が認識し合えるようにします。これはクラスター内での合意形成(consensus)を生み出すための極めて重要なステップです。
ステップ 2: PostgreSQL と Patroni のインストール
重要な注意点: initdb でデータベースを初期化しないでください。一貫性を保つために、Patroni にその作業を任せる必要があります。
sudo apt-get install postgresql-15 python3-pip -y
sudo pip3 install patroni[etcd]
ステップ 3: 「心臓部」である patroni.yml の設定
注意すべき主要なパラメータは以下の通りです:
scope: postgres-cluster
name: pg-node-1 # 各ノードに一意の名前を付ける
dcs:
ttl: 30 # リーダーキーの保持期間(秒)
loop_wait: 10
retry_timeout: 10
etcd:
hosts: 10.0.0.1:2379,10.0.0.2:2379,10.0.0.3:2379
postgresql:
listen: 0.0.0.0:5432
data_dir: /var/lib/postgresql/15/main
bin_dir: /usr/lib/postgresql/15/bin
authentication:
replication:
username: replicator
password: your_password
ステップ 4: システムの起動
次のコマンドで Patroni を起動します:
patroni patroni.yml
最初のノードが自動的にデータベースを初期化し、リーダーに昇格します。その後に起動したノードは、リーダーから pg_basebackup を自動的に実行します。手動でバックアップやリストアのコマンドを打つ必要はありません。非常に便利です!
フェイルオーバーのテスト:真実の瞬間
成果を確認する時です。patronictl コマンドを使ってステータスを確認しましょう:
patronictl -c patroni.yml list
1つの Leader ノードと複数の Replica ノードが表示されたテーブルが出てきます。ここで、Leader ノードのサービスを停止して「プラグを抜く」テストをしてみましょう:
sudo systemctl stop patroni
約5〜10秒後、残りのノードのログを見てください。1つの Replica が自動的に Leader に昇格します。旧ノードを再起動すると、自分が「退位」させられたことを自動的に理解し、静かに Standby として復帰します。
実戦経験から学んだ注意点
Patroni を実際に運用する上で、覚えておくべき「厄介な」ポイントがいくつかあります:
- HAProxy は必須: Patroni はアプリケーション側の接続先IPを自動で変更しません。HAProxy を使い、Patroni の REST API(ポート 8008)を叩いて、どのノードが真のマスターかを確認させる必要があります。
- Watchdog の設定: Patroni がフリーズした場合に備え、Linux Watchdog を使用してサーバーを自動再起動するようにしましょう。これにより、クラスターに悪影響を与える「半死半生」のノードを完全に排除できます。
- 同期帯域幅: DBサイズが 500GB を超える場合は、10Gbps ネットワークの使用を検討してください。新しいノードがクラスターに参加する際、膨大なデータを引き込むため、現在の Leader のネットワークを圧迫しやすくなります。
Patroni を使いこなすことは、システムをアップグレードするだけでなく、あなた自身の「安眠」にもつながります。24時間365日の待機に疲弊する代わりに、自動化にその役目を任せましょう。
