読者です 読者をやめる 読者になる 読者になる

HAProxy でスティッキーセッションするメモ

CentOS 7 の yum でさくっとインストールできるバージョンで試しています。

  • haproxy-1.5.14-3.el7.x86_64

appsession

アプリケーションが発行する cookie の値とサーバとの対応表のテーブルを HAProxy が保持する。

appsession PHPSESSID len 32 timeout 30m request-learn

この例だと PHPSESSID という名前のクッキーの値の先頭 32 バイトを用いてテーブルを作成する。有効期限は 30 分で有効期限が切れるとテーブルから削除される。

request-learn を付けると、HAProxy はリクエストの Cookie からもテーブルに追加する。request-learn がない場合、次のようなケースで問題になる。

  • サイトにアクセス
    • クライアントの Cookie なし
      • HAProxy は Cookie の値が対応表にないのでランダムに振り分ける
    • サーバが Set-Cookie を返す
      • HAProxy が Cookie の値を記録する
  • サイトにアクセス
    • クライアントが Cookie を送る
      • HAProxy が Cookie の値で対応表を引いて振り分けるサーバを決定する
    • サーバは Cookie を発行済みなので Set-Cookie を送らない
  • HAProxy を再起動する
  • サイトにアクセス
    • クライアントが Cookie を送る
      • HAProxy は Cookie の値が対応表にないのでランダムに振り分ける
    • サーバは Cookie を発行済みなので Set-Cookie を送らない
  • サイトにアクセス
    • クライアントが Cookie を送る
      • HAProxy は Cookie の値が対応表にないのでランダムに振り分ける
    • サーバは Cookie を発行済みなので Set-Cookie を送らない

PHP のセッションはクライアントが Cookie を送るとサーバは Set-Cookie を送らないので (session_regenerate_id とかしなければ) 、request-learn を指定しておくと無難。

prefixmode も指定できるけどあんまり使わなさそう。

この方法は対応表のテーブルを保持するために HAProxy でメモリを消費する。

ボットとか死活監視とかのセッションの必要がないリクエストは ignore-persist で除外しておけば、それらのリクエストによって appsession のテーブルが大きくなることを防止できる。

appsession PHPSESSID len 64 timeout 30m request-learn
acl ab hdr_sub(User-Agent) -i ApacheBench
ignore-persist if ab

cookie insert

HAProxy 自身が Cookie を発行する。

cookie HAPROXY insert nocache indirect preserve httponly secure maxidle 30m maxlife 8h
server ap01 192.168.33.21:80 check cookie ap01
server ap02 192.168.33.22:80 check cookie ap02

この例だと HAPROXY=ap01 のような Cookie が発行される。

nocache を指定すると、レスポンスヘッダに Cache-control: private を追加することでプロキシサーバがキャッシュしないようにする。スティッキーのための Cookie をプロキシに覚えさせるわけにはいかないので、nocache かもしくは後述の postonly を指定しておくのが無難。

indirect を指定すると、クライアントが送信した Cookie (上の例だと HAPROXY=ap01) は HAProxy が削除するので、サーバに Cookie の値は渡らない。

preserve を指定すると、サーバで setcookie('HAPROXY', '', time()-3600) とかで Cookie 削除のためのレスポンスを返すことができる。逆に、指定していなければ、サーバがそのようなレスポンスを送っても HAProxy が削除する。

httponlysecure はそのままの意味、domain も指定できる。

maxidle 30m は、指定した時間以上未アクセスだと無効になるように Cookie を発行する。

maxlife 8h は、Cookie を最初に発行してから指定時間以上経過すると無効になるように Cookie を発行する。

maxidlemaxlife は Cookie に HAPROXY=ap01|VwNGp|VwNGl のようなタイムスタンプに基づく値を付与することで実現される。

postonly を指定すれば POST リクエストのレスポンスにのみ Set-Cookie が発行されるようになる。 大抵のアプリではログイン画面の POST リクエストからがセッションの始まりになるので、postonly を指定しても良いかもしれない。

appsession とは異なり、Cookie の値から直接振り分け先が導出されるので、対応表のために余分なメモリを消費することはない。

cookie prefix

アプリケーションが発行するクッキーの値の先頭に HAProxy が追記する。

cookie PHPSESSID prefix
server ap01 192.168.33.21:80 check cookie ap01
server ap02 192.168.33.22:80 check cookie ap02

この例だと、アプリケーションが PHPSESSID=xyz123 という Cookie を発行したら、HAProxy がそれを PHPSESSID=ap01~xyz123 のように書き換える。

cookie rewrite

prefix とほとんど同じだけど、先頭に追記じゃなくてただの書き換えになる。

cookie HAPROXY rewrite
server ap01 192.168.33.21:80 check cookie ap01
server ap02 192.168.33.22:80 check cookie ap02

この例だと、アプリケーションが HAPROXY=hoge のように Cookie を発行したら、HAProxy がそれを HAPROXY=ap01 のように書き換える。 PHPSESSID のようなアプリケーションのセッション用 Cookie を指定してしまうと HAProxy がその値を書き換えてしまうので、セッションの維持ができない。

参考