Nginxパフォーマンスチューニング:ワーカープロセスと接続の最適化

推測に頼らず、Nginxのワーカープロセス、ワーカー接続、ファイル制限、キープアライブ動作、アップストリーム容量を調整します。

Nginxパフォーマンスチューニング:ワーカープロセスと接続の最適化

Nginxのパフォーマンスチューニングは、多くの場合、worker_processes(ワーカープロセス)とworker_connections(ワーカー接続)の2つの設定から始まります。これらの設定は、サーバーが同時に処理できるリクエスト数を決定するため、小さなミスがトラフィックの急増時にページの遅延、ダウンロードの停止、接続エラーとして現れる可能性があります。

良いニュースは、推測に頼る必要がないことです。NginxのワーカーモデルをCPU、ファイル制限、トラフィックパターン、アップストリームアプリケーションの動作に合わせて調整できます。

Nginxワーカーがトラフィックを処理する仕組み

Nginxは、マスタープロセスと1つ以上のワーカープロセスを使用します。マスタープロセスは設定を読み込み、ワーカーを起動し、リロードを処理します。ワーカーは実際のリクエスト処理を行います。

各ワーカーは、Nginxがイベント駆動モデルを使用しているため、多くの接続を処理できます。これは、リクエストごとに1つのプロセスやスレッドを必要とすることが多かった旧来のWebサーバーとは異なります。1つのNginxワーカーは、オペレーティングシステムが許可すれば、何千ものアイドル状態のキープアライブ接続を維持できます。

2つの中核となるディレクティブは、通常/etc/nginx/nginx.confに配置されます。

worker_processes auto;

events {
    worker_connections 1024;
}

worker_processes auto;は、利用可能なCPUコアごとにワーカーを作成するようNginxに指示します。最近のほとんどのLinuxサーバーでは、これが適切な出発点です。仮想マシンのサイズを変更したときに不適切になる値をハードコーディングすることを避けられます。

worker_connectionsは、各ワーカーが開くことができる同時接続の最大数を設定します。おおよその上限は次のとおりです。

worker_processes * worker_connections

4つのワーカーと4096のワーカー接続がある場合、理論上の最大値は16,384接続です。実際には、リバースプロキシトラフィックがクライアント側とアップストリーム側の両方の接続を使用する可能性があるため、使用可能な数はこれより少なくなります。

たとえば、NginxがNode.jsアプリにトラフィックをプロキシする場合、1つのユーザーリクエストが1つのクライアント接続と1つのアップストリーム接続を消費する可能性があります。つまり、16,384のオープン接続は、キープアライブとリクエストのタイミングによっては、約8,000のアクティブなプロキシリクエストをサポートできる可能性があります。

ワーカープロセスと接続値の選択

特別な理由がない限り、worker_processes autoから始めてください。この値をCPU数より手動で高く設定しても、効果はほとんどありません。コンテキストスイッチが増加し、負荷がかかった状態でのパフォーマンスが低下する可能性があります。

次に、予想される同時実行数に基づいてworker_connectionsを調整します。静的な内部ツールであれば1024で問題ないかもしれません。ロードバランサーの背後にある公開Webサイトでは、4096、8192、またはそれ以上が必要になる場合があります。

多くの本番サーバーで実用的なベースラインは次のようになります。

worker_processes auto;
worker_rlimit_nofile 65535;

events {
    worker_connections 4096;
    multi_accept on;
}

worker_rlimit_nofileは、Nginxワーカーが使用できるファイル記述子の制限を引き上げます。すべてのネットワークソケットがファイル記述子を使用するため、これは重要です。オペレーティングシステムの制限が低いままの場合、worker_connectionsを増やすだけでは効果がありません。

また、サービス管理側の制限も確認する必要があります。systemdシステムでは、Nginxに次のようなオーバーライドが必要になる場合があります。

[Service]
LimitNOFILE=65535

systemdの制限を変更した後、systemdをリロードし、Nginxを再起動します。より広範なコマンドリファレンスについては、Nginxサービス制御コマンドを参照してください。

multi_accept onには注意してください。これにより、ワーカーは準備完了通知を受け取った後、可能な限り多くの新しい接続を受け入れることができます。これはバースト時に役立ちますが、魔法ではありません。アップストリームアプリケーションが遅い場合、接続をより速く受け入れても、キューがより速く埋まるだけかもしれません。

Nginxに影響を与えるオペレーティングシステムの制限

Nginxの設定は、Linuxの制限の上に成り立っています。これらの制限が小さすぎると、Nginx自体の設定が十分に見えても、Nginxは上限に達してしまいます。

チューニング時には、以下の領域を確認してください。

  • Nginxプロセスのオープンファイル制限
  • カーネルのネットワークバックログ設定
  • 大量のプロキシトラフィックに対する一時ポートの可用性
  • アップストリームのキープアライブ動作
  • ロードバランサーのアイドルタイムアウト値

オープンファイル制限が最も一般的な障害です。Nginxのログにworker_connections are not enoughtoo many open filesのようなメッセージが記録されている場合は、Nginxとsystemdの両方の制限を確認する必要があります。

バックログ設定は、多くのクライアントが同時に接続する場合に重要です。カーネルのacceptキューがいっぱいになると、CPU使用率が正常に見えても、ユーザーは接続タイムアウトを経験する可能性があります。net.core.somaxconnnet.ipv4.tcp_max_syn_backlogなどの値は、高トラフィック時のチューニングでよく見直されます。

ランダムな例から大きなカーネル値をテストせずにコピーしないでください。1つのAPIサーバーを実行している小規模チームには、CDNエッジノードと同じ設定は必要ありません。段階的に調整し、測定し、メモを取りましょう。

人々を悩ませるもう1つの詳細があります。Nginxの接続制限は、パス上の唯一の接続制限ではありません。クラウドロードバランサーにはアイドルタイムアウトがあります。コンテナランタイムにはネットワークアドレス変換の制限がある場合があります。バックエンドアプリには、独自のワーカープールやデータベース接続プールがある場合があります。Nginxが20,000の接続を受け入れても、アプリが同時に処理できるリクエストが200しかない場合、ユーザーは依然として待機することになります。

これが、接続チューニングの変更には、簡単なエンドツーエンドのチェックを含める必要がある理由です。サーバー外部のホストから小さな負荷テストを実行し、Nginxのアクティブ接続を監視し、バックエンドも監視します。Nginxが安定している間にバックエンドのレイテンシが急上昇した場合、プロキシはその役割を果たしており、次の制限はその背後にあります。

リバースプロキシワークロードのチューニング

多くのNginxサーバーは、アプリケーションサーバーの前段でリバースプロキシとして機能します。その役割では、アップストリームの動作がNginxの容量と同じくらい重要です。

Nginxが同じバックエンドプールと繰り返し通信する場合は、アップストリームキープアライブを使用します。

upstream app_backend {
    server 127.0.0.1:3000;
    keepalive 32;
}

server {
    location / {
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_pass http://app_backend;
    }
}

これにより、新しいバックエンド接続を開くコストが削減されます。これは、ダッシュボードからのAPI呼び出しなど、アプリが多数の小さなリクエストを受信する場合に特に役立ちます。

また、タイムアウト値も確認してください。プロキシのタイムアウトが非常に長いと、クライアントが切断したりアプリケーションが応答を停止した後も、ワーカー接続が占有されたままになる可能性があります。タイムアウトが非常に短いと、正当な低速リクエストが中断される可能性があります。どこでも同じデフォルトを使用するのではなく、ワークロードに合わせてタイムアウト値を調整してください。

実際のシナリオ:サイトは1日のほとんどの時間は高速ですが、ニュースレター送信時に遅くなります。CPUは35%しか使用していませんが、Nginxのログには接続に関する警告が表示されます。これは、生のCPUではなく、接続制限、ファイル記述子、またはアップストリームのキューイングが原因である可能性があります。ワーカー接続を増やすと効果があるかもしれませんが、それはアプリとOSが追加の負荷をサポートできる場合に限ります。

もう1つの一般的なシナリオは、各ブラウザタブから多数の小さなAPI呼び出しを行うダッシュボードアプリです。10人のユーザーが何百もの短いリクエストを生成する可能性があります。その場合、単にworker_connectionsを増やすよりも、アップストリームキープアライブの方が重要であることがよくあります。これは、バックエンドへの繰り返しのTCPセットアップが不要なオーバーヘッドになるためです。

ファイルダウンロードサービスの場合、状況は異なります。少数のユーザーが大きなファイルをダウンロードしている間、長時間接続を開いたままにすることができます。長時間の転送に対応するために十分なワーカー接続が必要になる場合がありますが、sendfile、ディスクスループット、ネットワークスループット、クライアントのタイムアウト動作も確認する必要があります。

WebSocketやロングポーリングアプリの場合、アイドル接続は正常です。Waitingの数値が高いことは、自動的に悪いことではありません。問題は、それらのアイドル接続が新しいリクエストに十分な容量を残しているかどうか、そしてメモリ使用量が予測可能なままであるかどうかです。

チューニング中のstub_statusの読み方

stub_statusモジュールは、接続動作の簡単なビューを提供します。

Active connections: 291
server accepts handled requests
 1162447 1162447 4496426
Reading: 6 Writing: 17 Waiting: 268

Readingは、Nginxがリクエストヘッダーを読み取っていることを意味します。この数値が持続的に高い場合は、低速なクライアント、大きなヘッダー、または攻撃パターンを示している可能性があります。Writingは、Nginxが応答を送信していることを意味します。これは、クライアントがデータの受信に時間がかかっている場合や、応答が大きい場合に増加する可能性があります。Waitingは、アイドル状態のキープアライブ接続を意味します。この数値は、健全なサイトでは高くなる可能性があります。

acceptshandledのカウンターは、通常は一緒に増加する必要があります。受け入れられた接続が増加しても、処理された接続が遅れたりエラーが表示されたりする場合は、ワーカーの制限とファイル記述子の制限を確認してください。また、カーネルがNginxで処理する前に接続をドロップしていないかどうかも確認してください。

これらのカウンターは基本的なものですが、接続のプレッシャーとCPUのプレッシャーを分離できるため便利です。アクティブ接続が少なくCPU使用率が高い場合、問題はおそらくworker_connectionsではありません。アクティブ接続が多くCPU使用率が低い場合、接続制限、キープアライブ動作、アップストリームのキューイング、または低速なクライアントが原因である可能性が高くなります。

安全なベースライン設定

小規模な本番サーバーの場合、私は控えめに始めて測定することを好みます。

worker_processes auto;
worker_rlimit_nofile 65535;

events {
    worker_connections 4096;
    multi_accept on;
}

http {
    keepalive_timeout 30s;
    keepalive_requests 1000;
}

これは普遍的な最適な設定ではありません。多くの通常のリバースプロキシワークロードにとって妥当な出発点です。非常に小さなVMではより少なくて済む場合があり、ビジーなエッジプロキシでははるかに多くが必要になる場合があります。重要なのは、worker_connectionsworker_rlimit_nofileが調整されていることです。

ベースラインを適用した後、同様のトラフィック条件下で前後のメトリクスを比較します。リロード後の幸運な1分間だけでチューニング変更を判断しないでください。実際の動作を確認するために、十分な時間をかけてp95またはp99レイテンシ、エラー率、CPU、メモリ、バックエンドのキューイングを確認してください。

接続チューニングをランダムに見せる間違い

最初の間違いは、リクエストではなく接続をカウントすることです。ブラウザは1つの接続を複数のリクエストに再利用でき、HTTP/2は1つの接続で多くのリクエストを多重化できます。また、低速なクライアントは、ほとんど有用な作業をせずに接続を開いたままにすることができます。つまり、「1秒あたり500リクエストしかない」ということは、Nginxに必要な接続数がわかりません。

2番目の間違いは、アップストリーム接続を忘れることです。Nginxが静的ファイルを提供する場合、ほとんどの接続はクライアント向けです。Nginxがアプリにプロキシする場合、アクティブなリクエストは多くの場合バックエンドソケットも必要とします。アップストリームにキープアライブを使用する場合、一部のバックエンド接続は再利用のために開いたままになります。これは良いことですが、それでも両側でファイル記述子を消費します。

3番目の間違いは、アプリケーションを確認せずにNginxの制限を引き上げることです。Nginxが12,000の同時接続を受け入れられるようになったが、アプリケーションのワーカープロセスが16、データベースプールが50の接続しかないとします。Nginxはアプリが処理できる以上の作業を受け入れます。ユーザーは即時の接続エラーが少なくなるかもしれませんが、リクエストがキューで長く待機するため、レイテンシが悪化する可能性があります。

4番目の間違いは、どこでも長いキープアライブタイムアウトを使用することです。キープアライブは、繰り返しのTCPおよびTLSセットアップを回避できるため便利です。しかし、非常に長いタイムアウトは、トラフィックの急増後に多くのアイドルソケットを開いたままにする可能性があります。メモリ豊富なエッジプロキシでは問題ないかもしれませんが、小さなVMではアクティブな作業を圧迫する可能性があります。Waitingの数が非常に多く、リクエストの再利用が少ない場合は、より短いkeepalive_timeoutを試して、再度測定してください。

トラブルシューティングの例

エラーログにworker_connections are not enoughと表示された場合は、設定値、ワーカー数、プロセスのファイル制限を確認してください。

grep -R "worker_connections\\|worker_processes\\|worker_rlimit_nofile" /etc/nginx/nginx.conf /etc/nginx/conf.d
cat /proc/$(pgrep -o nginx)/limits | grep "open files"

pgrep -o nginxコマンドは通常、最も古いNginxプロセス(多くの場合マスター)を見つけます。システムによっては、メインPIDを確認するためにsystemctl status nginxを実行したほうがよい場合があります。

エラーログにtoo many open filesと表示された場合は、worker_connectionsを増やすだけでは不十分です。プロセスが記述子の制限に達しています。systemdサービスのLimitNOFILEを追加または調整し、systemdをリロードし、Nginxを再起動して、新しい制限が実際に適用されるようにします。

sudo systemctl edit nginx
sudo systemctl daemon-reload
sudo systemctl restart nginx

ユーザーがタイムアウトを経験しているが、NginxにCPUの余裕があり、接続に関する警告がない場合は、Nginxの背後を確認してください。アクセスログでアップストリームの応答時間を確認してください。アプリのワーカープールを確認してください。データベース接続を確認してください。リバースプロキシはスムーズにトラフィックを受け入れている一方で、実際のボトルネックは飽和状態のバックエンドである可能性があります。

トラフィックの急増により、リクエストがアクセスログに表示される前に接続リセットが発生する場合、問題はNginxのリクエスト処理よりも前の段階にある可能性があります。カーネルのバックログ設定、ロードバランサーのログ、ファイアウォールのステートテーブル、SYNフラッド保護を確認してください。Nginxは受信しなかったリクエストをログに記録できません。

変更を安全にテストする方法

編集して期待するだけで本番環境をチューニングしないでください。まず構文をテストします。

sudo nginx -t

次に、アクティブな接続が適切に処理されるようにNginxをリロードします。

sudo systemctl reload nginx

変更後は毎回エラーログを監視します。

sudo tail -f /var/log/nginx/error.log

また、リクエストレイテンシ、4xxおよび5xxレート、アクティブ接続、CPU、メモリ、アップストリーム応答時間を監視する必要があります。接続容量を増やすがアプリケーションのレイテンシを上げるチューニング変更は、真のメリットではない可能性があります。

より深い検証手順については、Nginx設定のテストとステータスの監視を参照してください。

専門家を呼ぶべき時

基本的なチューニング後もNginxのエラーが続く場合、トラフィックの急増が収益に影響を与える場合、または本番システムでカーネルのネットワーク設定を変更している場合は、経験豊富なDevOpsエンジニアまたはWebパフォーマンスの専門家に相談してください。決済フロー、ログインシステム、または重要なAPIの前段でNginxをチューニングしている場合も同様です。

ボトルネックが不明な場合も、専門家の助けが役立ちます。実際の問題が低速なデータベース、枯渇したアップストリームアプリプール、過負荷のTLS終端レイヤー、またはロードバランサーのタイムアウト不一致である場合、Nginxが問題のように見えることがあります。

重要なポイントは単純です。ワーカープロセスをCPUに合わせて調整し、ワーカー接続を同時実行数に合わせて調整し、Linuxのファイル制限が両方をサポートしていることを確認してください。一度に1つのレイヤーを変更し、リロード前に設定をテストし、理論上の最大値を信頼するのではなく、実際のトラフィックを測定してください。