コンテンツにスキップ

高可用性

コアが 1 台だけの構成は、単一障害点になります。すべてのチェックの時刻を決め、すべてのアラートを 評価し、API を提供するのが、その 1 台だからです。

高可用性(HA)は、同じストア群に対して複数のコアを動かすことでこれを解消します。どれが実際に 働くかは、自動のリーダー選出が決めます。

HA は任意の機能で、既定では無効です。HA を無効にした単一コアのデプロイは、これまでとまったく同じ 挙動になります。

Yagra の HA は、自動で選出されるアクティブ/パッシブのコアペア(スタンバイをさらに増やす ことも可能)です。

  • コア — 複数の yagra-core が、同じ PostgreSQL・NATS・VictoriaMetrics を共有します(任意で Redis なども)。そのうち 1 台がリーダーに選ばれ、処理を担います。残りはスタンバイとして待機し、 リーダーが落ちれば数秒以内に引き継ぎます。二重にポーリングすることも、通知が重複することも ありません。
  • ポーラー — このページですることは、何もありません。ポーラーは状態を持たないので、もともと 冗長にできます。プールごとに複数のポーラーを動かしておけば、失われたポーラーのノードは自動的に 残りへ移ります。詳しくは分散ポーリングを見てください。
  • ストア — ここは Yagra の仕事ではなく、動かす基盤側の仕事です。PostgreSQL のレプリケーション、 VictoriaMetrics の冗長化、NATS のクラスタリングは、それぞれの製品のツールで設定してください。 このページでは扱いません。Yagra は、与えられたアドレスにつなぐだけです。なお Redis を失っても 致命的ではありません。いつでも作り直せるミラーだからです。

つまり、HA が落ちないようにするのは全体を制御する部分です。その下にあるデータが失われないか どうかは、バックエンドサービスをどう配置したかで決まります。

すべてのコアに YAGRA_ENABLE_HA=true を設定し、同じストア群を指すようにします。

リーダーの選出は、PostgreSQL の advisory lock(データベースが用意している排他ロック)で動きます。 そのロックを持っているコアがリーダーです。リーダーが止まると — クラッシュでも、再起動でも、 アップグレードでも — スタンバイがロックを取り、数秒以内に引き継ぎます。

選出のために別のサービスを立てる必要はありません。すでに動かしているデータベースが、その役を 担います。

リーダーは、状態を持つ処理をすべて担当します。スケジューリングとポーラーへの割当、パッシブ イベントの受信、そしてアラートの評価と通知です。スタンバイは API を上げたまま読み取りに応じます。 ただし後述のとおりレディネスは拒否するので、トラフィックはリーダーへ向かいます。

実務上の注意が 2 つあります。

  • 各コアに YAGRA_CORE_ID を与えてください(例: core-a、core-b)。HA のログや診断が、どの インスタンスから出たものか分かるようになります。
  • リーダーが advisory lock に使う接続は、通常の接続プール(YAGRA_PG_MAX_CONNECTIONS、既定は コアあたり 20)とは別に 1 本増えます。PostgreSQL の max_connections は、全コアぶんの合計 にプラス 1 を見込んで決めてください。

関係する変数は次のとおりで、いずれもコアごとに読まれます。

変数 既定 用途
YAGRA_ENABLE_HA false リーダー選出へのオプトイン。未設定ならロックの取得すら行いません
YAGRA_CORE_ID unset(汎用ラベル) HA のログと診断でこのインスタンスを識別する名前
YAGRA_SESSION_KEY_FILE unset(コアごとのセッション) 共有のセッション署名鍵 — 後述

YAGRA_ENABLE_HA が未設定であれば、単一コアは HA 導入前のデプロイと同一の挙動になります。 変数の詳細は設定リファレンスを参照してください。

2 つのプローブエンドポイントがあり、その意味は明確に異なります。

エンドポイント 応答 意味
/healthz すべてのコアで常に 200 プロセスの死活 — 認証なし、ストアへのアクセスなし。再起動の判断に使います。
/readyz リーダーのみ 200、スタンバイは 503 レディネス —「トラフィックはここへ」。HA が無効なら常に 200。

ロードバランサのヘルスチェックは /readyz に向けてください。API と WebUI のトラフィックも、 これで振り分けます。

200 を返すコアはちょうど 1 台なので、クライアントは必ずリーダーにたどり着きます。フェイル オーバーのときは、新しいリーダーが 200 を返し始めることで、トラフィックが自動的に切り替わり ます。

/healthz は、死活を見るためだけに使ってください。スタンバイは健全ですがレディではありません。 間違ったエンドポイントを見て 503 だから再起動、としてしまうと本末転倒です。

各コアは、Prometheus の /metrics に yagra_core_is_leader という値も出します(リーダーは 1、 スタンバイは 0)。これを自分たちの監視に取り込んでおくと、フェイルオーバーが推測ではなく 目に見える出来事になります。詳しくは オブザーバビリティを見てください。

既定では、ログインセッションはコアのプロセスごとに保持されます。つまりフェイルオーバーが起きると、 全員がサインアウトされます。新しいリーダーは、インシデントの最中にチーム全員へ再ログインを 求めることになります。

解決策は、セッションの署名鍵を共有することです。同じ 32 バイトの鍵をすべてのコアにマウントし、 YAGRA_SESSION_KEY_FILE をそこへ向けてください。

こうすると、セッショントークンは状態を持たない署名付きの形になります。どのコアでもログインが 通り、コアの再起動やフェイルオーバーを越えて有効です。ログアウト、ユーザーの無効化、ロールの変更、 パスワードのリセットは、これまでどおりすべてのコアへ即座に反映されます。

Terminal window
# generate once, then distribute the same file to every core (mounted read-only)
head -c 32 /dev/urandom > session.key

鍵ファイルは、ちょうど 32 バイトの生データ(または 16 進で 64 文字)でなければなりません。

判断は安全側に倒してあります。YAGRA_SESSION_KEY_FILE を設定しているのに、ファイルが無い・ 読めない・サイズが違う場合、コアは起動を拒否します。壊れた鍵で黙ってセッションを発行することは ありません。

セッション鍵を設定せずに HA を有効にした場合は、コアは起動して警告を出すだけです。すべて動きます が、フェイルオーバーのたびにユーザーはログアウトされます。

リポジトリには、すぐ使える 2 コア構成のオーバーレイ docker-compose.ha.yml が同梱されています。 単一ノードのスタックに 2 台目のコア(core-b、API は **http://localhost:8081**)を重ね、両方で HA を有効にします。

Terminal window
docker compose -f docker-compose.yml -f docker-compose.ha.yml up --build

そのうえで、別のシェルから選出の様子を眺めます。

Terminal window
# exactly one core is ready (200), the other is a standby (503):
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8080/readyz # core-a
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8081/readyz # core-b
# see who won the election ("acquired leadership" in the winner's log):
docker compose logs core core-b | grep -i leader
# stop whichever core holds leadership and watch the standby take over:
docker compose stop core
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8081/readyz # 200 within seconds

このオーバーレイは、試すためのものです。本番の HA は、同じ部品を実際のロードバランサの後ろに 置いた形になります。たとえば Kubernetes の Deployment でコアを動かし、/readyz をレディネス プローブに指定すれば、Ready になるのは常にリーダーだけです。デプロイの方法そのものは インストールガイドで扱います。

  • アクティブ/パッシブであって、アクティブ/アクティブではありません。 スケジューリング、受信、 アラートは、すべて 1 台のリーダーが行います。スタンバイが増やすのは可用性であって、処理量では ありません。監視の処理量を増やしたいときは、コアではなくポーラーを増やしてください。
  • 書き込みはリーダーに集まります。 スタンバイは読み取りに応じながら待機します。/readyz で 振り分けておけば、クライアントは常に「すべてを行えるコア」を向いたままになります。
  • フェイルオーバーはゼロ秒ではなく、数秒です。 引き継ぎの最中に飛んでいたリクエストは失敗 しうるので、再試行してください。セッション鍵を共有していれば、誰も再ログインせずに済みます。