ちょっと話題の記事

Linuxでアウトバウンド帯域幅を制限する

2014.08.28

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

オンプレとAWSにまたがるシステムの場合、オンプレ側の帯域を圧迫しないようサーバからの通信に帯域制限をかけたい場合があります。

今回はLinuxの機能を利用しアウトバウンド通信の帯域を制限してみました。 Windows Serverの帯域制限についてはこちらをご覧ください。

帯域制限が無い状態

制限が無い状態でのアウトバウンド速度を計測してみましょう。 5GBのファイルをS3にアップロードしてみます。

[ec2-user@ip-10-0-0-37 ~]$ mkdir files
[ec2-user@ip-10-0-0-37 ~]$ dd if=/dev/zero of=files/5GB count=5120 bs=1M > /dev/null
[ec2-user@ip-10-0-0-37 ~]$ aws s3 sync files s3://qos.test --region=ap-northeast-1
Completed 238 of 732 part(s) with 1 file(s) remaining

 

dstatで確認すると約80Mbpsでした。 'send'カラムが送信量(Byte)です。

[ec2-user@ip-10-0-0-37 ~]$ sudo yum install -y dstat > /dev/null
[ec2-user@ip-10-0-0-37 ~]$ dstat 1
----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system--
usr sys idl wai hiq siq| read  writ| recv  send|  in   out | int   csw
  0   0 100   0   0   0|   0     0 |  52B  358B|   0     0 |  15    18
  0   0 100   0   0   0|   0     0 |  52B  358B|   0     0 |  15    18
  0   0 100   0   0   0|   0     0 |  52B  358B|   0     0 |  12    12
 17   5  75   0   1   2|   0     0 | 417k   83M|   0     0 |  28k   24k
 13   5  77   0   1   3|   0     0 | 561k  135M|   0     0 |  40k   29k
  8   3  87   0   1   2|   0     0 | 403k   96M|   0     0 |  22k   14k
 13   5  78   0   1   3|   0     0 | 622k  135M|   0     0 |  41k   31k
 12   6  76   0   2   4|   0     0 | 715k  142M|   0     0 |  45k   32k
 14   7  74   0   1   4|   0     0 | 767k  135M|   0     0 |  47k   34k
 10  10  74   0   1   5|   0     0 | 913k  136M|   0     0 |  49k   33k
  7   4  85   0   1   3|   0     0 | 743k   82M|   0     0 |  32k   10k
  7   4  85   0   1   3|   0     0 | 767k   82M|   0     0 |  32k   11k
  7   4  84   0   1   3|   0     0 | 775k   82M|   0     0 |  33k   11k
  8   2  86   0   1   3|   0     0 | 756k   82M|   0     0 |  31k 8837
  8   3  84   0   1   4|   0     0 | 781k   82M|   0     0 |  33k   11k
  6   4  85   0   1   3|   0     0 | 758k   82M|   0     0 |  33k   10k
  7   3  86   0   1   3|   0     0 | 745k   82M|   0     0 |  30k 7820
  7   3  86   0   1   3|   0     0 | 739k   82M|   0     0 |  32k   11k
  6   4  86   0   1   4|   0     0 | 801k   82M|   0     0 |  31k 7875

 

帯域制限の方法

それでは制限をかけてみましょう。 今回は1MBpsで制限をかけてみます。

tcによる帯域制限

下記コマンドを実行します。

[ec2-user@ip-10-0-0-37 ~]$ sudo tc qdisc add dev eth0 root tbf limit 1Mb buffer 200Kb rate 1Mbps

tc(traffic control)コマンドのトークンバケットフィルタ(TBF)を利用しています。 トークンバケットフィルタは下記の方式でアウトバウンドの帯域を制限しています。

  1. 送信されるデータはデータキューに入れられる
  2. データキューとは別に、あるサイズのバケツ内にトークンが溜まっている
  3. トークンは一定の速度で補充される
  4. バケットがいっぱいのとき、トークンは溢れるのでそれ以上溜まらない
  5. アウトバウンドのデータは、データキューから出るとき(送信時)トークンを消費する
  6. トークンの補充速度より消費量が多い場合、バケット内のトークン残量は減少し、バケットが空になるとトークンの補充速度と同量のデータしか送信されなくなる

したがって、トークンの補充速度を設定することでアウトバウンドの速度を制限できます。

tcコマンドのオプションは

tc qdisc add dev <<インターフェース>> root tbf limit <<データキューのサイズ>> buffer <<バケットのサイズ>> rate <<トークンの補充速度>>

なので、rateの値で帯域幅を指定できます。

先ほど実行したコマンドは、下記のような設定になります。

  • データキューのサイズ:1MB(tcコマンドでは'b'はbitではなくByteを意味します)
  • バケツのサイズ:200KB
  • トークン補充速度(帯域幅):1MBps

 

指定した帯域に制限されているか確認します。

[ec2-user@ip-10-0-0-37 ~]$ dstat 1
----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system--
usr sys idl wai hiq siq| read  writ| recv  send|  in   out | int   csw
  0   0  99   1   0   0|  12k  759k|   0     0 |   0     0 | 390   193
  0   0 100   0   0   0|   0     0 |  52B  838B|   0     0 |  26    26
  0   0 100   0   0   0|   0     0 |  52B  358B|   0     0 |  14    16
  0   0 100   0   0   0|   0     0 |  52B  358B|   0     0 |  14    16
  8   3  89   0   0   0|   0     0 |  52k   28k|   0     0 |7669  8721
  0   0 100   0   0   0|   0     0 |7052B  305k|   0     0 | 401   130
  0   0 100   0   0   0|   0     0 |5012B  266k|   0     0 | 357   115
  0   0 100   0   0   0|   0     0 |4412B  232k|   0     0 | 313   113
  0   0 100   0   0   0|   0     0 |3732B  172k|   0     0 | 266    96
  0   0 100   0   0   0|   0     0 |2576B  125k|   0     0 | 211    84
  0   0 100   0   0   0|   0     0 |2932B  141k|   0     0 | 228    92
  0   0 100   0   0   0|   0     0 |3132B  151k|   0     0 | 246    98
  0   0 100   0   0   0|   0     0 |2692B  130k|   0     0 | 214    88
  0   0 100   0   0   0|   0     0 |2692B  136k|   0     0 | 220    90
  0   0 100   0   0   0|   0     0 |3288B  150k|   0     0 | 241    96
  0   0 100   0   0   0|   0     0 |2852B  135k|   0     0 | 221    88
  0   0 100   0   0   0|   0     0 |2852B  139k|   0     0 | 232   104
  0   0 100   0   0   0|   0     0 |2932B  147k|   0     0 | 239   100

1MBpsを指定したのですが、約150KBpsくらいしか出ていません。

TCP Segmentation Offloadの無効化

黒ぶちめがねさんのブログに解決策が書いてありました。

データの分割/結合をNICに移管するTCP Segmentation Offload / Generic Segmentation Offloadという機能が影響しているようです。

確認したところ今回の環境でも有効になっていました。

[ec2-user@ip-10-0-0-37 ~]$ sudo ethtool -k eth0 | grep segmentation-offload
tcp-segmentation-offload: on
generic-segmentation-offload: on

 

下記コマンドで無効にします。

[ec2-user@ip-10-0-0-37 ~]$ sudo ethtool -K eth0 tso off gso off
[ec2-user@ip-10-0-0-37 ~]$ sudo ethtool -k eth0 | grep segmentation-offload
tcp-segmentation-offload: off
generic-segmentation-offload: off

 

それではもう一度、制限をかけた後のアウトバウンド速度を見てみましょう。

[ec2-user@ip-10-0-0-37 ~]$ dstat 1
----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system--
usr sys idl wai hiq siq| read  writ| recv  send|  in   out | int   csw
  0   0  99   1   0   0|  11k  719k|   0     0 |   0     0 | 385   188
  0   0 100   0   0   0|   0     0 | 182B  946B|   0     0 |  24    20
  0   0 100   0   0   0|   0     0 |  52B  358B|   0     0 |  12    10
  0   0 100   0   0   0|   0    16k|  52B  358B|   0     0 |  24    26
  0   0 100   0   0   0|   0     0 |  52B  358B|   0     0 |  12    12
  0   0 100   0   0   0|   0     0 |  52B  358B|   0     0 |  12    16
  0   0 100   0   0   0|   0     0 | 364B  626B|   0     0 |  24    22
  1   1  99   0   0   0|   0     0 | 192B  460B|   0     0 |  54    48
  7   3  91   0   0   0|   0     0 |  61k  846k|   0     0 |8408  8462
  0   0  99   0   0   0|   0     0 |  15k  977k|   0     0 |1761   150
  0   0 100   0   0   0|   0     0 |  15k  976k|   0     0 |1753   111
  0   0 100   0   0   0|   0     0 |  14k  976k|   0     0 |1720   115
  0   0 100   0   0   0|   0     0 |  14k  977k|   0     0 |1719   107
  0   0 100   0   0   0|   0     0 |  15k  977k|   0     0 |1741   118
  0   0 100   0   0   0|   0     0 |  15k  977k|   0     0 |1742   100
  0   0 100   0   0   0|   0     0 |  14k  977k|   0     0 |1729    94
  0   0 100   0   0   0|   0     0 |  14k  976k|   0     0 |1724    92

約1MBpsと期待通りに制限がかかっていることが確認できました。

これまでの設定は、サーバを再起動すると無効になるので、/etc/rc.local等に記載し、起動時に設定されるようにしておきましょう。

[ec2-user@ip-10-0-0-37 ~]$ cat /etc/rc.local
#!/bin/sh
#
# This script will be executed *after* all the other init scripts.
# You can put your own initialization stuff in here if you don't
# want to do the full Sys V style init stuff.

touch /var/lock/subsys/local

# Traffic Control
tc qdisc add dev eth0 root tbf limit 1Mb buffer 200Kb rate 1Mbps
ethtool -K eth0 tso off gso off

また、tcで設定した内容の確認、削除は下記コマンドで出来ます。

[ec2-user@ip-10-0-0-37 ~]$ sudo tc qdisc  show
qdisc tbf 8006: dev eth0 root refcnt 2 rate 8000Kbit burst 200Kb lat 843.8ms
[ec2-user@ip-10-0-0-37 ~]$ sudo tc qdisc del dev eth0 root

まとめ

今回は、tcコマンドを利用してLinuxサーバからのアウトバウンドに帯域制限をかけてみました。 利用したトークンバケットという仕組みはやや取っ付きにくいですが、SSDベースのEBSにおけるIOPST2インスタンスのCPUクレジットでも利用されている仕組みですので、理解しておいて損は無いと思います。

参考

tcコマンドによる帯域制限については下記ページの説明が分かりやすかったです。 Linux Advanced Routing & Traffic Control HOWTO

TCP Segmentation Offloadについては下記ページの説明が分かりやすかったです。 Segmentation and Checksum Offloading: Turning Off with ethtool