複数のWebサーバでSSLセッションキャッシュを共有してSSL処理を高速化(Apache + mod_ssl + mod_socache_memcache)

HTTPS(SSL利用)サイトがSEO的に優遇されるトレンドで、世間的にもHTTPS接続でサイト運用するサービスが増えてきています。
これが、ハイトラフィックサイトになってくると、このフロントエンドでSSL処理させることが負荷的にもなかなか辛いのです。


で、Apache 2.3以降では、Shared Object Cache Providerとして、memcachedが選択できるようになっています。
この仕組みを利用して、Apacheとmemcachedを並べることで、各サーバでユーザのSSL Session Cacheを共有しながらHTTPSリクエストを負荷分散できる構成を作ってみました。

WebサーバでSSLオフロード

常時SSLを利用したWebサイトを運用するために、SSLアクセラレータといったアプライアンス製品だとか、ソフトウェアだとApacheやNginxのSSLモジュールを使うケースが大半だと思います。

で、HTTPSリクエストが秒間数千〜数万〜数十万みたいなオーダーになってくると、一般的には、高価なアプライアンス製品に頼らざるをえないケースが多そうですよね。ただ、これだとスケールさせていくのに、ものすごい費用がかかることになる。かといって、従来どおりのOSSのWebサーバを横に並べていくだけだと後述するパフォーマンスが気になる。


まぁ、お金で解決するのは普通の話なので、ここからは、高価なアプライアンス製品を使うのではなく、オープンソースソフトウェアなWebサーバやProxyにSSLオフロードし、多くのリクエストをうまく捌くようにするにはどうしていくのかという話をします。

SSLオフロードによる処理負荷

結局、SSLを使うことのデメリットは、それに伴う処理負荷の増大をどうしていくかという話に尽きます。もちろん、暗号化方式や強度の設定により、負荷量は大きく異なるわけですが、それなりのパワーが必要になることにかわりはない。

SSL暗号化の強度が高くなればなるほど、暗号化/復号の処理負荷も高くなるのだ。一般に、鍵長を1024ビットから2048ビットへ増やすと、CPUの使用率は4〜7倍増えるといわれている。さらに、全てのWebページにわたってこの暗号化/復号処理をするとなると、Webサーバに掛かる負荷は膨大になってしまう。

常時SSLの落とし穴、Webサーバ負荷増大を回避するには? - TechTargetジャパン ネットワーク


じゃあ、この処理負荷をどうするのかという話になるんだけど、おおまかな方針としては以下のリンク先にほとんど書かれてあるのですが、SSL処理をできるだけさせないような仕組みを入れるという戦略になると思う。


箇条書きしてみると、、、

  • SSLセッションキャッシュタイムアウトの設定を見直す
  • SSLセッションキャッシュを共有させる
  • CipherSuiteの設定を見直す
  • SSLセッションチケットを使う

あたりになるのですが、今回は2つ目のSSLセッションキャッシュの共有について焦点を絞ります。(SSL Session Ticketは比較的新しいクライアントでしか対応していないみたいなので、そのうち取り上げます)

SSLセッションキャッシュを共有するということ

SSL処理負荷の大部分は、セッションの生成部分と言われています。通信のダンプを取ってみても、確かにこのハンドシェイクに時間(コスト)がかかっていました。

SSLセッションの再利用を行った場合のベンチマーク結果を示す。

・・・・・省略・・・・・

非常に大きな効果があり、5倍〜22倍近く速くなっている。

http://wizardbible.org/45/45.txt

とある通り、私の方でも簡単な検証してみたが、SSLセッションをキャッシュする/しないで、ハンドシェイクにかかる時間は、3〜10倍くらいの差がありました。


ApacheやNginxなどのSSLモジュールには、こうしたSSLセッションを生成した後にキャッシュする機構がありますが、デフォルトの設定ではソフトウェアが稼動しているホストのメモリ上に保存されます。
しかし、その前提において、負荷分散のためにWebサーバを横にスケールアウトさせていくと、どうなるか・・・は想像できると思います。

上の図でいうと、例えばユーザからのリクエストがサーバ1に割り振られると、そこでServer1からSSLセッションが生成され、サーバ1上にユーザのSSLセッションがキャッシュされるため、ユーザは次からサーバ1にアクセスするとSSLハンドシェイクといった重い部分の処理が省略され、レスポンス/レイテンシが高速になります。
ただし、L7レイヤで特別な処理をしない限り、ロードバランサは次に同一ユーザから来たアクセスをサーバ1に向けることを保障しません。つまりサーバ2にリクエストが向くと再びサーバ2においてSSLセッションが生成されます。バックエンドのサーバがN台ある場合、何度となくSSLセッション生成が必要となり、負荷分散のためにスケールアウトしているはずが、SSL処理を行うサーバの台数が多くなればなるほど、非常に非効率な状態であることがわかります。


なので、リクエストが増えていくに従ってスケールアウト戦略では、バックエンドのサーバが増大することになるはずなので、こうしたSSLセッションキャッシュを、共有できるような仕組みが必要となります。

Apache 2.4 + memcachedを使ってみる

前置きが随分と長くなりました&冒頭にも書きましたが、Apache 2.3以降では、こうしたSSLセッションキャッシュを格納するプロバイダとしてmemcachedが利用できるようになりました。memcachedは今や多くのサービスで使われているキャッシュストアで使い慣れている方も多いでしょう。


こういったものが必要となる局面は、ハイトラフィックな環境なので、Webサーバ&SSL終端となるApacheも、SSLセッションキャッシュストアとなるmemcachedも複数台共存するようなシーンを想定しています。(動作確認するだけであれば、何台も必要にはなりませんが。)
ということで試しに利用するには、サーバを準備して、Apache2.4系とmemcachedが必要となるので、インストールしてみましょう。

Apache 2.4系のインストール

公式のダウンロードページからダウンロードして、インストールしてください。(インストールの仕方は、インターネット上に多く公開されているので割愛)


尚、CentOSやRHEL向けには、先日RPMファイルの作成方法を書いたので、ご参考にどうぞ。

memcachedのインストール

こちらも公式のダウンロードページからどうぞ。(こちらも情報はたくさん公開されているので割愛)

ただ、多くのLinuxディストリビューションでパッケージが用意されているので、そちらを使うも良し、でしょう。

CentOSとかだと、下記のコマンドでインストールできるはず。

# yum -y install memcached

Apache2.4でSSLセッションキャッシュをmemcachedにつめる設定

とっても簡単。Apacheそのものとか、mod_sslとかの細かい設定の詳細は割愛しますが、Apacheの設定内("httpd.conf"や"extra/httpd-ssl.conf"など)で、、、

LoadModule socache_memcache_module lib64/httpd/modules/mod_socache_memcache.so

こんな感じで該当のモジュールを読み込んで、、、

SSLSessionCache memcache:192.168.0.201:11211,192.168.0.202:11211,192.168.0.203:11211

SSLSessionCacheディレクティブでこんな感じで指定します。memcachedのサーバが複数あるときはカンマ区切りで指定します。
設定が済んだら、Apache(httpd)プロセスを起動/再起動してください。ちょっと端折った説明になりましたが、設定はこれだけです。


あわせてmemcachedも起動してアクセスできる状態にしておいてください。コネクション数とかストアできるメモリ量とか、諸々必要に応じて設定しましょう。

SSLセッションキャッシュが共有されているかを確認する

下記で公開されているツール(rfc5077-client)を利用する事で、SSLセッションが再利用されているかどうかを確認することができます。

Various tools for testing RFC 5077
https://github.com/vincentbernat/rfc5077


インストール方法は、READMEにも書いてありますが、

# yum install openssl-devel gnutls-devel nss-devel libpcap-devel libev-devel nspr-devel pkgconfig
# git clone  https://github.com/vincentbernat/rfc5077.git
# cd ./rfc5077
# git submodule init
# git submodule update
# make

これでビルドできるはず。ビルドが完了したら、、、

$ ./rfc5077-client {IPアドレス}

こんな感じで実行してみます。ちなみに"-p"オプションでポート指定ができます。(デフォルトは443番ポート)



※クリックすると大きく表示されます。あと、後述しますが比較のために、ソースコードを書き換えてリクエスト回数をデフォルトの5回⇒10回にしています。


こういう感じの結果が出るはずです。
なんとなくわかると思いますが、SSLセッションキャッシュが共有されていると、最初の1回目でセッションが生成された後、2回目以降はSSLセッションが再利用されている(SSL Session IDがReuseされて、同一のものになっている)事が確認できます。


ちなみに、SSL Session Cacheをロードバランサ配下の各ホスト内で持つようにしている環境で実行してみると、以下のような結果になります。


この環境では、ロードバランサの配下に複数台のWebサーバがいますが、SSLセッションキャッシュが各サーバで共有されていないため、バックエンド側で同じWebサーバに接続しないと、SSLセッションが再利用されていないことがわかります。

ちなみに、このツールは、SSL Session Ticketの確認にも対応しているので、なかなか便利でございます。


あわせて、memcached-toolとかでmemcachedの中身も確認しておくと、参考になるかと思います。

本構成での耐障害性はどうか

気になるのはこの部分ですが、まずバックエンドにいるキャッシュストアのmemcachedを複数台構成にしておくことで、Apacheサイドでよしなに分散してくれます。
memcachedが1台落ちても、その分のキャッシュが消えるだけで、次回の生成時には振り分けなおしてくれるし、仮にmemcachedが全台ダウンしても、SSLハンドシェイクで失敗する、みたいなことはなくApacheは問題なく稼動していました。(SSLセッションキャッシュが共有できなくなるだけ)


これはおまけですが、L4ロードバランサのバックエンドに、Apache2.4 + mod_sslなサーバ8台とmemcachedなサーバ3台を置いてみて、そのうちの1台のmemcachedのキャッシュのヒット率を出したグラフが以下となります。


もともとはnginxのSSLモジュールで動いていたものを、Apache2.4へと切り替えたのですが、最初は8台中1台を切り替えて様子見、3日後に8台中4台入れて様子見、さらにその3日後に8台全て切り替えとやった結果、キャッシュのヒット率、つまりSSLセッションキャッシュを再利用できている状態の遷移が綺麗に確認できました!
上述していますが、SSLハンドシェイクのレイテンシも3〜10倍程度高速化されているし、なかなか良い感じです。

まとめ

今のところ、なかなか安定して動いていて良い感じです。マル。
それでは! =͟͟͞͞(๑•̀=͟͟͞͞(๑•̀д•́=͟͟͞͞(๑•̀д•́๑)=͟͟͞͞(๑•̀д•́