最低限押さえておけばよさそうなOpen vSwitchのQoS機能

どこかに書いてありそうで、いまいちまとまってなさそうなので、メモ。

基本的にQoS機能としては、ingress policingとegress shapingをサポートしている。ingressとegressはvSwitchから見てなので、

| VM | <--> (tap0) <--> | vSwitch | <--> (eth1)

とかいう構成だとすると、VMから入ってくる方がingressで、物理ネットワークに出て行く方がegressになる。policingとshapingは簡単に言うと、設定した帯域を超えたトラフィックをばっさり落としてしまうのがpolicingで、超えた分のトラフィックを平滑化して送り出すのがshaping。shapingはパケットを落とさないけど(もちろんキュー長の限界はある)、遅延は増加してしまうので、どちらを使うかは用途次第ということになる。

Open vSwitchでは、これらの実装としてLinuxのtc (traffic control)を使っている。ingress policingにはingress Qdiscとfilterの組み合わせ、egress shapingにはhtbまたはhfscを利用することができる。Open vSwitchからtcのカーネルモジュールを制御するためにnetlinkインタフェースを利用している。

ingress policing

ingress policingの設定はovs-vsctlコマンドで行う。ここでは10Mbpsにrate limitして、burst buffer sizeは1MBにしてみた。burst buffer sizeが小さいとパケットロスが頻発するので、設定したrate以下の帯域しか通らない。この辺のさじ加減は正直よく分からない。

$ sudo ovs-vsctl set Interface tap0 ingress_policing_rate=10000
$ sudo ovs-vsctl set Interface tap0 ingress_policing_burst=1000

tcコマンドを使って、実際に設定された内容を確認してみよう。tap0にingress Qdiscが追加され、filterによってrate limitがかかっていることがわかる。ingress_policing_rateとingress_policing_burstがそれぞれどう対応しているのかわかるはずだ。

$ sudo /sbin/tc qdisc show dev tap0
qdisc pfifo_fast 0: root refcnt 2 bands 3 priomap  1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1
qdisc ingress ffff: parent ffff:fff1 ----------------

$ sudo /sbin/tc filter show dev tap0 parent ffff:fff1
filter parent ffff: protocol all pref 49 basic 
filter parent ffff: protocol all pref 49 basic handle 0x1 
 police 0x1 rate 10000Kbit burst 1000Kb mtu 64Kb action drop overhead 0b 
ref 1 bind 1

ingress_policing_rateを0に設定すると、rate limitは解除される。

egress shaping

ここでは二つのshapingキューを作って、VMとキューを紐付けるルールによってフローを制御することを考える。

まず、キューを生成する。なんとも引数がややこしいがman pageを参照してちょうだい。

$ ovs-vsctl -- set port eth1 qos=@newqos \
                     -- --id=@newqos create qos type=linux-htb other-config:max-rate=40000000 queues=0=@q0,1=@q1 \
                     -- --id=@q0 create queue other-config:min-rate=10000000 other-config:max-rate=10000000 \
                     -- --id=@q1 create queue other-config:min-rate=20000000 other-config:max-rate=20000000

これでeth1に@q0と@q1というキューが作られる。それぞれのキューに10Mbpsと20Mbpsの帯域を設定する。さらに合計は40Mbpsに設定する。デフォルトのキューは@q0になるようだ。

あるVMからのトラフィックを@q1に振り向けるには、ovs-ofctlコマンドを使ってフローテーブルを設定する。これは入力ポートが3であれば、さきほど作ったキュー@q1にキューイングしろというルールになる。ovs-ofctl dump-flowsでも確認できる。入力ポート番号を知るには、ovs-ofctl show br0を実行する必要がある。この辺を自動化する方法はよくわからない。

$ sudo ovs-ofctl add-flow br0 "in_port=3 idle_timeout=0 actions=enqueue:1:1"

さて、ここでもtcの結果を確認しておこう。q0が1:1、q1が1:2のhtb classに対応している。

$ sudo /sbin/tc qdisc show dev eth1
qdisc htb 1: root refcnt 73 r2q 10 default 1 direct_packets_stat 0

$ sudo /sbin/tc class show dev eth1
class htb 1:1 parent 1:fffe prio 0 rate 10000Kbit ceil 10000Kbit burst 9063b cburst 9063b
class htb 1:fffe root rate 40000Kbit ceil 40000Kbit burst 9000b cburst 9000b
class htb 1:2 parent 1:fffe prio 0 rate 20000Kbit ceil 20000Kbit burst 9062b cburst 9062b

ルールを削除するときは、ovs-ofctl del-flowsコマンドを使う。キューの削除はovs-vsctl clearで。

$ sudo ovs-ofctl del-flows br0 "in_port=3"

$ sudo ovs-vsctl clear port eth1 qos