Linux tc qdisc 模拟网络丢包延时

1. Linux tc qdisc 模拟网络丢包延时

在 linux 下通过 tc qdisc 很容易对 rt 延时、丢包、带宽进行控制, 这样的话方便重现各种网络问题

1.1. 延时

1. give packets from eth0 a delay of 2ms
bash$ tc qdisc add dev eth0 root netem delay 2ms
 
2.change the delay to 300ms
bash$ tc qdisc change dev eth0 root netem delay 3ms
3.display eth0 delay setting
bash$ tc qdisc show dev eth0
 
4.stop the delay
bash$ tc qdisc del dev eth0 root
#corrupt
The following rule corrupts 5% of the packets by introducing single bit error at a random offset in the packet:
tc qdisc change dev eth0 root netem corrupt 5%

1.2. 模拟网络丢包

tc qdisc add dev eth0 root netem loss 1%

指定 ip 172.31.65.30 延时 17ms, 测试发现 181 和 183 这两句命令顺序无所谓。恢复正常: 179 行命令

179  tc qdisc del dev eth0 root
180  tc qdisc add dev eth0 root handle 1: prio
181  tc filter add dev eth0 parent 1:0 protocol ip pref 55 handle ::55 u32 match ip dst 172.31.65.30 flowid 2:1
182  tc qdisc ls
183  tc qdisc add dev eth0 parent 1:1 handle 2: netem delay 17ms

1.3. 指定 ip 和端口延时

指定 eth0 网卡, 来源 ip 是 10.0.1.1, 目的端口是 3306 的访问延迟 20ms, 上下浮动 2ms

# 指定 eth0 网卡, 来源 ip 是 10.0.1.1, 目的端口是 3306 的访问延迟 20ms, 上下浮动 2ms
tc qdisc add dev eth0 root handle 1: prio bands 4
tc qdisc add dev eth0 parent 1:4 handle 40: netem delay 20ms 2ms
tc filter add dev eth0 parent 1: protocol ip prio 4 basic match "cmp(u16 at 2 layer transport eq 3306)
                            and cmp(u8 at 16 layer network eq 10)
                            and cmp(u8 at 17 layer network eq 0)
                            and cmp(u8 at 18 layer network eq 1)
                            and cmp(u8 at 19 layer network eq 1)" flowid 1:4
                            
# 删除过滤
sudo tc filter del dev eth0 parent 1: prio 4 basic
sudo tc qdisc  del dev eth0 root
  • 0 layer 代表 sport
  • 2 layer 代表 dport

1.4. 指定端口 34001 上, 延时 5ms

tc qdisc add dev eth0 root handle 1: prio
tc qdisc add dev eth0 parent 1:3 handle 30: netem delay 5ms
tc filter add dev eth0 protocol ip parent 1:0 u32 match ip sport 34001 0xffff flowid 1:3

1.5. 控制网卡的带宽、延时、乱序、丢包

sudo tc qdisc add dev bond0 root handle 1: netem delay 10ms reorder 25% 50% loss 0.2%
sudo tc qdisc add dev bond0 parent 1: handle 2: tbf rate 1mbit burst 32kbit latency 10ms
/sbin/tc qdisc add dev bond0 root tbf rate 500kbit latency 50ms burst 15kb
// 同时模拟 20Mbps 带宽, 50msRTT 和 0.1%丢包率  
# tc qdisc add dev bond0 root handle 1:0 tbf rate 20mbit burst 10kb limit 300000  
# tc qdisc add dev bond0 parent 1:0 handle 10:0 netem delay 50ms loss 0.1 limit 300000 
tc qdisc change dev eth0 root netem reorder 50% gap 3 delay 1ms
tc qdisc change dev eth0 root netem delay 1ms reorder 15%
//在 eth0 上设置一个 tbf 队列, 网络带宽为 200kbit, 延迟 10ms 以内, 超出的包会被 drop 掉, 缓冲区为 1540 个字节
sudo /sbin/tc qdisc add dev eth0 root tbf rate 200kbit latency 10ms burst 15kb
sudo /sbin/tc qdisc ls dev eth0

在 eth0 上设置一个 tbf 队列, 网络带宽为 200kbit, 延迟 10ms 以内, 超出的包会被 drop 掉, 缓冲区为 1540 个字节

  • rate 表示令牌的产生速率, sustained maximum rate
  • latency 表示数据包在队列中的最长等待时间, packets with higher latency get dropped
  • burst 参数表示 maximum allowed burst:
    burst means the maximum amount of bytes that tokens can be available for instantaneously.

如果数据包的到达速率与令牌的产生速率一致, 即 200kbit, 则数据不会排队, 令牌也不会剩余
如果数据包的到达速率小于令牌的产生速率, 则令牌会有一定的剩余。
如果后续某一会数据包的到达速率超过了令牌的产生速率, 则可以一次性的消耗一定量的令牌。
burst 就是用于限制这"一次性"消耗的令牌的数量的, 以字节数为单位。

tbf: use the token buffer filter to manipulate traffic rates

限制 10MB, 排队等待超过 100ms 就触发丢包, 只限制了出去的流量, 没有限制进来的流量:

tc qdisc ls dev eth0 // 查看 eth0 上的队列规则  
sudo tc qdisc add dev eth0 root tbf rate 80mbit burst 1mbit latency 100ms 
//限制 80MB
sudo tc qdisc add dev eth0 root tbf rate 80mbps burst 1mbps latency 100ms

或者:

//Server: 4 Mbit 50 ms
tc qdisc add dev eth0 handle 1: root htb default 11
tc class add dev eth0 parent 1: classid 1:1 htb rate 1000Mbps
tc class add dev eth0 parent 1:1 classid 1:11 htb rate 4Mbit
tc qdisc add dev eth0 parent 1:11 handle 10: netem delay 50ms
//Client: 512 kbit 50 ms
tc qdisc add dev vmnet1 handle 1: root htb default 11
tc class add dev vmnet1 parent 1: classid 1:1 htb rate 1000Mbps
tc class add dev vmnet1 parent 1:1 classid 1:11 htb rate 512kbit
tc qdisc add dev vmnet1 parent 1:11 handle 10: netem delay 50ms

1.6. qdisc 的类别

QDisc(排队规则)是 queueing discipline 的简写, 它是理解流量控制 (traffic control) 的基础。无论何时, 内核如果需要通过某个网络接口发送数据包, 它都需要按照为这个接口配置的 qdisc(排队规则)把数据包加入队列。然后, 内核会尽可能多地从 qdisc 里面取出数据包, 把它们交给网络适配器驱动模块。最简单的 QDisc 是 pfifo 它不对进入的数据包做任何的处理, 数据包采用先入先出的方式通过队列。不过, 它会保存网络接口一时无法处理的数据包。

CLASSLESS QDisc(不可分类 QDisc)

  • [p|b]fifo: 使用最简单的 qdisc, 纯粹的先进先出。只有一个参数: limit, 用来设置队列的长度, pfifo 是以数据包的个数为单位; bfifo 是以字节数为单位。
  • pfifo_fast: 在编译内核时, 如果打开了高级路由器 (Advanced Router) 编译选项, pfifo_fast 就是系统的标准 QDISC。它的队列包括三个波段 (band)。在每个波段里面, 使用先进先出规则。而三个波段 (band) 的优先级也不相同, band 0 的优先级最高, band 2 的最低。如果 band0 里面有数据包, 系统就不会处理 band 1 里面的数据包, band 1 和 band 2 之间也是一样。数据包是按照服务类型 (Type of Service,TOS) 被分配多三个波段 (band) 里面的。
  • red: red 是 Random Early Detection(随机早期探测)的简写。如果使用这种 QDISC, 当带宽的占用接近于规定的带宽时, 系统会随机地丢弃一些数据包。它非常适合高带宽应用。
  • sfq: sfq 是 Stochastic Fairness Queueing 的简写。它按照会话 (session–对应于每个 TCP 连接或者 UDP 流)为流量进行排序, 然后循环发送每个会话的数据包。
  • tbf: tbf 是 Token Bucket Filter 的简写, 适合于把流速降低到某个值。

一个网络接口上如果没有设置 QDisc, pfifo_fast 就作为缺省的 QDisc。

CLASSFUL QDISC(分类 QDisc), 可分类的 qdisc 包括:

  • CBQ: CBQ 是 Class Based Queueing(基于类别排队)的缩写。它实现了一个丰富的连接共享类别结构, 既有限制 (shaping) 带宽的能力, 也具有带宽优先级管理的能力。带宽限制是通过计算连接的空闲时间完成的。空闲时间的计算标准是数据包离队事件的频率和下层连接(数据链路层)的带宽。
  • HTB: HTB 是 Hierarchy Token Bucket 的缩写。通过在实践基础上的改进, 它实现了一个丰富的连接共享类别体系。使用 HTB 可以很容易地保证每个类别的带宽, 它也允许特定的类可以突破带宽上限, 占用别的类的带宽。HTB 可以通过 TBF(Token Bucket Filter) 实现带宽限制, 也能够划分类别的优先级。
  • PRIO: PRIO QDisc 不能限制带宽, 因为属于不同类别的数据包是顺序离队的。使用 PRIO QDisc 可以很容易对流量进行优先级管理, 只有属于高优先级类别的数据包全部发送完毕, 才会发送属于低优先级类别的数据包。为了方便管理, 需要使用 iptables 或者 ipchains 处理数据包的服务类型 (Type Of Service,ToS)。

1.7. htb 分类 qdisc

tbf 能对流量无差别控制, htb 可以进一步进行更精细的控制

1.8. 针对 IP、端口限速案例

$cat qdisc_bw.sh
#!/bin/bash
#针对不同的 ip 进行限速
#清空原有规则
tc qdisc del dev eth0 root
#创建根序列
tc qdisc add dev eth0 root handle 1: htb default 1
#创建一个主分类绑定所有带宽资源(60M)
tc class add dev eth0 parent 1:0 classid 1:1 htb rate 60Mbps burst 15k
#到这里可以使用了, 整机速度限制到了 60M
#创建子分类, ceil 表示最大带宽
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 2Mbps ceil 1Mbps burst 15k
tc class add dev eth0 parent 1:1 classid 1:20 htb rate 20Mbps ceil 30Mbps burst 15k
#为了避免一个会话永占带宽, 添加随即公平队列 sfq.
#perturb: 是多少秒后重新配置一次散列算法, 默认为 10 秒
#sfq, 他可以防止一个段内的一个 ip 占用整个带宽
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 限速到 1Mbps
tc filter add dev eth0 protocol ip parent 1:0 prio 2 u32 match ip dst 0.0.0.0/0 flowid 1:10
#对 10.0.186.140 限速在 30Mbps
tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip dst 10.0.186.140 flowid 1:20
#对端口进行 filter 限流
#tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip sport 22 flowid 1:10
#查看以上规则
sudo tc class show dev eth0
sudo tc filter show dev eth0

限流 100MB 后的实际监控效果

image-20211031205539407

1.9. docker 中使用 tc

docker 里无法使用的 bug 可以参考 https://bugzilla.redhat.com/show_bug.cgi?id=1152231, 解决方法就是升级 tc 版本, tc qdisc add 时加上 direct_qlen 参数

1.10. 场景:

故障注入的 docker: 10.1.1.149

10.1.1.149 上会模拟各种网络故障, 但是中控机到该 docker 的连接需要不受影响

DEVICE_NAME=eth0

# 根规则, direct_qlen 1000 必须加, 否则在 docker 的虚拟网络跑不了
tc qdisc add dev ${DEVICE_NAME} root handle 1: htb  default 1024 direct_qlen 1000
# 建立两个类继承 root
tc class add dev ${DEVICE_NAME} parent 1:0 classid 1:1 htb rate 10000mbit
tc class add dev ${DEVICE_NAME} parent 1:0 classid 1:2 htb rate 10000mbit
#新版本的 tc 在 filter 设置完后, 所有网络都会断, 类似黑名单, 需要加 qdisc 才能恢复, 所以先让两个通道都能跑
# 队列采用公平的调度算法, 保证网络通畅, perturb 参数是每隔 10 秒换一次 hash, 进一步保障平均
tc qdisc add dev ${DEVICE_NAME} parent 1:1 sfq perturb 10
tc qdisc add dev ${DEVICE_NAME} parent 1:2 sfq perturb 10
# 加过滤规则
#1. 队列 1 是和跳板机交互的网络, 需要保持通畅
tc filter add dev ${DEVICE_NAME} protocol ip parent 1: prio 10 u32 match ip dst 11.136.106.200/32 flowid 1:1
#2. 其他所有主机走队列 2, 实现网络模拟
tc filter add dev ${DEVICE_NAME} protocol ip parent 1: prio 10 u32 match ip dst 0.0.0.0/0 flowid 1:2
#队列 2 开始网络模拟
#该命令将${DEVICE_NAME}网卡的耗时随机 delay 100ms, 延迟的尖刺在标准值的正负 30ms, 最后的百分比数字是尖刺的相关系数
# 这边用 replace 是因为之前已经用 add 加过规则了
tc qdisc replace dev ${DEVICE_NAME} parent 1:2 netem delay 100ms 30ms 25%
#该命令将 ${DEVICE_NAME} 网卡的传输设置为随机丢掉 10%的数据包, 成功率为 50%
tc qdisc replace dev ${DEVICE_NAME} parent 1:2 netem loss 10% 50%
#该命令将 ${DEVICE_NAME} 网卡的传输设置为随机产生 10%的重复数据包。
tc qdisc replace dev ${DEVICE_NAME} parent 1:2 netem duplicate 10%
#该命令将 ${DEVICE_NAME} 网卡的传输设置为: 有 25%的数据包会被立即发送, 其他的延迟 10ms, 相关性是 10%, 产生乱序
tc qdisc replace dev ${DEVICE_NAME} parent 1:2 netem delay 10ms reorder 25% 10% 
#该命令将 ${DEVICE_NAME} 网卡的传输设置为随机产生 9%的损坏的数据包
tc qdisc replace dev ${DEVICE_NAME} parent 1:2 netem corrupt 9%

恢复网络

#让网络恢复正常
tc qdisc replace dev ${DEVICE_NAME} parent 1:2 sfq perturb 10
# =================== 查看规则 ======================
tc filter show dev ${DEVICE_NAME}
tc class show dev ${DEVICE_NAME}
tc qdisc show dev ${DEVICE_NAME}
#====================== 清理 ======================
tc filter delete dev ${DEVICE_NAME} parent 1:0 protocol ip pref 10
tc qdisc del dev ${DEVICE_NAME} parent 1:2 netem
tc class del dev ${DEVICE_NAME} parent 1:0 classid 1:2
tc class del dev ${DEVICE_NAME} parent 1:0 classid 1:1
tc qdisc del dev ${DEVICE_NAME} root handle 1

1.11. 参考资料

https://netbeez.net/blog/how-to-use-the-linux-traffic-control/

猜你喜欢

转载自blog.csdn.net/wan212000/article/details/131226064
tc
今日推荐