Nginxログ分析をマスターして効率的なトラブルシューティングを実現

Nginxのアクセスログとエラーログをマスターすることで、効率的なトラブルシューティングを実現します。このガイドでは、重要なタイミングメトリクスを取得するためのカスタムログフォーマットの設定方法を詳しく説明し、Nginxまたは上流のアプリケーションサーバー内のパフォーマンスボトルネックを特定できるようにします。エラーログの重大度レベルを使用して502や504エラーなどの重大な問題を即座に診断し、強力なシェルコマンド(`grep`、`awk`)を活用してトラフィックパターンを迅速にフィルタリング、カウント、分析する方法を学びます。

Nginxログ分析をマスターして効率的なトラブルシューティングを実現

Nginxログは通常、「サイトがダウンしている」という状況を具体的な問題に変換する最速の方法です。アクセスログは、クライアントが何を要求し、どのようなステータスを受け取ったかを示します。エラーログは、Nginxが何を実行できなかったか(アップストリームへの接続、証明書の読み取り、ファイルのオープン、設定の解析、バックエンドからの応答の待機など)を示します。

優れたNginxログ分析とは、ファイルを凝視して怪しいものを見つけることではありません。それは、狭い質問をし、迅速にフィルタリングし、アクセスログをエラーログや上流のアプリケーションログと関連付けることです。アクセスログの502は症状です。対応するエラーログ行が通常、答えの始まりです。


1. Nginxログの基礎:アクセスログとエラーログ

Nginxは2つの異なるタイプのログを維持しており、それぞれが重要で別個の機能を果たします。

1.1 アクセスログ(access.log

アクセスログは、Nginxが処理するすべてのリクエストに関する詳細を記録します。これは、ユーザーの行動を理解し、トラフィックフローを監視し、応答時間を評価するために不可欠です。

デフォルトの場所: 通常は /var/log/nginx/access.log

目的: クライアントとのやり取り、成功したリクエスト、クライアントエラー、Nginxを介して返されるサーバーエラー、送信バイト数、ユーザーエージェント、および設定されている場合はリクエストのタイミングを追跡します。

1.2 エラーログ(error.log

エラーログは、Nginxの処理ライフサイクル中に発生する内部の問題、運用上の障害、および通信の問題を追跡します。このログは、バックエンドの接続性の問題やサーバー設定エラーのトラブルシューティングにおける決定的な情報源です。

デフォルトの場所: 通常は /var/log/nginx/error.log

目的: サーバー側のエラー、警告、およびシステムイベント(5xxエラー、設定ファイルの解析エラー)を追跡します。

エラーログの重大度レベル

Nginxは8つの重大度レベルを使用します。トラブルシューティング時には、通常は error レベル以上から開始することをお勧めします。重大度レベルは、error_log ディレクティブを使用して設定されます。

# 最小重大度レベルを 'warn' に設定
error_log /var/log/nginx/error.log warn;
レベル 説明 優先度
crit 重大なランタイム障害などの致命的な状態 最高
error リクエストの処理を妨げるエラーが発生しました
warn 予期しないことが発生しましたが、操作は継続されます
notice 正常だが重要な状態(サーバー再起動など)
info 情報メッセージ 最低

emergalertdebug レベルもあります。debug は非常にノイズが多くなる可能性があり、通常はデバッグサポート付きのNginxビルドが必要です。通常の本番設定ではなく、対象を絞ったトラブルシューティングに使用してください。

2. パフォーマンス分析のためのアクセスログのカスタマイズ

デフォルトのNginxアクセスログ形式(combined と呼ばれることが多い)は便利ですが、重要なパフォーマンスタイミング変数が欠けています。遅延を効果的にトラブルシューティングするには、Nginxがリクエストの処理に費やした時間とアップストリームサーバーが要した時間をキャプチャするカスタム形式を定義する必要があります。

2.1 パフォーマンスログ形式の定義

log_format ディレクティブ(通常は nginx.conf で定義)を使用して、カスタム形式(例:timing_log)を作成します。

log_format timing_log '$remote_addr - $remote_user [$time_local] ' 
                    '"$request" $status $body_bytes_sent ' 
                    '"$http_referer" "$http_user_agent" ' 
                    '$request_time $upstream_response_time';

server {
    listen 80;
    server_name example.com;
    
    # ここでカスタム形式を適用
    access_log /var/log/nginx/timing_access.log timing_log;
    # ... 残りの設定
}
変数 説明 トラブルシューティングの価値
$request_time 最初のバイトを受信してから最後のバイトを送信するまでの経過時間の合計。 値が高い場合は、ネットワークの遅延、Nginxの遅延、またはバックエンドの遅延を示します。
$upstream_response_time アップストリームサーバー(アプリケーションサーバーなど)の応答を待機するのに費やされた時間。 ここでの値が高い場合は、バックエンドアプリケーションがボトルネックであることを示します。
$status クライアントに返されるHTTPステータスコード。 エラー(4xx、5xx)のフィルタリングに不可欠です。

ログが集中管理システムに送信される場合は、JSONログ形式の使用を検討してください。JSONは人間が読むのは難しいですが、ツールが確実に解析するのははるかに簡単です。プレーンテキストログを保持する場合、awk のフィールド番号は、ユーザーエージェント、リクエストパス、または引用符で囲まれたフィールドにスペースが含まれていると壊れる可能性があることに注意してください。

また、リクエストIDのログ記録も検討してください。ロードバランサーやアプリケーションがすでにリクエストIDヘッダーを送信している場合は、それを渡してログに記録します。

log_format timing_log '$remote_addr [$time_local] '
                    '"$request" $status $body_bytes_sent '
                    'request_time=$request_time '
                    'upstream_time=$upstream_response_time '
                    'request_id=$request_id '
                    'upstream=$upstream_addr';

リクエストIDを使用すると、1つの遅いパブリックリクエストを1つのアプリケーションログエントリに接続できます。これがないと、タイムスタンプ、パス、クライアントIPで照合することになりますが、これは可能ですが、はるかに面倒です。

3. アクセスログエントリの解釈

カスタマイズされた形式を使用した典型的なエントリは次のようになります(最後にタイミング値が追加されています)。

192.168.1.10 - - [10/May/2024:14:30:05 +0000] "GET /api/data HTTP/1.1" 200 450 "-" "Mozilla/5.0" 0.534 0.528

診断:

  1. ステータスコード(200): 成功。
  2. リクエスト時間(0.534秒): 合計時間は0.5秒です。
  3. アップストリーム時間(0.528秒): ほとんどの時間はバックエンドアプリケーションの待機に費やされました(0.534 - 0.528 = 0.006秒 はNginxのオーバーヘッド)。

診断: このリクエストでは、バックエンドアプリケーションが500msのレイテンシの原因である可能性が高いです。Nginxのオーバーヘッドは小さいようです。

1行だけで一般化しないでください。遅いリクエストのサンプルを確認してください。ほとんどの遅いリクエストで $upstream_response_time が高い場合は、アプリまたはアップストリームネットワークに焦点を当ててください。$request_time が高く、$upstream_response_time が低い場合、遅延はクライアントのアップロード時間、クライアントのダウンロードの遅さ、バッファリング動作、またはNginx側の作業が原因である可能性があります。

ステータスコードを使用したトラブルシューティング

ステータスコード範囲 意味 一般的なアクション/ログソース
4xx(クライアントエラー) クライアントが無効または許可されていないリクエストを送信しました。 アクセスログで頻度が高いか確認します。404 Not Found(ファイルが見つからない)または 403 Forbidden(権限の問題)を探します。
5xx(サーバーエラー) Nginxまたはアップストリームサーバーが有効なリクエストを処理できませんでした。 すぐにエラーログで対応するエントリを確認します。
502 Bad Gateway Nginxがアップストリームアプリケーションから応答を取得できませんでした。 エラーログに詳細(Connection Refused、Timeout)が表示されます。
504 Gateway Timeout アップストリームサーバーが設定されたプロキシ制限内で応答するのに時間がかかりすぎました。 エラーログにタイムアウト警告が表示されます。タイムアウトを増やす前に、バックエンドのレイテンシを調査します。

proxy_read_timeout を増やすと症状を隠すことができますが、ユーザーは依然として長時間待つことになります。長時間実行されるエンドポイント、ストリーミング、または既知の低速な操作では有効ですが、通常のAPIリクエストでは、最初にバックエンドの調査をトリガーする必要があります。

4. エラーログでの重大な問題の診断

リクエストが5xxエラーになった場合、アクセスログはエラーが発生したことだけを通知します。エラーログは、その理由を通知します。

ケーススタディ:502 Bad Gateway

502エラーは、Nginxをリバースプロキシとして使用する場合の最も一般的な問題の1つです。これはほとんどの場合、バックエンドアプリケーションがダウンしているか、過負荷であるか、到達不能であることを示しています。

エラーログで次の特定のメッセージを探します。

4.1 接続が拒否されました(バックエンドダウン)

これは、Nginxがバックエンドポートに接続しようとしたが、リッスンしているものがなかったことを示します。つまり、アプリケーションサーバー(PHP-FPM、Gunicornなど)が停止しているか、正しく設定されていません。

2024/05/10 14:35:10 [error] 12345#0: *1 connect() failed (111: Connection refused) while connecting to upstream, client: 192.168.1.10, server: example.com, request: "GET /test"
  • アクション: バックエンドサービスが実行されているか、期待されるポートまたはUnixソケットでリッスンしているか、Nginxが同じアドレスを指しているかを確認します。停止した理由を理解した後にのみ再起動してください。

4.2 アップストリームが早期に接続を閉じました(バックエンドクラッシュ)

これは、Nginxが接続を確立したが、バックエンドサーバーが完全なHTTP応答を送信する前に接続を終了した場合に発生します。これは、多くの場合、アプリケーションコードの致命的なエラーまたはクラッシュを示唆しています。

2024/05/10 14:38:22 [error] 12345#0: *2 upstream prematurely closed connection while reading response header from upstream, client: 192.168.1.10, server: example.com, request: "POST /submit"
  • アクション: 特定の致命的なエラーについて、アプリケーションサーバーのネイティブエラーログ(PHP-FPMログ、Node.jsログなど)を確認します。

警告: 起動時にNginxが設定ファイルの読み取りに失敗した場合、エラーは設定された error.log の場所ではなく、標準エラー出力またはブートストラップログファイルに直接ダンプされることがよくあります。Nginxが起動に失敗した場合は、常に journalctl -xe またはシステムログを確認してください。

ケーススタディ:403 Forbidden

アクセスログの403は、アプリケーションの認可、Nginxのアクセスルール、ファイルシステムの権限、またはディレクトリインデックスの動作が原因で発生する可能性があります。アクセスログだけでは、どれが原因かを特定できません。

エラーログで次のような行を探します。

2024/05/10 15:02:01 [error] 12345#0: *12 directory index of "/var/www/site/" is forbidden

これは、Nginxがディレクトリに到達したが、提供するインデックスファイルがなく、ディレクトリリストが無効になっていることを意味します。修正方法は、期待される index.html を作成するか、index ディレクティブを調整するか、リクエストをアプリケーションにルーティングすることです。

権限の問題の場合、次のようなメッセージが表示されることがあります。

2024/05/10 15:04:44 [error] 12345#0: *15 open() "/var/www/site/private.txt" failed (13: Permission denied)

ファイルの所有権、ディレクトリの実行権限、該当する場合はSELinuxまたはAppArmorポリシー、およびNginxワーカーが実行されるユーザーを確認します。

ケーススタディ:499 Client Closed Request

Nginx固有のステータス 499 は、Nginxが応答を完了する前にクライアントが接続を閉じたことを意味します。これは、ユーザーがページを移動した場合、モバイルクライアントの接続が切れた場合、またはアップストリームの応答が遅すぎてクライアントが諦めた場合によく発生します。

すべての499をNginxのバグとして扱わないでください。タイミングを確認してください。多くの499のリクエスト時間が長く、遅いアップストリームと一致する場合、ユーザーは遅いリクエストを放棄している可能性があります。1つのクライアントまたはネットワークからすぐに発生する場合は、クライアントの動作が原因である可能性があります。

5. ログ分析のための実用的なシェルコマンド

本番環境では堅牢なログ監視システムが推奨されますが、Linuxコマンドラインは迅速なリアルタイムトラブルシューティングのための強力なツールを提供します。

5.1 リアルタイム監視

リクエストが届くときにログを監視します(修正のデプロイ後や新機能のテスト後に特に便利です)。

tail -f /var/log/nginx/access.log
# または、エラーのみ
 tail -f /var/log/nginx/error.log

ローテーションされて圧縮されたログの場合は、zgrep を使用します。

zgrep '" 50[0-9] ' /var/log/nginx/access.log*.gz

インシデントレビュー中はログローテーションが重要です。エラーは真夜中の直前、またはローテーションジョブが昨日のファイルを圧縮する直前に発生した可能性があります。

5.2 エラーのフィルタリングとカウント

過去1時間または1日で最も頻繁に発生する5xxエラーをすばやく見つけてカウントします。

# すべての5xxリクエストを検索
grep '" 50[0-9] ' /var/log/nginx/access.log | less

# 5xxエラーの分布をカウント(例:502と504の数)
grep '" 50[0-9] ' /var/log/nginx/access.log | awk '{print $9}' | sort | uniq -c | sort -nr

説明: awk '{print $9}' はHTTPステータスコードを抽出します(デフォルトまたはcombinedログ形式で、ステータスが9番目のフィールドであると仮定)。

カスタムログ形式を使用する場合は、カウントを信頼する前にフィールド番号を確認してください。より安全なクイックチェックとして、解析された行をいくつか出力します。

awk '{print NR, $0; if (NR == 3) exit}' /var/log/nginx/access.log

JSONログの場合は、フィールド番号の代わりに jq を使用します。

jq -r 'select(.status >= 500) | .status' /var/log/nginx/access.json \
  | sort | uniq -c | sort -nr

5.3 遅いリクエストの特定(カスタムログ形式が必要)

timing_log 形式を実装している場合($request_time が最後から2番目のフィールド、またはこの例ではフィールド16)。

# 最も遅い10のリクエストを検索(例:1秒以上かかるリクエスト)
awk '($16 > 1.0) {print $16, $7}' /var/log/nginx/timing_access.log | sort -nr | head -10

説明: このコマンドは、1.0秒より長くかかったリクエストについて、リクエスト時間とURI($7)を降順で出力します。

より読みやすいプレーンテキストのタイミング形式では、request_time=0.534 のような名前付きの値を使用します。そうすれば、フィールド番号の驚きを減らして、あまりエレガントではありませんが、遅い範囲をgrepできます。本格的な分析には、構造化ログをログシステムに送信し、ルートごとにパーセンタイルをクエリします。

5.4 トップリクエスト元IPアドレスの特定

潜在的なDoS攻撃、トラフィックの急増、または不審なアクティビティを特定するのに役立ちます。

# リクエストを行っているトップ20のIPを検索
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -20

トップIPは出発点であり、悪用の証拠ではありません。企業のNAT、CDNエッジ、またはロードバランサーにより、多くのユーザーが1つのソースとして表示される可能性があります。Nginxがプロキシの背後にある場合は、real_ip_header と信頼できるプロキシ範囲を使用して、実際のクライアントIPを慎重に設定してログに記録します。オープンインターネットからの任意の X-Forwarded-For ヘッダーを決して信頼しないでください。

実用的なトラブルシューティングフロー

ユーザーの症状と時間枠から始めます。「14:35 UTC頃にチェックアウトで502が返された」という情報は、「Nginxが壊れている」よりもはるかに役立ちます。

まず、ステータスをカウントします。

grep '10/May/2024:14:3' /var/log/nginx/access.log \
  | awk '{print $9}' | sort | uniq -c | sort -nr

プレーンテキストログでの日付フィルタリングは扱いにくく、正確なコマンドはログ形式によって異なります。迅速なインシデントチェックの場合は、大まかなフィルタリングでも、問題が主に502、504、403、または404のいずれであったかを示すことができます。

次に、一致するリクエストをいくつか取得します。

grep '" 502 ' /var/log/nginx/access.log | tail -20

タイムスタンプ、URI、アップストリーム時間、およびリクエストID(存在する場合)をメモします。次に、同じタイムスタンプの前後でエラーログを検索します。

grep '14:35' /var/log/nginx/error.log

エラーが connect() failed (111: Connection refused) を示している場合は、アップストリームサービスとそのポートを調査します。upstream timed out を示している場合は、バックエンドのレイテンシとキューイングを調査します。no live upstreams を示している場合は、アップストリームのヘルス、DNS、またはロードバランサーの設定を調査します。

最後に、同じリクエストIDまたはタイムスタンプを使用してバックエンドログを確認します。Nginxは多くの場合、ハンドオフがどこで失敗したかを示しますが、バックエンドログはアプリケーションがそのように動作した理由を示します。

障害が発生する前にログを有用にする

ロギングを改善するのに最悪のタイミングは、障害の発生中です。必要になる前に、リクエストのタイミング、アップストリームのタイミング、アップストリームアドレス、およびリクエストIDを追加してください。1つのサーバーが複数のアプリケーションをホストしている場合は、アクセスログとエラーログをサイトごとに分けてください。ローテーションによって、実際に調査するインシデントに十分な履歴が保持されるようにしてください。

何かが壊れたときは、ログをペアで読みます。アクセスログで何が起こったかを確認し、エラーログでNginxが何を実行できなかったかを確認し、アプリケーションログでアップストリームが次に何をしたかを確認します。この習慣により、トラブルシューティングの焦点が維持され、タイムアウトを変更したり、サービスをランダムに再起動したりするよりも、実際の障害に早くたどり着くことができます。