Nginxパフォーマンスのボトルネックを特定・解決するトラブルシューティングガイド

ログ、ステータスメトリクス、システムチェック、およびCPU、レイテンシ、メモリ、接続に関する実用的な修正を用いてNginxのボトルネックを診断します。

Nginxパフォーマンスのボトルネックを特定・解決するトラブルシューティングガイド

Nginxのパフォーマンス問題は、通常、ページの読み込みが遅い、APIコールがタイムアウトする、CPU使用率が上昇する、ユーザーが502や504エラーを目にするといった単純な形で現れます。難しいのは、Nginx自体がボトルネックなのか、それとも最初に不満を訴えるサービスに過ぎないのかを見極めることです。

私がNginxのトラブルシューティングを行う際、まずディレクティブを変更することから始めないようにしています。最初にいくつかの単純な質問をします。レイテンシはすべてのルートで上昇したのか、それとも特定のアップストリームにヒットするルートだけか?静的ファイルも遅いのか?エラーはデプロイ後、トラフィックの急増後、証明書の変更後、またはログ設定の変更後に発生したのか?このコンテキストがあれば、古い投稿からチューニングブロックをコピーするよりも、通常は時間を節約できます。

Nginxパフォーマンスメトリクスの理解

トラブルシューティングに飛び込む前に、何がパフォーマンスのボトルネックを構成し、どのメトリクスが重要な指標であるかを理解することが重要です。ボトルネックは、システム内の1つのコンポーネントが全体的な容量や速度を制限するときに発生します。Nginxの場合、これはリクエストの処理、接続の管理、またはコンテンツの効率的な提供能力に関連することがよくあります。

監視すべき主要なメトリクスは以下の通りです:

  • アクティブ接続数: Nginxが現在処理しているクライアント接続の数。
  • 1秒あたりのリクエスト数(RPS): Nginxがリクエストを処理する速度。
  • リクエストレイテンシ: Nginxがクライアントリクエストに応答するのにかかる時間。
  • CPU使用率: Nginxワーカープロセスが消費しているCPUリソースの割合。
  • メモリ使用量: Nginxプロセスが使用しているRAMの量。
  • ネットワークI/O: Nginxサーバーとの間のデータ転送速度。
  • ディスクI/O: Nginxが静的ファイルを直接提供したり、大量にログを記録したりする場合に関連します。

診断のためのNginx組み込みツール

Nginxは、運用状態を監視し、パフォーマンスデータを収集するためのいくつかの機能を提供しています。

stub_statusモジュールの使用

stub_statusモジュールは、Nginxの現在の状態に関する基本的でありながら重要な情報を提供します。サーバーアクティビティの概要を素早く把握するための優れた最初の手段です。

stub_statusの有効化

stub_statusを有効にするには、以下の設定ブロックをnginx.conf(通常は監視エンドポイントのserverブロック内)に追加します:

server {
    listen 80;
    server_name monitoring.example.com;

    location /nginx_status {
        stub_status on;
        access_log off;
        allow 127.0.0.1; # ローカルホストからのみアクセスを許可
        deny all;
    }
}

設定を変更した後、Nginxをリロードします:

sudo nginx -t # 設定のテスト
sudo nginx -s reload # Nginxのリロード

stub_status出力の解釈

ステータスページ(例:http://localhost/nginx_status)にアクセスすると、以下のような出力が表示されます:

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

各メトリクスの意味は以下の通りです:

  • Active connections: ReadingWritingWaiting接続を含む、現在アクティブなクライアント接続の数。
  • accepts: Nginxが受け入れた接続の総数。
  • handled: Nginxが処理した接続の総数。理想的には、acceptshandledは等しいべきです。handledが大幅に低い場合、リソース制限(例:worker_connections制限)を示している可能性があります。
  • requests: Nginxが処理したクライアントリクエストの総数。
  • Reading: Nginxが現在リクエストヘッダーを読み取っている接続の数。
  • Writing: Nginxが現在クライアントにレスポンスを書き込んでいる接続の数。
  • Waiting: リクエストを待機しているアイドル状態のクライアント接続の数(例:keep-alive接続)。ここでの数値が高い場合、効率的なkeep-alive使用を示す一方で、アクティブ接続が少なくリソースが制約されている場合、ワーカープロセスが待機に拘束されている可能性もあり、懸念事項となることがあります。

高度なメトリクスのためのNginx Plus APIの活用

Nginx Plusユーザー向けに、Nginx Plus APIは監視のためのより詳細でリアルタイムなJSONインターフェースを提供します。このAPIは、ゾーン、サーバー、アップストリーム、キャッシュなどに関する詳細なメトリクスを提供し、詳細なパフォーマンス分析や監視ダッシュボードとの統合に非常に役立ちます。

Nginx Plus APIの有効化

Nginx Plus設定でAPI用のロケーションを設定します:

http {
    server {
        listen 8080;

        location /api {
            api write=on;
            allow 127.0.0.1; # セキュリティのためにアクセスを制限
            deny all;
        }

        location /api.html {
            root /usr/share/nginx/html;
        }
    }
}

Nginxをリロードし、http://localhost:8080/apiにアクセスしてJSON出力を表示します。このAPIは、詳細な接続統計、リクエスト処理時間、アップストリームの健全性、キャッシュパフォーマンスなど、広範なデータを提供し、stub_statusよりもはるかに細かい粒度でのトラブルシューティングを可能にします。

Nginxアクセスログとエラーログ

Nginxログは、パフォーマンストラブルシューティングのための情報の宝庫です。すべてのリクエストと発生したエラーを記録します。

詳細ログの設定

log_formatをカスタマイズして、リクエスト処理時間($request_time)やアップストリーム応答時間($upstream_response_time)などの有用なパフォーマンスメトリクスを含めることができます。

http {
    log_format perf_log '$remote_addr - $remote_user [$time_local] "$request" ' 
                        '$status $body_bytes_sent "$http_referer" ' 
                        '"$http_user_agent" "$http_x_forwarded_for" ' 
                        'request_time:$request_time upstream_response_time:$upstream_response_time ' 
                        'upstream_addr:$upstream_addr';

    access_log /var/log/nginx/access.log perf_log;
    error_log /var/log/nginx/error.log warn;

    # しきい値より遅いリクエストをログに記録する例
    # これはやや高度で、カスタムモジュールや別の解析ツールが必要になる場合があります。
    # 多くの場合、メインのaccess_logを解析して遅いリクエストを見つける方が簡単です。
}

遅いリクエストとエラーの特定

  • 遅いリクエスト: grepawkなどのツールを使用して、アクセスログから特定の$request_timeまたは$upstream_response_timeのしきい値を超えるリクエストを抽出します。これにより、問題のあるアプリケーションや外部サービスを特定するのに役立ちます。
    awk 'match($0, /request_time:([0-9.]+)/, m) && m[1] > 1.0 {print $0}' /var/log/nginx/access.log
    
    これにより、固定のログフィールド番号に依存することを避けられます。リクエストパス、ユーザーエージェント、リファラーにスペースが含まれると、固定番号は機能しなくなります。
  • エラー: error.logを監視して、「upstream timed out」、「no live upstreams」、「too many open files」などの重大な問題を確認します。これらのエラーは、バックエンドの問題やNginxのリソース制限を直接示しています。

外部システム監視ツール

Nginxのパフォーマンスは、多くの場合、基盤となるサーバーのリソースに結びついています。システムレベルの監視は、重要なコンテキストを提供します。

  • CPU使用率(tophtopmpstat: Nginxワーカープロセスによる高いCPU使用率は、複雑な設定(正規表現、SSLハンドシェイク)、非効率なコード、または単に高負荷を示している可能性があります。
    top -c # CPU使用率でソートされたプロセスを表示
    
  • メモリ使用量(free -hhtop: 過剰なメモリ消費は、大きなバッファサイズ(proxy_buffers)、メモリリーク、または異常に多くのアクティブ接続を示している可能性があります。
    free -h # 人間が読みやすい形式でメモリ使用量を表示
    
  • ディスクI/O(iostatiotop: Nginxが大量の静的コンテンツを提供している場合や、大量にログを記録している場合に関連します。高いディスクI/Oは、ストレージのボトルネックまたは過剰なログ記録を意味する可能性があります。
    iostat -x 1 10 # 1秒ごとに10回、拡張ディスク統計を表示
    
  • ネットワークI/O(netstatssiftop: ネットワークトラフィックを監視して、飽和状態や過剰な再送信がないか確認します。これらは、ネットワークのボトルネックやNginxとクライアント/アップストリーム間の問題を示している可能性があります。
    netstat -antp | grep nginx # Nginx接続を表示
    

一般的なNginxパフォーマンスのボトルネックとその解決策

監視データを活用して、一般的な問題とその修正方法を見てみましょう。

1. 高いCPU使用率

症状: topでNginxワーカープロセスが、中程度の負荷でもCPUの大部分を消費していることが示される。

原因:

  • マルチコアCPUに対するワーカープロセス数が少なすぎる: Nginxが利用可能なすべてのコアを活用できていない可能性があります。
  • 複雑なif文や正規表現: 過度に複雑な正規表現や設定内の多数のif文はCPU負荷が高くなる可能性があります。
  • 非効率なSSL/TLS設定: より多くのCPUを必要とする弱い暗号の使用、または利用可能な場合にハードウェアアクセラレーションを活用していない。
  • 過剰なログ記録: 特に複雑なlog_formatルールを使用している場合、ディスクに大量のデータを書き込む。
  • TLS、圧縮、またはリクエスト処理のオーバーヘッド: 高コストなTLSハンドシェイク、高い圧縮レベル、重いリライトルール、または非常に大きなリクエストヘッダーはCPUを押し上げる可能性があります。

解決策:

  • worker_processesの最適化: worker_processes auto;(推奨)に設定するか、CPUコア数に設定します。各ワーカープロセスはシングルスレッドであり、1つのCPUコアを完全に活用できます。
    worker_processes auto;
    
  • 設定の簡素化: if文と正規表現を見直します。より単純なロジックのためにmapディレクティブやtry_filesの使用を検討してください。
  • SSL/TLSの最適化: 最新のTLS設定を使用し、適切な場所でssl_session_cachessl_session_timeoutを有効にして、繰り返されるハンドシェイク作業を削減します。
  • ログの制御: バッファリングされたアクセスログを使用するか、ノイズの多い静的アセットのアクセスログを無効にします(そこでリクエストごとのレコードが必要ない場合)。
  • バックエンドの調査: Nginxが待機している場合、ボトルネックはアップストリームにあります。バックエンドアプリケーションを最適化します。

2. 応答時間の遅さ

症状: ログで高い$request_timeまたは$upstream_response_time。ページの読み込みが遅い。

原因:

  • アップストリーム(バックエンド)サーバーの問題: 最も一般的な原因。アプリケーションサーバーが応答の生成に時間がかかっている。
  • 適切な最適化なしでの大容量ファイル転送: sendfilegzipを使用せずに大きな静的ファイルを提供している。
  • ネットワークレイテンシ: クライアントとNginx、またはNginxとアップストリーム間のネットワークが遅い。
  • キャッシングの欠如: 動的コンテンツを繰り返し取得している。

解決策:

  • アップストリームのヘルスチェックとタイムアウトの最適化: proxy_read_timeoutproxy_connect_timeoutproxy_send_timeoutを設定します。アップストリームサーバーにヘルスチェックを実装します。
    location / {
        proxy_pass http://backend_app;
        proxy_read_timeout 90s; # 必要に応じて調整
        proxy_connect_timeout 5s;
    }
    
  • gzip圧縮の有効化: テキストベースのコンテンツの場合、gzipは転送サイズを大幅に削減します。
    gzip on;
    gzip_comp_level 5;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
    
  • sendfiletcp_nodelayの有効化: 効率的な静的ファイル提供のために。
    sendfile on;
    tcp_nodelay on;
    
  • キャッシングの実装: 動的コンテンツにはproxy_cacheを使用し、静的アセットにはexpiresヘッダーを設定します。
    # 静的アセットの例
    location ~* \.(js|css|png|jpg|jpeg|gif|ico)$ {
        expires 30d;
        log_not_found off;
    }
    

3. 接続エラー / 接続数の上限超過

症状: クライアントが接続失敗、502または504応答、または断続的なタイムアウトを受信する。stub_statusは受け入れられた接続が急速に増加していることを示し、エラーログにはworker_connections are not enoughtoo many open files、またはアップストリーム接続失敗が記載される場合があります。

原因:

  • worker_connections制限に達した: Nginxが新しい接続を受け入れられない。
  • オープンファイル数が多すぎる(ulimit): オペレーティングシステムのファイル記述子制限に達した。
  • バックエンドの飽和: アップストリームサーバーが圧倒され、接続を受け入れていない。
  • DDoSまたは異常に高い正当なトラフィック。

解決策:

  • worker_connectionsの増加: eventsブロック内でこのディレクティブを高い値(例:10240以上)に設定します。これはワーカープロセスあたりの最大接続数です。
    events {
        worker_connections 10240;
    }
    
  • ファイル記述子制限の調整: オペレーティングシステムのオープンファイル制限を増やします。適切な場合はnginx.confworker_rlimit_nofile 65535;を追加し、ほとんどの最新のLinuxディストリビューションではsystemdを使用してサービス制限をLimitNOFILE=65535に設定します。
  • keepalive_timeoutの最適化: 長いkeep-aliveタイムアウトは、クライアントが接続を再利用していない場合、不必要にワーカープロセスを拘束する可能性があります。Waiting接続が多くrequestsが少ない場合は、タイムアウトを短くします。
    keepalive_timeout 15s; # デフォルトは75s
    
  • ロードバランシングとスケーリングの実装: 複数のバックエンドサーバーにトラフィックを分散します。Nginxのロードバランシング機能(ラウンドロビン、最小接続数、IPハッシュ)を検討してください。
  • レート制限: limit_reqまたはlimit_connモジュールを使用して、単一のクライアントからの過剰なリクエストや接続からサーバーを保護します。

4. 高いメモリ使用量

症状: NginxワーカープロセスがかなりのRAMを消費。サーバーが過度にスワップする可能性があります。

原因:

  • 大きなバッファサイズ: proxy_buffersclient_body_buffer_sizefastcgi_buffersが高く設定されすぎている。
  • 大規模なキャッシング: 大きなproxy_cache_pathサイズ。
  • 多数のアクティブ接続: 各接続はある程度のメモリを必要とします。

解決策:

  • バッファサイズの調整: ログが実際のバッファ問題(設定されたプロキシまたはFastCGIバッファに対して応答ヘッダーが大きすぎるなど)を示している場合にのみ、バッファサイズを増やします。413 Request Entity Too Largeは、プロキシ応答バッファではなく、client_max_body_sizeなどのリクエスト本文制限によって制御されます。
    proxy_buffer_size 4k;
    proxy_buffers 8 8k;
    
  • キャッシングの最適化: キャッシュサイズと削除ポリシー(proxy_cache_pathパラメータ)を管理します。
  • keepalive_timeoutの見直し: 前述のとおり、過度に長いkeepalive_timeoutは、アイドル接続のためにワーカープロセスとそれに関連するメモリをアクティブに保つ可能性があります。

パフォーマンスのためのNginx設定のベストプラクティス

特定の問題のトラブルシューティングに加えて、以下の一般的なベストプラクティスは、最適なNginxパフォーマンスを維持するのに役立ちます:

  • worker_processes auto;: すべてのCPUコアを活用します。
  • worker_connections: 予想される同時接続数とファイル記述子制限に一致する値を設定します。ビジーなサーバーでは40968192が一般的な開始点ですが、適切な値はワークロードによって異なります。
  • sendfile on;: 効率的な静的ファイル提供のために。
  • tcp_nodelay on;: 小さなパケットの即時送信を保証し、インタラクティブなサービスのレイテンシを改善します。
  • keepalive_timeout: クライアントの動作に基づいて調整します。15〜30秒が適切なバランスであることが多いです。
  • gzip on;: テキストベースのコンテンツに対して圧縮を有効にします。
  • proxy_buffering on;: 一般的には、バッファリングをオンにしておきます。これにより、Nginxはアップストリームからの応答をディスクに(必要に応じて)スプールし、可能な限り迅速にクライアントに送信してアップストリームを解放できます。リアルタイムの低レイテンシストリーミングが絶対的に重要であり、その影響を理解している場合にのみ無効にしてください。
  • expiresヘッダー: クライアント側で静的コンテンツを積極的にキャッシュします。
  • if文と正規表現を最小限に: パフォーマンス向上のためにmapディレクティブやtry_filesを選択します。
  • 静的ファイルにはaccess_log off;を使用: ログ記録が厳密に必要でない場合、頻繁にアクセスされる静的アセットのディスクI/Oを削減します。
  • HTTP/2: 最新のブラウザ向けにHTTP/2を有効にして、HTTPS上の多重化とヘッダー圧縮を改善します。
    listen 443 ssl http2;
    

トラブルシューティングのワークフローと戦略

パフォーマンス問題に直面した場合、構造化されたアプローチに従います:

  1. ベースラインの定義: 正常な期間中の通常の運用メトリクス(CPU、メモリ、接続数、RPS、レイテンシ)を理解します。
  2. 症状の監視: 特定の症状(例:高いCPU、遅いリクエスト、接続エラー)を特定し、ツール(stub_status、ログ、top)を使用して確認します。
  3. 仮説を立てる: 症状に基づいて、根本原因について仮説を立てます(例:「高いCPUは非効率な正規表現が原因である」)。
  4. テストと分析: 変更を実装し(例:正規表現の簡素化)、メトリクスへの影響を監視します。新しいログエントリやstub_status出力を分析します。
  5. 反復: 問題が解決しない場合は、仮説を洗練させ、プロセスを繰り返します。
  6. 文書化: 将来の参照のために、行った変更とその影響の記録を保持します。

最高のNginxパフォーマンス修正は、通常、地味です。つまり、遅延がどこにあるかを証明し、1つのことを変更し、その後同じメトリクスを監視します。$upstream_response_timeが高い場合は、Nginxを非難する前にアプリケーションパスをチューニングします。アップストリーム時間が空の状態で静的ファイルが遅い場合は、ディスク、ネットワーク、圧縮、静的ファイル設定を確認します。エラーがファイル記述子やワーカー接続について言及している場合は、それらの制限をペアで修正します。この習慣により、トラブルシューティングは伝承ではなく証拠に基づいたものになります。