SNMPによる監視とSyslog
同じインターフェースの累積値から速度を計算し、4秒の切断をSyslogで確認します。最後にカウンターの折返しと再起動を扱い、速度計算に使える区間を選びます。
説明
SNMPの定期取得では、装置の状態や通信量の推移を調べます。Syslogでは、切断などのイベントを発生時刻とともに収集します。取得の間に短い障害が起きると、定期取得だけでは気付かない場合があります。
同じインターフェースの累積値から速度を計算し、4秒の切断をSyslogで確認します。最後にカウンターの折返しと再起動を扱い、速度計算に使える区間を選びます。
1OIDでインターフェースを指定
SNMPはOIDで値を指定して取得します。ここではSNMPv3の認証・暗号鍵を設定済みのmonitorを使います。ifIndexが7のインターフェースを10:00:00に取得します。
snmpget -v3 -l authPriv -u monitorSNMPv3で保護した取得 edge1対象装置 IF-MIB::ifName.7インターフェース名 IF-MIB::ifOperStatus.7動作状態 IF-MIB::ifHCInOctets.764ビットの受信累積値末尾の7は表の行を選ぶインデックスです。ifHCInOctetsは受信したオクテットの累積値で、この値だけでは毎秒の通信量は分かりません。authPrivは認証と暗号化を要求します。
260秒後の同じOID
同じインターフェースを10:01:00に再取得します。
snmpget -v3 -l authPriv -u monitor同じ条件で再取得 edge1同じ装置 IF-MIB::ifName.7 IF-MIB::ifOperStatus.7 IF-MIB::ifHCInOctets.7同じ行の名前・状態・累積値受信累積値が750,000,000オクテット増えました。状態は両時点ともupですが、その間ずっとupだったかはこの2回の取得だけでは分かりません。
3累積値から平均速度を求める
polls.jsonは時刻と稼働時間、同じOIDの値をまとめた取得記録です。最初の2回の差を秒数で割ります。
python3 -c取得記録から区間平均を計算 'import json; p=json.load(open("polls.json")); a,b=p[:2]; dt=(b["uptime"]-a["uptime"])/100; delta=b["in_octets"]-a["in_octets"]; print(dt,delta,delta*8/dt/1e6)'稼働時間の差・受信量の差・Mbit/ssysUpTimeの単位は1/100秒なので、6000の差は60秒です。750,000,000×8÷60で100 Mbit/sになります。これは区間平均であり、瞬間最大値やアプリケーションだけのデータ速度ではありません。
4定期取得の間に起きた切断
装置のSyslogから同じifIndexのイベントを抽出します。この試料では装置と収集側の時刻が同期しています。
grep 'ifIndex=7 '同じインターフェースを抽出 syslog.txt転送されたイベント記録10:00:20にDOWN、10:00:24にUPが記録されています。4秒の状態変化は毎分のupという取得結果と両立します。記録された時刻はイベントの時刻で、収集サーバーへの到着時刻とは別です。
5Syslogのヘッダーを分解
RFC 5424形式の先頭を分解します。<131>のPRIにはfacilityとseverityが入り、その次の1は形式のバージョンです。
python3 -cPRIとヘッダーを解析 'from pathlib import Path; line=Path("syslog.txt").read_text().splitlines()[0]; head,rest=line.split(">",1); pri=int(head[1:]); print(pri//8,pri%8); print(rest.split(" ",6))'8で割った商と余り・後続フィールド131=16×8+3で、facility16はlocal0、severity3はErrorです。続いて時刻、ホスト名、アプリ名、プロセスID、メッセージIDが並びます。-は構造化データなしを示し、その後が本文です。PRIだけでイベントの原因まで分かるわけではありません。
632ビットカウンターの折返し
古い32ビットのカウンターでは、増え続けても最大値の次が0になります。再起動もリセットもなく、折返しは1回以下と分かっている区間を計算します。
python3 -c折返し前後の差を計算 'before=4294000000; after=1000000; print(after-before); print((after-before)%(2**32))'単純差と32ビットの差単純な差は負ですが、2の32乗を法とした差は1,967,296オクテットです。ただし複数回の折返しやリセットがあると、この式だけでは増加量を復元できません。高速度のインターフェースでは64ビットのHCカウンターを使います。
7再起動をまたぐ区間
10:02:00の記録を含めて稼働時間とカウンターを表示します。
python3 -c時刻とリセット関連の値を一覧 'import json; p=json.load(open("polls.json")); print([(x["time"],x["uptime"],x["in_octets"],x["discontinuity"]) for x in p])'時刻・稼働時間・累積値・不連続時刻この試料では10:01:30に管理機能が再起動しています。sysUpTimeが小さくなり、カウンターも再開したため、10:01→10:02の単純差を速度に使えません。ifCounterDiscontinuityTimeはカウンターの不連続を確認する情報で、値0は管理機能の再初期化以降に不連続がないことを表します。
8速度を出せる区間だけを計算
この試料では稼働時間が増加し、不連続時刻も変わっていない区間を採用します。再起動をまたぐ区間は次の取得を待ちます。
python3 -cリセットをまたぐ区間を除外 'import json; p=json.load(open("polls.json")); print([(a["time"],b["time"],round((b["in_octets"]-a["in_octets"])*800/(b["uptime"]-a["uptime"])/1e6,2) if b["uptime"]>a["uptime"] and b["discontinuity"]==a["discontinuity"] else None) for a,b in zip(p,p[1:])])'連続した取得の条件と平均速度有効な区間だけ100 Mbit/sと表示できました。実装時にはインターフェースの同一性、時刻の逆行、カウンターの折返しも扱います。定期取得は量の推移、Syslogは発生イベントを補います。どちらか一方の欠落を、もう一方が常に埋められるわけではありません。
まとめ
累積カウンターは時刻差と値の差から速度へ換算します。
イベント時刻、定期取得、リセット情報を照合し、未観測の時間を正常と決め付けません。
操作履歴と解説を振り返れます。
シェル / 通信とファイル
snmpget -v3 -l authPriv -u monitor edge1 IF-MIB::ifName.7 IF-MIB::ifOperStatus.7 IF-MIB::ifHCInOctets.7IF-MIB::ifName.7 = STRING: eth0 IF-MIB::ifOperStatus.7 = INTEGER: up(1) IF-MIB::ifHCInOctets.7 = Counter64: 1000000000
snmpget -v3 -l authPriv -u monitor edge1 IF-MIB::ifName.7 IF-MIB::ifOperStatus.7 IF-MIB::ifHCInOctets.7IF-MIB::ifName.7 = STRING: eth0 IF-MIB::ifOperStatus.7 = INTEGER: up(1) IF-MIB::ifHCInOctets.7 = Counter64: 1750000000
python3 -c 'import json; p=json.load(open("polls.json")); a,b=p[:2]; dt=(b["uptime"]-a["uptime"])/100; delta=b["in_octets"]-a["in_octets"]; print(dt,delta,delta*8/dt/1e6)'60.0 750000000 100.0
grep 'ifIndex=7 ' syslog.txt<131>1 2026-11-09T10:00:20+09:00 edge1 ifmon 42 LINK_DOWN - ifIndex=7 ifName=eth0 <133>1 2026-11-09T10:00:24+09:00 edge1 ifmon 42 LINK_UP - ifIndex=7 ifName=eth0
python3 -c 'from pathlib import Path; line=Path("syslog.txt").read_text().splitlines()[0]; head,rest=line.split(">",1); pri=int(head[1:]); print(pri//8,pri%8); print(rest.split(" ",6))'16 3 ['1', '2026-11-09T10:00:20+09:00', 'edge1', 'ifmon', '42', 'LINK_DOWN', '- ifIndex=7 ifName=eth0']
python3 -c 'before=4294000000; after=1000000; print(after-before); print((after-before)%(2**32))'-4293000000 1967296
python3 -c 'import json; p=json.load(open("polls.json")); print([(x["time"],x["uptime"],x["in_octets"],x["discontinuity"]) for x in p])'[('10:00:00', 360000, 1000000000, 0), ('10:01:00', 366000, 1750000000, 0), ('10:02:00', 3000, 2000000, 0)]python3 -c 'import json; p=json.load(open("polls.json")); print([(a["time"],b["time"],round((b["in_octets"]-a["in_octets"])*800/(b["uptime"]-a["uptime"])/1e6,2) if b["uptime"]>a["uptime"] and b["discontinuity"]==a["discontinuity"] else None) for a,b in zip(p,p[1:])])'[('10:00:00', '10:01:00', 100.0), ('10:01:00', '10:02:00', None)]演習完了