こんにちは。データ収集ツールFluentdのメンテナーの福田です。
今回の記事では、有名なWebサーバーアプリケーションであるnginxの最大接続数のチューニングについて調査した事例を紹介します。
最大接続数をチューニングするにあたり、reuseport設定とファイルディスクリプター数上限(worker_rlimit_nofile)を考慮する必要がある、という点が重要です。
この調査はクリアコードのFluentd法人様向けサポートサービスの一貫で実施したもので、 Fluentdとセットで周辺のフリーソフトウェアのサポートを実施した事例となっています。
nginxなどのwebサーバーの性能チューニングや、クリアコードの法人様向けサポートサービスに興味のある方は、ぜひ本記事をご覧ください。
背景
今回のサポート事例は、nginxの限界性能についてお客様からご質問をいただき、最大接続数のチューニングの仕方について調査した、というものになります。
nginxでは、同時に多くの接続が生じると、次のようにworker_connections are not enough
やToo many open files
といったエラーが発生します。
[alert] xxxx: *xxxx 1024 worker_connections are not enough while connecting
to upstream, client: xxxx, server: xxxx, request: "xxxx", upstream: "xxxx", host: "xxxx"
[alert] xxxx: *xxxx socket() failed (24: Too many open files) while
connecting to upstream, client: xxxx, server: xxxx, request: "xxxx", upstream: "xxxx", host: "xxxx"
[crit] xxxx: accept4() failed (24: Too many open files)
このようなエラーが発生せずに処理できる最大の接続数はどう決まるのか、およびその正しいチューニング方法について、調査しました。
なお、本記事は以下の環境を前提とします。
- nginx: version 1.16.1
- OS: RHEL 8.6
基本的な考え方と計算式
今回の事例では、nginxをフォワードプロキシとして利用していました。
検証したところ、およその最大接続数は基本的には次の計算式で求められました。
- およその最大接続数 = worker_processes(ワーカープロセス数) * worker_connections(各ワーカーの最大接続数) / 2
2で割るのは、1接続あたりプロキシ元とプロキシ先の2つ接続を持つ必要があるからだと推測できます(これは設定によって変わるかもしれません)。
しかし、この値付近までエラーなく接続を安定して処理するには、後述する次の2つ設定が必要でした。
- reuseport
- ファイルディスクリプター数(worker_rlimit_nofile)
reuseport設定で各ワーカーに接続を分散させる
同時に大量の接続を行う検証をすると、1つのワーカーに接続が偏る傾向が分かりました。
このため、接続が偏ったワーカーが先行して限界(worker_connections)に達し、とても早い段階でworker_connections are not enough
エラーが発生してしまいました。
[alert] xxxx: *xxxx 1024 worker_connections are not enough while connecting
to upstream, client: xxxx, server: xxxx, request: "xxxx", upstream: "xxxx", host: "xxxx"
この接続の偏りを解決する方法を調べたところ、reuseport設定を使うことで解決することが分かりました。
resuseport
設定を使うと、ソケットのSO_REUSEPORTオプションを利用し、各ワーカーが独自に同じポートに対してソケットをバインドするようになります。
これにより、さきほどの計算式の最大接続数の値の90%程度までエラーなく接続を処理できるようになることを確かめられました。
以下、reuseport
設定の注意点です。
reuseport
は、listen設定に付与する形になります。
ただし同じIPとポート番号のセットに重複して設定をするとduplicate listen options
の設定エラーになるので、IPとポート番号のセット毎に1回だけ設定する必要があります。
listen
のIPを省略する場合は、0.0.0.0
を指定したことになります。
$ nginx -t
nginx: [emerg] duplicate listen options for ...
(-t
オプションで設定のバリデーションを行えます)
例:
# IPとポートの組み合わせ毎にreuseportを付与する
server {
listen 8080 reuseport;
}
server {
listen 192.168.0.1:8080 reuseport;
}
server {
listen 192.168.0.2:8080 reuseport;
}
# 重複して付与しないように注意する
server {
listen 8080;
}
また、reuseportの説明文にセキュリティーに関する注意があります。
Inappropriate use of this option may have its security implications.
この点について説明します。
reuseport
を使うと、nginxのワーカープロセスと同じユーザーで実行されている他のプロセスも、そのポートのデータを読めてしまいます。
正しく使用しないとセキュリティー上のリスクとなるため、注意が必要です。
好ましい対策としては、user設定を利用するなどして、nginxのワーカープロセスをnginx専用ユーザーで動作させるように徹底してください。
ファイルディスクリプター数を十分大きくしておく
ファイルディスクリプター数上限の限界に達すると、最大接続数まで余裕があっても次のようなToo many open files
のエラーになってしまいます。
[alert] xxxx: *xxxx socket() failed (24: Too many open files) while
connecting to upstream, client: xxxx, server: xxxx, request: "xxxx", upstream: "xxxx", host: "xxxx"
[crit] xxxx: accept4() failed (24: Too many open files)
対策として、worker_rlimit_nofileを十分大きな値にしておく(少なくとも想定する最大接続数よりも大きい値にしておく)のが良いです。
この設定により、ワーカープロセスのファイルディスクリプター数上限のソフトリミットとハードリミットの双方を変更することができます。
上限値は、システムとしての1プロセスの上限である/proc/sys/fs/nr_open
で定義されている値となります。
例:
$ cat /proc/sys/fs/nr_open
1048576
また、unitファイルにおけるLimitNOFILE
でも同様の設定が可能です。
SELinuxのhttpd_setrlimit
の制限を受ける場合はworker_rlimit_nofile
の設定が失敗するらしいので、その場合はLimitNOFILE
の方が良さそうです。
まとめ
本記事では、有名なWebサーバーアプリケーションであるnginxの最大接続数のチューニングについて調査した事例を紹介しました。
以下の点を考慮してチューニングしてください!
- およその最大接続数 = worker_processes(ワーカープロセス数) * worker_connections(各ワーカーの最大接続数) / 2
- 2で割る点は、設定によって異なる可能性がある
- reuseportの設定をしないと、特定のワーカーに接続が偏って早い段階でエラーになることがある
- worker_rlimit_nofileなどでファイルディスクリプター数上限を大きくしておく必要がある
この調査はクリアコードのFluentd法人様向けサポートサービスの一貫で実施したもので、 Fluentd単体に限らず、このようにFluentdとその周辺のフリーソフトウェア(Redis, Postfix, K8s, Embulkなど)をセットでサポートする事例やご相談もあります。
このようにクリアコードはフリーソフトウェア全般を得意としておりますので、 もしFluentdや周辺のアプリケーションの運用でお困りのことがありましたら、 ぜひお問い合わせフォームからお気軽にご相談ください。
同様のサポート事例として次の記事もあるので、ぜひご覧ください。