はじめに
環境
- asianux7(※CentOS7.2相当)※ゲストOS(検証用)
- Windows10 Professional ※ホストOS(検証用)
- VirtualBox5.2.6
- Vagrant2.0.1
- nginx 10.12.2
- Apache httpd 2.4.29(比較用)
- Apache JMeter 4.0 (負荷ツール)
長文なのでひとことで(忙しい人向け)
APIサービス提供など軽量なデータを扱うwebサーバではkeep-aliveはoffにした方が良いと思うよ。
本記事の目的
本番で起きたリクエストエラーの対策としてkeep-aliveをoffにしました。その際の経緯などを備忘録として記載します。
事象が起きたのはnginxですがApache httpdでも同様の事象が起きるかどうか検証しました。
本番作業時におきたこと
nginxの設定ファイル変更を反映させたよ
nginxの設定ファイルを少し変更したのでnginxをリロードしました(合計4回実施)
$ sudo systemctl reload nginx
夜間作業でしたが、特にトラブルもなく(と思った)作業終了しました。
翌日クライアントサイドから連絡があったよ
クライアント側(お客様)からリクエストが数件エラーとなった旨のメール連絡がありました。
エラーのタイミングは計2回で発生時刻はnginxリロードの時刻と同じでした。やってもうたか!
nginxのaccessログを確認してみたよ
accessログをみましたがHTTPステータスは全て200で特にエラーはみつかりませんでした。
errorログも出ていません。
事象をまとめてみるよ
事象は以下です。
- クライアント側には正常なレスポンスが返っていない。(クライアント側からは詳しいエラー情報を聞き出せなかった)
- サーバ側はリクエストを受け取っていない。
- 何度かリロード作業を行っているが必ず発生するわけではなく通信流量が多いときに発生する傾向があった。
再現試験実施
再現試験準備
事象から負荷が高い時にnginxをreloadすると起きると仮定しました。
※高いといっても秒間数件程度です
負荷をかけるツールは色々あると思いますが、昔から慣れ親しんだApache JMeterを使いました。
nginxとは別にApache httpdでも同じ条件で実施して比べてみました。
負荷設定
1TPSと10TPSの負荷設定で60秒間リクエストを投げる。その間に各webサーバのリロードを実施してみる。
①nginx 1TPS
②Apache httpd 1TPS
③nginx 10TPS
④Apache httpd 10TPS
再現試験結果
①nginx 1TPS ⇒ 再現せず
②Apache 1TPS ⇒ 再現せず
③nginx 10TPS ⇒ 再現!
④Apache httpd 10TPS ⇒再現せず
エラー内容
JMeterで発生しているエラーは org.apache.http.NoHttpResponseException
でした。
3スレッド使ってリクエストしていますが3件エラーということは各スレッドのリクエストがリロードのタイミングでエラーとなっているということ。
nginx側のログに記録がないという事と合わせ、この結果はサーバ側からコネクションが一方的に切られている様にみえます。乱暴だなnginx君!
考察
HTTP1.1の場合、デフォルトでkeep-alive(持続的な接続)が有効となります。
この場合webサーバからのレスポンスヘッダでConnection:keep-aliveを返して同じコネクションを使う様にクライアントに指示をしていますが、上記の結果からクライアントはnginxに裏切られている(勝手にコネクションを閉じられる)形です。
対策
keep-aliveをオフにする
今回の対象のサーバは1リクエストあたり100バイトほどのデータのやり取りしかしてなく、スループットも最大100TPS程度なのでkeep-aliveで性能向上を求める必要もなく、運用重視で迷うことなくkeep-aliveをoffにすることにしました。
Apache JMeterでの試験でもエラーは起きなくなりました。
本番環境でその後何度もnginxのreloadを実施しましたが同様のエラーが再現することはなくなりました。
nginxの設定
nginxのkeep-aliveのON/OFFとkeep-aliveタイムアウトの設定は同じディレクティブで行います。0がOFFです。
該当ディレクティブ省略時のデフォルト値は75(75秒間同じ接続を使いまわす)です。
keepalive_timeout 0;
Apache httpdの設定
Apacheのkeep-aliveの設定は素直にOn/Off。
ディレクティブ省略時のデフォルトはOnです。
KeepAlive Off
keep-aliveタイムアウトの設定は別のディレクティブで設定します。
省略時のデフォルト値は5(秒)です。
KeepAliveTimeout 65
おわりに
nginxは軽量で性能面ではApacheより優秀なwebサーバと思いますが、思わぬ罠が潜んでいることを思い知りました。
エラーになってもブラウザから再リクエストすれば良いサービスであれば問題ないですが、絶対エラーが許されないサービスの場合は安全サイドに倒して設定を見直す必要があると思います。