PrometheusとGrafanaでLinuxネットワークパフォーマンスを監視する:完全ガイド

Networking tutorial - IT technology blog
Networking tutorial - IT technology blog

深夜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はvethbr-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台以上のノードからのスクレイプを余裕でこなす。初期セットアップコストに十分見合う投資だ。

Share: