AWS の ALB を利用していて、スティッキーセッション(維持設定)が効かない状態に遭遇したので、覚えとして記事にしておきます。
スティッキーセッション (維持設定)
AWS の Application Load Balancer (ALB) には、スティッキーセッション機能(維持設定)があり、ユーザーのセッションを特定のターゲット(サーバー)に結びつけることができます。これにより、ユーザーのセッション中のすべてのリクエストが同じターゲットに送信されます。
要は、ターゲットグループ内に複数のサーバーがある場合、一度あるサーバーにアクセスすると、次回以降も同じサーバーにアクセスするように設定できるということです。
スティッキーセッション機能には「期間ベースの維持」と「アプリケーションベースの維持」の 2種類があります。
「期間ベースの維持」では、ロードバランサーで生成された Cookie を用いてセッションを維持するのに対し、「アプリケーションベースの維持」では、ターゲットが作成した Cookie を元にクライアントとターゲットの結びつきを維持するようになっています。
問題となったシステムの構成
構成としては、以下のように同じホストヘッダーの異なるパス ( /aaa/* と /bbb/* )に、それぞれ別のターゲットグループを割り当てて、いずれのターゲットグループとも「期間ベースの維持」を利用していました。
ところが、このサイトにアクセスしたとき、まれに維持設定が効かず、セッションが途切れてしまうのです。(具体的には Laravel アプリケーションで CSRF 対応用のトークンが合致せず、 419 エラーとなりました。)
エラーが発生するケースを根気よく試行した結果、片方のターゲットグループにアクセスしている最中に、もう一方のターゲットグループのパスにアクセスして、そのまま元のターゲットグループの処理を続けるとエラーになることがわかりました。
「期間ベースの維持」の場合、ロードバランサーは「AWSALB」という名前のクッキーを生成し、このクッキーを以てユーザーのセッションを維持しています。ところが同じホスト名で「期間ベースの維持」を設定したターゲットグループを複数作ってしまうと、 AWSALB のクッキーの値がもう一方のターゲットグループの値で上書きされてしまい、セッションが維持できなくなっていたようです。
維持設定の修正
AWS の「Application Load Balancer のスティッキーセッション」のドキュメントをよく見ると、以下の記述がありました。
Application Load Balancers の複数のレイヤーを使用している場合、アプリケーションベースの Cookie を使用して、すべてのレイヤーでスティッキーセッションを有効にできます。ただし、期間ベースの Cookie の場合、AWSALB が使用可能な唯一の名前であるため、1 つのレイヤーでのみスティッキーセッションを有効にできます。
最初に読んだときは「レイヤー」の意味がよくわからなかったのですが、どうやら「レイヤー」とは同じセッション Cookie のスコープを持つ ALB のルールのことのようです。(同じホスト名を持つルールのまとまり)
というわけで、一方のターゲットグループの維持設定を「アプリケーションベースの維持」に変更し、アプリの Cookie 名を Laravel のセッション Cookie 名に設定することで、無事セッションが維持されるようになりました。
そのほかの解決方法
そもそも、 Web アプリケーションのセッションを Redis などに入れてサーバー間で共有していれば、 ALB の維持設定に頼ることもなかったので、今回の問題は発生していなかったのかなと思います。