ELBのスティッキーセッションについて調べてみた

2015.07.23

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

藤本です。
私事ですが、先週「AWS認定Sysopsアドミニストレーター - アソシエイトレベル」を受験しまして、
無事合格しました(8888
更に昨日「AWS認定デベロッパー - アソシエイトレベル」を受験しまして、
無事合格しました(8888
トリプルアソシエイトになりました。

今回はELBのスティッキーセッションがどういうものなのか理解できていなかったので調べてみました。

概要

ELBにクライアントとバインド先を固定するスティッキーセッションという機能があります。
HTTPレスポンスにELBでCookieを埋め込んで、
そのクッキーを基にバインド先のインスタンスを固定するというものです。

ELBのスティッキーセッションはCookieの有効期限設定が2種類あります。

  • 任意の有効期限を指定する
  • アプリケーションのCookieに従う

スティッキーセッションを利用したい場合、
アプリケーションのセッション保持に合わせてバインド先を固定したいことが多いと思うので、
後者でセッションCookieと合わせて利用することがよいでしょう。

ELBのデフォルト設定は負荷が分散されるようにスティッキーセッション機能はオフとなっており、
利用したい場合、手動でオンに設定する必要があります。

今回はこれらの設定した時にCookieがどのように変わるのか、
実際にどのような挙動をするのか、というところを覗いてみた、というエントリーです。

スティッキーセッションはELB固有の機能ではなく、LBの一般的な機能のようです。
ApacheやNginxでもモジュールを利用することで実装可能です。
物理のロードバランサーも名前は違えど同様の機能は存在します。

今回はELBで確認したことで、他のロードバランサーも同様な動作をするとは限らないことをご了承ください。

環境

◯ バックエンドのWebAPサーバ

  • 台数 : 2台
  • OS : Amazon Linux 2015.03 (HVM)
  • Web/AP : Django 1.7.3
  • セッション管理 : ローカルディスクのsqlite3

◯ ELB

  • インスタンス : 2台のWebAPサーバ

◯ クライアント

  • OS : OSX 10.10.3

◯ Webアプリケーション仕様

  • /login時にユーザ名を渡して、ユーザー名をセッションに登録します。
  • /helloをリクエストすると、
    同一セッションであれば、Hello, ユーザー名を返して、
    同一セッションでなければ、Who are you?を返します。

やってみた

Webアプリケーション準備

さささっと実装します。

$ curl -c cookie.txt -d "{\"username\":\"shinji\"}" -X POST http://52.68.182.132:8000/login/
{"message":"ok", "number":"1"}"

$ curl -b cookie.txt http://52.68.182.132:8000/hello/
{"message":"Hello, shinji", "number":"1"}"

$ curl http://52.68.182.132:8000/hello/
{"message":"Who are you?", "number":"1"}"

numberはWebAPサーバが何号機かを表します。
セッションIDの有効期限は1分で設定しています。

スティッキーセッションOFF

まずデフォルトのスティッキーセッションがオフの状態でCookie、動作を確認します。

EC2のロードバランサーの設定を表示します。
ポート構成の維持設定を確認します。
「無効」となっていれば、スティッキーセッションがOFFの状態です。
EC2_Management_Console

この状態で確認します。

$ date; curl -c cookie_disable.txt -d "{\"username\":\"shinji\"}" -X POST http://lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com/login/; cat cookie_disable.txt
2015年 7月23日 木曜日 18時26分54秒 JST
{"message":"ok", "number":"1"}
# Netscape HTTP Cookie File
# http://curl.haxx.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.

#HttpOnly_lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com FALSE   /   FALSE   1437643674  sessionid   gdttodjroqd83j589n5ysv2lu1jiij5w

$ date; curl -b cookie_disable.txt http://lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com/hello/
2015年 7月23日 木曜日 18時27分02秒 JST
{"message":"Who are you?", "number":"2"}

$ date; curl -b cookie_disable.txt http://lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com/hello/
2015年 7月23日 木曜日 18時27分04秒 JST
{"message":"Hello, shinji", "number":"1"}

$ date; curl -b cookie_disable.txt http://lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com/hello/
2015年 7月23日 木曜日 18時27分08秒 JST
{"message":"Who are you?", "number":"2"}

$ date; curl -b cookie_disable.txt http://lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com/hello/
2015年 7月23日 木曜日 18時27分49秒 JST
{"message":"Hello, shinji", "number":"1"}

$ date; curl -b cookie_disable.txt http://lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com/hello/
2015年 7月23日 木曜日 18時27分53秒 JST
{"message":"Who are you?", "number":"2"}

$ date; curl -b cookie_disable.txt http://lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com/hello/
2015年 7月23日 木曜日 18時27分55秒 JST
{"message":"Who are you?", "number":"1"}

一つ目のリクエストで1号機に対して、セッション登録を行っています。
返ってきたCookie名はsessionid(Djangoデフォルト)です。

有効期限はunixtimeで記載されています。
unixtime : 1437643674
 ↓
JST : 2015/07/23 18:27:54

はい、1分間ですね。

次のリクエストはログインしていない2号機にアクセスしたため、セッションがありません。
プロダクション環境であれば、事件です。

次のリクエストはログインした1号機にアクセスしたため、Helloが返ってきています。

最後の有効期限を超えたリクエストはログインした1号機に関わらず、
既にCookieの有効期限が切れたためにセッションがない状態となっています。

スティッキーセッションON(有効期限はアプリケーションのCookieに従う)

次にスティッキーセッションを有効とします。

EC2のロードバランサーの設定を表示します。
ポート構成の維持設定を変更します。
EC2_Management_Console

「アプリケーションによって生成されたCookieの維持を有効化」を選択し、
Cookie名は先ほど確認した「sessionid」を入力します。
EC2_Management_Console

設定が反映されます。
EC2_Management_Console

それでは確認しましょう。

$ date; curl -c cookie_application.txt -d "{\"username\":\"shinji\"}" -X POST http://lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com/login/; cat cookie_application.txt
2015年 7月23日 木曜日 18時39分45秒 JST
{"message":"ok", "number":"1"}
# Netscape HTTP Cookie File
# http://curl.haxx.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.

#HttpOnly_lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com FALSE   /   FALSE   1437644445  sessionid   yo17p8qllqb76he4omofpb4vbhi1szhg
#HttpOnly_lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com FALSE   /   FALSE   1437644445  AWSELB  ED79359F1E52D63A864058DE73E691A6144778307C194D6361EF08762FD12AE4865CAAE37F5F04D6ED90C1249E00FD09724BCC0F145F76200C802090515B8B602032753E387447453347A8152B19DAF44C5033C6498AECC1AE65FE91C94E8F67CD1488D0AE

$ date; curl -b cookie_application.txt http://lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com/hello/
2015年 7月23日 木曜日 18時39分57秒 JST
{"message":"Hello, shinji", "number":"1"}

$ date; curl -b cookie_application.txt http://lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com/hello/
2015年 7月23日 木曜日 18時39分59秒 JST
{"message":"Hello, shinji", "number":"1"}

$ date; curl -b cookie_application.txt http://lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com/hello/
2015年 7月23日 木曜日 18時40分43秒 JST
{"message":"Hello, shinji", "number":"1"}

$ date; curl -b cookie_application.txt http://lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com/hello/
2015年 7月23日 木曜日 18時40分46秒 JST
{"message":"Who are you?", "number":"2"}

 date; curl -b cookie_application.txt http://lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com/hello/
2015年 7月23日 木曜日 18時41分24秒 JST
{"message":"Who are you?", "number":"1"}

一つ目のリクエストで1号機に対して、セッション登録を行っています。
返ってきたCookie名はsessionidとAWSELBです。
AWSELBが増えました。

有効期限は両方とも同じで1分です。
unixtime : 1437644445
 ↓
JST : 2015/07/23 18:40:45

次のリクエストはまた1号機へアクセスされました。
有効期限を迎えるまでは全て1号機へアクセスされますね。

有効期限を超えたリクエストはセッションは保持されず、2号機へアクセスされました。
また1号機へアクセスできても、アプリケーションのセッションも無効状態となっています。

スティッキーセッションON(有効期限は任意の値を指定する)

最後にスティッキー任意の有効期限を設定したスティッキーセッションです。

維持設定画面で「アプリケーションによって生成された Cookie の維持を有効化」を選択し、
有効期限を設定します。
有効期限を空欄のままにするとクライアント側でCookieを削除しない限り、
永続的にクライアントとバインド先が紐付けられます。
EC2_Management_Console

設定が反映されます。
EC2_Management_Console

それでは確認しましょう。

$ date; curl -c cookie_elb.txt -d "{\"username\":\"shinji\"}" -X POST http://lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com/login/; cat cookie_elb.txt
2015年 7月23日 木曜日 18時52分31秒 JST
{"message":"ok", "number":"2"}# Netscape HTTP Cookie File
# http://curl.haxx.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.

#HttpOnly_lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com FALSE   /   FALSE   1437645211  sessionid   fd3vvupsibkl4uzdub5sqap63mflrgj3
lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com    FALSE   /   FALSE   1437645271  AWSELB  ED79359F1E52D63A864058DE73E691A6144778307CA7620373879658E8855E2F43978DAF18794386BF69F2900AF28605342E5380CFEF28B9F8536EC6FCDA97F608F6B4B653

$ date; curl -b cookie_elb.txt http://lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com/hello/
2015年 7月23日 木曜日 18時52分40秒 JST
{"message":"Hello, shinji", "number":"2"}

$ date; curl -b cookie_elb.txt http://lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com/hello/
2015年 7月23日 木曜日 18時52分43秒 JST
{"message":"Hello, shinji", "number":"2"}

$ date; curl -b cookie_elb.txt http://lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com/hello/
2015年 7月23日 木曜日 18時53分26秒 JST
{"message":"Hello, shinji", "number":"2"}

$ date; curl -b cookie_elb.txt http://lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com/hello/
2015年 7月23日 木曜日 18時53分32秒 JST
{"message":"Who are you?", "number":"2"}

$ date; curl -b cookie_elb.txt http://lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com/hello/
2015年 7月23日 木曜日 18時53分38秒 JST
{"message":"Who are you?", "number":"2"}

$ date; curl -b cookie_elb.txt http://lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com/hello/
2015年 7月23日 木曜日 18時54分30秒 JST
{"message":"Who are you?", "number":"2"}

$ date; curl -b cookie_elb.txt http://lb-sticky-698733798.ap-northeast-1.elb.amazonaws.com/hello/
2015年 7月23日 木曜日 18時54分33秒 JST
{"message":"Who are you?", "number":"1"}

一つ目のリクエストで2号機に対して、セッション登録を行っています。
返ってきたCookie名は変わらずsessionidとAWSELBです。
ただし、今回は有効期限が異なりますね。
sessionidが1分、AWSELBが2分、設定通りです。

unixtime : 1437645211
 ↓
JST : 2015/07/23 18:53:31

unixtime : 1437645271
 ↓
JST : 2015/07/23 18:54:31

次のリクエストはまた2号機へアクセスされました。
有効期限を迎えるまでは全て2号機へアクセスされますね。

まず先にアプリケーションのセッションキーが有効期限を迎えますが、
スティッキーセッションはまだ有効期限を迎えていないため、
引き続き2号機へアクセスされますが、ユーザー名は返ってこなくなりました。

更にスティッキーセッションの有効期限も切れると、
1号機へアクセスされるようになりました。

まとめ

今回はAWSのELBの解説というよりもセッションとは?クッキーとは?のような説明となりました。

IaaSを利用する上でDesign For Failureの考え方は必須だと思います。
AWSはこの考えを実現するための仕組みを数多く用意しています。
この数多くの仕組みを使わないのはもったいないです。
何でもアプリケーション側で実装する前にAWSの各機能を調べると嬉しい事があるかもしれません。