1. 程式人生 > >關注雲端計算,與中國雲端計算共成長

關注雲端計算,與中國雲端計算共成長

1 指令碼與函式

2 tc 與 htb (cbq 版這裡

一 指令碼與函式

#建立函式

注意必須先定義好函式,後才能呼叫,所以通常將函式定義在shell指令碼頭部!!!

此函式定義了兩個變數clasid_vlan與classid_ip 用於接受傳遞引數值

  1. #classid 
  2. tc_classid() { 
  3. classid_vlan=$1 
  4. classid_ip=$2 
  5. if [[ "$classid_vlan" -ge "1" && "$classid_vlan" -le "9" ]];then 
  6. echo $(((classid_vlan-1)*lie+(classid_ip-1))) 
  7. fi 
  8. if [[ "$classid_vlan" -ge "10" && "$classid_vlan" -le "49" ]];then 
  9. echo "ip $classid_vlan error" 
  10. fi 
  11. if [[ "$classid_vlan" -ge "50" && "$classid_vlan" -le "59" ]];then 
  12. echo $((($classid_vlan-41)*lie+(classid_ip-1))) 
  13. fi 

#呼叫函式

下面將在另一個函式tc_show()中呼叫了先前定義的tc_classid()函式

  1. #show 
  2. tc_show() { 
  3. local tmp_1=$1 
  4. local tmp_2=$2 
  5. local tmp_3=$3 
  6. local tmp_4=$4 
  7. show=`tc_classid $tmp_3 $tmp_4` 
  8. if ["$tmp_2"=down];then 
  9. tmp_2=eth1
  10. else 
  11. tmp_2=eth0
  12. fi 
  13. tc class show dev $tmp_2|grep ":${show}[]" | awk'{print "speed:" $11}' 
  14. echo"$tmp_1 $tmp_2 $tmp_3 $tmp_4" 
  15. echo"classid:$show" 

向函式傳遞引數bash shell將函式作為小型指令碼處理,函式可以使用標準引數環境變數來表示命令列傳遞給函式的引數!!

注意,因為函式為自己的引數值使用專用的引數環境變數,所以函式無法從指令碼命令列直接訪問指令碼引數值,也就是說函式不識別執行指令碼時後面附帶的引數值。如果要呼叫 則可以將指令碼的引數賦值給函式中的區域性變數,例如

local tmp_1=$1
local tmp_2=$2
local tmp_3=$3
local tmp_4=$4

#tc classid 傳遞兩個引數 $tmp_3 與 $tmp_4 ,如下

show=`tc_classid $tmp_3 $tmp_4` 

函式返回值
bash shell將函式看成小型指令碼,並以退出狀態結束,我們可以通過$?檢視三種不同的退出狀態,如果不使用return 來指定返回值,則以最後一個執行的命令退出狀態為返回值,
從這點我們發現函式的預設退出狀態是不可靠的!所以我們可以

使用return 來返回函式值;還可以使用函式輸出,對於命令輸出可以捕獲並存放到shell變數中,函式的輸出也可以捕獲並放到shell變數中!

這裡使用反引號show=`tc_classid $tmp_3 $tmp_4`來包圍函式及其傳遞的引數,是為了將函式返回值賦值給變數 $show

函式中的變數
這裡要提及一個作用域的概念,表示說變數的可見區域。函式內定義的變數和普通變數有不同的作用域,指令碼外部定義的變數要覆蓋函式內定義的變數。函式使用兩個型別的變數:
全域性變數:是shell指令碼內處處有效的變數,預設情況下指令碼中定義的變數都是全域性變數,而預設定義的變數在函式內部也可以正常訪問的
區域性變數使用local定義作用與函式內部的,比如上邊的local tmp_1=$1

#函式庫共享函式

cat /etc/init.d/network | grep -i 'functions'
. ./network-function

指令碼 /etc/init.d/network 通過 . ./network-function 函式庫 

../點符號符來使得指令碼能以呼叫庫檔案的函式,同理我們也可以定義自己的函式庫檔案

#完整指令碼

  1. #!/bin/bash 
  2. #tc htb  
  3. #20110927 by dongnan ver0.2 
  4. #variables 
  5. vlan=59
  6. ip=254
  7. ip_duan=10.0 
  8. num=0
  9. down_dev=eth1
  10. up_dev=eth0
  11. lie=253
  12. vip_download=1600Kbit
  13. vip_upload=1600Kbit
  14. iptables=/sbin/iptables
  15. tc=/sbin/tc
  16. #function 
  17. #classid 
  18. tc_classid() { 
  19. classid_vlan=$1 
  20. classid_ip=$2 
  21. if [[ "$classid_vlan" -ge "1" && "$classid_vlan" -le "9" ]];then 
  22.    echo $(((classid_vlan-1)*lie+(classid_ip-1))) 
  23. fi 
  24. if [[ "$classid_vlan" -ge "10" && "$classid_vlan" -le "49" ]];then 
  25.    echo "ip $classid_vlan error" 
  26. fi 
  27. if [[ "$classid_vlan" -ge "50" && "$classid_vlan" -le "59" ]];then 
  28.    echo $((($classid_vlan-41)*lie+(classid_ip-1))) 
  29. fi 
  30. #show 
  31. tc_show() { 
  32. local tmp_1=$1 
  33. local tmp_2=$2 
  34. local tmp_3=$3 
  35. local tmp_4=$4 
  36. show=`tc_classid $tmp_3 $tmp_4`   
  37. if [ "$tmp_2" = down ];then 
  38.     tmp_2=eth1
  39. else 
  40.     tmp_2=eth0
  41. fi 
  42. tc class show dev $tmp_2 | grep ":${show}[ ]" | awk '{print "speed:" $11}' 
  43. echo "$tmp_1 $tmp_2 $tmp_3 $tmp_4" 
  44. echo "classid:$show" 
  45. #change 
  46. tc_change() { 
  47. local tmp_1=$1 
  48. local tmp_2=$2 
  49. local tmp_3=$3 
  50. local tmp_4=$4 
  51. local tmp_5=$5 
  52. show=`tc_classid $tmp_3 $tmp_4`   
  53. echo "$tmp_1 $tmp_2 $tmp_3 $tmp_4 $tmp_5" 
  54. echo $show 
  55. if [ "$tmp_2" = "dual" ];then 
  56.     tc class change dev $down_dev parent 1:9999 classid 1:$show htb rate $vip_download ceil $vip_downlad burst 20kb 
  57.     tc class change dev $up_dev parent 2:9999 classid 2:$show htb rate $vip_upload ceil $vip_upload burst 20kb 
  58. fi 
  59. if [ "$tmp_2" = "down" ];then 
  60.     tc class change dev $down_dev parent 1:9999 classid 1:$show htb rate $tmp_5 ceil $tmp_5 burst 20kb 
  61. fi 
  62. if [ "$tmp_2" = "up" ];then 
  63.     tc class change dev $up_dev parent 2:9999 classid 2:$show htb rate $tmp_5 ceil $tmp_5 burst 20kb 
  64. fi 
  65. #clear 
  66. tc_clear() { 
  67.     tc qdisc del dev $down_dev root handle 1: 
  68.     tc qdisc del dev $up_dev root handle 2: 
  69. #start 
  70. tc_start() { 
  71. #root handle|parent class  
  72. tc qdisc add dev $down_dev root handle 1: htb 
  73. tc class add dev $down_dev parent 1: classid 1:9999 htb rate 25Mbit 
  74. tc qdisc add dev $up_dev root handle 2: htb 
  75. tc class add dev $up_dev parent 2: classid 2:9999 htb rate 25Mbit 
  76. #class 
  77. for((i=1;i<=$vlan;i++));do 
  78.     if [ "$i" -ge "10" -a "$i" -le "49" ];then 
  79.         continue 
  80.     fi 
  81.     #echo $i 
  82.     for((k=2;k<=ip;++k));do 
  83.     ((num++)) 
  84.     echo "\$classid is $num" 
  85.     #echo "\$vlan is $vlan" 
  86.     #echo "\$ip is 10.0.$i.$k" 
  87. #down-eth1 
  88. tc class add dev $down_dev parent 1:9999 classid 1:$num htb rate 800Kbit ceil 900Kbit burst 15k 
  89. tc qdisc add dev $down_dev parent 1:$num sfq quantum 1514b perturb 10 
  90. tc filter add dev $down_dev protocol ip parent 1: prio 1 u32 match ip dst "$ip_duan.$i.$k" flowid 1:$num 
  91. #up-eth0 
  92. tc class add dev $up_dev parent 2:9999 classid 2:$num htb rate 800Kbit ceil 1600Kbit burst 10k 
  93. tc qdisc add dev $up_dev parent 2:$num sfq quantum 1514b perturb 10 
  94. tc filter add dev $up_dev parent 2: protocol ip prio 10 handle $num fw classid 2:$num 
  95. #up-eth0-iptables_mark 
  96. iptables -t mangle -A PREROUTING -i $down_dev -s "$ip_duan.$i.$k" -j MARK --set-mark $num 
  97.     done 
  98. done 
  99. #main 
  100. case "$1" in 
  101.    start) 
  102.        echo "start!" 
  103.        tc_start 
  104.        exit 0 
  105.        ;; 
  106.    clear) 
  107.        echo "clear!" 
  108.        tc_clear 
  109.        exit 0 
  110.        ;; 
  111.    change) 
  112.        echo "change!" 
  113.        if [ $# -eq 1 -o $# -lt 5 ];then 
  114.           echo $? 
  115.       echo "Usage: $0 change arg1(type) arg2(vlan) arg3(ip_end) arg4(bandwidth)" 
  116.           echo "Usage: $0 change down 1 100 800Kbit" 
  117.           echo "Usage: $0 change up 1 199 800Kbit" 
  118.           exit 1 
  119.       else 
  120.           tc_change $1 $2 $3 $4 $5 
  121.           exit 0 
  122.        fi 
  123.        ;; 
  124.    show) 
  125.       echo "show!" 
  126.       if [ $# -eq 1 -o $# -lt 4 ];then 
  127.       echo "Usage: $0 show arg1(type) arg2(vlan) arg3(ip_end)" 
  128.           echo "Usage: $0 show down 1 100 || show up 1 199" 
  129.           exit 1 
  130.       else 
  131.           tc_show $1 $2 $3 $4 
  132.       fi 
  133.       ;; 
  134.     *) 
  135.        echo "Usage: $0 {start|clear|change|show}" 
  136.        exit 1 
  137. esac 

二 tc 與 htb

使用tc之前,請記住以下幾條!

0 佇列分為有類佇列和無類佇列

1 每個網絡卡只能有一種佇列,比如cbq htb 或者pfifo_fast, tbf,sfq 前兩個為有類佇列,後三個為無類佇列

2 佇列裡必須要有一個父類

3 佇列裡可以有多個子類,每個子類可以制定相應佇列規則

4 無論是佇列,還是classfilter都有ID佇列最大Classid 數量9999個超過則報錯,這其中包含父類與子類

5 利用佇列我們只能對對網絡卡傳送的資料進行控制,這也是為什麼要在兩個網絡卡上做流量控制,外網絡卡(eth0)做上行控制,內網絡卡(eth1)做下行控制!

6 如果要根據ip地址控制內網頻寬,則必須能夠在瓶頸處控制,也就是資料包必須經過閘道器,而且要使用過濾器比如u32!

概念

TCTrafficControl的縮寫,我們可以通過配置TC HTB規則來實現流量控制.
TC規則涉及到佇列(queue),分類器(class)和過濾器(filter)三個概念.

佇列(queue)用來實現控制網路的收發速度.通過佇列,linux可以將網路資料包快取起來,然後根據使用者的設定,在儘量不中斷連線(TCP)的前提下來平滑網路流量.需要注意的是,linux對接收佇列的控制不夠好,所以我們一般只用傳送佇列,即控發不控收

分類器(class)用來表示控制策略.很顯然,很多時候,我們很可能要對不同的IP實行不同的流量控制策略,這時候我們就得用不同的class來表示不同的控制策略了.

過濾器(filter)用來將使用者劃入到具體的控制策略中(即不同的class).正如前述,我們要對A,B兩個IP實行不同的控制策略(C,D),這時,我們可 filterA劃入到控制策略C,將B劃入到控制策略Dfilter劃分的標誌位可用u32打標功能或IPtablesset-mark功能來實現。

應用場景

1 閘道器上控制內網頻寬,上面的指令碼其實就是以下幾句tc命令為基礎的!

eth0 外網絡卡

  1. #建立根佇列

  2. tc qdisc add dev eth1 root handle 1: htb 

  3. #建立父類

  4. tc class add dev eth1 parent 1: classid 1:9999 htb rate 30Mbit

  5. #建立子類

  6. tc class add dev eth1 parent 1:9999 classid 1:1 htb rate 512Kbit ceil 400Kbit 

  7. #建立過濾器

  8. tc filter add dev eth1 protocol ip parent 1: prio 1 u32 match ip dst 10.0.5.82 flowid 1:1 

  9. #更改子類規則

  10. tc class change dev eth1 parent 1:9999 classid 1346 htb rate 1200Kbit ceil 1200Kbit 

eth1 內網絡卡

  1. #與上邊基本相同,不同處為dev eth0,fiter規則不同

  2. tc qdisc add dev eth0 root handle 2: htb 
  3. tc class add dev eth0 parent 2: classid 2:9999 htb rate 30Mbit 
  4. tc class add dev eth0 parent 2:9999 classid 2:1 htb rate 512Kbit ceil 400Kbit 
  5. tc filter add dev eth0 parent 2: protocol ip prio 10 handle 2:1 fw classid 2:1
  6. iptables -t mangle -A PREROUTING -i eth1 -s 10.0.5.82 -j MARK --set-mark $num 

2 伺服器上限制某個服務使用的頻寬,比如http 80

  1. tc qdisc add dev eth1 root handle 1: htb default 30 
  2. tc class add dev eth1 parent 1: classid 1:1 htb rate 6mbit burst 15k 
  3. tc class add dev eth1 parent 1:1 classid 1:10 htb rate 5mbit burst 15k 
  4. tc class add dev eth1 parent 1:1 classid 1:20 htb rate 1mbit ceil 6mbit burst 15k 
  5. tc class add dev eth1 parent 1:1 classid 1:30 htb rate 1kbit ceil 6mbit burst 15k 
  6. tc qdisc add dev eth1 parent 1:10 handle 10: sfq perturb 10 
  7. tc qdisc add dev eth1 parent 1:20 handle 20: sfq perturb 10 
  8. tc qdisc add dev eth1 parent 1:30 handle 30: sfq perturb 10 
  9. tc filter add dev eth1 protocol ip parent 1:0 prio 1 u32 match ip sport 80 0xffff flowid 1:10 
  10. tc filter add dev eth1 protocol ip parent 1:0 prio 1 u32 match ip sport 22 0xffff flowid 1:20 

佇列規定:根、控制代碼、父輩兄弟

每塊網絡卡都有一個出口“根佇列規定”(root),預設情況下是 pfifo_fast 佇列

佇列規定。每個佇列規定都指定一個控制代碼(handle),以便以後的配置語句能夠引用這個佇列規定。
佇列規定的控制代碼有兩個部分:一個主號碼和一個次號碼。習慣上把根佇列規定稱為“1:”,等價於“1:0”。佇列規定的次號碼永遠是 0。類的主號碼必須與它們父輩的主號碼一致。

(default 30)表示當某個ip流不滿足任何已設定的filter規則時,將自動歸入class 30.

HTB(Hierarchical Token Bucket, 分層的令牌桶)
HTB 就象 CBQ 一樣工作,但是並不靠計算閒置時間來整形。它是一個分類的令
牌桶過濾器。它只有很少的引數
 

SFQ(Stochastic Fairness Queueing,隨機公平佇列)是公平佇列演算法家族中的一個
簡單實現。它的精確性不如其它的方法,但是它在實現高度公平的同時,需要的
計算量卻很少。
perturb
多少秒後重新配置一次雜湊演算法。如果取消設定,雜湊演算法將永遠不會重新配置(不建議這樣做);10 秒應該是一個合適的值

quantum
一個流至少要傳輸多少位元組後才切換到下一個佇列。卻省設定為一個最大包的長度(MTU 的大小)。不要設定這個數值低於 MTU!

rate是指在頻寬緊張的情況下的最大網路速度,當頻寬空閒時,class可通過向其兄弟借用頻寬而達到ceil大的網路速度,注意,借用表兄弟之間也可借用帶框.prio用來指示借用頻寬時的競爭力,prio越小,優先順序越高,競爭力越強.

man tc

  1. Usage: ... qdisc add ... htb [default N] [r2q N] 
  2.  default  minor id of class to which unclassified packets are sent {0}  
  3. r2q      DRR quantums are computed as rate in Bps/r2q {10} 
  4. debug    string of 16 numbers each 0-3 {0}  
  5. ... class add ... htb rate R1 [burst B1] [mpu B] [overhead O] 
  6. [prio P] [slot S] [pslot PS]  
  7. [ceil R2] [cburst B2] [mtu MTU] [quantum Q] 
  8. rate     rate allocated to this class (class can still borrow) 
  9.  burst    max bytes burst which can be accumulated during idle period {computed} 
  10. mpu      minimum packet size used in rate computations 
  11. overhead per-packet size overhead used in rate computations 
  12. ceil     definite upper class rate (no borrows) {rate} 
  13.  cburst   burst but for ceil {computed} 
  14. mtu      max packet size we create rate map for {1600} 
  15. prio     priority of leaf; lower are served first {0}  
  16. quantum  how much bytes to serve from leaf at once {use r2q}