ロードバランサー配下での認証セッション保持──スティッキーセッション設定の甘さが引き起こす間欠的な認証失敗

はじめに:本番環境で起きる「たまに認証が切れる」という報告

複数台のアプリケーションサーバーの前段にロードバランサーを置いた構成で、ユーザーから「ログイン直後なのに、ボタンをクリックしたら認証が切れていた」という報告を受けたことはないでしょうか。

再現性がなく、特定のユーザーだけが報告し、リロードしたら治ったといった間欠的な現象です。こうした症状の背景には、スティッキーセッション(セッションアフィニティ)の設定不足、または設定は存在しても運用時の構成変更で無効化されているケースが多くあります。

本記事では、実際の検証を通じて、ロードバランサー配下でセッションを保持する際の落とし穴と、実務的な対策を整理します。

スティッキーセッションが「あるはず」で起こる失敗

ロードバランサーの設定を確認すると、スティッキーセッションが有効になっているはずなのに、なぜ認証が切れるのか。まずここを掘り下げる必要があります。

検証の前提条件

以下の構成を想定して検証を進めました。

  • ロードバランサー: レイヤー7(アプリケーション層)で負荷分散
  • アプリケーションサーバー: 3台(A、B、C)、各サーバーはローカルメモリにセッションストアを保持
  • セッション識別: Cookie(JSESSIONID など)をスティッキーキーとして使用
  • セッションの有効期限: 30分

この構成で起きやすい問題を順序立てて検証しました。

ロードバランサーのスティッキー設定が「有効」でも機能しない場合

問題1:Cookie のパス指定がロードバランサーの認識範囲外

多くのロードバランサーは、特定の Cookie キーを見てセッションアフィニティを判定します。しかし、その Cookie が実際にリクエストに含まれていなければ、判定そのものが機能しません。

# サーバー側で発行している Cookie の例
Set-Cookie: JSESSIONID=ABC123DEF456; Path=/app; HttpOnly

# ロードバランサーが監視している Cookie キー
JSESSIONID

ここで問題が発生する場面:

  • ユーザーが /app/login でログインし、セッション Cookie が発行される
  • その後、/other-app/ という別パスにアクセスするリクエストを送信
  • Cookie の Path が /app に限定されているため、/other-app/ へのリクエストには Cookie が含まれない
  • ロードバランサーはセッションキーを見つけられず、ラウンドロビンで別サーバーへ振り分け
  • 別サーバーにはセッションがないため、認証失敗

この状況は、マイクロサービスやサブドメイン分割の構成で特に起こりやすいです。

問題2:ロードバランサーの再起動やフェイルオーバー

ロードバランサー自体がフェイルオーバーされると、それまで保持していたセッション親和性のマッピング情報が失われることがあります。

  • ロードバランサーがメモリ内にセッション→サーバーのマッピングを保持していた場合
  • フェイルオーバーによってメモリが初期化される
  • ユーザーの次のリクエストは、別のサーバーに振り分けられる可能性がある

ロードバランサーが複数台冗長構成の場合、マッピング情報の同期がなければこの問題は避けられません。

問題3:サーバー追加時の既存セッションの扱い

本番運用中にサーバーを追加する場合、既存ユーザーのセッションをどう扱うかが曖昧だと問題になります。

# 運用中のサーバー群
Server A (セッション保持中)
Server B (セッション保持中)
Server C (セッション保持中)

# 新しいサーバーを追加
Server D (新規、セッションなし)

# ロードバランサーの振り分けルール
- 既存ユーザーのセッションキーが見つかれば、対応サーバーへ
- 見つからなければ、新規ラウンドロビン対象に含める

ロードバランサーの設定が「新規リクエストは Server D に含める」となっていると、既存セッションのあるユーザーは正常に保持されますが、セッションの有効期限切れ後に再度ログインしたユーザーは Server D に振り分けられる可能性があります。その時点で Server D にはセッションストアがなければ、認証失敗につながります。

実装・設定側で確認すべき項目

1. ロードバランサー側の設定確認

# AWS ALB の場合
TargetGroup の属性:
  - Stickiness: Enabled
  - Stickiness duration: 1 day (または適切な期間)
  - Stickiness type: lb_cookie または app_cookie
    - app_cookie の場合は Cookie 名を明示

# その他のロードバランサーの場合
  - セッションキーとして使用する Cookie 名
  - タイムアウト(マッピング情報の有効期限)
  - ハッシュアルゴリズム(IP ベース vs Cookie ベース)

設定を確認する際は、以下の点を整理してください:

  • スティッキー機能は有効か
  • 有効期限は、セッションの有効期限より長いか
  • Cookie ベースなら、対象となる Cookie 名は正しいか
  • ロードバランサーの冗長構成の場合、マッピング情報の同期方式は何か

2. アプリケーション側で確認すべきこと

セッション Cookie の発行時に、ロードバランサーが識別できる形式になっているか確認します。

// Java の例
response.addCookie(new Cookie("JSESSIONID", sessionId));
// または
response.addCookie(new Cookie("JSESSIONID", sessionId) );

重要なのは、Cookie のパスが / に設定されているか、または複数のアプリケーション間で共有できる設定になっているかです。

3. 運用プロセスの確認

スティッキーセッション設定が正しくても、運用時の判断で無効化されることがあります。

  • サーバーの追加・削除時に、ロードバランサーの設定を更新しているか
  • ロードバランサーのバージョンアップやパッチ適用時に、スティッキー設定が保持されているか
  • セッションストアの移行(メモリ→Redis など)を行う場合、ロードバランサー側の設定をどう変更するか

セッションストア分離への段階的な移行

スティッキーセッション設定の甘さを根本的に解決するには、セッションストアをサーバーローカルから分離(Redis など)することが有効です。ただし、既存運用中の本番環境では段階的な移行が必要になります。

# Phase 1: 現状(サーバーローカルストア + スティッキー)
Client → LB → Server A (Session Store in Memory)
              → Server B (Session Store in Memory)
              → Server C (Session Store in Memory)

# Phase 2: 移行中(デュアルライト)
Client → LB → Server A (Write to both Local & Redis)
              → Server B (Write to both Local & Redis)
              → Server C (Write to both Local & Redis)
         ↓
      Redis (Shared Session Store)

# Phase 3: 完全移行(ストア分離)
Client → LB → Server A (Read/Write from Redis)
              → Server B (Read/Write from Redis)
              → Server C (Read/Write from Redis)
         ↓
      Redis (Shared Session Store)

Phase 2 では、セッション更新時にローカルストアと Redis の両方に書き込み、読み込み時はローカルストアを優先します。こうすることで、既存のスティッキー設定を活かしつつ、段階的に Redis への依存を高めることができます。

実務投入時の追加確認項目

実装や本番投入前に、以下の項目を確認してください。

  1. セッション有効期限とスティッキー設定の整合性
    • セッション有効期限が 30 分なら、スティッキーマッピングの有効期限も 30 分以上必要
    • ただし、マッピング有効期限を長くしすぎると、サーバー障害時に古いマッピングが残る
  2. 障害時の動作検証
    • セッション保持中のサーバーが落ちた場合、ユーザーはどうなるか
    • ロードバランサーがサーバーの健全性を判定するまでの時間は
    • その間のリクエストはどのサーバーに振り分けられるか
  3. ロードバランサーのログ確認
    • スティッキー設定が機能しているか、ログで確認できるか
    • セッションキーが見つからないリクエストはどれくらいの頻度で発生しているか
  4. 負荷分散の実効性
    • スティッキー設定により、実際に負荷が均等に分散されているか
    • 特定サーバーへのリクエスト集中が起きていないか

まとめ:スティッキーセッションは「設定したら終わり」ではない

ロードバランサー配下でのセッション管理は、単に設定を有効にするだけでは不十分です。Cookie のパス指定、ロードバランサーの冗長構成、運用時の構成変更といった複数の要因が絡み合い、間欠的な認証失敗を引き起こします。

現場では「なぜか時々ログインが切れる」という報告が、実は明確な原因を持つことが多いです。本記事で示した検証項目を順序立てて確認することで、原因特定と対策が格段に進みやすくなります。

そして、長期的には、セッションストアの分離によって、スティッキー設定への依存を減ら