はじめに
npmの socket-io-sticky-session
(以降、sio-sticky
)が、stickyを有効にしたAWSのALBなどのLoadBalancerの後ろにいて、
LoadBalancerからKeep-Alive接続される場合、X-Forwarded-For
などのヘッダによって正しいWorkerに割り振られないことがある。
という話です。
説明
症状
sio-sticky
はX-Forwarded-For
などのHTTPヘッダをみてNode.jsの Webサーバ/WebSocketサーバ などにStickyに接続を振り分けてくれます。これは、SocketIOのpollingやwebsocketで使われるsid
というSessionIDを持つリクエストを同じプロセスのNode.jsに振り分ける時に大変重宝します(sidは異なるプロセスのNode.jsで共有されないので)。
しかし、以下のような構成の場合、
ブラウザがALBのCookieをちゃんと送っていても、Stickyが上手く行かずにエラー(BadRequest: Session ID unknown)になることがあります。
原因
原因は、 ALB と sio-sticky の間の keep-alive 接続だと思われます。
sio-sticky は 新規のTCP接続があった時に、配下のWorker(Node.js)にその接続を渡すので、keep-aliveされたあとの2回目以降のHTTPリクエストのヘッダなどを見たりしません。
また、ALBはどのターゲットにリクエストを送るかは制御していますが、どのkeep-aliveされた接続にどのHTTPリクエストを流すかは気にしてはいないので、基本的に運任せになります。
下の図で説明しますと、
- ① Browser1 と Browser2 は、異なるWorker(Node.js)に振り分けられていたとする
- ② Browser2からのリクエストがしばらくなくても、ALBとWorkerの間のTCP接続は維持される(keep-alive)
- ③ Browser1からALBにリクエストがある場合、この別のWorkerとkeep-aliveされた接続にリクエストが流されることがある。異なるworkerにリクエストが行くと、
Session ID unknown
と怒られてしまう。
というイメージです。
検証
sio-stickyのkeep-alive時の動作についての検証は下記を見てください。
https://github.com/mokemokechicken/socket-io-sticky-session/blob/feature/for_test_keep-alive/NOTE-keep-alive.md
ちなみに、ALBからsio-stickyへの接続は通常の設定ではkeep-aliveされますし、今回の現象も確認することができます。
さいごに
sio-stickyのような仕組みだとTCP接続時に振り分けるので辛いのでしょうね。
keep-aliveを無効にするのも非効率ですし、sio-stickyなどを使わずに1つのNode.jsを直接ALBにぶら下げるのがBetterなのかなぁ、と思います。