MySQLの高可用性:Galeraマルチマスタクラスター構築ハンズオンガイド

Database tutorial - IT technology blog
Database tutorial - IT technology blog

午前3時のデータベース緊急事態

週末の楽しみを台無しにするものといえば、午前3時に届く「プライマリデータベースノードのクラッシュ」を知らせるアラート通知でしょう。標準的なシングルマスタ構成では、障害が発生すると、手動でスレーブを昇格させるかサービスを復旧させるまで、アプリケーションは停止したままになります。MySQL、PostgreSQL、MongoDBをさまざまな本番環境で運用してきた経験から、Galera Clusterは手動介入なしでデータの可用性を維持するための最も信頼性の高い方法の一つであると確信しています。

Galera Clusterの構築は、単にスクリプトを実行するだけではありません。データセンター全体がオフラインになったとしても稼働を維持するためのアーキテクチャをしっかりと理解する必要があります。このガイドでは、実戦で鍛えられた高可用性構成の手順を解説します。

なぜマルチマスタが従来のレプリケーションより優れているのか

なぜ標準的なMySQLレプリケーションではなくGaleraを選ぶのでしょうか? 従来の構成の多くは「非同期レプリケーション」を使用しています。このモデルでは、マスタがデータを書き込み、その後にログをスレーブに送信します。ログが転送される前にマスタのハードウェアが故障した場合、データは失われます。1時間あたり1万ドルの売上を処理するフィンテックアプリやEコマースプラットフォームにとって、そのリスクは許容できません。

Galera Clusterは完全同期型マルチマスタレプリケーションを利用します。クラスター内のすべてのノードは同一のピア(対等な存在)です。ノードAが書き込みを受信すると、即座にノードBとノードCにブロードキャストされます。トランザクションはクラスター全体がコンセンサス(合意)に達して初めてコミットされます。つまり、書き込みがどこで行われたかに関わらず、すべてのノードが常に最新のデータを保持していることになります。

トレードオフの評価

Galeraは強力ですが、万能な解決策ではありません。本番環境のワークロードを移行する前に、パフォーマンスへの影響を理解しておく必要があります。

メリット

  • 真の高可用性: 単一障害点(SPOF)が存在しません。1台のサーバーがダウンしても、他のサーバーが瞬断なくトラフィックを処理し続けます。
  • データ整合性の保証: 同期コミットにより、ノード障害時のデータ損失がゼロになります。
  • 読み取りのスケーラビリティ: 読み取りクエリをすべてのノードに分散できます。3ノード構成の場合、実質的に読み取りのスループットが3倍になります。
  • 自己修復機能: 新しいノードは、ステート・スナップショット・トランスファー(SST)を介して自動的に参加し、同期されます。

課題

  • 書き込みレイテンシの増加: 書き込みごとに5ms〜15ms程度のレイテンシが発生することを覚悟してください。すべてのノードがトランザクションを承認してから完了するためです。
  • 「最も弱いリンク」による影響: ノードCのCPUやディスクが遅い場合、ノードAとノードBのパフォーマンスもそれに引きずられて低下します。
  • ロールバックの競合: 2人のユーザーが全く同じ行を異なるノードで同時に(ミリ秒単位で)更新した場合、クラスターは一方を強制的にロールバックさせます。

本番環境の標準:3ノード構成

本番環境では常に最小3ノードを目指してください。これにより、恐ろしい「スプリットブレイン」シナリオを回避できます。2ノード構成で相互接続が切断された場合、両方のノードが「相手が死んだ」と判断して制御権を奪い合おうとする可能性があります。3ノードあれば、通信可能な2つのノードが「クォーラム(定足数、つまり過半数)」を形成します。それらはオンラインを維持し、孤立したノードはデータ破損を防ぐために安全に自身をシャットダウンします。

今回のセットアップでは、Ubuntu 22.04とMariaDBを使用します。開始する前に、セキュリティグループで以下の特定のポートの通信が許可されていることを確認してください。

  • 3306: 標準のMySQLトラフィック
  • 4567: Galera Clusterレプリケーション
  • 4568: インクリメンタル・ステート・トランスファー (IST)
  • 4444: ステート・スナップショット・トランスファー (SST)

ステップバイステップの実装手順

1. MariaDBのデプロイ

パッケージマネージャーを更新し、3つのノードすべてにMariaDBサーバーを同時にインストールします。

sudo apt update
sudo apt install mariadb-server -y

2. Galeraの設定

/etc/mysql/mariadb.conf.d/60-galera.cnfにカスタム設定ファイルを作成します。クラスターアドレスはすべてのノードで共通ですが、wsrep_node_addressは各サーバーに合わせてカスタマイズする必要があります。

[galera]
# クラスターの基本設定
wsrep_on                 = ON
wsrep_provider           = /usr/lib/galera/libgalera_smm.so
wsrep_cluster_name       = "prod_cluster_01"
wsrep_cluster_address    = "gcomm://10.0.0.1,10.0.0.2,10.0.0.3"
binlog_format            = row
default_storage_engine   = InnoDB
innodb_autoinc_lock_mode = 2

# ノードの識別情報
wsrep_node_address       = "10.0.0.1" # ノードのローカルIPを使用
wsrep_node_name          = "node-01"    

3. クラスターの初期化

すべてのノードを一度に起動することはできません。最初のノードは、まだ参加すべきピアが存在しないため、クラスターを「ブートストラップ」する必要があります。ノード1でサービスを停止し、ブートストラップコマンドを実行します。

sudo systemctl stop mariadb
sudo galera_new_cluster

これで、ノード1が新しいネットワークのプライマリコンポーネントになります。

4. ピアノードの接続

ノード2とノード3で、単にMariaDBサービスを再起動します。これらのノードは設定ファイル内のIPを確認し、ノード1に接続して同期プロセスを開始します。

sudo systemctl restart mariadb

5. クラスターの状態確認

MariaDBシェルにログインしてセットアップを確認します。クラスターサイズをチェックして、すべてのノードが認識されているか確認してください。

mysql -u root -p -e "SHOW STATUS LIKE 'wsrep_cluster_size';"

出力結果が 3 であれば、マルチマスタクラスターは正式に稼働しています。

現場からの教訓

クラスターを構築することは第一歩に過ぎません。維持するためには特定の運用習慣が必要です。大規模なデプロイを管理してきた中で学んだ3つの教訓を紹介します。

SSTよりもISTを優先するよう最適化する

ノードが再起動すると、ステートトランスファー(状態転送)が行われます。SSTは全データの消去とコピーを伴うため、データが500GBもある場合はネットワーク帯域を使い果たしてしまいます。一方、ISTは欠落しているトランザクションのみを送信します。ISTを利用しやすくするために、書き込み量に応じて gcache.size1G2G などの大きな値に増やしてください。これにより、短時間の停止であればフル同期なしで復旧できるようになります。

ロードバランサーの導入

Galeraはデータを同期しますが、トラフィックの管理は行いません。アプリが単一のIPを指している場合、そこが依然として単一障害点になります。ノードの前面に ProxySQLHAProxy を配置してください。これらのツールはノードの状態を監視し、1台のサーバーが応答しなくなった場合に自動的にトラフィックを転送します。

巨大な書き込み操作の管理

Galeraは小規模で高速なトランザクションには優れています。しかし、500万行の DELETE を一度に実行すると、クラスター全体が停止する可能性があります。これは、すべてのノードがその巨大な書き込みセットを同時に処理しなければならないためです。大きな操作は、常に2,000行から5,000行程度の小さなチャンクに分割して実行し、クラスターの応答性を維持してください。

高可用性モデルへの移行は、単一サーバーの管理とは異なる運用アプローチが求められます。Galeraの導入には初期の複雑さが伴いますが、複数の障害ドメインにわたってデータがリアルタイムで複製されているという安心感は、その投資に見合う価値があります。

Share: