ELB で SSL 復号するときしないときのベンチマーク

[AWS ELB (Elastic Load Balancing)](http://aws.amazon.com/jp/elasticloadbalancing/) に SSL 通信を復号させるとどれだけパフォーマンス変わるの？ってことでベンチマーク取った。

結果を誤解されると良くないので最初に言っておくと、 **SSL の復号処理は ELB かどうかに関わらず重い** 。ベンチマークを取ると[平文の10分の1以下になる](http://shinobra.com/2012/05/sawanoboly/nginx_stunnel01)という話もある。

あと、秒間数十アクセス程度、または常にアクセス数が多い状況であれば、ELB に SSL 処理させるのが妥当。今回想定しているのは、スマホアプリにプッシュ通知して、ユーザが一斉にアクセスするような状況。しかも、ELB をウォームアップする余裕もないような。

今回の実験環境はこんな感じ。

```
叩く側のインスタンスA
    ↓
AWS ELB
    ↓
叩かれる側のインスタンスB
``` 

インスタンスはどちらも m1.small で Amazon Linux 2013.09.2 (ami-0d13700c)

インスタンスAには Apache Bench を入れる。

```bash
sudo yum install httpd-tools
```

インスタンスBには nodejs で簡単なサーバを立てる。

```bash
sudo yum install npm --enablerepo=epel
npm install express

cat <<EOT >index.js
var app = require('express')();
app.get('/*', function(req, res) {
  if (req.query.wait) {
    setTimeout(function() {
      res.write('OK');
      res.end();
    }, req.query.wait);
  } else {
    res.write('OK');
    res.end();
  }
  console.log('%s %s %s', new Date(), req.method, req.url);
});
app.listen(10080);
EOT

node index.js
```

ELB のセットアップ

 * 443 (HTTPS) → 10080 (HTTP)
   * 証明書は適切に設定
 * 80 (HTTP) → 10080 (HTTP)
 * 配下にインスタンスBを入れる

この設定で、インスタンスAから ab コマンドを打ってみた。まずは HTTP から。

```text
$ ab -n 5000 -c 200  http://yourloadbalancer/?wait=200
（中略）
Document Path:          /?wait=200
Document Length:        2 bytes

Concurrency Level:      200
Time taken for tests:   7.462 seconds
Complete requests:      5000
Failed requests:        0
Write errors:           0
Total transferred:      505286 bytes
HTML transferred:       10032 bytes
Requests per second:    670.07 [#/sec] (mean)
Time per request:       298.476 [ms] (mean)
Time per request:       1.492 [ms] (mean, across all concurrent requests)
Transfer rate:          66.13 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        1    2   2.4      1      22
Processing:   219  290  53.8    276     620
Waiting:      202  249  60.4    233     620
Total:        220  292  54.9    278     628
（後略）
```

だいたい毎秒 670 リクエスト。では次に HTTPS を。

```text
$ ab -n 5000 -c 200  https://yourloadbalancer/?wait=200
（中略）
Server Port:            443
SSL/TLS Protocol:       TLSv1,RC4-SHA,2048,128

Document Path:          /?wait=200
Document Length:        2 bytes

Concurrency Level:      200
Time taken for tests:   104.943 seconds
Complete requests:      5000
Failed requests:        0
Write errors:           0
Total transferred:      594967 bytes
HTML transferred:       10002 bytes
Requests per second:    47.64 [#/sec] (mean)
Time per request:       4197.717 [ms] (mean)
Time per request:       20.989 [ms] (mean, across all concurrent requests)
Transfer rate:          5.54 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       96 2015 597.0   1993    4804
Processing:   504 2135 532.8   2112    4500
Waiting:      472 2122 534.9   2099    4500
Total:       1936 4150 516.0   4058    6810
（後略）
```

結果は HTTP の時の10分の1以下の毎秒 48 リクエスト。うーん、やっぱり重いんだな。何度か ab 叩いてみたけど、結果はそんなに変わらなかった。

[DHE はむっちゃ重いよ](http://vincent.bernat.im/en/blog/2011-ssl-perfect-forward-secrecy.html) という話を聞いたので、Cipher を DHE-RSA-AES256-SHA とか AES256-SHA とか RC4-SHA とかにして試してみたけど、どれも結果が秒間 50 リクエスト未満だった。

というわけで、ELB に SSL を復号させているシステムで、どうにもパフォーマンスが出ないといった場合はここを疑っておくといいんじゃないかと思った。対策としては、SSL の復号処理をバックエンドにやらせるとか、自前でロードバランサを組むとか。

なんか ELB 使えないみたいな流れになってしまったので補足しておくと、アクセス数が一定して多い場合は、ELB が自動でスケールするからパフォーマンスが出ないといったことにはならないと思う。

あと ELB には Pre-Warming というものがあって、事前に ELB をスケールさせておくことが出来る。これについては [[AWSマイスターシリーズ]Amazon Elastic Load Balancing (ELB)](http://www.slideshare.net/AmazonWebServicesJapan/20130612-aws-meisterregenerateelbpublic) の36ページに書かれているけどサポートに加入する必要がある。

あと最後に、 **SSL の復号処理は ELB かどうかに関わらず根本的に重い**

ブクマで ab -k をつけたら
ab で -k オプションつければ