Linux TC (帯域制御、帯域保証) 設定ガイドライン
Abstract
このドキュメントはLinuxにおいて帯域制限のためにtcを用いる際のガイドラインです。
tcは様々な用途に活用できるものですが、プロダクションにおいて特定のserver daemonのトラフィックを制限するというシナリオで活用することを目的としています。
tcのより詳しい詳細については別にドキュメントを書きましたのでそちらを参照してください。
よくわかるLinux帯域制限
Root qdiscの選定
帯域制限を行いたい場合のqdiscは主に以下のようになるでしょう。
- TBF
- PRIO + 内部qdiscとしてTBF
- HTB
それぞれ用途に合わせて適切なものがあるのですが、機能としてはHTBが前者2つの上位互換となるので、迷った場合にはHTBを使えば問題ありません。ということで以後HTBの設定について解説します。
class構造,トラフィックのclassify, filterの設定
すべてのトラフィックを一括で制限したい場合
HTBを使う場合は最低限のclass構造を設定します。filterはdefaultの設定のみ行います。
1 2 |
tc qdisc add dev eth0 root handle 1: htb default 1:1 tc class add dev eth0 parent 1: classid 1:1 htb rate... |
トラフィックを分類して特定のトラフィックを制限したい場合
特定のトラフィックのみを制限したい場合にはそのトラフィックを特定するためのfilterと言われる分類ルールを記述する必要があります。
filterには以下のような情報が活用できます。
- 送信元(source)のIP
- 送信元(source)のポート番号
- 送信先(destination)のIP
- 送信先(destination)のポート番号
- プロトコル(tcp, udp, icmp, ...)
- device(eth0, ...)
- TOSフィールド
- その他
残念ながら特定のprocessからの通信を抽出するようなfilterは(少なくとも簡単には)つくることができません。
以下のような2分割を行うclass構造に対応するfilterを考えます。
1 2 3 4 |
tc qdisc add dev eth0 root handle 1: htb default 1:10 tc class add dev eth0 parent 1: classid 1:1 htb rate... tc class add dev eth0 parent 1:1 classid 1:10 htb rate... tc class add dev eth0 parent 1:1 classid 1:20 htb rate... |
source / destination IP によるfilter
例として以下のケースを考えます。
tcを設定するコンピュータAのprivate IP: 192.168.1.5
主な通信先となるコンピュータBのprivate IP: 192.168.1.10
subnet mask: 255.255.255.0 (= /24)
filterを設置するclassは1:0、
filterにヒットした場合の分類先は1:20とします。
送信元がAの場合にヒットさせたいときは以下のようにします。(注:tcは送信するパケットの制御のみを行うため、大抵の場合このfilterには意味がありません)
1 |
tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip src 192.168.1.5/32 flowid 1:20 |
送信先がBの場合にヒットさせたいときは以下のようにします。
1 |
tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip dst 192.168.1.10/32 flowid 1:20 |
送信先がLAN内のIPである場合にヒットさせたいときは以下のようにします。
1 |
tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip dst 192.168.1.0/24 flowid 1:20 |
source / destination port によるfilter
送信元portが5555の場合にヒットさせたいときは以下のようにします。
1 |
tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip sport 5555 0xffff flowid 1:20 |
送信先portが6666の場合にヒットさせたいときは以下のようにします。
1 |
tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip dport 6666 0xffff flowid 1:20 |
複合的なfilterの例
ANDの結合は一行に複数のルールを並べることで実現できます。
送信先IPが192.168.1.10で送信先portが6666である場合にヒットさせたいときは以下のようにします。
1 2 3 4 |
tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 \ match ip dst 192.168.1.10/32 \ match ip dport 6666 0xffff \ flowid 1:20 |
ORの結合は一つのclassに複数のルールを設置することで実現できます。
送信元portが5555 or 5556 or 5557の場合にヒットさせたいときは以下のようにします。
1 2 3 |
tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip sport 5555 0xffff flowid 1:20 tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip sport 5556 0xffff flowid 1:20 tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip sport 5557 0xffff flowid 1:20 |
今回上げた以上に複雑なAND/ORを組み合わせたルールについては自分は検証したことがありません。
検証
classにどの程度のpacketが流れているかは以下のコマンドで確認できます。
1 |
tc -s class show dev eth0 |
実際にトラフィックを流しながらfilterの設定が正しいか確認しましょう。具体的な確認方法については「トラフィックの確認」で解説しています。
その他
このドキュメントでは一部のみを取り上げています。完全な情報については以下のURLを参照してください。
Linux Advanced Routing & Traffic Control HOWTO Chapter 9. Queueing Disciplines for Bandwidth Management
ただしこのfilterの設定、class構造の設定は極めて難解です。iproute2のtcコマンドの代わりに、こういった設定が簡単に行えるとされているTCNGを利用してみてもいいかもしれません。
rate, ceilの設定
rate, ceilは分類したclassにどの程度のトラフィックを割り当てるかをbitrateで指定する仕組みです。
一般的にトラフィックを制御するためにコントロールするものにはbitrateとパケット数があると思いますが、残念ながらパケット数を制御する方法はtcでは提供されていません。
表記について
tcコマンドにおいては単位の表記に注意が必要です。文脈によるが以下の様に解釈されます。
- bps: Byte Per Second
- bit: Bit Per Second
- b: Byte
- bit: Bit
bpsがByte Per Secondとなることは大きな誤解を生む原因となります。。この単位は使用しないことが推奨されます。このドキュメントでも以降Per Secondとなる単位についてはすべてbitまたはbits/sと表記します。
なおK, M, G等の単位については問題なく使用できます。ただしそれぞれ10^3, 10^6, 10^8として扱われ、2^10, 2^20, 2^30ではないことには注意が必要です。
rate
rateはclass構造の最上位とそれ以外で意味合いが異なります。
最上位では単にbitrateの上限を意味します。これはceilと同じです。そのため最上位classでは必ずrate=ceilとするようにしてください。
それ以外ではclassに対して「ここまでのbitrateが出る」ことを保証する(=帯域保証する)数値です。
class構造の親のrateが子のrateの合計と同一かそれ以下になるようにしてください。
例として以下のようなclass図で1Gbits/sの回線に接続している場合を考えます。
1:10は確実に300Mbits/sが与えられて欲しい(=帯域保証したい)トラフィック、1:20はその他のトラフィックで特に帯域保証は必要ないものとします。
1 2 3 4 5 6 7 8 9 |
root(1:) | 1:1 | 1:10 | ------ | | 1:100 1:200 |
1:1には実際の回線の上限のトラフィックを設定します。
1:10には回線について「確実に出るであろうbitrate」を設定します。ここでは1Gbits/sなのですが、確実にその速度が出るかはわかりません。他のマシンが帯域を使いすぎている場合も考えられます。ここでは、おおまかな計算として8掛けしてrate: 800Mbits/sとします。
1:100は帯域保証が必要なトラフィックのclassです。rate: 300Mbits/sとします。
1:200はその他のトラフィックのclassです。帯域保証は必要ないため、rate: 1Mbits/sとします。(0は設定できないため)
1 2 3 4 |
tc class add dev eth0 parent 1: classid 1:1 htb rate 1000Mbit ceil... tc class add dev eth0 parent 1:1 classid 1:10 htb rate 800Mbit ceil... tc class add dev eth0 parent 1:10 classid 1:100 htb rate 300Mbit ceil... tc class add dev eth0 parent 1:10 classid 1:200 htb rate 1Mbit ceil... |
ここで、1:200には帯域保証が不要ではなく、1:100に保証されている帯域以外については1:200が「確実に」使用したい(=帯域保証したい)場合には、800 - 300 = 500でrate: 500Mbits/sとします。
1 2 3 4 |
tc class add dev eth0 parent 1: classid 1:1 htb rate 1000Mbit ceil... tc class add dev eth0 parent 1:1 classid 1:10 htb rate 800Mbit ceil... tc class add dev eth0 parent 1:10 classid 1:100 htb rate 300Mbit ceil... tc class add dev eth0 parent 1:10 classid 1:200 htb rate 500Mbit ceil... |
ceil
ceilはclassに対して「これ以上のbitrateが出ないようにする」(=帯域制限する)数値です。
class構造の親のceilを子のceilの最大のものが超えないようにしてください。
引き続きrateの時に利用した例を使って説明します。
1:10については300Mbits/sまで出て欲しいが逆に300Mbit/s以上は出ないで欲しい。
1:20については回線を目一杯使用しても構わない。
という場合
1:1は回線の最大値である1Gbits/sとします。ceil: 1000Mbits/s(注:それ以上の値でも構いません)
1:10は制限したい数値にします。ceil: 300Mbits/s
1:20も回線の最大値である1Gbits/sとします。ceil: 1000Mbits/s
1 2 3 4 |
tc class add dev eth0 parent 1: classid 1:1 htb rate 1000Mbit ceil 1000Mbit burst... tc class add dev eth0 parent 1:1 classid 1:10 htb rate 800Mbit ceil 1000Mbit burst... tc class add dev eth0 parent 1:10 classid 1:100 htb rate 300Mbit ceil 300Mbit burst... tc class add dev eth0 parent 1:10 classid 1:200 htb rate 1Mbit ceil 1000Mbit burst... |
burst, cburstの設定
burstはrateに、cburstはceilにそれぞれ対応するbufferのようなものの大きさです。詳細な理解については別ドキュメントを参照してください。
ここでは設定方法のみを説明したいと思います。
qdiscからのパケットの送信は常に行われているわけではなく0-100ms程度のランダムな間隔で行われています。また、qdiscへのパケットの到着も一定ではなくパケットを送信するapplication, server processの都合により時間ごとに偏りがでます。burstを大きくすることでこのような偏りに対応して、1sec, 10secなどの単位で設定したbitrateを「出し続ける」ことができます。
burstの設定は最小の値を計算して、それを測定しながら安定して設定したbitrateが出るまで大きくしていく作業となります。
quantum, r2qの設定
quantum, r2qの設定は通常必要ないです。デフォルトのr2q=10で問題ありません。
ただし設定によりkernelのwarningが出ることがあります。その場合も無視してしまっても大丈夫なのですが、r2q経由ではなく明示的quantumを指定することで警告を避けることができます。詳細は別ドキュメントを参照してください。
計算
burstの「最小でもこのくらいにしておかなくてはならない」という値については計算で得ることができます。
burst[Bytes] = bitrate[bits/s] / 8 * 10[msec]
となります。なぜ10msecなのか等については別ドキュメントを参照してください。
検証
最小の値を計算したら、実際に検証しながら数値を大きくしていきます。大抵の場合最小の値の3〜20倍程度の大きさとなるでしょう。
例
例としてrate, ceilのいずれかを100Mbits/sとしたい場合には以下のようにしていきます。
Step1. 最小の値を計算する。
= 100 * 10^6 / 8 * 10 * 10^-3
= 100 / 8 * 10 * 10^3
= 125kBytes
Step2. 実際に利用するHTBフィルタの該当classにrate=ceil=検証中のbitrate, burst=cburst=検証中の値となるように設定する
他の設定がボトルネックになったり、逆に他の設定のせいで制限が外れてしまうことを防ぐようにします。
次のようなフィルタで100Mbitの箇所について検証する場合、こうなります。
1 2 3 4 5 6 7 8 9 |
# 例 tc class add dev eth0 parent 1: classid 1:1 htb rate 1000Mbit ceil 1000Mbit burst... tc class add dev eth0 parent 1:1 classid 1:10 htb rate 100Mbit ceil 1000Mbit burst... tc class add dev eth0 parent 1:1 classid 1:20 htb rate 1Mbit ceil 1000Mbit burst... # 検証のため、一次このようにする tc class add dev eth0 parent 1: classid 1:1 htb rate 1000Mbit ceil 1000Mbit burst 125kB cburst 125kB tc class add dev eth0 parent 1:1 classid 1:10 htb rate 100Mbit ceil 100Mbit burst 125kB cburst 125kB tc class add dev eth0 parent 1:1 classid 1:20 htb rate 1Mbit ceil 1000Mbit burst 125kB cburst 125kB |
そして1:10にトラフィックを流しながらburst値を大きくしていきます。できれば実際のアプリケーションによるトラフィックが望ましいです。
以下は例です。
burst[Bytes] | bitrate[Mbit/s] |
---|---|
125kB | 50MB |
250kB | 89MB |
500kB | 98MB |
1000kB | 101MB |
1500kB | 101MB |
この例では1000KB以上が必要であることがわかりました。
なお今回は1Gbit/sの回線において100Mbit/sの制限値であったため検証を行えましたが、実環境では他への影響を考慮して、限界値に近い800Mbit/s等の検証を行うのは危険な場合があります。その場合は他の測定値を参考にしながら、十分大きめの値を設定してください。
例えば今回の例では最終的には次のような設定としても構いません。
1 2 3 4 |
# すべてのburstを10MBとした tc class add dev eth0 parent 1: classid 1:1 htb rate 1000Mbit ceil 1000Mbit burst 10MB cburst 10MB tc class add dev eth0 parent 1:1 classid 1:10 htb rate 100Mbit ceil 1000Mbit burst 10MB cburst 10MB tc class add dev eth0 parent 1:1 classid 1:20 htb rate 1Mbit ceil 1000Mbit burst 10MB cburst 10MB |
内部qdiscの設定
HTBのclass構造で葉(末端)となるclassには必ず内部qdiscが付属します。特に指定しない場合も暗黙的に付きますが、tcのshowコマンドでデバッグが行いにくくなることや、どのようなパラメータが設定されているか不明となることなどデメリットが多くあります。そのため、内部qdiscは必ず明示的に指定してください。
内部qdiscの種類
内部qdiscには以下のようなものがあります。
- PFIFO
- HTBの内部qdiscには一般的にこれを用いる
- PFIFO_FAST
- PFIFOでTOSを考慮して優先度の高いIPパケットが先に送信されるようにしたもの
- ただしHTB内ではclass番号が小さいものが先に処理されるルールがあり、こちらが優先されるため、使うメリットが弱くなっている。具体的には1:10と1:20があった場合には、「1:10の優先パケット→1:10の通常パケット→1:20の優先パケット→1:20の通常パケット」の順に処理される
- SFQ
- 複数のセッションに公平に送信機会を与える仕組みを持つ
- 制限目一杯のパケットを送信することが多いclass内で、すべてのセッションのlatencyを公平にしたい場合には役立つ
- 一方通常の通信でも処理のオーバーヘッドによりlatencyが増えてしまうため、必要性がない場合は使用しない方が良い。
設定方法
PFIFOを設定する場合は以下のようにします。
1 2 3 4 5 6 |
tc qdisc add dev eth0 root handle 1: htb default 1:10 tc class add dev eth0 parent 1: classid 1:1 htb rate... tc class add dev eth0 parent 1:1 classid 1:10 htb rate... tc class add dev eth0 parent 1:1 classid 1:20 htb rate... tc qdisc add dev eth0 parent 1:10 handle 100: pfifo limit 1000 tc qdisc add dev eth0 parent 1:20 handle 200: pfifo limit 1000 |
limitはqdiscにいくつのpacketを貯めておけるかというパラメータです。通常は1000や10000などにしておけば問題ありません。
limitが小さいとbitrateがでなかったりpacketのdropが発生する原因となります。
limitが大きいと帯域を目一杯使用している場合にlatencyが悪化する原因となる場合があります。
qdiscでどの程度のdropが発生していて、どの程度のpacketが貯まっているかは以下のコマンドで確認できます。
1 |
tc -s qdisc show dev eth0 |
実際にトラフィックを流しながら設定を確認しましょう。具体的な確認方法については「トラフィックの確認」で解説しています。
トラフィックの確認
実際に流れているトラフィックを確認するにはtc -s
コマンドを利用します。具体例を挙げながら解説していきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# 実行例 $ tc -s class show dev eth0 class htb 1:10 parent 1:1 leaf 100: prio 0 rate 1000Kbit ceil 200000Kbit burst 10Mb cburst 10Mb Sent 1713585774 bytes 4330181 pkt (dropped 0, overlimits 0 requeues 0) rate 8152bit 5pps backlog 0b 0p requeues 0 lended: 3589837 borrowed: 36342 giants: 0 tokens: 1310624255 ctokens: 6553482 class htb 1:1 root rate 1000Mbit ceil 1000Mbit burst 10Mb cburst 10Mb Sent 2642090316 bytes 15137342 pkt (dropped 0, overlimits 0 requeues 0) rate 21592bit 24pps backlog 0b 0p requeues 0 lended: 36342 borrowed: 0 giants: 0 tokens: 1310718 ctokens: 1310718 class htb 1:20 parent 1:1 leaf 200: prio 0 rate 1000Kbit ceil 1000Mbit burst 10Mb cburst 10Mb Sent 928504542 bytes 10807161 pkt (dropped 0, overlimits 0 requeues 0) rate 13440bit 19pps backlog 0b 0p requeues 0 lended: 10804882 borrowed: 0 giants: 0 tokens: 1310705000 ctokens: 1310718 $ sudo tc -s qdisc show dev eth0 qdisc htb 1: root refcnt 2 r2q 10 default 20 direct_packets_stat 0 Sent 2642281754 bytes 15139127 pkt (dropped 0, overlimits 76801 requeues 0) rate 0bit 0pps backlog 0b 0p requeues 0 qdisc pfifo 100: parent 1:10 limit 10000p Sent 1713662136 bytes 3626607 pkt (dropped 0, overlimits 0 requeues 0) rate 0bit 0pps backlog 0b 0p requeues 0 qdisc pfifo 200: parent 1:20 limit 10000p Sent 928619618 bytes 10806239 pkt (dropped 0, overlimits 0 requeues 0) rate 0bit 0pps backlog 0b 0p requeues 0 |
tc -s class
ではclassにおけるトラフィックを確認できます。tc -s qdisc
では各qdisc (HTB, HTBの内部qdisc)におけるトラフィックを確認できます。
これらにおいて、主に確認すべき点は各classのSent byte, dropped, overlimitsです。
Sent XXX bytesはそのclass, qdiscが送信したパケットの累計バイト数を表しています。流したトラフィックが想定したclassを通っているかがわかります。
droppedはenqueueできなかったパケット数です。latencyより安定したtroughputを重視する場合は内部qdiscのlimitを増やすことでdropを抑えることができます。
overlimitsはdequeueしようとした際にrateやceilなど何らかの制限に引っかかりdequeueできなかった回数です。速度制限が有効かどうかの目安となります。
設定例
ここでは完全な設定例をいくつか示します。
すべてのトラフィックをまとめて帯域制限する
すべての項目を500Mbit/sに制限する例です。
1 2 3 4 |
tc qdisc del dev eth0 root tc qdisc add dev eth0 root handle 1: htb default 1 tc class add dev eth0 parent 1: classid 1:1 htb rate 500Mbit ceil 500Mbit burst 10MB cburst 10MB tc qdisc add dev eth0 parent 1:1 handle 10: pfifo limit 1000 |
この例では帯域保証は必要ないため、rateはceilと同一の値としてしまっています。
rateを0にしても同様の動作となります。
先頭のqdisc delは既に設定が存在した場合にそれを削除するものです。安全のため、かならず記述しておくことをおすすめします。
特定のトラフィックにのみ帯域制限をかける
192.168.1.10宛のトラフィックのみを100Mbit/sに制限する例です。帯域保証はありません。
DBにおいて定期的にバックアップを行う際の帯域制限などのようなイメージでしょうか。
1 2 3 4 5 6 7 8 9 10 |
tc qdisc del dev eth0 root tc qdisc add dev eth0 root handle 1: htb default 10 tc class add dev eth0 parent 1: classid 1:1 htb rate 1000Mbit ceil 1000Mbit burst 10MB cburst 10MB tc class add dev eth0 parent 1:1 classid 1:10 htb rate 1Mbit ceil 1000Mbit burst 10MB cburst 10MB tc class add dev eth0 parent 1:1 classid 1:20 htb rate 1Mbit ceil 100Mbit burst 10MB cburst 10MB tc qdisc add dev eth0 parent 1:10 handle 100: pfifo limit 1000 tc qdisc add dev eth0 parent 1:20 handle 200: pfifo limit 1000 tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip dst 192.168.1.10/32 flowid 1:20 |
特定のトラフィックにのみ帯域制限をかけながら帯域保証も行う
source portが5555, 5556のトラフィックを200Mbit/sに制限する例です。帯域保証も同200Mbit/sとします。
一応その他のトラフィックについてもある程度(200Mbit/s)の帯域保証を行うようにします。
イメージとしてRedisのreplicationを想定しています。レプリケーションが切れたり遅延したりしないように帯域保証が必要で、かつ新規レプリケーションの際には膨大なトラフィックが流れるため、帯域制限も必要となります。
1 2 3 4 5 6 7 8 9 10 11 |
tc qdisc del dev eth0 root tc qdisc add dev eth0 root handle 1: htb default 1:20 tc class add dev eth0 parent 1: classid 1:1 htb rate 1000Mbit ceil 1000Mbit burst 10MB cburst 10MB tc class add dev eth0 parent 1:1 classid 1:10 htb rate 200Mbit ceil 200Mbit burst 10MB cburst 10MB tc class add dev eth0 parent 1:1 classid 1:20 htb rate 200Mbit ceil 1000Mbit burst 10MB cburst 10MB tc qdisc add dev eth0 parent 1:10 handle 100: pfifo limit 1000 tc qdisc add dev eth0 parent 1:20 handle 200: pfifo limit 1000 sudo tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip sport 5555 0xffff flowid 1:10 sudo tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip sport 5556 0xffff flowid 1:10 |
1:20のceilを1000Mbitにしているため、1:10のトラフィックが少ない場合は1:20が1:10に保証された分も使用することができるようになっています。
なお、上記の例では回線全体で保証できるbitrateが明示されていません。やや冗長な表現となりますが、以下のように修正することで回線全体の保証を明示できます。(回線全体での保証を800Mbits/sとする)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
tc qdisc del dev eth0 root tc qdisc add dev eth0 root handle 1: htb default 200 tc class add dev eth0 parent 1: classid 1:1 htb rate 1000Mbit ceil 1000Mbit burst 10MB cburst 10MB # ここが回線全体の帯域保証を表す tc class add dev eth0 parent 1:1 classid 1:10 htb rate 800Mbit ceil 1000Mbit burst 10MB cburst 10MB tc class add dev eth0 parent 1:10 classid 1:100 htb rate 200Mbit ceil 200Mbit burst 10MB cburst 10MB tc class add dev eth0 parent 1:10 classid 1:200 htb rate 200Mbit ceil 1000Mbit burst 10MB cburst 10MB tc qdisc add dev eth0 parent 1:100 handle 1000: pfifo limit 1000 tc qdisc add dev eth0 parent 1:200 handle 2000: pfifo limit 1000 sudo tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip sport 5555 0xffff flowid 1:100 sudo tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip sport 5556 0xffff flowid 1:100 |
チェックリスト
作成したtcの設定をプロダクションに適用する前に、必ず以下の項目を確認してください。
- htbの各classについてrate, ceil, burst, cburstが明示的に設定されている
- 最上位classではrate==ceilとなっている
- htbの各末端classについて内部qdiscとその設定が明示的に設定されている
- 帯域の制限(XXXbits/s以上出ないこと)、帯域の保証(XXXbits/sまで出ること)について実環境で検証されている
注意
このガイドラインは策定されたばかりで十分にプロダクションでの実績をもつものではありません。個々のケースにおいては無条件に利用するのではなく、あくまで参考程度にお考えください。