STUDY · NETWORK GUIDE

5-6 Queuing 基礎 — FIFO・WFQ・CBWFQ・LLQ で輻輳をさばく

輻輳時にどのパケットを先に送るかを決めるキューイングを csr1000v (IOS-XE 17.03.08a) の実機で扱う。csr1000v の IF は 900k 送っても輻輳しないため、親 shaper (shape average 500k) で egress を絞って輻輳点を人為的に作り、その配下に子 CBWFQ / LLQ をぶら下げる階層型ポリシーを組む。同じ輻輳に FIFO (無差別) / WFQ (flow 公平) / CBWFQ (クラス別に帯域保証) / LLQ (priority で低遅延) を当て、受信側 PC2 の実測レート・loss・jitter の差で 4 機構を対比する。CBWFQ は帯域を守り、LLQ は遅延を守るが priority は無制限ではないことを実機の数値で示す第 5 章 QoS 編の実機節。

1. 前節の振り返りと本節の内容

前節 5-5 Policing と Shaping では、契約帯域を超えたトラフィックを policing で drop するか shaping で buffer に待たせるかを csr1000v の実機で扱いました。police も shape も同じトークンバケツ (token bucket) の上に立つメーターで、違いは溢れの行き先 (捨てるか待たせるか) だけであることを、受信側 PC2 の実測レートと show カウンタで確かめました。

5-5 の police / shape が扱ったのは、単一クラスのレートを天井で縛る 計量 (メーター) でした。1 本のトラフィックが契約帯域をどれだけ超えたかを測り、超過分をその場で処理します。ここには「複数のトラフィックのうちどれを先に送るか」という順序の概念はありません。

本節はここから軸を変え、輻輳 (送りたい量 > 送れる量) が起きたとき、複数クラスの間でどのパケットを先に送り、どれを待たせるか を扱います。この送出順を決める仕組みをキューイング (queuing)、あるいはスケジューラ (scheduler) と呼びます。5-5 のメーターが「レートを縛る」レイヤだったのに対し、本節のスケジューラは「並び順を決める」レイヤで、両者は QoS の別の層に属します。実運用では両者を組み合わせ、shaping で絞った帯域の内側で、クラスごとに送出順をつけます。5-3 から 5-4 でつけたマーキングの印は、ここで初めて意味を持ちます。印ごとに別のキューへ振り分け、キューに優先順位をつけるのがキューイングだからです。

本節では、代表的な 4 つのキューイング機構を csr1000v の同一の輻輳に当てて対比します。来た順に送る FIFO (First-In First-Out)、flow ごとに公平に配分する WFQ (Weighted Fair Queuing)、クラスごとに帯域を保証する CBWFQ (Class-Based Weighted Fair Queuing)、優先クラスを低遅延で送る LLQ (Low Latency Queuing) の 4 つです。それぞれの機構の挙動の違いは、受信側 PC2 の実測レート・パケットロス・jitter (遅延のばらつき) の差に現れます。

2. キューイングは輻輳時だけ働く — 親 shaper で輻輳を作る

キューイングを実機で観察するうえで、最初に押さえておかなければならない前提があります。キューイングは輻輳が起きているときにしか働きません。輻輳とは、あるインタフェースから送り出したいパケットの量が、そのインタフェースの送り出せる速さを超えている状態です。送りたい分をそのまま送り出せる間は、パケットはキューに溜まらず、スケジューラが並べ替える対象そのものがありません。

この csr1000v のインタフェースは 1 Gbps 相当の帯域を持ちます。本節で送る 2 クラス合計 900 kbps 程度のトラフィックでは、インタフェースは輻輳せず、すべて素通りします。この状態でキューイングポリシーを当てても、キューが埋まらないためスケジューラは働かず、「ポリシーを当てたのに効かない」という結果になります。

そこで、egress (出口) に人為的なボトルネックを作ります。5-5 で使った shaping を「親」のポリシーとして出口に当て、送り出せる速さを送出量より低い値に絞ります。絞ったレートを超えたパケットは親 shaper の出力キューに溜まり、そこで初めて輻輳が発生します。その輻輳した親 shaper の配下に、子のキューイングポリシー (CBWFQ や LLQ) を service-policy でぶら下げ、溜まったパケットをクラス別に並べ替えます。この親子の入れ子構造を 階層型ポリシー (hierarchical policy) と呼びます。csr1000v の IOS-XE 17.03 では、この階層型ポリシーが受理されて動作します。

トポロジと輻輳点の関係は以下のとおりです。PC1 が 2 クラス (VOICE と DATA) を並行して送出し、R1 (csr1000v) の出口 Gi3 に親 shaper 500 kbps を置いてボトルネックを作り、その配下でスケジューラがクラスを並べ替えます。効果は受信側 PC2 が 2 クラスを別々に受けた実測レートで測ります。

検証ラボ。PC1 が VOICE(EF) と DATA(AF11) を並行送出し、R1(csr1000v) の出口 Gi3 に親 shaper 500k を置いて輻輳点を作る。その配下に子 CBWFQ/LLQ をぶら下げる階層型ポリシーで、輻輳時の送出順を決める。効果は PC2 の 2 クラス別受信レートで実測する。

本節の検証は、5-5 と同じ csr1000v の L3 2 セグメント構成をそのまま流用します。まず使用した機器と IOS-XE を確認します。

snippet
R1# show version | include Cisco IOS-XE Software|CSR|Version
Cisco IOS XE Software, Version 17.03.08a
Cisco IOS Software [Amsterdam], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Version 17.3.8a, RELEASE SOFTWARE (fc3)
cisco CSR1000V (VXE) processor (revision VXE) with 1104920K/3075K bytes of memory.

機種は csr1000v (仮想の CSR 1000V ルータ)、ソフトウェアは IOS-XE 17.03.08a です。この csr1000v はデモ用にスループットの上限が 1000 kb/s に絞られており、送り出せる天井が 1 Mbps であることが、900 kbps 程度の送出で素通りする理由になります。

2 クラスのトラフィックは、5-4 で作った class-map をそのまま流用して分類します。VOICE は DSCP EF、DATA は DSCP AF11 でマークして送出します。

snippet
R1# show class-map
 Class Map match-any class-default (id 0)
   Match any

 Class Map match-any CM-DATA (id 2)
   Match   dscp af11 (10)

 Class Map match-any CM-VOICE (id 1)
   Match   dscp ef (46)

CM-VOICEmatch dscp ef (EF = 46)、CM-DATAmatch dscp af11 (AF11 = 10) です。送出側 PC1 は、この DSCP に対応する ToS バイトでマークして 2 クラスを並行送出します。DSCP 値を 4 倍 (2 bit 左シフト) したものが、ECN が 0 のときの ToS バイトになるため、EF (46) は ToS 0xb8、AF11 (10) は ToS 0x28 です。

まず親 shaper を当てない状態で、VOICE 400 kbps と DATA 500 kbps を並行送出したときの PC2 の受信レートが baseline です。輻輳が無い状態を先に押さえておきます。

snippet
## VOICE (dscp ef, port 5001) 受信側 PC2 iperf サーバレポート
[  1] 0.00-8.06 sec   396 KBytes   403 Kbits/sec   0.082 ms 0/276 (0%)
## DATA (dscp af11, port 5002) 受信側 PC2 iperf サーバレポート
[  1] 0.00-8.04 sec   494 KBytes   503 Kbits/sec   0.082 ms 0/344 (0%)

親 shaper が無いため、VOICE は 403 Kbits/sec、DATA は 503 Kbits/sec で、送出したレートがそのまま受信されています。パケットロスは両方とも 0%、jitter は 0.082 ms です。合計 906 kbps は天井 1 Mbps の下に収まるため輻輳せず、キューは働きません。

次に、出口 Gi3 に親 shaper 500 kbps を置いて、同じ 906 kbps を送ります。送出合計が絞ったレートを超えるため、ここで輻輳が発生するはずです。

snippet
## VOICE (dscp ef, port 5001) 受信側 PC2 iperf サーバレポート
[  1] 0.00-9.60 sec   276 KBytes   235 Kbits/sec  12.587 ms 84/276 (30%)
## DATA (dscp af11, port 5002) 受信側 PC2 iperf サーバレポート
[  1] 0.00-9.58 sec   300 KBytes   257 Kbits/sec  14.867 ms 135/344 (39%)

VOICE は 403 kbps から 235 Kbits/sec へ、DATA は 503 kbps から 257 Kbits/sec へ落ち、パケットロスが VOICE 30%・DATA 39% に達しました。jitter も baseline の 0.082 ms から 12〜14 ms へ跳ね上がっています。輻輳が実際に起きていることを、親 shaper のカウンタで確認します。

snippet
R1# show policy-map interface GigabitEthernet3
 GigabitEthernet3

  Service-policy output: PM-PARENT-FIFO

    Class-map: class-default (match-any)
      720 packets, 1081540 bytes
      5 minute offered rate 0000 bps, drop rate 0000 bps
      Match: any
      Queueing
      queue limit 64 packets
      (queue depth/total drops/no-buffer drops) 64/296/0
      (pkts output/bytes output) 424/636830
      shape (average) cir 500000, bc 2000, be 2000
      target shape rate 500000

(queue depth/total drops/no-buffer drops) 64/296/0 の行が、輻輳が起きた証拠です。queue depth が 64 で、これは queue limit 64 packets のキューが満杯になっていることを示します。total drops が 296 で、キューに入りきらなかった 296 パケットが落ちています。親 shaper でボトルネックを作ったことで輻輳が成立し、これ以降の 4 機構が並べ替える対象 (溜まったキュー) が用意できました。

3. FIFO — 既定の無差別

まず最も基本的な FIFO (First-In First-Out) を見ます。FIFO は 1 つのキューにすべてのパケットを来た順に入れ、来た順に送り出す、クラスを区別しない既定の動作です。親 shaper に子ポリシーをぶら下げず、class class-defaultshape average 500000 を置くだけで、その class-default が単一の FIFO キューになります。

snippet
policy-map PM-PARENT-FIFO
 class class-default
  shape average 500000
!
interface GigabitEthernet3
 service-policy output PM-PARENT-FIFO

この状態で VOICE 400 kbps と DATA 500 kbps を並行送出し、PC2 の受信レートを見ます。

snippet
## VOICE (dscp ef, port 5001) 受信側 PC2 iperf サーバレポート
[  1] 0.00-11.66 sec   336 KBytes   236 Kbits/sec  12.075 ms 115/349 (33%)
## DATA (dscp af11, port 5002) 受信側 PC2 iperf サーバレポート
[  1] 0.00-11.59 sec   359 KBytes   254 Kbits/sec  15.462 ms 179/429 (42%)

VOICE は 236 Kbits/sec・loss 33%・jitter 12 ms、DATA は 254 Kbits/sec・loss 42%・jitter 15 ms です。VOICE と DATA が ほぼ同じ程度に劣化 しています。FIFO は EF も AF11 も区別せず、来た順に 1 つのキューへ入れるため、輻輳したときにどちらのクラスも同じように待たされ、同じように落ちます。カウンタでも、キューは class-default の 1 つだけです。

snippet
R1# show policy-map interface GigabitEthernet3
 GigabitEthernet3

  Service-policy output: PM-PARENT-FIFO

    Class-map: class-default (match-any)
      681 packets, 1026820 bytes
      5 minute offered rate 28000 bps, drop rate 10000 bps
      Match: any
      Queueing
      queue limit 64 packets
      (queue depth/total drops/no-buffer drops) 64/248/0
      (pkts output/bytes output) 433/654696
      shape (average) cir 500000, bc 2000, be 2000
      target shape rate 500000

class-map は class-default の 1 つだけで、その 1 つのキューに total drops 248 が集中しています。VOICE と DATA のパケットはこの単一キューを共有するため、クラス別の統計は現れません。VOICE の jitter は 12 ms に達しています。音声のような遅延に敏感なトラフィックであっても、FIFO は他のトラフィックの後ろで平等に待たせるため、遅延に差はつきません。この無差別の動作が起点となり、以降の機構ではクラスの重要度が帯域や優先度としてどう表現されるかが変わります。

4. WFQ — flow ごとに公平

次に WFQ (Weighted Fair Queuing) を見ます。WFQ は、FIFO の単一キューを flow ごとに自動で分けます。flow とは、送信元・宛先 IP アドレスと送信元・宛先ポート番号などの組でハッシュされるトラフィックの区切りで、通信の 1 本ぶんに相当します。WFQ は手動でクラスを定義しなくても flow を自動で識別し、各 flow へ帯域を公平に配分します。親 shaper の class-default に fair-queue を足すだけで有効になります。

snippet
policy-map PM-PARENT-WFQ
 class class-default
  shape average 500000
   fair-queue

同じ 2 クラスを並行送出したときの受信レートを見ます。VOICE と DATA は送信元ポートが異なるため、別々の flow として扱われます。

snippet
## VOICE (dscp ef, port 5001) 受信側 PC2 iperf サーバレポート
[  1] 0.00-10.49 sec   300 KBytes   234 Kbits/sec  12.065 ms 140/349 (40%)
## DATA (dscp af11, port 5002) 受信側 PC2 iperf サーバレポート
[  1] 0.00-10.42 sec   326 KBytes   256 Kbits/sec   4.121 ms 202/429 (47%)

VOICE は 234 Kbits/sec、DATA は 256 Kbits/sec で、2 つの flow がほぼ均等に配分されています。受信レートだけを見ると FIFO と近い値ですが、管理する単位が flow に変わっています。カウンタを見ると、flow 単位でパケットを落とす flowdrops が現れます。

snippet
R1# show policy-map interface GigabitEthernet3
 GigabitEthernet3

  Service-policy output: PM-PARENT-WFQ

    Class-map: class-default (match-any)
      539 packets, 814968 bytes
      5 minute offered rate 22000 bps, drop rate 10000 bps
      Match: any
      Queueing
      queue limit 64 packets
      (queue depth/total drops/no-buffer drops/flowdrops) 16/230/0/230
      (pkts output/bytes output) 309/467208
      shape (average) cir 500000, bc 2000, be 2000
      target shape rate 500000
      Fair-queue: per-flow queue limit 16 packets

FIFO のカウンタには無かった flowdrops の欄が加わり、230 パケットが flow 単位で落ちています。Fair-queue: per-flow queue limit 16 packets の行が、flow ごとに 16 パケットぶんのキューを割り当てていることを示します。WFQ は flow を単位に帯域を配分する仕組みで、IP Precedence の高い flow にはより多くの帯域を割り当てる重み付けを持ちます。ただし、これは flow ごとの自動的な配分であり、「VOICE クラスに最低 300 kbps を保証する」といった管理者が定義するクラス別の帯域保証はできません。ここまでの FIFO (無差別) と WFQ (flow 単位の配分) は、いずれも管理者が定義したクラスの重要度を明示的に保証する仕組みを持ちません。次の CBWFQ と LLQ で、クラスの重要度を帯域と遅延の 2 つの側面から表現します。

5. CBWFQ — クラス別に bandwidth を保証する

CBWFQ (Class-Based Weighted Fair Queuing) は、class-map で定義したクラスごとに bandwidth (最低保証帯域) を割り当てます。輻輳したときも、各クラスに保証した帯域が回されるため、重要なクラスへ多く帯域を配分できます。子ポリシー PM-CHILD-CBWFQ で VOICE に 300 kbps、DATA に 150 kbps を割り当て、それを親 shaper の class-default 配下に service-policy でぶら下げる階層型で組みます。

snippet
policy-map PM-CHILD-CBWFQ
 class CM-VOICE
  bandwidth 300
 class CM-DATA
  bandwidth 150
 class class-default
  fair-queue
snippet
policy-map PM-PARENT-CBWFQ
 class class-default
  shape average 500000
   service-policy PM-CHILD-CBWFQ

bandwidth 300bandwidth 150 が、それぞれのクラスに保証する最低帯域 (minimum rate) です。親 shaper 500 kbps に対し、保証の合計は 300 + 150 = 450 kbps で、残る約 50 kbps は余剰帯域として扱われます。bandwidth は「そのクラスに最低これだけは回す」という下限の指定であり、500 kbps 全体を常に 300:150 で厳密に分割するわけではありません。余剰帯域は既定の重みで配分されます。この状態で同じ 2 クラスを並行送出したときの受信レートを確認します。

snippet
## VOICE (dscp ef, port 5001) 受信側 PC2 iperf サーバレポート
[  1] 0.00-12.43 sec   482 KBytes   318 Kbits/sec  12.240 ms 8/344 (2.3%)
## DATA (dscp af11, port 5002) 受信側 PC2 iperf サーバレポート
[  1] 0.00-14.50 sec   304 KBytes   172 Kbits/sec  27.676 ms 219/431 (51%)

VOICE は 318 Kbits/sec・loss 2.3%、DATA は 172 Kbits/sec・loss 51% です。受信レートの比 318:172 は約 1.85:1 で、割り当てた bandwidth の比 300:150 = 2:1 に近い値 になっています。VOICE の受信 318 kbps は保証帯域 300 kbps を上回り、余剰帯域も回っています。FIFO や WFQ では両クラスが同じ程度に劣化したのに対し、CBWFQ では VOICE に最低保証帯域が確保され、loss が 2.3% まで抑えられました。しわ寄せは保証帯域の小さい DATA に集まり、DATA の loss は 51% です。クラスの重要度を、最低保証帯域という形で表現できています。この配分を、階層表示のカウンタで確認します。

snippet
R1# show policy-map interface GigabitEthernet3
 GigabitEthernet3

  Service-policy output: PM-PARENT-CBWFQ

    Class-map: class-default (match-any)
      940 packets, 1421280 bytes
      5 minute offered rate 3000 bps, drop rate 0000 bps
      Match: any
      Queueing
      queue limit 64 packets
      (queue depth/total drops/no-buffer drops) 0/358/0
      (pkts output/bytes output) 582/879984
      shape (average) cir 500000, bc 2000, be 2000
      target shape rate 500000

      Service-policy : PM-CHILD-CBWFQ

        Class-map: CM-VOICE (match-any)
          427 packets, 645624 bytes
          5 minute offered rate 2000 bps, drop rate 0000 bps
          Match:  dscp ef (46)
          Queueing
          queue limit 64 packets
          (queue depth/total drops/no-buffer drops) 64/68/0
          (pkts output/bytes output) 359/542808
          bandwidth 300 kbps

        Class-map: CM-DATA (match-any)
          513 packets, 775656 bytes
          5 minute offered rate 2000 bps, drop rate 0000 bps
          Match:  dscp af11 (10)
          Queueing
          queue limit 64 packets
          (queue depth/total drops/no-buffer drops) 63/290/0
          (pkts output/bytes output) 223/337176
          bandwidth 150 kbps

        Class-map: class-default (match-any)
          0 packets, 0 bytes
          5 minute offered rate 0000 bps, drop rate 0000 bps
          Match: any
          Queueing
          queue limit 64 packets
          (queue depth/total drops/no-buffer drops/flowdrops) 0/0/0/0
          (pkts output/bytes output) 0/0
          Fair-queue: per-flow queue limit 16 packets

親 shaper (PM-PARENT-CBWFQ) の配下に、子ポリシー (PM-CHILD-CBWFQ) が入れ子で表示されています。これが階層型ポリシーの姿です。子の中で CM-VOICEbandwidth 300 kbps・total drops 68、CM-DATAbandwidth 150 kbps・total drops 290 です。VOICE の drops 68 に対し DATA の drops 290 と、保証帯域の少ない DATA のほうが 4 倍以上多く落ちています。bandwidth の割り当てが、クラス別の drop 数の差として現れました。輻輳時に「どのクラスに多く帯域を回すか」を制御できる点が、FIFO・WFQ との決定的な違いです。

このクラス別のスケジューリングを 1 枚にまとめると、下図のようになります。同じ輻輳を、FIFO (無差別) から WFQ (flow 公平)、CBWFQ (クラス別に帯域保証)、LLQ (priority で低遅延) へと、機構を切り替えて対比します。

同じ輻輳 (親 shaper 500k・VOICE+DATA 競合) を 4 機構でさばく。FIFO は 1 キューで無差別 (両方等劣化)、WFQ は flow ごとに公平、CBWFQ はクラス別に bandwidth を保証 (VOICE 318k / DATA 172k = 帯域比)、LLQ は VOICE を priority キューで追い越し即送出 (低遅延)。

CBWFQ は帯域を守りますが、jitter は VOICE で 12 ms のままで、FIFO と変わりません。帯域を多く配分することと、遅延を短く保つことは別の話だからです。遅延を守るには、次の LLQ が必要になります。

6. LLQ — priority で優先低遅延

LLQ (Low Latency Queuing) は、CBWFQ に厳格優先キュー (priority queue) を追加した構成です。音声のような遅延に敏感なクラスを priority キューに入れ、他のクラスに先んじて即座に送出します。子ポリシーで VOICE を priority 200、DATA を bandwidth 200 にし、CBWFQ と同じ階層型で組みます。

snippet
policy-map PM-CHILD-LLQ
 class CM-VOICE
  priority 200
 class CM-DATA
  bandwidth 200
 class class-default
  fair-queue
snippet
policy-map PM-PARENT-LLQ
 class class-default
  shape average 500000
   service-policy PM-CHILD-LLQ

priority 200 が、VOICE を厳格優先で 200 kbps まで即送出する指定です。bandwidth 200 の DATA は、VOICE を送り終えた後に送られます。この状態で VOICE 300 kbps と DATA 500 kbps を並行送出し、受信レートと jitter を見ます。

snippet
## VOICE (dscp ef, port 5001) 受信側 PC2 iperf サーバレポート
[  1] 0.00-10.07 sec   245 KBytes   200 Kbits/sec   0.076 ms 88/259 (34%)
## DATA (dscp af11, port 5002) 受信側 PC2 iperf サーバレポート
[  1] 0.00-11.61 sec   449 KBytes   317 Kbits/sec  15.305 ms 116/429 (27%)

VOICE の jitter は 0.076 ms で、FIFO や CBWFQ の 12 ms の約 160 分の 1 です。priority キューで他クラスを追い越して即送出するため、待ち時間がほとんど乗らず、遅延のばらつきが桁違いに小さくなっています。一方の DATA は jitter 15 ms で、VOICE を送り終えるまで待たされています。LLQ は遅延を守る 機構であり、その効果が jitter の 2 桁の差として実機に現れました。FIFO と LLQ で同じ VOICE の遅延がどう変わるかを、下図で対比します。

同じ VOICE を、FIFO は DATA と同じ 1 キューで待たせ (jitter 12ms)、LLQ は priority キューで DATA を追い越して即送出する (jitter 0.076ms)。LLQ は VOICE の遅延を守る。ただし priority 200k を超えた VOICE は輻輳時に drop され、設定レートは上限としても働く。

ここで注意すべきは、priority に設定したレートは上限としても働く という点です。VOICE の受信レートは 200 Kbits/sec で頭打ちになり、loss は 88/259 = 34% に達しています。300 kbps を送っているのに 200 kbps しか受信できていないのは、priority 200 の 200 kbps を超えたぶんが輻輳時に捨てられているためです。priority キューには暗黙の policing がかかっており、設定したレートを超えた分は、輻輳時に drop されます。priority は「他クラスより先に送る」優先度であって、設定レートを超えて無制限に流せるものではありません。この drop をカウンタで確認します。

snippet
R1# show policy-map interface GigabitEthernet3
 GigabitEthernet3

  Service-policy output: PM-PARENT-LLQ

    Class-map: class-default (match-any)
      846 packets, 1273672 bytes
      5 minute offered rate 10000 bps, drop rate 0000 bps
      Match: any
      Queueing
      queue limit 64 packets
      (queue depth/total drops/no-buffer drops) 0/294/0
      (pkts output/bytes output) 552/829144
      shape (average) cir 500000, bc 2000, be 2000
      target shape rate 500000

      Service-policy : PM-CHILD-LLQ

        queue stats for all priority classes:
          Queueing
          queue limit 512 packets
          (queue depth/total drops/no-buffer drops) 0/89/0
          (pkts output/bytes output) 171/258552

        Class-map: CM-VOICE (match-any)
          260 packets, 393120 bytes
          5 minute offered rate 4000 bps, drop rate 2000 bps
          Match:  dscp ef (46)
          Priority: 200 kbps, burst bytes 5000, b/w exceed drops: 89

        Class-map: CM-DATA (match-any)
          582 packets, 879984 bytes
          5 minute offered rate 7000 bps, drop rate 1000 bps
          Match:  dscp af11 (10)
          Queueing
          queue limit 64 packets
          (queue depth/total drops/no-buffer drops) 0/205/0
          (pkts output/bytes output) 377/570024
          bandwidth 200 kbps

        Class-map: class-default (match-any)
          4 packets, 568 bytes
          5 minute offered rate 0000 bps, drop rate 0000 bps
          Match: any
          Queueing
          queue limit 64 packets
          (queue depth/total drops/no-buffer drops/flowdrops) 0/0/0/0
          (pkts output/bytes output) 4/568
          Fair-queue: per-flow queue limit 16 packets

queue stats for all priority classes の行が、優先クラス専用のキューです。その CM-VOICE の行に Priority: 200 kbps, burst bytes 5000, b/w exceed drops: 89 とあり、b/w exceed drops が 89 です。priority に設定した 200 kbps を超えた 89 パケットが、輻輳時に捨てられています。priority は「他クラスより先に送る」優先度を与えますが、送れる量そのものは設定した rate で上限が決まります。優先クラスに設定した rate を超えるトラフィックを流すと、その超過分は輻輳時に落ちるため、priority の rate は流したい音声の帯域に見合った値に設定する必要があります。DATA は bandwidth 200 kbps で、優先クラスを送り終えた後に配分されるため、jitter 15 ms で待たされています。

7. CBWFQ と LLQ の使い分け

CBWFQ と LLQ は、どちらもクラスの重要度を表現しますが、守る対象が異なります。CBWFQ が守るのは 帯域 で、LLQ が守るのは 遅延 です。同じ輻輳に 4 機構を当てた結果を並べると、それぞれの性格の違いがはっきりします。

機構VOICE 受信VOICE jitterDATA 受信核心
FIFO236 kbps・loss 33%12 ms254 kbps・loss 42%無差別。VOICE も DATA も区別しない
WFQ234 kbps12 ms256 kbpsflow ごとに公平。クラス優先はできない
CBWFQ318 kbps・loss 2.3%12 ms172 kbps・loss 51%bandwidth 比で帯域を保証 (VOICE 優遇)
LLQ200 kbps・loss 34%0.076 ms317 kbpspriority で低遅延 (200k で頭打ち + drop)

CBWFQ は VOICE に多くの帯域を回して loss を 2.3% まで下げましたが、jitter は 12 ms のままで FIFO と変わりません。帯域は守れても、遅延は守れていません。一方の LLQ は VOICE の jitter を 0.076 ms まで下げましたが、priority 200 kbps を超えた分は drop され、loss は 34% です。遅延は守れても、設定 rate を超える帯域は流せません。

この違いから、実運用では扱うトラフィックの性質で使い分けます。遅延が命の音声 (EF) には LLQ の priority を割り当て、jitter を最小に抑えます。音声は遅延が大きいと通話品質が崩れるため、帯域より遅延の保証が優先されます。帯域を確保したい重要データや映像には CBWFQ の bandwidth を割り当て、必要な帯域を保証します。実際の設計では、この 2 つを 1 つの policy-map に同居させ、音声を priority、映像や業務データを複数の bandwidth クラスに分けて、輻輳時の帯域と遅延を同時に制御します。LLQ が CBWFQ に priority キューを追加した構成である、という構造が、この同居を可能にします。

8. 落とし穴・補足

キューイングは、輻輳の作り方やスケジューラの性質を取り違えると、効いていないのに効いたつもりになったり、その逆になったりします。本節の実機に関わる注意点をまとめます。

キューイングは輻輳時のみ発火する。 本節で最初に確認したとおり (§2)、キューイングは送りたい量が送れる量を超えたときにしか働きません。csr1000v のインタフェースは 1 Gbps 相当で、900 kbps 程度の送出では輻輳せず、キューが埋まらないためスケジューラは動きません。「CBWFQ や LLQ を当てたのに効かない」という結果の大半は、輻輳が起きていないことが原因です。本節は親 shaper (shape average 500000) で egress を絞り、送り出せる速さを送出量より低くしてボトルネックを作りました。実機で total drops が出て初めて、スケジューラが並べ替える対象が用意できます。

輻輳を作るには複数クラスを競合させる。 単一のストリームだけを流しても、「どのキューが優先されるか」は見えません (§2)。本節は VOICE と DATA の 2 クラスを並行送出し、送出合計 (906 kbps) を親 shaper (500 kbps) より大きくして競合させました。2 クラスを別々のポートで受信し、それぞれの受信レート・loss・jitter の差でスケジューラの効果を測ります。

階層型ポリシーで親 shaper に子をぶら下げる。 csr1000v の IOS-XE 17.03 では、親の class-default に service-policy で子ポリシーをぶら下げる階層型ポリシーが受理され、show policy-map interface が親子の入れ子で表示されます (§5・§6)。輻輳点を作る親の shaping と、クラスを並べ替える子の CBWFQ / LLQ を、この入れ子で組み合わせます。

priority は無制限ではない。 LLQ の priority キューには暗黙の policing がかかっており、設定した rate を超えたトラフィックは輻輳時に drop されます (§6)。本節では priority 200 に対し VOICE を 300 kbps 送ったため、200 kbps を超えた分が b/w exceed drops: 89 で捨てられ、受信は 200 kbps で頭打ちになりました。priority は「他クラスより先に送る」優先度であって、設定レートを超えたトラフィックまで通す指定ではありません。priority の rate は、流す音声の帯域に見合った値に設定します。

bandwidth と priority は kbps で明示する。 本節では bandwidth 300priority 200 のように帯域を kbps で明示しました。bandwidth percentpriority percent も使えますが、percent が何の帯域に対する割合かは構成によって変わります。階層型ポリシーで親を shape average 500000 にした場合、子の percent はその親の shape レート (500 kbps) を基準に計算されますが、shaper を挟まない構成ではインタフェースの帯域が基準になります。基準が構成依存で読み取りにくいため、親 shaper で絞ったレートの内側に収める意図を明確にするには、本節のように kbps で直接指定するのが確実です。

キューイングは出口方向のみ。 キューイングは出力キューを使う仕組みなので、service-policy output の出口方向にしか当てられません。5-5 のポリシングは入口・出口のどちらにも当てられましたが、輻輳管理は出力キューで行うため、本節はすべて Gi3 の output に当てています。

仮想機のカウンタと実測レートの扱い。 この csr1000v では、CBWFQ の bandwidth 比配分も LLQ の低遅延も実機の数値として観測できました。とはいえ効き目の正本は、5-5 と同じく受信側 PC2 の実測レート・loss・jitter に置きます。仮想機は QoS のデータプレーンを完全にはエミュレートしないことがあるため、show のカウンタは補助として扱うのが安全です。

最後に、本節で組んだ CBWFQ と LLQ の QoS 定義を running-config から抜き出すと次のようになります。親子の階層型ポリシーが、記事で見てきた形でまとまっています。

snippet
R1# show running-config | section class-map CM-|policy-map PM-
policy-map PM-CHILD-CBWFQ
 class CM-VOICE
  bandwidth 300
 class CM-DATA
  bandwidth 150
policy-map PM-PARENT-CBWFQ
 class class-default
  shape average 500000
   service-policy PM-CHILD-CBWFQ
policy-map PM-CHILD-LLQ
 class CM-VOICE
  priority 200
 class CM-DATA
  bandwidth 200
policy-map PM-PARENT-LLQ
 class class-default
  shape average 500000
   service-policy PM-CHILD-LLQ

9. 次節

本節では、輻輳時に複数クラスの送出順を決めるキューイングを csr1000v の実機で組み立てました。キューイングは輻輳時にしか働かないため、親 shaper (shape average 500000) で egress を絞ってボトルネックを作り、その配下に子ポリシーをぶら下げる階層型ポリシーで 4 機構を対比しました。FIFO は無差別 (VOICE も DATA も 236 kbps・254 kbps で等劣化)、WFQ は flow ごとに公平 (クラス優先はできない)、CBWFQ は bandwidth 比でクラス別に帯域を保証 (VOICE 318 kbps・DATA 172 kbps = 300:150 の比)、LLQ は priority で低遅延 (VOICE jitter 0.076 ms・ただし 200 kbps で頭打ち + drop) であることを、受信側 PC2 の実測レート・loss・jitter で確かめました。CBWFQ は帯域を守り、LLQ は遅延を守る、という守る対象の違いが使い分けの軸になります。

本節では、キューイングを効かせるために親 shaper で輻輳点を作り、その配下に子の CBWFQ / LLQ をぶら下げる階層型ポリシーを道具として使いました。この親子の入れ子構造そのものが、次節の主題です。親で全体のレートを絞り、子でクラス別に帯域と遅延を制御する構成は、物理インタフェースの速度より低い契約速度で WAN 回線を借りる サブレート (sub-rate) の環境で必須になります。次節 5-7 では、この階層型 QoS = HQoS (Hierarchical QoS) を主題として、parent / child policy-map を複数段で組み、サブレート WAN における帯域配分を実機で見ていきましょう。