深夜3時に叩き起こされた話
本番のマイクロサービス環境を運用し始めて数ヶ月が経った頃、ユーザーからランダムなクレームが届き始めた。ページの読み込みが遅い、APIがタイムアウトする、接続が切れる――そういった内容だ。当時使っていたZabbixでCPUとメモリを確認しても異常なし。ディスクI/Oも正常。何も引っかからなかった。
原因はサーバーがピーク時間帯にネットワークインターフェースを静かに飽和させていたことだった。パケットはドロップされ、TCPの再送が急増していたのに、まったく把握できていなかった。既存の監視ではネットワーク層のメトリクスを十分な粒度とスピードで取得できていなかったのだ。
この経験をきっかけに、観測スタックをPrometheusとGrafanaでゼロから作り直した。ネットワークに特化した構成で。3ヶ月後には謎のスローダウンはなくなり、ユーザーが気づく2〜4分前にアラートが上がるようになった。
根本原因:ほとんどの監視設定が見落としているもの
標準的なシステム監視はCPU、メモリ、ディスクをカバーする。ネットワークは表面的な扱いになりがちで、帯域幅の入出力やpingレイテンシ程度だ。しかし実際の問題を診断するにはそれでは全然足りない。必要なメトリクスはこういったものだ:
- TCP再送 — 値が高いと上流での輻輳やパケットロストを意味する
- インターフェースごとのネットワークエラーとドロップ — ドライバレベルの問題、ケーブル不良、NICの設定ミス
- コネクション状態 — TIME_WAITやCLOSE_WAITのソケットが多すぎるとエフェメラルポートが枯渇する
- インターフェースごとの帯域使用率 — 合計だけでなく、インターフェース別の時系列トレンド
- ソケットキューの深さ — 受信/送信バッファのオーバーフローはデータを静かに捨てる
Linuxはこれらすべてを/proc/net/と/proc/sys/net/経由で公開している。Prometheusのnode_exporterは自動的にスクレイプしてくれる。あとは正しく繋ぎ合わせるだけだ。
選択肢の比較:何を使うべきか、その理由
選択肢1:netdata
Netdataはインストールが速く、美しい組み込みダッシュボードを持っている。最初期に使っていた。ただし弱点がある。リアルタイム閲覧に最適化されており、長期保存やアラート連携には向いていない。まともなクエリ言語なしにネットワークイベントとアプリケーションログを相関させようとすると苦痛になる。
選択肢2:Telegraf + InfluxDB + Grafana(TIGスタック)
InfluxDBをすでに使っている場合は堅実な選択肢だ。Telegrafのネットワーク入力プラグインは充実している。ただしInfluxDBのライセンスが2023年に変更され、Prometheusと並行して別の時系列DBを運用するコストは割に合わないと判断した。
選択肢3:Prometheus + node_exporter + Grafana(推奨)
結局これが定着した。node_exporterはPrometheusプロジェクト自体がメンテナンスしており、完全なネットワーク統計を含む何百ものLinuxカーネルメトリクスを公開し、GrafanaやAlertmanagerとネイティブに統合される。PromQLは単純な帯域追跡から複雑な再送率計算まで、必要なクエリを自由に構築できる柔軟性を与えてくれる。
最善のアプローチ:ステップバイステップのセットアップ
ステップ1:Linuxホストにnode_exporterをインストールする
GitHubから最新リリースをダウンロードし、systemdサービスとして実行する:
# ダウンロードと展開
wget https://github.com/prometheus/node_exporter/releases/download/v1.8.1/node_exporter-1.8.1.linux-amd64.tar.gz
tar xvf node_exporter-1.8.1.linux-amd64.tar.gz
sudo mv node_exporter-1.8.1.linux-amd64/node_exporter /usr/local/bin/
# systemdサービスを作成する
sudo useradd --no-create-home --shell /bin/false node_exporter
cat <<EOF | sudo tee /etc/systemd/system/node_exporter.service
[Unit]
Description=Prometheus Node Exporter
After=network.target
[Service]
User=node_exporter
Group=node_exporter
Type=simple
ExecStart=/usr/local/bin/node_exporter \\
--collector.netstat \\
--collector.netdev \\
--collector.conntrack \\
--collector.sockstat
Restart=on-failure
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now node_exporter
起動確認とメトリクスの公開確認:
curl http://localhost:9100/metrics | grep node_network
ステップ2:Prometheusがnode_exporterをスクレイプするよう設定する
prometheus.ymlにスクレイプジョブを追加する:
scrape_configs:
- job_name: 'linux-nodes'
scrape_interval: 15s
static_configs:
- targets:
- '192.168.1.10:9100'
- '192.168.1.11:9100'
labels:
env: 'production'
region: 'ap-northeast-1'
編集後にPrometheusをリロードする:
sudo systemctl reload prometheus
# またはHTTP API経由:
curl -X POST http://localhost:9090/-/reload
ステップ3:クエリすべき重要なネットワークメトリクス
ネットワーク問題を診断するとき、まず手を伸ばすPromQLクエリはこれらだ:
インターフェースごとのネットワーク受信/送信帯域幅(バイト/秒):
rate(node_network_receive_bytes_total{device!~"lo|veth.*"}[5m]) * 8
パケットドロップ率(重要 — ゼロに近い値であるべき):
rate(node_network_receive_drop_total[5m])
rate(node_network_transmit_drop_total[5m])
TCP再送率:
rate(node_netstat_Tcp_RetransSegs[5m])
状態別アクティブコネクション数(TIME_WAIT、ESTABLISHEDなど):
node_sockstat_TCP_tw # TIME_WAIT
node_sockstat_TCP_inuse # ESTABLISHEDコネクション
node_sockstat_sockets_used # 使用中の総ソケット数
NICエラー — ハードウェアやドライバの問題を検出するのに有効:
rate(node_network_receive_errs_total[5m])
rate(node_network_transmit_errs_total[5m])
ステップ4:Grafanaダッシュボードを構築する
ゼロから作る必要はない。GrafanaのオフィシャルライブラリからダッシュボードID 1860(Node Exporter Full)をインポートしよう。ネットワークを含むほとんどのLinuxシステムメトリクスをカバーしており、カスタマイズの土台として十分だ。
ネットワーク重視のダッシュボードを構築するなら、以下の構成でパネルを作る:
- パネル1 — インターフェースごとの帯域幅:
rate(node_network_receive_bytes_total)クエリを使用、ビジュアライゼーション:時系列、単位:バイト/秒 - パネル2 — ドロップ/エラー率:受信ドロップと送信ドロップをスタック表示、1パケット/秒の閾値ライン
- パネル3 — TCP再送:5分レートを表示するシングルスタット、10/秒超で赤色
- パネル4 — ソケット状態:TIME_WAITのゲージ、閾値は10,000
ステップ5:アラートルールを設定する
Prometheusの設定ディレクトリにnetwork_alerts.ymlを配置する:
groups:
- name: network
rules:
- alert: HighPacketDropRate
expr: rate(node_network_receive_drop_total[5m]) > 10
for: 2m
labels:
severity: warning
annotations:
summary: "{{ $labels.instance }}でパケットドロップ率が高い"
description: "インターフェース{{ $labels.device }}が{{ $value | humanize }}パケット/秒をドロップしています"
- alert: HighTCPRetransmits
expr: rate(node_netstat_Tcp_RetransSegs[5m]) > 50
for: 3m
labels:
severity: critical
annotations:
summary: "{{ $labels.instance }}でTCP再送ストームが発生"
- alert: NetworkInterfaceSaturated
expr: |
rate(node_network_transmit_bytes_total{device!~"lo|veth.*"}[5m]) * 8
/ node_network_speed_bytes{device!~"lo|veth.*"} > 0.85
for: 5m
labels:
severity: warning
annotations:
summary: "{{ $labels.instance }} / {{ $labels.device }}でNICが飽和に近づいています"
- alert: TooManyTimeWaitSockets
expr: node_sockstat_TCP_tw > 15000
for: 10m
labels:
severity: warning
annotations:
summary: "TIME_WAITソケットが過多 — コネクションリークの可能性"
prometheus.ymlでこのファイルを参照する:
rule_files:
- "/etc/prometheus/network_alerts.yml"
実運用から得たTips
ネットワークパネルから仮想インターフェースを除外する
Dockerとsystemd-networkdはveth、br-、docker0といったインターフェースを大量に作る。これらがダッシュボードを散らかしてしまう。クエリの正規表現で除外しよう:
rate(node_network_receive_bytes_total{device!~"lo|veth.*|br-.*|docker.*"}[5m])
ネットワークドロップとシステム負荷を相関させる
パケットドロップが急増したとき、最初の疑問は「NICの問題か、CPUの問題か?」だ。パケットドロップと同時にsoftirqのCPU時間が高い場合は、割り込みハンドラが追いついていないことを意味する。その場合はNICの割り込みアフィニティを確認するか、RSS(Receive Side Scaling)を有効にすることを検討しよう:
# CPUごとの割り込み分散を確認する
cat /proc/interrupts | grep eth0
# 割り込みアフィニティを設定する(例:eth0のIRQをコア0〜3に分散)
echo 0f > /proc/irq/$(grep eth0 /proc/interrupts | awk '{print $1}' | tr -d ':')/smp_affinity
Conntrackテーブルの枯渇に注意する
負荷の高いサーバーでは、netfilterのコネクション追跡テーブルが満杯になり、新しいコネクションが静かにドロップされる。これは私が経験した中で最も厄介な障害モードの一つだ。明確なエラーが出ず、リクエストがなぜか失敗するだけ。使用率を監視しておこう:
# 80%超でアラート
node_nf_conntrack_entries / node_nf_conntrack_entries_limit
常時70%を超えている場合は上限を引き上げよう:
sysctl -w net.netfilter.nf_conntrack_max=262144
echo 'net.netfilter.nf_conntrack_max = 262144' >> /etc/sysctl.d/99-conntrack.conf
重いクエリにはRecording Rulesを使う
ネットワークメトリクスは多くのホストにまたがる多数のインターフェースがあるため、カーディナリティが高くなりやすい。コストの高いクエリをRecording Rulesとして事前計算しておくことで、Grafanaのダッシュボードをレスポンシブに保てる:
groups:
- name: network_recording
interval: 30s
rules:
- record: instance:node_network_transmit_bytes:rate5m
expr: sum by (instance) (rate(node_network_transmit_bytes_total{device!~"lo|veth.*"}[5m]))
数ヶ月運用してみてわかったこと
このスタックを数ヶ月運用すると、インフラへの考え方が変わる。インシデントへの対応が速くなるだけでなく、真の意味でのキャパシティプランニングができるようになる。どのサーバーが1週間後にNIC飽和に近づいているか把握でき、デプロイ後に異常なTCP再送パターンを示すサービスを発見でき、カーネルアップデートがネットワーク動作に影響を与えたかどうかも追跡できる。
node_exporterが幅広さをカバーし、PromQLが必要な次元を自由にスライスする柔軟性を与え、Grafanaが視覚的にまとめてくれる。このスタック全体が2vCPU/4GBの単一VMで動き、20台以上のノードからのスクレイプを余裕でこなす。初期セットアップコストに十分見合う投資だ。

