Nginxワーカープロセス最適化ガイド:最大パフォーマンスを実現する実践的アプローチ

高トラフィック環境向けにNginxサーバーを最適化するための実践ガイドです。コアパフォーマンスディレクティブの設定方法を解説。`worker_processes`をCPUコア数に合わせるベストプラクティス、`worker_connections`による同時接続数の最大化、OSのファイルディスクリプタ制限(`ulimit`)との整合性確保について学びます。具体的な設定例と必須のチューニングポイントを提供し、レイテンシを最小化しサーバースループットを劇的に向上させます。

Nginxワーカープロセス最適化ガイド:最大パフォーマンスを実現する実践的アプローチ

Nginxは少ないプロセスフットプリントで多数の同時接続を処理できますが、それはワーカー制限が基盤となるマシンと整合している場合に限ります。最初に注目される2つの設定はworker_processesworker_connectionsです。これらは有用ですが、過剰チューニングも容易です。両方を巨大な数値に設定しても、空き容量が生まれるわけではありません。ボトルネックをファイルディスクリプタ、メモリ、アップストリームサーバー、ネットワークスタックに移すだけになる可能性があります。

実践的な目標は、Nginxに十分なワーカーを与えてCPUコアを活用させ、実際のトラフィックに対応する接続スロットを確保し、通常のバースト時に限界に達しないようにオペレーティングシステムの制限を設定することです。

Nginxワーカーアーキテクチャの理解

Nginxはマスター・ワーカーモデルで動作します。マスタープロセスは設定の読み取りと検証、ポートへのバインド、ワーカープロセスの管理を担当します。システムリソースの監視や必要に応じたワーカーの再起動などの非クリティカルなタスクも実行します。

ワーカープロセスは実際の処理を担当します。これらのプロセスは(標準的なNginxコンパイルでは)シングルスレッドであり、ノンブロッキングシステムコールを使用します。各ワーカーはイベントループを使用して数千の同時接続を効率的に処理し、1つのプロセスがブロックせずに複数のリクエストを管理できるため、これがNginxのパフォーマンスの鍵です。

適切な最適化には、ワーカー数(CPUリソースに紐付け)と各ワーカーが処理できる最大接続数のバランスを取ることが含まれます。

worker_processesの設定:CPUコアファクター

worker_processesディレクティブは、Nginxが生成するワーカープロセスの数を決定します。この設定は、NginxがサーバーのCPUリソースをどのように利用するかに直接影響します。

ベストプラクティス:ワーカーをコアに一致させる

最も一般的で推奨されるベストプラクティスは、ワーカープロセス数をサーバーで利用可能なCPUコア数と等しく設定することです。これにより、コンテキストスイッチによる過剰なオーバーヘッドを発生させることなく、すべてのコアが効率的に利用されます。

ワーカー数がコア数を超えると、オペレーティングシステムは競合するNginxプロセス間でCPUフォーカスを頻繁に切り替える必要があり(コンテキストスイッチ)、レイテンシが発生し全体的なパフォーマンスが低下します。

autoディレクティブの使用

最新バージョンのNginx(1.3.8以降)では、最もシンプルで効果的な設定はautoパラメータを使用することです。Nginxが利用可能なCPUコア数を自動検出し、ワーカープロセスを適切に設定します。

# ほとんどのデプロイメントに推奨される設定
worker_processes auto;

手動設定

手動制御が必要な場合や古いバージョンを使用している場合は、正確なワーカー数を指定できます。システムユーティリティを使用してコア数を確認できます:

# CPUコア数を確認
grep processor /proc/cpuinfo | wc -l

システムに8コアある場合、設定は次のようになります:

# ワーカープロセスを8に手動設定
worker_processes 8;

ヒント: 利用可能なコア数に一致させるのが最も安全な出発点です。異常なI/O負荷のワークロードでは異なる値をテストしても構いませんが、実際のトラフィック下でベンチマークを取ってから採用してください。通常の静的ファイル配信、プロキシ、TLS終端では、autoが通常最も予想外のない選択です。

worker_connectionsの設定:同時接続ファクター

worker_connectionsディレクティブはeventsブロック内で設定され、単一のワーカープロセスが処理できる最大同時接続数を定義します。これにはクライアントへの接続、アップストリームプロキシサーバーへの接続、内部ヘルスチェック接続が含まれます。

最大クライアント数の計算

Nginxサーバーが処理できる理論上の最大同時クライアント接続数は次のように計算されます:

$$\text{Max Clients} = \text{worker_processes} \times \text{worker_connections}$$

4つのワーカープロセスがあり、ワーカーあたり10,000接続の場合、Nginxは理論上40,000の同時接続を処理できます。

この数値は大まかな上限に過ぎません。プロキシされたリクエストは、1つのクライアント接続と1つのアップストリーム接続を同時に使用する場合があります。WebSocketやロングポーリングのトラフィックは、通常のページリクエストよりもはるかに長くスロットを占有する可能性があります。キープアライブ接続も、ほとんど作業をしないまま開いたままになることがあります。Nginxが主に静的ファイルを提供している場合、計算は単純な式に近くなります。リバースプロキシとして機能している場合は、余裕を持たせてください。

接続制限の設定

ビジーなサーバーでは、メモリとファイルディスクリプタの制限がサポートできることを前提に、worker_connectionsを数千以上に設定するのが一般的です。大きな値を盲目的にコピーしないでください。予想される同時接続数にバースト用の余裕を加えた値に基づいて選択してください。

# eventsブロックの設定例

events {
    # ワーカープロセスあたりの最大同時接続数
    worker_connections 16384;

    # バースト時に役立つ可能性がありますが、負荷下での公平性をテストしてください。
    multi_accept on;
}

システム制限(ulimit)の制約

重要なのは、worker_connections設定は、プロセスあたりのオープンファイルディスクリプタ(FD)数のオペレーティングシステム制限(多くの場合ulimit -n設定で制御)によって制約されることです。

NginxはOSが許可するファイルディスクリプタ以上の接続を開くことはできません。すべての接続(クライアントソケット、ログファイル、プロキシソケット)にはファイルディスクリプタが必要なため、システム制限を十分に高く設定することが重要です。

ファイルディスクリプタ制限の確認と引き上げ

  1. 現在の制限を確認:

    ulimit -n
    
  2. 制限を一時的に引き上げる(現在のセッションのみ):

    ulimit -n 65536
    
  3. 制限を恒久的に引き上げる(/etc/security/limits.conf経由):

    次の行を追加し、nginx_userをNginxが実行されるユーザー(多くの場合www-dataまたはnginx)に置き換えます:

    # /etc/security/limits.conf
    nginx_user soft nofile 65536
    nginx_user hard nofile 65536
    

警告: Nginxワーカーユーザーのプロセスあたりファイルディスクリプタ制限がworker_connectionsよりも高く、ログ、アップストリームソケット、キャッシュファイル、その他のオープンファイル用の余裕があることを確認してください。システム全体の制限も重要ですが、プロセスごとの制限が最も驚かされるポイントです。

Nginxがsystemdで管理されている場合、/etc/security/limits.confだけでは不十分な場合があります。多くのディストリビューションでは、ユニットファイルからの制限でサービスを開始します。アクティブな制限を以下で確認してください:

cat /proc/$(pgrep -o nginx)/limits | grep "open files"

systemdオーバーライドの場合は、次を使用します:

sudo systemctl edit nginx

次に追加します:

[Service]
LimitNOFILE=65536

メンテナンスウィンドウ中にsystemdをリロードし、Nginxを再起動します:

sudo systemctl daemon-reload
sudo systemctl restart nginx

高度なチューニングと監視

コアディレクティブに加えて、いくつかの追加考慮事項がパフォーマンスの微調整に役立ちます:

1. ワーカープロセスの固定

高性能環境、特に複数のCPUソケット(NUMAアーキテクチャ)を備えたシステムでは、worker_cpu_affinityディレクティブを使用するとよいでしょう。これにより、特定のワーカープロセスを特定のCPUに制限するようOSに指示し、CPUキャッシュをホットに保ち、メモリ局所性の問題を回避することでパフォーマンスを向上させることができます。

8コアシステムの例:

worker_processes 8;
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;

この設定は複雑で、通常は極端な高負荷状況でのみ有益です。ほとんどのデプロイメントではworker_processes autoで十分です。

2. パフォーマンスメトリクスの監視

最適化を適用した後は、その影響を監視することが重要です。Nginx Stub Statusモジュール(またはPrometheus/Grafanaなどのツール)を使用して、主要なメトリクスを追跡します:

メトリクス 説明 最適化チェック
アクティブ接続 現在処理中の総接続数。 理論上の最大値を下回っている必要があります。
読み取り/書き込み/待機 異なる状態の接続。 待機数が多い場合は、長時間のHTTPキープアライブ(良い)または処理リソース不足(悪い)を示すことがよくあります。
リクエストレート 1秒あたりのリクエスト数。 設定変更後の実際のパフォーマンス向上を測定するために使用されます。

すべてのコアでCPU使用率が高く、リクエストレートが高い場合、worker_processesは適切に設定されている可能性があります。ピークトラフィック時にアイドル状態のCPUコアがある場合は、設定を見直すか、Nginx外部のブロッキングI/O操作を確認してください。

3. 接続オーバーフロー戦略

サーバーが最大接続制限(worker_processes * worker_connections)に達した場合、新しい接続は失敗するか、タイムアウトするまでキューに留まる可能性があります。worker_connectionsを増やすことは、Nginxが実際のボトルネックである場合にのみ役立ちます。アップストリームアプリケーションサーバーが飽和状態にある場合、制限を引き上げると、より多くのリクエストが遅いバックエンドの後ろに積み重なるため、障害がさらに悪化する可能性があります。

エラーログをシグナルとして使用します。worker_connections are not enoughのようなメッセージは、Nginxの制限を直接示しています。upstream timed outconnect() failed、または502/504応答の増加は、バックエンドの容量、ネットワークの問題、またはタイムアウト設定をより強く示しています。

妥当な初期設定

小規模または中規模のリバースプロキシの場合、これは妥当なベースラインです:

worker_processes auto;
worker_rlimit_nofile 65536;

events {
    worker_connections 8192;
    multi_accept off;
}

なぜここでmulti_accept offなのか?これは多くのシステムでデフォルトの保守的な設定です。有効にすると、ワーカーが保留中のacceptキューを迅速に排出するのに役立ちますが、一部のトラフィックパターンでは、1つのワーカーが大きなバッチを取得し、他のワーカーがアイドル状態になる可能性があります。バーストトラフィックがあり、有効にするテスト済みの理由がある場合は、有効にしてください。汎用Webサーバーをチューニングする場合は、ベースラインをシンプルに保ち、最初に測定してください。

サーバーが多数のWebSocket接続、サーバー送信イベント、または長時間実行されるAPIストリームを処理する場合は、接続制限をより積極的に引き上げ、メモリに細心の注意を払ってください。20,000のほとんどアイドル状態のWebSocketクライアントを持つサーバーは、20,000の短い静的ファイルリクエストを処理するサーバーとは異なるプロファイルを持ちます。

変更を検証する方法

本番環境を変更する前に、小さなベースラインを取得します:

nginx -T | grep -E 'worker_processes|worker_connections|worker_rlimit_nofile'
ss -s
ulimit -n

変更後、Nginxが実際にそれをロードしたことを確認します:

sudo nginx -t
sudo systemctl reload nginx
ps -o pid,comm,nlwp,pcpu,pmem -C nginx
cat /proc/$(pgrep -n nginx)/limits | grep "open files"

次に、実際のトラフィック中の動作を監視します。すべてのCPUコアがビジーでレイテンシが上昇している場合、Nginxは有用な作業を行っており、CPU容量に達している可能性があります。CPUが低いが接続がキューイングまたはタイムアウトしている場合は、ファイルディスクリプタ、アップストリームの飽和、DNS解決、ディスクI/O、またはファイアウォール制限を確認してください。ワーカーチューニングは1つのレバーであり、パフォーマンス全体の話ではありません。

数値をコンテキストで読む

よくある間違いは、「アクティブ接続」を「アクティブユーザー」と同じものとして扱うことです。そうではありません。1つのブラウザがアセット用に複数の接続を開くことができます。1つのAPIクライアントがリクエスト間で接続を維持する場合があります。1つのWebSocketクライアントがほとんどトラフィックを送信せずに何時間も接続を保持する場合があります。worker_connectionsをサイジングするときは、人ではなく同時ソケットの観点で考えてください。

リバースプロキシの場合、アップストリーム側も覚えておいてください。4,000のクライアントがプロキシ応答を待っている場合、Nginxは数千のアップストリームソケットも保持している可能性があります。そのため、サーバーは単純なクライアント側の計算が示すよりも前にファイルディスクリプタを使い果たす可能性があります。これは、アップストリームアプリケーションが遅くなると特に顕著です。リクエストが長時間開いたままになり、同時実行性が高まり、Nginxは着信リクエストレートが変わっていなくてもより多くのソケットを消費し始めます。

キープアライブ設定もこれに影響します。長いキープアライブタイムアウトは接続のチャーンを減らし、ビジーなサイトに役立ちますが、アイドルソケットをより長く保持します。非常に短いキープアライブタイムアウトはソケットをより速く解放しますが、TLSハンドシェイクと接続セットアップのオーバーヘッドを増加させる可能性があります。完璧な値はありません。トラフィック形状をガイドとして使用してください。短い訪問が多い公開Webサイトは、少数の永続的なクライアントを持つ内部APIとは異なるバランスが必要になる場合があります。

コンテナ内でチューニングする場合は、コンテナ内およびホストまたはオーケストレーターレベルで制限を確認してください。Kubernetesポッド、Dockerコンテナ、またはsystemdサービスは、テストに使用したホストシェルよりも低いnofile制限を持つ場合があります。ログインセッションだけでなく、実行中のNginxプロセスを常に確認してください。

ベストプラクティスのまとめ

ディレクティブ 推奨値 根拠
worker_processes auto(またはコア数) 最適なCPU使用率を確保し、コンテキストスイッチのオーバーヘッドを最小化します。
worker_connections 数千から開始し、測定された同時実行性に基づいて増加 他のボトルネックを隠すことなく接続の余裕を提供します。
OS制限(ulimit -n ワーカーあたりの接続ニーズより高く、余裕を持たせる クライアントソケット、アップストリームソケット、ログ、キャッシュファイル用のファイルディスクリプタを提供します。
multi_accept 有効にする前にテスト バースト時に役立つ可能性がありますが、すべてのワークロードで自動的に優れているわけではありません。

最適なNginxワーカー設定は通常、シンプルです:worker_processes auto、実際の同時実行性を反映した接続制限、ワークロードに十分なファイルディスクリプタ制限。チューニングし、アクティブなプロセス制限を確認し、エラーログを監視し続けてください。症状がアップストリームを指している場合は、Nginxにアプリケーションが完了できない作業をさらに受け入れさせるのではなく、アップストリームを修正してください。