2026年7月23日
2026年7月23日
Nginxの接続数グラフ(Connections over Time)の読み方|stub_statusの値と異常の見分け方
監視ツールの「Connections over Time」のような、Nginxの接続数を時系列で表したグラフは、ほとんどの場合、Nginxの stub_status モジュールが出す数値をもとにしています。グラフを正しく読むには、まずその数値が何を数えているかを知る必要があります。
この記事では、stub_statusの各値の意味、グラフの作り方、よくある形とその原因の見分け方を説明します。内容は2026年10月時点のnginx.orgの公式ドキュメントと、NGINXが公開している nginx-prometheus-exporter に合わせています。
stub_statusの値の意味
stub_statusを有効にしたURLを開くと、次のような出力が得られます。
Active connections: 291
server accepts handled requests
16630948 16630948 31070465
Reading: 6 Writing: 179 Waiting: 106
公式ドキュメントでの定義は次のとおりです。
| 値 | 種類 | 意味 |
|---|---|---|
| Active connections | 現在の数 | クライアントとの接続の数。Waitingの接続も含む |
| accepts | 起動からの累計 | 受け付けたクライアント接続の総数 |
| handled | 起動からの累計 | 処理した接続の総数。通常は accepts と同じで、worker_connections の上限などに達したときだけ少なくなる |
| requests | 起動からの累計 | クライアントからのリクエストの総数 |
| Reading | 現在の数 | リクエストヘッダーを読んでいる接続の数 |
| Writing | 現在の数 | クライアントにレスポンスを書き出している接続の数 |
| Waiting | 現在の数 | 次のリクエストを待っているアイドルな接続の数(キープアライブ中の接続) |
グラフを読むうえで大事な点は次の3つです。
- Activeは、おおむねReading・Writing・Waitingの合計になる(それぞれ別々に数えているので、ぴったり一致しないことがある)。Activeの山が大きくても、その大半がWaitingなら、アイドルな接続が多いだけで負荷は高くない
- accepts・handled・requestsは累計値なので、そのままグラフにすると右肩上がりの線になる。1秒あたりの増え方(レート)に直して見る
- 数えているのはクライアントとの接続で、Nginxから裏側(PHP-FPMやアプリケーションサーバー)への接続は含まない
なお、stub_statusのページを見ている自分の接続も、その瞬間はWritingとして数えられます。アクセスの無いサーバーでもWritingが1になるのはこのためです。
stub_statusを有効にする
stub_statusは標準のビルドには含まれず、--with-http_stub_status_module を付けてビルドする必要があります。nginx.orgのパッケージやUbuntu・Debianのパッケージには通常含まれているので、次のコマンドで確認します。
nginx -V 2>&1 | grep -o with-http_stub_status_module
with-http_stub_status_module と表示されれば使えます。外部に公開しないよう、ローカルホストだけで待ち受けるserverブロックに設定します。たとえば /etc/nginx/conf.d/status.conf を作ります。
server {
listen 127.0.0.1:8080;
location = /stub_status {
stub_status;
access_log off;
allow 127.0.0.1;
deny all;
}
}
sudo nginx -t
sudo systemctl reload nginx
curl http://127.0.0.1:8080/stub_status
グラフを作る
Prometheusとnginx-prometheus-exporterを使う
継続的に監視するなら、NGINXが公開している nginx-prometheus-exporter でstub_statusの値をPrometheusの形式に変換し、PrometheusとGrafanaでグラフにする方法が一般的です。Dockerで動かす場合の例です。
docker run -d --name nginx-exporter --network host \
nginx/nginx-prometheus-exporter:1.5.1 \
--nginx.scrape-uri=http://127.0.0.1:8080/stub_status \
--web.listen-address=127.0.0.1:9113
エクスポーターは http://127.0.0.1:9113/metrics で値を出します。イメージのタグは、GitHubのリリース一覧で最新版を確認して指定してください。Dockerを使わない場合は、GitHubのリリースからバイナリを入手してsystemdで動かします。
Prometheusの設定(prometheus.yml)に取得先を追加します。
scrape_configs:
- job_name: nginx
static_configs:
- targets: ['127.0.0.1:9113']
エクスポーターが出す値と、stub_statusの対応は次のとおりです。
| メトリクス | 種類 | stub_statusの値 |
|---|---|---|
nginx_connections_active |
Gauge | Active connections |
nginx_connections_reading |
Gauge | Reading |
nginx_connections_writing |
Gauge | Writing |
nginx_connections_waiting |
Gauge | Waiting |
nginx_connections_accepted |
Counter | accepts |
nginx_connections_handled |
Counter | handled |
nginx_http_requests_total |
Counter | requests |
nginx_up |
Gauge | 最後の取得が成功したか(1か0) |
Grafanaのパネルには、次のようなPromQLを使います。
# 現在の接続数(状態別に並べる)
nginx_connections_reading
nginx_connections_writing
nginx_connections_waiting
# 1秒あたりの新規接続数とリクエスト数
rate(nginx_connections_accepted[5m])
rate(nginx_http_requests_total[5m])
# 処理されずに落とされた接続(0 が正常)
rate(nginx_connections_accepted[5m]) - rate(nginx_connections_handled[5m])
# 1接続あたりのリクエスト数(キープアライブの再利用の度合い)
rate(nginx_http_requests_total[5m]) / rate(nginx_connections_accepted[5m])
ReadingとWritingとWaitingを積み上げグラフにすると、合計がおおむねActiveと重なり、内訳の変化が読みやすくなります。
とりあえず記録だけしたい場合
監視基盤が無いサーバーで、短期間の傾向を見たいだけなら、stub_statusを定期的にCSVに記録する方法もあります。
while true; do
s=$(curl -s http://127.0.0.1:8080/stub_status)
echo "$(date +%FT%T),$(echo "$s" | awk '/^Active/{a=$3} /^ [0-9]/{acc=$1; req=$3} /^Reading/{print a","$2","$4","$6","acc","req}')"
sleep 10
done >> /var/tmp/nginx_conn.csv
列は「時刻, Active, Reading, Writing, Waiting, accepts累計, requests累計」です。表計算ソフトで開いてグラフにできます。累計の列は、前の行との差を取ってから見ます。
よくあるグラフの形と原因
| グラフの形 | 考えられる原因 | 確認すること |
|---|---|---|
| Activeが高いが、ほとんどがWaiting | キープアライブ中のアイドル接続が多いだけ。異常ではないことが多い | keepalive_timeout(既定75秒)。短くするとWaitingは減るが、再接続が増える |
| Writingが増え続ける | 裏側の応答が遅い(PHP-FPMやデータベース)、または大きなファイルを遅い回線に送っている | $request_time や $upstream_response_time をログに出して遅いリクエストを探す。PHP-FPMの空きプロセス |
| Readingが多い | リクエストヘッダーを送り終えない接続が多い。遅い回線や、接続を占有する攻撃(Slowloris型)の可能性 | アクセスログの送信元、client_header_timeout |
| acceptsとhandledの差が増える | worker_connections か、開けるファイル数の上限に達して接続を落としている |
エラーログの「worker_connections are not enough」、worker_rlimit_nofile |
| 新規接続数とリクエスト数が同時に急増 | アクセスの集中(告知、クローラー、攻撃) | アクセスログの送信元IPとURL、ユーザーエージェント |
| 決まった時刻に山ができる | cronのバッチ、外部の監視、クローラーの巡回 | 山の時刻のアクセスログ |
| 1接続あたりのリクエスト数が1に近い | キープアライブが効いていない(クライアント側か、手前のロードバランサーが毎回接続を切っている) | keepalive_timeout、ロードバランサーの設定 |
上限との比較
1つのワーカープロセスが同時に開ける接続の数は worker_connections で決まり、既定値は512です。この数には、クライアントとの接続だけでなく、裏側のサーバーへのプロキシ接続なども含まれます。リバースプロキシとして使っている場合は、1つのリクエストでクライアント側と裏側の2つの接続を使うことがあるので、Activeの値が「ワーカー数 × worker_connections」の半分程度に近づいてきたら、設定の見直しを検討します。
# 現在の設定値を確認する
sudo nginx -T 2>/dev/null | grep -E 'worker_processes|worker_connections|worker_rlimit_nofile|keepalive_timeout'
まとめ
- 接続数のグラフの多くはstub_statusの値がもとになっている
- Activeはおおむね Reading + Writing + Waiting。Waitingが多いだけなら負荷は高くない
- accepts・handled・requestsは累計値なので、レートに直して見る
- Writingの増加は裏側の遅延、acceptsとhandledの差は接続数の上限を疑う
- stub_statusはローカルホストだけに公開し、継続監視にはnginx-prometheus-exporterとPrometheusを使う
関連記事: