雲邊通信中如何對邊緣節點進行流量整形?


1、問題背景

之前項目需要一個邊緣集群場景下的鏡像緩存功能,但隨之而來的問題是:

由於邊緣節點網絡帶寬資源有限,加上雲邊網絡的不穩定性,當邊緣節點頻繁拉取雲端鏡像文件時會占用大量的網絡帶寬,這樣會阻礙其他網絡應用程序的運行,比如此時我們的交互式SSH會話可能會變得異常遲鈍以至於無法使用。因此我們需要一種方法來對高帶寬應用進行流量整形。

目前可以根據 image id、container id來對特定容器進行帶寬限制,下一步准備在k8s環境中進行運用。

代碼地址:YRXING/bandwidth-limit

2、高效Linux限流神器——Trickle

如果在容器內使用trickle工具,加上--cap-add=NET_ADMIN選項

2.1、安裝與使用

yum install trickle

#源碼編譯安裝
wget https://codeload.github.com/mariusae/trickle/zip/master
mv master trickle-master.zip
unzip trickle-master.zip
yum -y install autoconf automake libtool libevent-devel
autoreconf -if
./configure
make
make install

#使用時,僅需要放在你想要運行的命令之前
trickle -d <download-rate> -u <update-rate> <command> #單位 KB/s

#trickle還可以以守護進程形式啟動,它可以限制通過trickle啟動的所有程序的總帶寬使用
trickle -d 1000

2.2、trickle如何工作的

Trickle通過控制socket數據讀寫量來控制和限制應用的上傳/下載速度。它使用另一個版本的BSD套接字API,但區別就是trickle還管理socket調用。

但是要注意的是trickle使用動態鏈接和加載,所以它只對於使用glibc庫的程序有用。由於trickle可以設置數據在socket上的傳輸延遲,所以它可以用來限制一個應用的網絡帶寬。

Trickle通過在程序運行時,預先加載一個速率限制 socket 庫 的方法,trickle 命令允許你改變任意一個特定程序的流量。trickle 命令有一個很好的特性是它僅在用戶空間中運行,這意味着,你不必需要 root 權限就可以限制一個程序的帶寬使用。要能使用 trickle 程序控制程序的帶寬,這個程序就必須使用非靜態鏈接庫的套接字接口。當你想對一個不具有內置帶寬控制功能的程序進行速率限制時,trickle 就派上用場了。

socket編程中,可以通過修改發送和接收緩沖區的大小,完全利用可用的帶寬。

int ret, sock, sock_buf_size;
sock = socket( AF_INET, SOCK_STREAM, 0 );
sock_buf_size = BDP;
ret = setsockopt( sock, SOL_SOCKET, SO_SNDBUF,
                   (char *)&sock_buf_size, sizeof(sock_buf_size) );
ret = setsockopt( sock, SOL_SOCKET, SO_RCVBUF,
                   (char *)&sock_buf_size, sizeof(sock_buf_size) );

3、控制網卡帶寬——wondershaper

3.1、安裝與使用

yum install wondershaper

#使用
wondershaper <interface> <download-rate> <update-rate> #單位KB/s
#比如限制eth0網卡帶寬
wondershaper etho 1000 500
#解除限制
wondershaper clear etho

wondershaper實際上是一個shell腳本,它使用tc來定義流量調整命令,使用QoS來處理特定的網絡接口。外發流量通過放在不同優先級的隊列中,達到限制傳出流量速率的目的,而傳入流量通過丟包的方式來達到速率限制的目的。

4、Linux自帶高級流控——tc

Linux操作系統中的流量控制器TC(Traffic Control)用於Linux內核的流量控制,主要是通過在輸出端口處建立一個隊列來實現流量控制。tc屬於 iproute2 可以直接修改內核的流控設置。

iproute2簡介
iproute2是linux下管理控制TCP/IP網絡和流量控制的新一代工具包,旨在替代老派的工具鏈net-tools,即大家比較熟悉的ifconfig,arp,route,netstat等命令。
要說這兩套工具本質的區別,應該是net-tools是通過procfs(/proc)和ioctl系統調用去訪問和改變內核網絡配置,而iproute2則通過netlink套接字接口與內核通訊。

其次,net-tools的用法給人的感覺是比較亂,而iproute2的用戶接口相對net-tools來說相對來說,更加直觀。比如,各種網絡資源(如link、IP地址、路由和隧道等)均使用合適的對象抽象去定義,使得用戶可使用一致的語法去管理不同的對象

img

Linux流量控制主要分為建立隊列、建立分類和建立過濾器三個方面。

4.1、原理

它以qdisc-class-filter的樹形結構來實現對流量的分層控制

Traffic Control

Traffic Control

  • qdisc通過隊列將數據包緩存起來,用來控制網絡收發的速度
  • class用來表示控制策略
  • filter用來將數據包划分到具體的控制策略中

類(Class)組成一個樹,每個類都只有一個父類,而一個類可以有多個子類。某些QDisc(例如:CBQ和HTB)允許在運行時動態添加類,而其它的QDisc(例如:PRIO)不允許動態建立類。允許動態添加類的QDisc可以有零個或者多個子類,由它們為數據包排隊。此外,每個類都有一個葉子QDisc,默認情況下,這個葉子QDisc使用pfifo的方式排隊,我們也可以使用其它類型的QDisc代替這個默認的QDisc。而且,這個葉子QDisc有可以分類,不過每個子類只能有一個葉子QDisc。 當一個數據包進入一個分類QDisc,它會被歸入某個子類。 我們可以使用以下三種方式為數據包歸類,不過不是所有的QDisc都能夠使用這三種方式:

  • tc過濾器(tc filter): 如果過濾器附屬於一個類,相關的指令就會對它們進行查詢。過濾器能夠匹配數據包頭所有的域,也可以匹配由ipchains或者iptables做的標記。
  • 服務類型(Type of Service): 某些QDisc有基於服務類型(Type of Service,ToS)的內置的規則為數據包分類。
  • skb->priority: 用戶空間的應用程序可以使用SO_PRIORITY選項在skb->priority域設置一個類的ID。 樹的每個節點都可以有自己的過濾器,但是高層的過濾器也可以直接用於其子類。 如果數據包沒有被成功歸類,就會被排到這個類的葉子QDisc的隊中。

4.2、應用

  1. 針對端口進行限速

    #查看現有的隊列
    tc -s qdisc ls dev eth0
    
    #查看現有的分類
    tc -s class ls dev eth0
    
    #創建隊列
    tc qdisc add dev eth0 root handle 1:0 htb default 1 
    #添加一個tbf隊列,綁定到eth0上,命名為1:0 ,默認歸類為1
    #handle:為隊列命名或指定某隊列
    
    #創建分類
    tc class add dev eth0 parent 1:0 classid 1:1 htb rate 10Mbit burst 15k
    #為eth0下的root隊列1:0添加一個分類並命名為1:1,類型為htb,帶寬為10M
    #rate: 是一個類保證得到的帶寬值.如果有不只一個類,請保證所有子類總和是小於或等於父類.
    #ceil: ceil是一個類最大能得到的帶寬值.
    
    #創建一個子分類
    tc class add dev eth0 parent 1:1 classid 1:10 htb rate 10Mbit ceil 10Mbit burst 15k
    #為1:1類規則添加一個名為1:10的類,類型為htb,帶寬為10M
    
    #為了避免一個會話永占帶寬,添加隨即公平隊列sfq.
    tc qdisc add dev eth0 parent 1:10 handle 10: sfq perturb 10
    #perturb:是多少秒后重新配置一次散列算法,默認為10秒
    #sfq,他可以防止一個段內的一個ip占用整個帶寬
    
    #使用u32創建過濾器
    tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip sport 22 flowid 1:10
    
    #刪除隊列
    tc qdisc del dev eth0 root
    
    配置完成后加入本地啟動文件:  
    /etc/rc.local
    
  2. 針對ip進行限速:

    情景: 因為帶寬資源有限(20Mbit≈2Mbyte),使用git拉取代碼的時候導致帶寬資源告警,所以對git進行限速,要求:內網不限速;外網下載速度為1M左右。(注意:此處需要注意單位轉換1byte=8bit)...

    #!/bin/bash
    #針對不同的ip進行限速
    
    #清空原有規則
    tc qdisc del dev eth0 root
    
    #創建根序列
    tc qdisc add dev eth0 root handle 1: htb default 1
    
    #創建一個主分類綁定所有帶寬資源(20M)
    tc class add dev eth0 parent 1:0 classid 1:1 htb rate 20Mbit burst 15k
    
    #創建子分類
    tc class add dev eth0 parent 1:1 classid 1:10 htb rate 20Mbit ceil 10Mbit burst 15k
    tc class add dev eth0 parent 1:1 classid 1:20 htb rate 20Mbit ceil 20Mbit burst 15k
    
    #避免一個ip霸占帶寬資源(1有講到)
    tc qdisc add dev eth0 parent 1:10 handle 10: sfq perturb 10
    tc qdisc add dev eth0 parent 1:20 handle 20: sfq perturb 10
    
    #創建過濾器
    #對所有ip限速
    tc filter add dev eth0 protocol ip parent 1:0 prio 2 u32 match ip dst 0.0.0.0/0 flowid 1:10
    #對內網ip放行
    tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip dst 12.0.0.0/8 flowid 1:20
    
  3. 控制出口流量

    基於 classless 隊列,我們可以進行故障模擬,也可以用來限制帶寬。TC 使用 linux network netem 模塊進行網絡故障模擬。

    #模擬網絡延遲
    tc qdisc add dev eth0 root netem delay 100ms #添加一個固定延遲到本地網卡eth0
    tc qdisc change dev eth0 root netem delay 100ms 10ms #延遲有10ms的上下波動
    tc qdisc change dev eth0 root netem delay 100ms 10ms 25% #添加25%的相關概率
    tc qdisc change dev eth0 root netem delay 100ms 10ms distribution normal  #讓波動成正太分布
    #模擬網絡丟包
    tc qdisc change dev eth0 root netem loss 1% #丟包率1%
    tc qdisc change dev eth0 root netem loss 1% 25% #當前丟包概率與上一條數據包丟包概率有25%的相關性
    #模擬數據包重復
    tc qdisc change dev eth0 root netem duplicate 1%
    #模擬數據包損壞
    tc qdisc change dev eth0 root netem corrupt 2%
    #模擬數據包亂序
    tc qdisc change dev eth0 root netem gap 5 delay 10ms #沒5th的包延遲10ms
    tc qdisc change dev eth0 root netem delay 10ms reorder 25% 50% # 25%的立刻發送(50%的相關性),其余的延遲 10ms
    
  4. 控制入口流量

    使用 TC 進行入口限流,需要把流量重定向到 ifb 虛擬網卡,然后在控制 ifb 的輸出流量

    # 開啟 ifb 虛擬網卡
    modprobe ifb numifbs=1
    ip link set ifb0 up
    
    # 將 eth0 流量重定向到 ifb0
    tc qdisc add dev eth0 ingress handle ffff:
    tc filter add dev eth0 parent ffff: protocol ip prio 0 u32 match u32 0 0 flowid ffff: action mirred egress redirect dev ifb0
    
    # 然后就是限制 ifb0 的輸出就可以了
    # ......
    
  5. 監視

    #顯示隊列的情況
    tc [-s] qdisc|class|filter| ls dev eth0 # -s 顯示詳細信息
    

4.3、隊列規則Qdisc詳解

無分類的qdisc:只能用於root隊列

  • [p|b]fifo:簡單先進先出(p是package b是byte)

  • pfifo_fast:根據數據包的tos將隊列划分到3個band,內核會檢查數據包的TOS字段,將“最小延遲”的包放到band0。每個band內部先進先出,一個網絡接口上如果沒有設置QDisc,pfifo_fast就作為缺省的QDisc。

    img

    不要將 pfifo_fast qdisc 與后面介紹的 PRIO qdisc 混淆,后者是 classful 的! 雖然二者行為類似,但 pfifo_fast 是無類別的,這意味你無法通過 tc 命令向 pfifo_fast 內添加另一個 qdisc

  • TBF:對於沒有超過預設速率的流量直接透傳,但也能容忍超過預設速率的短時抖動。如果想對一個網卡限速,TBF是第一選擇。

    img

    在實際實現中,token基於字節數,而不是包數。下面是一些可配置的參數:

    limit or latency:
    	limit是因等待token而被放入隊列的字節數,latency是每個包在TBF中停留的時間。limit基於latency、bucket size、rate、			peakrate來計算。
    burst/buffer/maxburst:
    	bucket的大小,單位是字節。總體來說,越大的rates就需要越大的緩沖區。要在intel網卡上實現10mbit/s整流,至少需要10kbyte緩沖區,最小緩	沖區的大小可以通過rate/HZ來計算得到。如果緩沖區太小,可能會丟包,因為 token 到來太快導致無法放入 bucket 中。
    mpu:
    	最小包單元,對於以太網,任何一個包的字節數不會少於64
    rate:
    	流量整形速率
    peakrate:
    	默認情況下,包到了之后只要有 token 就會被立即發送。這可能不是你期 望的,尤其當 bucket 很大的時候。peakrate 可指定 			bucket 發送數據的最快速度
    mtu/minburst:
    	計算最大可能的 peakrate 時,用 MTU 乘以 100(更准確地說,乘以 HZ 數,例如 Intel 上是 100,Alpha 上是 1024)
    
  • SFQ:按照會話對流量排序並循環發送每個會話的數據包,SFQ將TCP和UDP流量平均的分配至多個FIFO隊列中,每個隊列對應一個或多個會話,然后按照輪詢的方法依次從每個FIFO隊列中取數據發送。保證數據發送的公平性。這種方式保證每一個會話都不會被其他會話所淹沒,會話通過散列算法映射到某個隊列里去。

    SFQ 只有在實際出向帶寬已經非常飽和的情況下才有效,這一點非常重要!說的更明確一點:沒用配套的整流配置的話,單純在(連接 modem 的)以太網接口上配置SFQ 是毫無意義的。

    img

    參數和用法:

    perturb:
    	每隔多少秒就重新配置哈希算法,10s是個不錯的選擇。
    quantum:
    	當前queue允許出隊的最大字節數,默認是一個MTU。
    limit:
    	SFQ能緩存的最大包數。
    

使用建議

  • 單純對出口流量限速使用TBF,針對大帶寬進行限速,需要將bucket調大。
  • 如果帶寬已經被打滿,想確保帶寬沒有任何單個session占據,推薦使用SFQ。
  • 如果你不需要整形,只是想看看網絡接口是否過載,使用pfifo(內部沒有bands,但會記錄backlog的大小)。

有分類的qdisc(可以包括多個隊列)

如果想對不同的流量做不同的處理,那么classful qdisc非常有用。

  • CBQ:基於類的排隊規則是最復雜最花哨的,CBQ 的工作原理是:在發送包之前等待足夠長的時間,以將帶寬控制到期望 的閾值。為實現這個目標,它需要計算包之間的等待間隔。由於它與Linux的工作方式不是太匹配,導致它的算法不精確。

    相關參數:

    avpkt:
    	平均包長,單位是字節,計算maxidle會用到
    bandwidth:
    	設備物理帶寬,計算idle time會用到
    cell:
    	包長的增長步長,默認值是8,必須是2的冪。
    maxburst:
    	計算 maxidle 時用到,單位:包數(number of packets)。
    	當 avgidle == maxidle 時,可以並發發送 maxburst 個包,直到 avgidle == 0。 注意 maxidle 是無法直接設置的,只能通過	 這個參數間接設置。
    minburst:
      前面提到,overlimit 情況下 CBQ 要執行 throttle。理想情況下是精確 throttle calculated idel time,然后發送一個包。		但對 Unix 內核來說,通常很難調度 10ms 以下精度的事件,因此最好的方式就是 throttle 更長一段時間,然后一次發 送 					minburst 個包,然后再睡眠 minburst 倍的時間。
    	從較長時間跨度看,更大的 minburst 會使得整形更加精確,但會導致在毫秒級別有更大的波動性。
    minidle:
    	如果 avgidle < 0,那說明 overlimits,需要等到 avgidle 足夠大才能發送下一個包。 為防止突然的 burst 打爆鏈路帶寬,當 	avgidle 降到一個非常小的值之后,會 reset 到 minidle。minidle單位是負微秒
    mpu:
    	最小包長
    rate:
    	期望離開這個qdisc的流量速率
    

    除了整形之外,CBQ 也能完成類似 PRIO queue 的功能 。每次硬件層請求一個數據包來發送時,都會開啟一個 weighted round robin (WRR)過程, 從優先級最高的 class 開始(注意,優先級越高對應的 priority number 越小)

    allot:
    	每次輪到一個class時發送的數據量大小
    prio:
    	內部的classes都有一個優先級prio
    weight:
    	這個參數用於 WRR 過程。每個 class 都有機會發送數據。如果要指定某個 class 使用更大的帶寬,就調大其 weight
      CBQ 會將一個 class 內的所有權重歸一化,因此指定用整數還是小數都沒關系:重要的是比例。大家的經驗值是 “rate/10”,這個值		看上去工作良好。歸一化后的 weight 乘以 allot,決定了每次能發送的數據量
    

    除了限制特定類型的流量,還能指定哪些 class 能從另外哪些 class 借容量(borrow capacity)或者說,借帶寬

    isolated/sharing:
    	配置了 isolated 的 class 不會向 sibling classes 借出帶寬,sharing作用相反
    bounded/borrow:
    	也可以配置 class 為 bounded,這表示它不會向其他 siblings 借帶寬,borrow作用相反
    

    示例配置:

    截屏2021-06-19 下午3.32.12

    # webserver 限制為 5Mbps
    # SMTP 流量限制為 3Mbps
    # webserver + SMTP 總共不超過6Mbps
    # 無力網卡是100Mbps
    # 每個class之間可以互借寬帶
    
    tc qdisc add dev eth0 root handle 1:0 cbq bandwidth 100Mbit avpkt 1000 cell 8
    tc class add dev eth0 parent 1:0 classid 1:1 cbq bandwidth 100Mbit rate 6Mbit weight 0.6Mbit prio 8 \
    allot 1514 cell 8 maxburst 20 avpkt 1000 bounded # 這個1:1class是bounded類型,因此總帶寬不會超過設置的6Mbps限制。
    
    tc class add dev eth0 parent 1:1 classid 1:3 cbq bandwidth 100Mbit \
    rate 5Mbit wight 0.5Mbit prio 5 allot 1514 cell 8 maxburst 20 avpkt 1000
    tc class add dev eth0 parent 1:1 classid 1:4 cbq bandwidth 100Mbit \
    rate 3Mbit wight 0.3Mbit prio 5 allot 1514 cell 8 maxburst 20 avpkt 1000
    
    tc qdisc add dev eth0 parent 1:3 handle 30: sfq
    tc qdisc add dev eth0 parent 1:4 handle 40: sfq
    
    #這些過濾規則直接作用在root qdisc,作用是將流量分類到下面正確的qdisc
    tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip sport 80 0xffff flowid 1:3
    tc filter add dev eth0 parent 1:0 protocol ip prio 1 u32 match ip sprot 25 0xffff flowid 1:4
    

    沒有匹配到以上兩條規則的流量會進入1:0接受處理,而這里是沒有限速的。

    如果SMTP+web的總帶寬超過6Mbps,那總帶寬將根據給定的權重參數分為兩部分,5/8給webserver,3/8給郵件服務。也就是說webserver在任何情況下至少能獲得5/8 * 6Mbps = 3.75Mbps帶寬。

  • HTB:由於CBQ太復雜,devik設計了HTB。

    適用場景:

    有一個固定總帶寬,想將其分割成幾個部分,分別用作不同的目的;每個部分的帶寬是保證的;還可以指定每個部分向其他部分借帶寬。

    img

    HTB工作方式與CBQ類似,但不是借助於計算空閑時間來實現整形,在內部,它其實是一個 classful TBF。

    示例配置:

    # 功能和前面CBQ配置一樣的HTB配置
    tc qdisc add dev eth0 root handle 1: htb default 30
    
    tc class add dev eth0 parent 1: classid 1:1 htb rate 6mbit burst 15k
    
    tc class add dev eth0 parent 1:1 classid 1:10 htb rate 5mbit burst 15k
    tc class add dev eth0 parent 1:1 classid 1:20 htb rate 3mbit ceil 6mbit burst 15k
    tc class add dev eth0 parent 1:1 classid 1:30 htb rate 1kbit ceil 6mbit burst 15k
    
    #htb作者推薦在這些class內部適用SFQ
    tc qdisc add dev eth0 parent 1:10 handle 10: sfq perturb 10
    tc qdisc add dev eth0 parent 1:20 handle 20: sfq perturb 10
    tc qdisc add dev eth0 parent 1:30 handle 30: sfq perturb 10
    
    #將流量導向這些class的過濾器
    U32="tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32"
    U32 match ip dport 80 0xffff flowid 1:10
    U32 match ip sport 25 0xffff flowid 1:20
    

    未分類的流量會進入30: 這個band帶寬很小,但能夠從剩余的可用帶寬中借帶寬來用。

  • PRIO:優先級排隊規則實際上並不進行整形,它僅僅根據你配置的過濾器把流量分類,缺省會自動創建三個FIFO類。(它不允許動態添加類)

    img

    prio像是pfifo_fast的升級版,它也有多個band,但每個band都是一個class,如果想基於tc filters而不僅僅是TOS flags做流量的優先級分類,這個qdisc非常有用。由於prio沒有流量整形的功能,因此針對SFQ的忠告也適用於這里。

    img

    相關參數:

    bands:
    	需要創建的band數量
    priomap:
    	如果沒有提供 tc filters 來指導如何對流量分類,那 PRIO qdisc 將依據 TC_PRIO 優先級來決定優先級
    

    PRIO qdisc 里面的 band 都是 class,默認情況下名字分別為 major:1major:2major:3, 因此如果你的 PRIO qdisc 是 12:,那 tc filter 送到 12:1 的流量就有更高的優先級。

    重復一遍:band 0 對應的 minor number 是 1! band 1 對應的 minor number 是 2 ,以此類推。

    示例配置:

    截屏2021-06-19 下午2.21.10

    高吞吐量流量將會進入優先級比較低的30:這個band,而交互式流量會進入優先級更高的bands 10:或20:

    tc qdisc add dev eth0 root handle 1: prio #立即創建類 1:1 1:2 1:3
    tc qdisc add dev eth0 parent 1:1 handle 10: sfq
    tc qdisc add dev eth0 parent 1:2 handle 20: tbf rate 20kbit buffer 1600 limit 3000
    tc qdisc add dev eth0 parent 1:3 handle 30: sfq
    
    #查看排隊規則
    tc -s qdisc ls dev eth0
    

4.4、用過濾器對流量進行分類

class用來表示控制策略,只用於有分類的qdisc上。每個class要么包含多個子類,要么只包含一個子qdisc。當然,每個class還包括一些列的filter,控制數據包流向不同的子類,或者是直接丟掉。

filter用來將數據包划分到具體的控制策略中,filter是在qdisc內部。包括以下幾種:

  • u32:根據協議、IP、端口等過濾數據包

    # 匹配源/目的IP
    match ip src/dst 1.2.3.0/24
    # 匹配源/目的端口,任何IP協議
    match ip sport/dport 80 0xffff
    # 匹配ip protocol(tcp udp icmp gre ipsec)
    # 使用/etc/protocols里面的協議號,例如ICMP是1: match ip protocol 1 0xffff
    
  • fwmark:根據iptables MARK來過濾數據包

    # 可以用ipchains/iptables等工具對包打標(mark),這些mark在不同接口之間路由時是不會丟失的
    # 例如:只對從eth0進入eth1的流量進行整形的功能
    tc filter add dev eth1 protocol ip parent 1: prio 1 handle 6 fw flowid 1:1
    iptables -A PREROUTING -t mangle -i eth0 -j MARK --set-mark 6
    
    #下面的命令會打印 mangle 表內所有的 mark 規則,已經每個規則已經匹配到多少包和字節數:
    iptables -L -t mangle -n -v
    
  • tos:根據tos字段過濾數據包

    #選擇交互式、最小延遲流量:
    tc filter add dev ppp0 parent 1: protocol ip prio 10 u32 match ip tos 0x10 0xff flowid 1:4
    #高吞吐流量(bulk traffic)對應的過濾條件是 0x08 0xff
    

    截屏2021-06-19 下午7.32.39

截屏2021-06-19 下午5.34.35

當enqueue一個包時,在每一個分叉的地方都需要查詢相關的過濾規則。

一種配置是:在1:1配置一個filter,將包送到12: 。在12:配置一個filter,將包送到12:2

另一種配置是將兩個filter都配置在1:1,但將更精確的filter放到更下面的位置有助於提升性能。

需要注意的是,包是無法向上過濾的(filter a packet ‘upwards’)。 另外,使用 HTB 時,所有的 filters 必須 attach 到 root

示例配置:

# 假設一個名為10:的PRIO qdisc,我們想將端口22的流量都導向優先級最高的band
tc filter add dev eth0 protocol ip parent 10: prio 1 u32 match ip dport 22 0xffff flowid 10:1
tc filter add dev eth0 protocol ip parent 10: prio 1 u32 match ip sprot 80 0xffff flowid 10:1
tc filter add dev eth0 protocol ip parent 10: prio 2 flowid 10:2 #為匹配上面的包都發送到優先級次高的band 10:2

# 精確匹配單個IP地址
tc filter add dev eth0 parent 10: protocol ip prio 1 u32 match ip dst 4.3.2.1/32 flowid 10:1
tc filter add dev eth0 parent 10: protocol ip prio 1 u32 match ip src 1.2.3.4/32 flowid 10:1
tc filter add dev eth0 parent 10: protocol ip prio 2 flowid 10:2
#這會將 dst_ip=4.3.2.1 或 src_ip=1.2.3.4 的流量送到優先級最高的隊列,其他流量送到優先級次高的隊列

#還可以將多個match級聯起來,同時匹配源IP和port
tc filter add dev eth0 parent 10: protocol ip prio 1 u32 match ip port src 4.3.2.1/32 \
match ip port 80 0xffff flowid 10:1

4.5、ingress shaping

IMQ

中介隊列設備用來解決兩個局限:

  • 只能進行出口整形
  • 一個隊列規定只能處理一塊網卡的流量,無法設置全局的限速

對外部進來的包先打上標記(mark),打了標的包在經過 netfilter NF_IP_PRE_ROUTINGNF_IP_POST_ROUTING hook 點時會被捕獲,送到 IMQ 設備上 attach 的 qdisc。就能實現入向整型(ingress shaping);將接口們作為 classes(treat interfaces as classes),就能設置全局限速

示例配置:

tc qdisc add dev imq0 root handle 1: htb default 20

tc class add dev imq0 parent 1: classid 1:1 htb rate 2mbit burst 15k

tc class add dev imq0 parent 1:1 classid 1:10 htb rate 1mbit
tc class add dev imq0 parent 1:1 classid 1:20 htb rate 1mbit

tc qdisc add dev imq0 parent 1:10 handle 10: pfifo
tc qdisc add dev imq0 parent 1:20 handle 20: sfq

tc filter add dev imq0 parent 10:0 protocol ip prio 1 u32 match \
ip dst 10.0.0.230/32 flowid 1:10

接下來選中流量,給他們打上標記,以便被正確送到imq0設備:

iptabels -t mangle -A PREROUTING -i eth0 -j IMQ --todev 0
ip link set imq0 up

ifb——見5.3

5、Linux網絡測試工具

5.1、實時網絡流量監控——iftop

Iftop工具主要用來顯示本機網絡流量情況及各相互通信的流量集合,如單獨同哪台機器間的流量大小,非常適合於代理服務器和iptables服務器使用,這樣可以方便的查看各客戶端流量情況。iftop可以用來監控網卡的實時流量(可以指定網段)、反向解析IP、顯示端口信息等,詳細的將會在后面的使用參數中說明。

# 安裝所需依賴包
yum -y install flex byacc libpcap ncurses ncurses-devel libpcap-devel

wget http://www.ex-parrot.com/pdw/iftop/download/iftop-0.17.tar.gz  #下載安裝包
tar zxvf iftop-0.17.tar.gz  #解壓
cd iftop-0.17
./configure --prefix=/usr/local/iftop  #配置安裝目錄
make && make install   #編譯、安裝
cp /usr/local/iftop /usr/local/bin

截屏2021-06-17 下午3.24.08

  • 默認使用第一個網絡接口eth0
  • 默認顯示rates的3列數據分別表示:最近2秒,10秒和40秒的平均流量。
  • peak指網絡速率的尖峰值(最大)
  • cum表示累積流量cumulative,在交互界面時按下T就可以看到主機對之間累計的網絡數據流量

交互界面操作

  • T - 顯示或隱藏累積網絡數據量(cumulative),會在顯示主機對的3列網絡速率rate左邊再增加一列顯示累積數據量cum
  • S - 顯示源端端口
  • D - 顯示目的端端口
  • n - 顯示主機IP地址而不是解析的主機名
  • 1/2/3 - 按照指定列進行排序
  • < - 根據源名字排序
  • > - 根據目的名字排序
  • P - 暫停顯示(否則就不斷更新當前顯示)
  • j/k - 滾動顯示
  • ? - 幫助

過濾和排序&啟動參數

在交互模式,按下l(表示limit)會在頂端顯示一個文本輸入框,可以輸入過濾規則,只顯示特定內容。

iftop大多數啟動參數和交互界面的快捷鍵相關。

使用-f參數是一種過濾特性數據包的方法,可以組合網絡,主機或端口。例如以下只顯示在/dev/wlan0無限網卡接口的ssh數據包

iftop -i wlan0 -f "dst port 22"
過濾器 描述
dst host 1.2.3.4 所有目標地址是1.2.3.4的數據包
src port 22 所有從端口22發出的數據包
dst portrange 22-23 端口范圍是22到23的數據包
gateway 1.2.3.5 使用網關地址1.2.3.5的數據包

5.2、帶寬測試工具——iperf3

Iperf可以測試最大TCP和UDP帶寬性能,具有多種參數和UDP特性,可以根據需要調整,可以報告帶寬、延遲抖動和數據包丟失.對於每個測試,它都會報告帶寬,丟包和其他參數,可在Windows、Mac OS X、Linux、FreeBSD等各種平台使用,是一個簡單又實用的小工具。

Iperf3也是C/S(客戶端/服務器端)架構模式,在使用iperf3測試時,要同時在server端與client端都各執行一個程序,讓它們互相傳送報文進行測試。

yum install -y iperf3

服務端命令行

-s    表示服務器端;
-p    定義端口號;
-i    設置每次報告之間的時間間隔,單位為秒,如果設置為非零值,就會按照此時間間隔輸出測試報告,默認值為零

客戶端命令行

-c    表示服務器的IP地址;
-p    表示服務器的端口號;
-t    參數可以指定傳輸測試的持續時間,Iperf在指定的時間內,重復的發送指定長度的數據包,默認是10秒鍾.

-i    設置每次報告之間的時間間隔,單位為秒,如果設置為非零值,就會按照此時間間隔輸出測試報告,默認值為零;

-w    設置套接字緩沖區為指定大小,對於TCP方式,此設置為TCP窗口大小,對於UDP方式,此設置為接受UDP數據包的緩沖區大小,限制可以接受數據包的最大值.

--logfile    參數可以將輸出的測試結果儲存至文件中.

-J  來輸出JSON格式測試結果.
-R  反向傳輸,缺省iperf3使用上傳模式:Client負責發送數據,Server負責接收;如果需要測試下載速度,則在Client側使用-R參數即可.
-u	采用udp協議

6、實驗過程

實驗一共兩個節點,IP為172.16.9.3/172.16.9.5。通過改變5號機的tc策略,將其帶寬限制在某個值以內,然后用3號機測試上傳速度是否符合預期。本次實驗主要測試tc的入口流量限制能否有效。

先看一下兩個節點之間原本的帶寬。

#3號機
iperf3 -s -p 80
#5號機
ieprf3 -c 172.16.9.3 -p 80 -R #測試下載速度

image-20210618100011686

現在限制5號機入口流量

#按照前面方法將流量重定向到ifb0網卡
tc qdisc add dev ifb0 root tbf rate 100mbit latency 50ms burst 1600

截屏2021-06-18 上午10.08.18

可以看到帶寬為80Mbit,雖然有誤差,但已經達起到了限制入口流量的作用。經過調試發現這么大的誤差跟burst參數值的設置有關,我索性增加10倍,改為16000。

讓我們多測幾次看一看誤差有多少,發現只要burst值設置的夠大,基本有4%-5%的誤差,可以接受。

限制帶寬 實際帶寬 Burst 誤差
10mbit 9.67mbit 16000 3.3%
100mbit 96mbit 16000 4%
200mbit 142mbit 1600 29%
350mbit 336mbit 160000 4%
350mbit 333mbit 16000 4.9%
500mbit 478mbit 16000 4.4%

7、拓展

7.1、百度網盤是怎么限速的?

百度網盤是在服務器端做了限速,控制發送數據的頻率來實現,而pandownload破解限速的原理就是開啟多線程。

7.2、QoS

服務質量(英語:Quality of Service,縮寫QoS)是一個術語,在分組交換網絡領域中指網絡滿足給定業務合約的幾率;或在許多情況下,非正式地指分組在網絡中兩點間通過的幾率。QoS是一種控制機制,它提供了針對不同用戶或者不同數據流采用相應不同的優先級,或者是根據應用程序的要求,保證數據流的性能達到一定的水准。QoS的保證對於容量有限的網絡來說是十分重要的,特別是對於流多媒體應用,例如VoIPIPTV等,因為這些應用常常需要固定的傳輸率,對延遲也比較敏感。

分組排序和帶寬分配的QoS機制分別是排隊和帶寬管理。然而,在實施之前,必須使用分類工具區分流量。根據策略對流量進行分類使組織能夠為其最重要的應用程序確保資源的一致性和足夠的可用性。

流量可以按端口或 IP 進行粗略分類,也可以使用更復雜的方法(例如按應用程序或用戶)進行分類。后面的參數允許更有意義的識別,並因此對數據進行分類

7.3、ifb虛擬網卡是什么?

Linux TC是一個控發不控收的框架,然而這是對於TC所置於的位置而言的,而不是TC本身的限制,事實上,你完全可以自己在ingress點上實現一個隊列機制,說TC控發不控收只是因為Linux TC目前的實現沒有實現ingress隊列而已。

ifb驅動模擬一塊虛擬網卡,它可以被看作是一個只有TC過濾功能的虛擬網卡,說它只有過濾功能,是因為它並不改變數據包的方向,即對於往外發的數據包被 重定向到ifb之后,經過ifb的TC過濾之后,依然是通過重定向之前的網卡發出去,對於一個網卡接收的數據包,被重定向到ifb之后,經過ifb的TC 過濾之后,依然被重定向之前的網卡繼續進行接收處理,不管是從一塊網卡發送數據包還是從一塊網卡接收數據包,重定向到ifb之后,都要經過一個經由ifb 虛擬網卡的dev_queue_xmit操作。

image-20210629202959459

除 了ingress隊列之外,在多個網卡之間共享一個根Qdisc是ifb實現的另一個初衷,可以從文件頭的注釋中看出來。如果你有10塊網卡,想在這10 塊網卡上實現相同的流控策略,你需要配置10遍嗎?將相同的東西抽出來,實現一個ifb虛擬網卡,然后將這10塊網卡的流量全部重定向到這個ifb虛擬網 卡上,此時只需要在這個虛擬網卡上配置一個Qdisc就可以了。

7.4、數據包傳輸過程中用到的各種隊列

驅動程序隊列(又名環形緩沖區)

在IP堆棧和NIC之間是驅動程序隊列,該隊列不包含數據包數據,他由指向稱為套接字內核緩沖區SKB的其他數據結構的描述符組成,這些數據結構保存數據包數據,並在整個內核中使用。

驅動程序隊列存在的原因是為了確保每當系統有數據要傳輸時,這些數據都可以立即傳輸給 NIC,也就是說它充當生產者-消費者中的緩存作用,使得數據的生產和消費異步起來,提高效率。

截屏2021-06-18 上午11.40.03

來自堆棧的大數據包

大多數NIC都有一個固定的最大傳輸單元MTU,對於以太網默認為1500字節。如果應用程序向TCP套接字寫入2000字節,則IP堆棧需要創建兩個IP數據包並通過驅動程序隊列傳輸,更多的數據包意味着更多的網絡協議頭部開銷。為了避免這種情況,Linux內核實施了很多優化:TCP分段卸載(TSO)、UDP分段卸載(UFO)和通用分段卸載(GSO)。

圖 3 - 啟用 TSO、UFO 或 GSO 時,將大數據包發送到設備。 這會大大增加驅動程序隊列中的字節數。

當啟用 TSO、UFO 或 GSO 時,可以將大數據包發送到 NIC。這會大大增加驅動程序隊列中的字節數。得注意的是,Linux 還具有接收端優化,其操作類似於 TSO、UFO 和 GSO。這些優化的目的還在於減少每個數據包的開銷。具體來說,通用接收卸載 ( GRO ) 允許 NIC 驅動程序將接收到的數據包組合成一個大數據包,然后將其傳遞到 IP 堆棧。在轉發數據包時,GRO 允許重構原始數據包,這是保持 IP 數據包端到端性質所必需的。然而,有一個方面的影響,當大數據包在轉發操作的傳輸側被分解時,它會導致流的多個數據包同時排隊。數據包的這種“微突發”會對流間延遲產生負面影響。

飢餓和延遲

盡管有其必要性和好處,但 IP 堆棧和硬件之間的隊列引入了兩個問題:飢餓和延遲。

如果NIC驅動程序喚醒以從隊列中拉出數據包進行傳輸,並且隊列為空,則硬件將錯過傳輸機會,從而降低系統的吞吐量。這被稱為飢餓。如果在一個繁忙的系統中,IP堆棧將獲得更少的機會把數據包添加到緩沖區,那么硬件很可能在新的數據包到來之前消耗完緩沖區,因此需要一個大的緩沖區來減少飢餓的可能並確保高吞吐量,但它的缺點是引入大量延遲。

圖 4 - 批量流數據包(藍色)后面的交互數據包(黃色)

上圖驅動程序隊列中,幾乎充滿了高帶寬,大容量的TCP段,排在最后的是來自 VoIP 或游戲流的數據包(黃色)。VoIP 或游戲等交互式應用程序通常以固定間隔發出小數據包,這些數據包對延遲敏感,而高帶寬數據傳輸會產生更高的數據包速率和更大的數據包。這種較高的數據包速率可以填充交互數據包之間的緩沖區,從而導致交互數據包的傳輸延遲。

因此,為驅動程序隊列選擇合適的大小是一個“Goldilocks problem”。

字節隊列限制BQL

字節隊列限制 (BQL) 是最近的 Linux 內核 (> 3.3.0) 中的一項新功能,它試圖自動解決驅動程序隊列大小的問題。這是通過添加一個層來實現的,該層基於計算在當前系統條件下避免飢餓所需的最小緩沖區大小來啟用和禁用對驅動程序隊列的排隊。回想之前,排隊數據量越小,排隊數據包所經歷的最大延遲越低。

理解 BQL 不會改變驅動程序隊列的實際大小是關鍵。而是 BQL 計算當前時間可以排隊的數據量(以字節為單位)的限制。超過此限制的任何字節都必須由驅動程序隊列上方的層保留或丟棄。

BQL 基於測試設備是否處於飢餓狀態。如果它被餓死,那么 LIMIT 會增加,允許更多的數據排隊,從而減少餓死的機會。如果設備在整個時間間隔內都處於忙碌狀態,並且隊列中仍有字節要傳輸,則隊列大於當前條件下系統所需的隊列,並且減少 LIMIT 以限制延遲,這個機制有點類似於TCP的擁塞控制機制。

BQL 通過將排隊數據量限制為避免飢餓所需的最低限度來減少網絡延遲。它還具有非常重要的副作用,將大多數數據包排隊的點從驅動程序隊列(一個簡單的 FIFO)移動到隊列規則 (QDisc) 層,該層能夠實現更復雜的排隊策略。

排隊規則Qdisc

驅動程序隊列是一個簡單的先進先出 (FIFO) 隊列。它平等地對待所有數據包,並且沒有區分不同流的數據包的能力。這種設計使 NIC 驅動程序軟件保持簡單和快速。夾在 IP 堆棧和驅動程序隊列之間的是排隊規則 (QDisc) 層。該層實現了 Linux 內核的流量管理功能,包括流量分類、優先級和速率整形。

默認情況下,每個網絡接口都分配了一個pfifo_fast QDisc,它基於 TOS 位實現了一個簡單的三頻段優先級方案。類和過濾器的概念不在介紹。

傳輸層和排隊規則之間的緩沖

在查看前面的圖時,您可能已經注意到在排隊規則層之上沒有數據包隊列。這意味着網絡數據包直接放入排隊規則中,對於具有單個隊列的 QDisc,會出現上圖中針對驅動程序隊列概述的相同問題。也就是說,單個高帶寬或高數據包速率流會消耗隊列中的所有空間,從而導致數據包丟失並給其他流增加顯着的延遲。

從 Linux 3.6.0 (2012-09-30) 開始,Linux 內核有一個名為 TCP Small Queues 的新功能,旨在解決 TCP 的這個問題。

另一個部分解決方案是使用一個 QDisc,它有許多隊列,理想情況下每個網絡流一個。隨機公平隊列 ( SFQ ) 和具有受控延遲的公平隊列 ( fq_codel ) QDisc 都很好地解決了這個問題,因為它們實際上每個網絡流都有一個隊列。

如何在Linux中操作隊列大小

ethtool命令用於控制以太網設備驅動程序隊列的大小。ethtool 還提供低級接口統計信息以及啟用和禁用 IP 堆棧和驅動程序功能的能力。

截屏2021-06-18 下午3.45.28

而BQL算法是自調整的,因此不需要過多地弄亂它。如果非要修改,那么需要覆蓋計算出的LIMIT值的上限,根據NIC的位置和名稱,可以在/sys目錄中找到BQL的狀態和配置。在我的服務器上,eth0目錄是/sys/devices/pci0000:00/0000:00:14.0/net/eth0/queues/tx-0/byte_queue_limits

這個目錄下主要的兩個文件:

  • limit_max:LIMIT的可配置最大值,將此值設置得較低以優化延遲
  • limit_min:LIMIT的可配置最小值,將此值設置得較高以優化吞吐量

什么是txqueuelen

image-20210618161418089

Linux 中傳輸隊列的長度默認為 1,000 個數據包,這是一個很大的緩沖,尤其是在低帶寬下。txqueuelen 僅用作某些排隊規則的默認隊列長度。

對於大多數排隊規則,tc命令行上的limit參數會覆蓋txqueuelen的默認值,也可以通過ip命令設置。

ip link set txqueuelen 500 dev eth0

7.5、微服務限流是限制帶寬嗎?

微服務限流是通過對並發/請求進行限速來保護系統,防止系統過載,限流的方式有:

  • QPS:限制每秒的請求數
  • 並發數:避免開啟過多縣城導致資源耗盡
  • 連接數:限制TCP連接數

所以,微服務的限流並不是限制帶寬,而是限制一定時間區間內的並發數量,而常見的限流算法:漏桶算法、令牌桶算法等是針對這個的。Golang標准庫中也自帶了限流算法的實現golang.org/x/time/rate,該限流器是基於Token Bucket實現的。

參考鏈接:

How to limit network bandwidth on Linux

Linux Advanced Routing & Traffic Control HOWTO

Trickle: A Userland Bandwidth Shaper for Unix-like Systems

Queue in the Linux Network Stack

https://cloud.tencent.com/developer/article/1409664

圖解linux網絡包接收過程

多線程下載 一個大文件的速度更快的真正原因是什么

QoS實現之限速

linux網絡工具iproute2的使用簡介

Linux TC的ifb原理以及ingress流控


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM