コンテンツにスキップ

Kestrelの冗長化とは?ロードバランサーとWindowsフェイルオーバークラスター

2026-10-27 更新約10分で読めます

最終確認: 2026-10-27
Kestrelとは?に戻る

30秒で復習

  • Kestrel の冗長化は2通り:アクティブ/アクティブ(ロードバランサーの後ろに複数台)と、アクティブ/スタンバイ(Windowsフェイルオーバークラスター=WSFC で1台ずつ)。
  • WSFC では、Kestrel のアプリを Windows サービスにして、汎用サービス(Generic Service)の役割として登録する。利用者はクラスターの名前(仮想の名前とIP)につなぎ、障害時は名前とIPごと別のノードへ移る。
  • 切り替えの間(数十秒)は通信が切れる。メモリ上のセッションは消える。
  • いちばんの落とし穴は Data Protection の鍵。ノードごとに別の鍵だと、フェイルオーバーした瞬間に全員ログアウトされる。鍵は共有の場所に置く。
  • 両方のノードに同じ証明書(クラスターの名前で発行)、同じ設定ファイル、同じ gMSA(両ノードを許可グループに入れる)をそろえる。SPN はクラスターの名前で gMSA に登録する。
  • 汎用サービスが見ているのは「サービスが動いているか」だけ。アプリが固まっても気づけないので、ヘルスチェックの仕組みを足す。
  • 覚え方:「同じ看板(名前とIP)を、店ごと隣の建物へ引っ越す」

用語の正式名称と読み方

用語 意味
冗長化 1台が壊れても止まらないように、予備を用意しておくこと
アクティブ/アクティブ 全部の台が同時に働く。ロードバランサーで振り分ける
アクティブ/スタンバイ 1台だけが働き、ほかは待機。障害時に入れ替わる
WSFC Windows Server Failover Clustering。Windowsフェイルオーバークラスター
ノード クラスターに参加しているサーバー1台1台
役割(ロール) クラスターで守る単位。ここでは「汎用サービス」
クライアントアクセスポイント 利用者がつなぐ、クラスターの名前とIP(ネットワーク名+IPアドレスのリソース)
フェイルオーバー 障害時に、役割を別のノードへ移すこと
クォーラム クラスターが「自分たちは過半数そろっている」と判断する仕組み。監視(ウィットネス)を使う
Data Protection ASP.NET Core がログインの Cookie などを暗号化するための仕組みと、その鍵

質問:Kestrelの冗長化には、どんな方式がある?

答え: 「全部の台を同時に動かす」か「1台を動かして、もう1台は待たせる」かの2通り。

アクティブ/アクティブ アクティブ/スタンバイ(WSFC) ロードバランサー 壊れた台を外す Kestrel 1 稼働中 同時に働く Kestrel 2 稼働中 同時に働く 全台が働く・ほぼ止まらない web-app(10.0.0.50) クラスターの名前とIP ノード1 稼働中 ノード2 待機(停止) 障害時に移る(数十秒止まる)
観点 アクティブ/アクティブ(ロードバランサー) アクティブ/スタンバイ(WSFC)
動いている台数 全台 1台だけ(ほかは待機)
前に置くもの ロードバランサー・リバースプロキシ(→プロキシとリバースプロキシ) クラスターの名前とIP(クライアントアクセスポイント)
障害時 ロードバランサーが壊れた台を外す。ほぼ止まらない 役割ごと別ノードへ移る。数十秒止まる
性能 台数を増やせば上がる 1台分のまま
アプリに求めること 状態を持たない(セッションや一時ファイルを各台に置かない) 同時に1つしか動かないことを前提にできる
向いているもの 普通のWebアプリ・API 同時に1つだけ動かしたい処理(スケジューラー、順番に処理するキュー)、同じ名前・IPで待ち受け続けたいもの
  • 普通の Webアプリなら、アクティブ/アクティブのほうが止まりにくく、台数も増やせる。
  • WSFC は、「2つ同時に動くと困る」アプリや、すでに WSFC の基盤があってそこに載せたい場合に選ばれる。

質問:WSFCでKestrelを動かすには?

答え: Kestrel のアプリを Windows サービスとして作り、クラスターに汎用サービスの役割として登録する。役割の中には、IPアドレス・ネットワーク名・サービスの3つのリソースが入る。

利用者 https://web-app.corp.example.com フェイルオーバークラスター ノード1(稼働中) ノード2(待機) 役割:汎用サービス IPアドレス 10.0.0.50 ネットワーク名 web-app サービス MyKestrelApp サービスは IP・名前に依存させる 障害時に、役割ごと ここへ移る(IP・名前も一緒) 移る

1. アプリを Windows サービスにする

var builder = WebApplication.CreateBuilder(args);
builder.Host.UseWindowsService();          // Windows サービスとして動かす

// ログイン Cookie などの鍵を、両ノードから見える共有の場所に置く(→ 落とし穴1)
builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\fileserver\dpkeys\myapp"))
    .SetApplicationName("myapp");

builder.Services.AddHealthChecks();        // 生きているかを外から確かめる口
var app = builder.Build();
app.MapHealthChecks("/healthz");
app.Run();

2. 両方のノードに、同じものを用意する

# 各ノードで:サービスを登録(起動はクラスターに任せるので「手動」にする)
New-Service -Name "MyKestrelApp" -BinaryPathName "C:\apps\myapp\MyApp.exe" -StartupType Manual

# 各ノードで:実行アカウントを gMSA にする(パスワード欄は空)
sc.exe config MyKestrelApp obj= "CORP\svc-web$"

3. クラスターに汎用サービスの役割を作る

# どれか1台で:役割を作る(ネットワーク名 web-app と、仮想IPを付ける)
Add-ClusterGenericServiceRole -ServiceName "MyKestrelApp" -Name "web-app" -StaticAddress 10.0.0.50

# Windows 認証(Kerberos)を使うなら、クラスターの名前で SPN を gMSA に登録する
setspn -S HTTP/web-app.corp.example.com CORP\svc-web$
  • 利用者は https://web-app.corp.example.com のように、クラスターの名前でつなぐ。ノードの名前(node1 など)は使わない。
  • サービスのリソースが、IPアドレスとネットワーク名のリソースに依存するようにしておく(作った直後の既定でそうなっていることが多い)。IPが付く前にサービスが起動して、待ち受けに失敗するのを防ぐ。
  • 汎用サービスの設定にある「ネットワーク名をコンピューター名として使用する」を有効にすると、アプリから見たコンピューター名がクラスターの名前になる。

質問:WSFCで気をつけることは?(落とし穴)

ノード1 ノード2 両方のノードに、同じものを用意する ✓ アプリ本体と appsettings.json ✓ 証明書(クラスターの名前で発行) ✓ gMSA(許可グループに両ノード) ✓ サービス(起動は「手動」) 共有の場所に置く(どのノードからも同じものを見る) Data Protection の鍵 共有フォルダ・DB ログ 中央へ送る データベース クラスターの外 鍵が共有されていないと、フェイルオーバーで全員ログアウトされる

落とし穴1:フェイルオーバーしたら全員ログアウトされる(Data Protection の鍵)

ASP.NET Core は、ログインの Cookie や CSRF 対策のトークンを Data Protection の鍵で暗号化している。何も設定しないと、鍵はノードごと・実行アカウントごとのローカルに作られる。

  • ノード1の鍵で作った Cookie は、ノード2では読めない → フェイルオーバーした瞬間に全員ログアウト、送信途中のフォームもエラーになる。
  • 対策:鍵を両ノードから見える共有フォルダ(またはDB・Azure Blob など)に置き、SetApplicationName をそろえる。鍵の保存場所は、gMSA だけが読めるようにアクセス権を絞る。

落とし穴2:証明書が片方のノードにしかない

  • HTTPS の証明書は、両方のノードの「コンピューター(LocalMachine)の個人」ストアに入れる。
  • 証明書の名前(SAN)は、クラスターの名前(web-app.corp.example.com)で発行する。ノード名で発行すると、利用者のブラウザで証明書エラーになる。
  • gMSA が秘密鍵を読めるようにアクセス権を付ける(両ノードで)。

落とし穴3:gMSA が片方のノードで使えない

  • gMSAの「パスワードを取り出してよいグループ」に、両方のノードのコンピューターアカウントを入れる。
  • 新しくノードを足したときも、グループへの追加を忘れない。

落とし穴4:アプリが固まっても、切り替わらない

  • 汎用サービスのリソースが見ているのは、サービスが「実行中」かどうかだけ。アプリの中が固まって応答しなくても、サービスが動いていれば正常とみなされる。
  • 対策の例:/healthz を定期的に確かめる監視を置き、応答がなければサービスを再起動する・アラートを出す。アプリ側で、固まったことに気づいたら自分で終了する作りにする(終了すればクラスターが検知して、再起動やフェイルオーバーをする)。

落とし穴5:設定ファイルやログが、ノードごとにバラバラ

  • appsettings.json やアプリ本体は、両ノードに同じものを配置する。配置の手順に「両方のノードへ」を入れる。
  • ログをノードのローカルに書くと、フェイルオーバーの前後でログが2か所に分かれる。中央のログ基盤(SIEM など)へ送る。
  • アプリがファイルを保存するなら、共有ディスク(クラスターのディスク)や共有フォルダを使う。

落とし穴6:その他

項目 内容
切り替えにかかる時間 数十秒くらい通信が切れる。利用者側(呼び出す側のプログラム)にリトライを入れる
再起動とフェイルオーバーの回数 「同じノードで何回再起動を試すか」「何回失敗したら別ノードへ移るか」をクラスターの設定で決める
フェイルバック 元のノードが直ったとき、自動で戻すか。業務時間外だけ戻すなどにできる
クォーラム 2台構成なら、ファイル共有やクラウドの監視(ウィットネス)を必ず置く
NLB との同居 Windows の NLB(ネットワーク負荷分散)と WSFC は、同じサーバーで一緒に使えない
データベース DB 側も冗長化しているなら、接続先はリスナー名にする。EF Core なら接続のリトライ(EnableRetryOnFailure)を有効にする

質問:アクティブ/アクティブにするなら?

答え: 前にロードバランサー(またはリバースプロキシ)を置き、Kestrel を2台以上並べる。アプリが状態を持たないことが条件。

やること 内容
ヘルスチェック ロードバランサーから /healthz を確かめ、応答しない台を外す
セッション メモリではなく、Redis や SQL Server の分散キャッシュに置く
Data Protection の鍵 WSFC と同じく、全台で共有する
Forwarded Headers ロードバランサーの後ろなので、本当の接続元IPと https を受け取る設定をする(→Kestrelとは)
一度に1つだけ動かしたい処理 夜間バッチなどは、全台で動くと二重に実行される。別のサービスに分けるか、DB のロックで1台だけにする

具体例

場面 おすすめ
すでに WSFC 基盤があり、社内向けの業務アプリを載せたい 汎用サービスの役割+共有の Data Protection 鍵+クラスター名の証明書
夜間に1つだけ動かす集計サービス(二重起動は困る) WSFC の汎用サービス(同時に1つしか動かない)
利用者が多い Web API ロードバランサー+Kestrel 複数台(アクティブ/アクティブ)
止まる時間を数秒以内にしたい アクティブ/アクティブ。WSFC では数十秒止まる

関連メモ

確認したいこと

  • 今の WSFC の役割は、汎用サービスか。サービスのリソースは IP・ネットワーク名に依存しているか
  • Data Protection の鍵はどこに保存されているか(ノードごとになっていないか)。フェイルオーバー後にログアウトされないか試したか
  • 証明書はクラスターの名前で発行され、両方のノードに入っているか
  • gMSA の許可グループに、全ノードのコンピューターアカウントが入っているか
  • アプリが固まったときに気づく仕組み(ヘルスチェックの監視)はあるか
  • フェイルオーバーの訓練(手動で役割を移す)を、定期的にしているか

参考資料