オンプレミスで稼働中の基幹システムをAWSにマイグレーションする提案で、現行システムとの互換性および高可用性のために2個のEC2インスタンスでDb2 HADR(高可用性災害リカバリー)クラスタを構成し、クラスタ管理ソフトのCLUSTERPROでテークオーバーを自動化する案が採用されていました。
CLUSTERPRO等のクラスタ管理ソフトをAWSでも使用することは、オンプレミスで蓄積した知見を継承できる、オンプレミスとAWSで運用を共通化できる等の利点があります。一方でAWSには各種のマネージドサービスがあるのでCLUSTERPROがなくてもテークオーバーを自動化できるかもしれません。脱オンプレミスを目指すなら共通化を意識する必要もありません。
冒頭の提案には間に合いませんが、AWSのマネージドサービスによるテークオーバー自動化の可能性を検討してみます。
1. Db2 HADRのテークオーバーの流れ
Db2 HADRのテークオーバーの流れを次のようなステップで定義し、ステップごとに手段や考慮点を整理していくことにします。
- 障害検知
- テークオーバー発動の判断とコマンド投入
- IPアドレス引継ぎ
- 両系活性(スプリットブレイン)防止
前提として、HADRペア(プライマリとスタンバイ)が同期している状態とし、プライマリに発生した障害を検知しスタンバイにテークオーバーするプロセスの自動化を検討します。それ以外にも考慮すべきシチュエーションはありますが本記事ではスコープ外とします。HADRの同期モードはSYNCまたはNEARSYNCとします。
それでは各ステップを見ていきましょう。
2. 障害検知
2-1. 障害検知手段の整理
障害検知の前提として、Db2 HADRプライマリを消失した場合はもちろん、スローダウンの場合にも障害とみなしてテークオーバーする方針とします。具体的にはSQLコールの応答時間を監視し、しきい値を超えたらスローダウン発生と判定します。これにDb2を構成する主要なコンポーネントの監視を併用し、コンポーネント障害を先に検知したらスローダウンの判定を待たずにテークオーバーします。
監視にはCloudWatchを使用しますが、有料の高解像度メトリクスは使わないことにします。無料の標準メトリクスの解像度(メトリクスの評価間隔)は最高で1分なので、これを踏まえて目標リカバリータイム(RTO)を評価間隔×2の2分と仮置きし、プライマリで障害が発生してからスタンバイが新プライマリに昇格完了まで2分以内に完了する仕組みを目指すことにします。
Db2を構成するコンポーネントは一旦Db2、OS、インフラ部分(サーバ、ネットワーク、ストレージ)に分類しましたが、スローダウンパターンはSQLコールの応答計測で全コンポーネントをカバーし、停止障害パターンはDb2とそれ以外のコンポーネントに分けて障害を検知することにしました。この関係を下図に示します。
SQLコールの応答遅延は、SQLコールの応答時間をCloudWatchのPutMetricData API(あるいはaws cloudwatch put-metric-dataコマンド)でCloudWatchに送信して、カスタムメトリクスとして監視します。更新タイプのSQLコールは処理が全てのコンポーネントに依存する(どのコンポーネントが遅延してもSQLコールが遅延する)ので、ひとつの検知手段で全コンポーネントの遅延を監視できます。ロック待ちを障害と誤検知しないため、監視用SQLコールの投入先テーブルを表スペースレベルで分離する等、ロックが発生しないよう設計します。
停止障害のうちDb2プロセス停止は、CloudWatchエージェントのprocstatプラグインでdb2syscのpid_countを監視します。CloudWatchはこのエージェントからカスタムメトリクスを受け取ります。
Db2プロセス以外の4つのコンポーネントの障害監視は、CloudWatchがStatusCheckFailed_xxxxという名前の標準メトリクスを1対1で提供しており、更にそれらを包括するStatusCheckFailedという標準メトリクスも用意しています。4つのうちどれか一つのコンポーネントに障害があればStatusCheckFailedが障害を示すので、このメトリクスを監視します。
2-2. 検知間隔の整理
理解しやすいように具体的な数値をあてはめて検討します。まずSQLコールの応答時間を障害と判定するしきい値を20000ミリ秒(20秒)とします。監視プログラムはDb2のインスタンスに同居し、「SQLコールを実行して応答時間(ミリ秒)を測定」→「PutMetricDataで応答時間をメトリクスデータとして送信」→「(『20000-直前の応答時間』または0の大きい方)ミリ秒Sleep」という処理を繰り返します。通常時の応答時間は1秒未満とします。
CloudWatchがメトリクスデータを集計し評価する間隔は標準解像度最短の1分とします。ちなみにこの間隔を期間(Period)、期間毎の集計値をデータポイントといいます。1分間のメトリクスデータ数は20秒間隔の場合通常3個、異常時には2個、1個またはゼロになることもあります。
CloudWatchでは個々のメトリクスデータの到着時刻や到着順を知ることができず、1分単位の統計値(最大、最小、平均)とメトリクスデータ数の情報からOKかALARM発報かを判定しなければなりません。その判定と理由を下表に整理しました。
この表の判定では、期間を1分間(無償の範囲の最小値)とする制約の下でRTO 2分の達成を目指しました。表の中で最も検知に時間がかかるのは、「〇 〇 ー」のパターンでOKとなった次の期間の終わり(60秒後)に結局ALARMと判定するケースです。このとき障害発生(二つ目の〇の直後)から次の期間でALARM判定されるまでの最長時間は約100秒です。後続処理(テークオーバー起動からスタンバイDb2のプライマリ昇格完了まで)の猶予は約20秒となります。
期間中のメトリクスデータ数が2個の場合も判定をALARMとすればテークオーバーの発動を早められますが、プライマリが正常稼働している状態でテークオーバーを発動する可能性とのトレードオフになります。
メトリクスデータの送信頻度を上げれば、パターンは複雑になりますがALARM判定の確度を上げながら判定までの時間は短縮できるかもしれません。また高解像度メトリクスや他の監視手段の併用により判定精度向上や検知時間短縮は期待できると思いますが、今回はそこまでは追求しないことにしました。
停止障害検知の2つのメトリクスに対しては、メトリクスが異常を示したらアラームを発報し、それ以外では発報しないことにします。何らかのコンポーネント障害によりSQLコールの応答が遅延している状況で、こちらのアラームの方が先に発報する可能性に期待します。これらのメトリクスのメトリクスデータ数については、Db2の稼働状況との因果関係はないのでアラームの条件に含める必要はないと考えます。
2-3. CloudWatch Alarmのしきい値と欠落データ処理
前項で整理したALARMの判定ルールを実現するために、次の二つのCloudWatch Alarmを定義します。これにより応答が20秒よりも長く遅延した場合にはALARM1を発報させ、期間中のメトリクスデータ数が1の場合にはALARM2を発報させます。
ALARM1: 期間中のメトリクスデータの最大値(Maximum) > 20000[ミリ秒]
ALARM2: 期間中のメトリクスデータ数(Sample count) < 2
実はこれだけだとメトリクスデータ数が0の場合はALARMが発報しません。メトリクスデータ数が0の場合はCloudWatchのメトリクスのSample countの統計値は「欠落データ」という状態になり、Sample count < 2が成立せずALARM2は発報してくれないのです。
「欠落データ」の状態には「欠落データの処理」で対応します。ここに「Breaching」を設定することで、「欠落データ」の場合にもSample count < 2と同様に「しきい値を超えた」と判断します。
この「欠落データの処理」にはAWSのドキュメントに「データが断続的な場合にアラームが早く ALARM 状態になる誤アラームを回避しようとするロジック」と紹介されているロジックが含まれており、「欠落データの処理」よりも過去の期間の統計値に基づいた判定を優先しようとします。残念ながらこの回避ロジックの詳細や制御方法は開示されていないので、代わりに今回の検知パターンで実験をしてみました。実験結果を下図に示します。
「欠落データの処理」を設定することにより、少なくともALARM2については期待通りのタイミングでアラームが発報されることを確認できました。ALARM1は5期間連続で欠落データが続いた後にアラームを発報しましたが、これがドキュメントの「評価範囲は 5 データポイント」という記述に符合するのかもしれません。
「欠落データ」に対処する仕組みとしては、CloudWatchには「欠落データの処理」の他にMetric MathのFILLという関数も用意されているので、こちらも検討してみます。この関数の使い方は、例えばFILL(m1,25000)という式で「グラフ化したメトリクス」を作成し、このメトリクスに対してCloudWatch Alarmを設定するというものです。ここでm1は今回の監視対象のメトリクス、25000はm1が「欠落データ」の時にm1として使われる値です。これに> 20000というしきい値を設定すれば、「欠落データ」の時も25000 > 20000が成立してアラームを発報できそうです。
これも実験してみたところ、「欠落データ」の時にアラームを発報することはできたのですが、以下の2点の課題が残りました。
- アラームは2つ目の期間の後(2分経過後)に発報された。FILL関数の結果は次の期間に評価されると考えられる
- メトリクスデータが届いた時のFILL関数の結果は期間中のメトリクスデータの平均値になっていた。期間中に20000を超える値のメトリクスデータが届いても、平均が20000以下になってアラームが発報しない可能性がある
「欠落データ」の対処方法については、今回の監視パターンに対してはSample countのしきい値と欠落データ処理=Breachingの組み合わせで目的を達成できそうです。
3. テークオーバー発動の判定とコマンド投入
障害検知のために3つのメトリクスを定義してアラームをセットすることを前章で述べました。ここではアラームが発報されてからテークオーバーを発動するまでの流れを検討します。まず大まかな流れを下図に示します。
テークオーバー発動の判定のうちのCloudWatch Alarmの評価とコマンド投入処理の起動はComposite Alarmが担当します。Composite Alarmでは3つのメトリクスのどれかひとつのアラームが発報したらアクションを実行するように複合アラームのルール式を定義し、アクションとしてLambda関数を関連付けます。
CloudWatch Alarmはテークオーバー発動のトリガーとして使用しますが、それとは別に、トリガーにはならないが発動の可否に影響する条件があります。例えばHADRの起動中、停止中、メンテナンス中などの状態では万一アラームが発報したとしてもテークオーバーは発動したくありません。
図ではこれらの状態を追跡してDynamoDBテーブルに保持する仕組みを別途用意する前提で、Lambda関数がテーブルを参照してテークオーバーコマンド投入の可否を最終判断する流れとしました。このテーブルにはEC2インスタンスの識別情報も持たせて、テークオーバーコマンド投入先(そのためのSSM Run Commandの投入先)の判定にも利用します。
Db2 HADRのテークオーバーコマンドにはいくつかのオプションがありますが、障害時のテークオーバーではBY FORCEまたはBY FORCE PEER WINDOW ONLYオプションを使用します。PEER WINDOW ONLYはプライマリとスタンバイの間で通信障害が発生している状況で、通信断からコマンド投入までに一定時間以上経過していたらテークオーバーを失敗させることにより、通信断のために更新ログが追いついていない(データ不整合のリスクのある)スタンバイへのテークオーバーを防止するためのオプションです。一定時間(HADR_PEER_WINDOWパラメータで指定)内であればどちらのオプションでもテークオーバーは成功するので、十分な経過時間を確保した上でBY FORCE PEER WINDOW ONLYオプションを採用し、想定外の状況では一旦コマンドを失敗させて人間が介入する余地を作るのがよさそうです。
4. IPアドレス引継ぎ
クライアントアプリからJDBC等で接続するためのDb2エンドポイントのIPアドレスには仮想IPアドレス(VIP)を使用することにします。CLUSTERPRO等のHAクラスタをAWS環境で構築する際に設定するVIPと同様の仕組みです。テークオーバー時にはVIPをスタンバイ側に引き継いで、クライアントアプリは同じIPアドレスで接続をリトライして処理を再開します。
VIPの仕組みの概要を以下に説明します。手順はip addrコマンドを使用する方法で検証済です。
- VPCのCIDR範囲外のプライベートIPアドレスを選び、これをip addr addコマンド、nmcliコマンド、設定ファイル(/etc/sysconfig/network-scripts等)でプライマリ側、スタンバイ側のEC2インスタンスのENIに割り当てておく
ipコマンドの例: ip addr add 10.100.0.10/32 dev eth0 (10.100.0.10/32がVIP、eth0はネットワークインターフェース名)
※ipコマンドは揮発性のためOS起動時に毎回投入する必要有 - VPCのルートテーブルのルート定義の送信先にVIP(上の例では10.100.0.10/32)、ターゲットにプライマリ側EC2インスタンスのENI IDを登録する (VIP宛のパケットがプライマリ側のENIへ転送される)
コマンドの例は省略しますが、EC2のReplaceRoute APIやec2 replace-route CLIでルートテーブルを更新できます - テークオーバー発動のタイミングでルートテーブルのターゲットをスタンバイ側EC2インスタンスのENI IDに置き換える (VIP宛のパケットがスタンバイ側のENIへ転送されるようになる)
5. 両系活性(スプリットブレイン)防止
プライマリとスタンバイ(HADRピア)の間の通信断などにより互いの状態を確認できない中でテークオーバーが発動し、新旧プライマリが同時に稼働してしまうことをスプリットブレインと呼びます。
HADRピア間で通信ができないと更新ログの転送が完了しない(したがって更新のコミットが完了しない)ので、新旧が並立していてもデータの整合性は辛うじて保たれています。しかしこの状態(DISCONNECTED_PEER)の時間は有限で、通信断検知後に一定時間(HADR_PEER_WINDOW)が経過すると旧プライマリが単独でコミットするようになります。ここまで来るとデータの整合性が損なわれるので、そうなる前に旧プライマリの停止が求められます。対策としては、テークオーバー発動のためのLambda関数に、旧プライマリを強制停止する仕組み(EC2のStopInstances APIやec2 stop-instances CLIの投入など)も一緒に組み込んでおきます。
併せてDISCONNECTED_PEER状態の時間の長さも、プライマリの強制停止コマンド投入が間に合うように調整します。値はDb2のHADR_PEER_WINDOWパラメータで定義できます。
他にもHADRピア間の通信タイムアウト(HADR_TIMEOUT)、更新のコミットのタイムアウト(DB2_HADR_PEER_WAIT_LIMITレジストリ)が関係してきますが、名前を挙げるにとどめて深追いはしないでおきます。
6. まとめ
本記事では、Db2 HADRのテークオーバー自動化をAWSのマネージドサービスで実現する可能性を検討しました。CloudWatch、Lambda、Systems Manager等を組み合わせることで、CLUSTERPRO等のクラスタ管理ソフトを使わない自動化の仕組みを構築できそうです。CloudWatchの使用は無償の標準解像度メトリクスの範囲としましたが、その制約下でRTOは2分以内を目標にできることがわかりました。
本記事で検討したシチュエーションはHADRペアが成立した状態でのプライマリ障害ケースに限定しましたが、それでも各章の検討を通じて重要な考慮点を整理できました。
本記事のスコープの外には、テークオーバー判定テーブルの仕組み、新プライマリの昇格後にHADRペアを再構成する手順、スタンバイ側の障害への対応方法、CloudWatchを補完する監視方法など、重要かつ興味深い検討テーマがまだまだ残っています。いずれこれらも紐解いて、AWSのマネージドサービスによるDb2 HADR自動化の全体像を描いてみたいと思います。
末筆ながら、本記事が基幹システムのAWSへのマイグレーション検討のヒントになれば幸いです。



