【実務・中級編】 pingにおけるパケットロス発生の原因分析とエッジケース – トラブルシューティング&ネットワーク運用監視実践ガイド

夜間対応のピリついた空気、突然鳴り響くアラート音、そしてダッシュボードに浮かび上がる「パケットロス発生」の赤い文字。
インフラエンジニアやWebアプリケーションのSREであれば、誰もが一度はこの心臓が冷えるような瞬間を経験していることだろう。

「おい、フロントのAPIサーバーからバックエンドのデータベース層への疎通が時々途切れるぞ。pingを打ってみろ」
現場で障害が起きたとき、私たちが真っ先に手に取るのはいつだってこの古き良き ping だ。しかし、百戦錬磨のエンジニアであれば誰もが知っているはずだ。「pingがロスしている=物理回線が断線している」とは限らないという冷徹な事実を。

今回は、実務の現場で私たちの裏をかき、幾度となく迷宮へいざなってきた「pingパケットロス」の深層、そしてその裏に潜むルーターのCPU高負荷とQoS(Quality of Service)の罠について、徹底的に解き明かしていこう。

—

1. RFCが定義するICMPの正体と、現場での「誤解」

そもそも ping が利用するICMP(Internet Control Message Protocol、RFC 792)は、IP通信の「制御およびエラー通知」を行うためのプロトコルだ。データ転送を主目的とするTCPやUDPとは異なり、ネットワーク機器の健康状態を診断するための「いわば健康診断の問診票」のような位置づけにある。

ここで、多くのエンジニアが陥りがちな最初の罠がある。
「pingで10%のロスがあるから、Web APIの通信も10%失敗しているはずだ」――これは大いなる誤解である。

なぜなら、多くのネットワーク機器(ルーターやレイヤー3スイッチ)において、コントロールプレーン(CPU処理)とデータプレーン(ASIC/NPによるハードウェア転送)は厳格に分離されているからだ。ユーザーのWeb APIリクエスト(TCP 443など)はデータプレーンで超高速に処理される一方、宛先がルーター自身であるICMPパケットは、コントロールプレーンのCPUへとわざわざ引き渡されて処理される。

つまり、「データプレーンは健全でWeb APIは正常に流れているが、コントロールプレーンが手一杯でICMPだけが捨てられている」というシチュエーションが、実務の現場では日常茶飯事に起きるのだ。

—

2. なぜロスるのか?:ルーターのCPU高負荷とレートリミッティング

では、なぜコントロールプレーンはICMPを捨てるのか。その最大の理由は、ネットワーク機器の自己防衛機能にある。

大規模データセンターやクラウドのエッジルーターには、DDoS攻撃や不正なスキャンから身を守るため、コントロールプレーンへのトラフィック流入量を制限する CoPP(Control Plane Policing) や ICMP Rate Limiting といった機能がデフォルトで有効化されている。

シーケンスで見るICMP処理の舞台裏

通常の ping(ICMP Echo Request)がルーターに到達した際、内部では以下のような挙動が行われている。

[クライアント (ping)]                   [ターゲット・ルーター]
       |                                       |
       | --- (1) ICMP Echo Request ----------->| (データプレーン着信)
       |                                       | (CPUへ転送: コントロールプレーン)
       |                                       |
       |                                       | [CPU高負荷 or レートリミット発動]
       |                                       |   --> 一定数を超えるICMPを「破棄 (Drop)」
       |                                       |
       | <--- (2) ICMP Echo Reply (※運良く残った場合) ---|
       |     (またはタイムアウト)               |

もし、ルーターのCPU使用率が何らかの原因(ルーティングプロセスの暴走、SNMPポーリングの集中、あるいは大量のログ出力など)で100%に張り付いていた場合、ルーターはルーティングの維持という「本業」を優先するため、低優先度に設定されたICMPパケットを容赦なくバッファからあふれさせ(Tail Drop)、破棄する。これが、パケットロスの正体だ。

—

3. QoSポリシーが仕掛ける「見せかけのロス」

CPU高負荷だけでなく、ネットワーク機器に設定された QoS(QoS/CoS/DiffServ) もまた、pingのロスを演出する巧妙な演出者となる。

限られた帯域幅を効率的に使うため、ネットワークの要所ではトラフィックの優先度制御が行われる。例えば、VoIPや基幹システムのトランザクションデータを最優先(Expedited Forwardingなど)とし、監視用の ping や traceroute のようなICMPトラフィックは「ベストエ esfuerzo(Best-Effort)」、あるいはそれ以下の最低優先度に分類されることが多い。

ネットワークが一時的に混雑した際、ルーターのキューイングアルゴリズム(WRED: Weighted Random Early Detectionなど)は、高優先度パケットを守るために、低優先度のICMPパケットを意図的に間引き始める。

実務で使えるQoS確認・設定サンプル(Cisco IOS-XEの例)

現場の機器でどのようなQoSポリシーが適用されているかを確認、あるいはデバッグする際の設定・確認スニペットを共有しよう。

! 現在適用されているポリシーマップの状態を確認し、ドロップカウンタを見る
show policy-map interface GigabitEthernet0/0/1

! [出力結果の読み方]
!   Matched: 1054232 packets  -> 処理された総パケット数
!   Dropped: 12453 packets    -> キュー溢れやWREDにより破棄されたパケット数
!   もしDroppedの中にICMPが含まれている場合、それはQoSによる意図的な間引きである。

もし、アプリケーションのメトリクス(レイテンシやエラーレート)は完全に健康であるにもかかわらず、ping だけが数パーセントロスしている場合、私たちは「あぁ、あそこのコアスイッチのQoSポリシーがICMPを間引いているな」と冷静に判断し、パニックを防ぐことができるのだ。

—

4. エッジケースを見極めるためのデバッグ手法と検証コード

「では、本当にネットワークが壊れているのか、それとも単なるICMPの優先度低下・レートリミットなのか?」
これを切り分けるために、現場のシニアエンジニアたちが使っている実践的なアプローチをいくつか紹介しよう。

① プロトコルを変えて疎通確認を行う(hping3 や tcpping)

ICMPがダメなら、TCPやUDPのパケットを使って同様の検証を行う。例えば、Web APIサーバーであれば、HTTP/HTTPSのポート(ポート80や443)に対してSYNパケットを送り、ハンドシェイクの応答速度やロス率を計測する。

Python環境があれば、以下のスクリプトを使ってTCPレイヤーでの疎通と応答時間を正確に測ることができる。

import socket
import time
import sys

def tcp_ping(host, port, count=5, timeout=2.0):
    """
    ICMPではなくTCPコネクション(SYN)ベースで疎通とレイテンシを測定するスクリプト
    ルーターのICMPレートリミットに惑わされない実用的な診断ツール
    """
    print(f"=== TCP Ping to {host}:{port} ===")
    success = 0
    
    for i in range(count):
        start_time = time.time()
        try:
            # ソケットを作成して接続を試みる
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            s.settimeout(timeout)
            s.connect((host, port))
            
            end_time = time.time()
            latency = (end_time - start_time) * 1000.0
            print(f"Seq {i+1}: Connected to {host} time={latency:.2f} ms")
            success += 1
            s.close()
        except socket.timeout:
            print(f"Seq {i+1}: Request timed out (TCP Handshake failed)")
        except socket.error as e:
            print(f"Seq {i+1}: Connection error: {e}")
        
        time.sleep(1.0)
        
    print(f"\n--- Statistics for {host}:{port} ---")
    loss_rate = ((count - success) / count) * 100
    print(f"{count} packets transmitted, {success} received, {loss_rate:.1f}% packet loss\n")

if __name__ == "__main__":
    # 使用例: ターゲットのホストとポートを指定
    target_host = "api.internal.example.com"
    target_port = 443
    tcp_ping(target_host, target_port)

このスクリプトを実行し、ping ではロスが発生しているのに tcp_ping ではロスが 0% であれば、その障害の正体は「データプレーンの異常」ではなく「コントロールプレーンの負荷、またはICMP固有の制限」だと即座に断定できる。

② ペイロードサイズやDFフラグを変えてみる

もし経路上のどこかでMTU(Maximum Transmission Unit)のミスマッチが起きている場合、フラグメンテーションが必要なパケットだけがロスすることがある。

Linux環境であれば、以下のコマンドでパケットサイズを指定しつつ、ルーターでの断片化を禁止する(DFフラグの付与)テストを行う。

# サイズ1400バイト、断片化禁止でpingを実行
ping -M do -s 1372 192.168.1.1

もしこれで Frag needed and DF set というエラーが返ってきたら、それはICMPのレートリミットではなく、経路上の PMTUD(Path MTU Discovery) の失敗、あるいはルーターの設定不備によるパケット破棄である。

—

5. シニアエンジニアからの教訓

ネットワークの世界は、目に見えないパケットのドラマで満ちている。
画面に表示された「Packet Loss: 5%」という数字にただ怯えるのではなく、「このパケットは今、ルーターのどのレイーンを通り、どのような優先度で扱われているのか」を脳内でビジュアライズできるようになること。それが、真の意味でインフラを掌握するということだ。

障害対応の現場で次に ping のロスに直面したときは、ぜひ今回の話を思い出してほしい。慌てて回線事業者へ連絡を入れる前に、まずはそのロスの裏側に隠された「ルーターの優しさ(レートリミット)」や「QoSの厳格なルール」を見極められる、クールなエンジニアであってほしい。

コメント

タイトルとURLをコピーしました