在前面的几篇文章中,我们解决了pod连接主机、pod连接外网、pod与相同节点或不同节点的pod连接、用clusterIP和nodeport的方式访问pod等几个问题,可以说,对于组织pod的网络的每一环都已经完成了。
和CPU、内存这类资源一样,我们肯定希望网络资源对于每个pod来说也是平均分配的,起码能做到每个pod的数据包发送与接收的速度都能控制在一定的范围内,节点的带宽不能被某个pod耗光而影响其它的pod,所以这一章我们来讨论如何控制pod的进出流量速率。
首先我们来看看这回linux给我们准备了哪些工具;
linux自带流量控制框架,这个框架允许用户在数据发送前配置数据包排队规则qdisc(queueing discipline),对流量进行限制或整形,linux的tc只控制发送速率不控制接收速率,当然要控制接收速率也是有办法实现的。
对于限制网卡的发送速率,一般有两种方式:
对于第一种方式,我们选择无类别队列,对于第二种方式,我们选择可分类队列。
主要有:
在这里我们只介绍下如何创建tbf队列,因为后面主要是使用这个队列来限制pod的发送速率,下面的命令可以控制eth0的发送速率为100mbit:
tc qdisc add dev eth0 root tbf rate 100mbit burst 100mbit limit 100mbit
## rate:传输速率
## burst:桶的大小
## limit:确定最多有多少数据(byte)在队列中等待令牌
流量控制单位kbps:千字节/秒 mbps:兆字节/秒 kbit:KBits/秒 mbit:MBits/秒 bps:字节数/秒(如果不带单位,则默认单位是这个)
不同于无分类队列,可分类队列使用时步骤稍微复杂些,产要分三步:
可分类队列主要有:
示例配置:访问mysql的速率限制80mbit,访问http服务的速率限制20mbit
tc qdisc add dev eth0 root handle 1: htb default 11 ##使用htb队列,默认流量去分类11
tc class add dev eth0 parent 1: classid 1:11 htb rate 80mbit ceil 80mbit ## 创建分类11,速率限制80mbit
tc class add dev eth0 parent 1: classid 1:12 htb rate 20mbit ceil 20mbit ## 创建分类12,速率限制20mbit
tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip dport 3306 0xffff flowid 1:11 ## 所有访问3306端口的流量导到分类11中
tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip dport 80 0xffff flowid 1:12 ## 所有访问80端口的流量导到分类12中
也可以通过来源IP+目标端口来引导流量
tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip src 192.168.6.160 match ip dport 23 oxffff flowid 1:11
也可以通过数据标记来引导流量:
iptables -t mangle -A POSTROUTING -d 10.244.1.10 -j MARK –set-mark 100 ## 用iptables给数据打标记
tc filter add dev eth0 protocol ip parent 1:0 prio 2 handle 100 fw flowid 1:11 ## 标记了100的数据包引导到分类11中
首先还是在测试的主机上创建一个pod:(笔者的测试主机IP为192.168.6.160)
ip netns add pod-a
ip link add eth0 type veth peer name veth-pod-a
ip link set eth0 netns pod-a
ip netns exec pod-a ip addr add 192.168.10.10/24 dev eth0
ip netns exec pod-a ip link set eth0 up
ip netns exec pod-a ip route add default via 169.254.10.24 dev eth0 onlink
ip link set veth-pod-a up
echo 1 > /proc/sys/net/ipv4/conf/veth-pod-a/proxy_arp
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -I FORWARD -s 192.168.0.0/16 -d 192.168.0.0/16 -j ACCEPT
ip route add 192.168.10.10 dev veth-pod-a scope link
然后在主机上安装iperf3:
yum install iperf3
在pod-a中启动服务端:
ip netns exec pod-a iperf3 -s
再开一个终端,测试一下限速前的速度:
iperf3 -c 192.168.10.10 -i 1 -t 10
Connecting to host 192.168.10.10, port 5201
[ 5] local 192.168.6.160 port 50768 connected to 192.168.10.10 port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 8.19 GBytes 70.3 Gbits/sec 0 1.35 MBytes
[ 5] 1.00-2.00 sec 8.27 GBytes 71.0 Gbits/sec 0 1.35 MBytes
[ 5] 2.00-3.00 sec 8.18 GBytes 70.3 Gbits/sec 0 1.35 MBytes
[ 5] 3.00-4.00 sec 8.36 GBytes 71.8 Gbits/sec 0 1.35 MBytes
[ 5] 4.00-5.00 sec 8.46 GBytes 72.7 Gbits/sec 0 1.35 MBytes
[ 5] 5.00-6.00 sec 8.40 GBytes 72.2 Gbits/sec 0 1.35 MBytes
[ 5] 6.00-7.00 sec 8.50 GBytes 73.1 Gbits/sec 0 1.42 MBytes
[ 5] 7.00-8.00 sec 8.25 GBytes 70.8 Gbits/sec 0 1.57 MBytes
[ 5] 8.00-9.00 sec 8.53 GBytes 73.2 Gbits/sec 0 1.57 MBytes
[ 5] 9.00-10.00 sec 8.45 GBytes 72.6 Gbits/sec 0 1.57 MBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 83.6 GBytes 71.8 Gbits/sec 0 sender
[ 5] 0.00-10.04 sec 83.6 GBytes 71.5 Gbits/sec receiver
iperf Done.
可以看到,限制前的速度为71G/秒;
因为pod使用的是veth网卡对,所以我们可以通过主机端的网卡,达到控制pod流量的目的;
我们控制主机网卡veth-pod-a
的发送速率,就相当于是控制pod的接收速率,我们限制pod的接收速率为100mbit:
tc qdisc add dev veth-pod-a root tbf rate 100mbit burst 100mbit limit 100mbit
此时再测:
iperf3 -c 192.168.10.10 -i 1 -t 10
Connecting to host 192.168.10.10, port 5201
[ 5] local 192.168.6.160 port 50626 connected to 192.168.10.10 port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 24.9 MBytes 209 Mbits/sec 0 389 KBytes
[ 5] 1.00-2.00 sec 11.6 MBytes 97.5 Mbits/sec 0 389 KBytes
[ 5] 2.00-3.00 sec 11.6 MBytes 97.5 Mbits/sec 0 389 KBytes
[ 5] 3.00-4.00 sec 11.7 MBytes 98.0 Mbits/sec 0 389 KBytes
[ 5] 4.00-5.00 sec 10.8 MBytes 90.2 Mbits/sec 0 389 KBytes
[ 5] 5.00-6.00 sec 11.7 MBytes 98.0 Mbits/sec 0 389 KBytes
[ 5] 6.00-7.00 sec 11.6 MBytes 97.5 Mbits/sec 0 389 KBytes
[ 5] 7.00-8.00 sec 11.0 MBytes 92.3 Mbits/sec 0 389 KBytes
[ 5] 8.00-9.00 sec 11.6 MBytes 97.5 Mbits/sec 0 389 KBytes
[ 5] 9.00-10.00 sec 11.6 MBytes 97.5 Mbits/sec 0 389 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 128 MBytes 107 Mbits/sec 0 sender
[ 5] 0.00-10.05 sec 126 MBytes 105 Mbits/sec receiver
iperf Done.
看到限速后的结果为105mbit/秒左右,符合预期
删除接收限速:
tc qdisc del dev veth-pod-a root tbf rate 100mbit burst 100mbit limit 100mbit
因为linux的tc框架控发不控收,所以我们不能像上面一样,通过控制主机端veth-pod-a
网卡的接收速率来控制pod发送速率的目的,但也不是完全没有办法:
虽然不能在主机端控制pod的发送,但是可以直接在容器里控制,命令和上面的一样,只不过是在pod-a的ns中执行:
ip netns exec pod-a tc qdisc add dev eth0 root tbf rate 100mbit burst 100mbit limit 100mbit
因为是测试从容器发送的速率,所以我们要把iperf3的服务端调整一下,服务端跑在主机上,然后再在容器中进行发送测试:
ip netns exec pod-a iperf3 -c 192.168.6.160 -i 1 -t 10
Connecting to host 192.168.6.160, port 5201
[ 5] local 192.168.10.10 port 49224 connected to 192.168.6.160 port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 26.6 MBytes 223 Mbits/sec 0 641 KBytes
[ 5] 1.00-2.00 sec 11.2 MBytes 94.4 Mbits/sec 0 641 KBytes
[ 5] 2.00-3.00 sec 11.2 MBytes 94.4 Mbits/sec 0 641 KBytes
[ 5] 3.00-4.00 sec 11.2 MBytes 94.4 Mbits/sec 0 641 KBytes
[ 5] 4.00-5.00 sec 11.2 MBytes 94.4 Mbits/sec 0 641 KBytes
[ 5] 5.00-6.00 sec 12.5 MBytes 105 Mbits/sec 0 641 KBytes
[ 5] 6.00-7.00 sec 11.2 MBytes 94.4 Mbits/sec 0 641 KBytes
[ 5] 7.00-8.00 sec 11.2 MBytes 94.4 Mbits/sec 0 641 KBytes
[ 5] 8.00-9.00 sec 11.2 MBytes 94.4 Mbits/sec 0 641 KBytes
[ 5] 9.00-10.00 sec 11.2 MBytes 94.4 Mbits/sec 0 641 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 129 MBytes 108 Mbits/sec 0 sender
[ 5] 0.00-10.04 sec 126 MBytes 105 Mbits/sec receiver
iperf Done.
结果是105mbit/秒,也是符合预期
删除限制:
ip netns exec pod-a tc qdisc del dev eth0 root tbf rate 100mbit burst 100mbit limit 100mbit
通常我们会把容器的命令执行权限开给业务方,这样他们就有可能在容器里把这个速率限制取消,有没有一种办法是在主机端限制pod的发送速率呢?有。
ifb网卡也是linux虚拟网络设备,类似于tun/tap/veth,只不过ifb的原理要简单得多,可以看作是一张只有tc过滤功能的虚拟网卡,而且它不会改变数据包的流向,比如把某张网卡接收流量导给ifb网卡,经过ifb的流量控制过滤后,继续走原网卡的接收流程,发送也是如此;这样我们就可以把pod在主机一端的网卡的接收重定向到ifb网卡,然后通过控制ifb网卡的发送速率,来间接控制pod的发送速率。
首先要确认内核有加载ifb模块,如果没有则加载
modprobe ifb //需要加载ifb模块
然后创建ifb网卡,并设置发送队列长度为1000:
ip link add ifb0 type ifb
ip link set dev ifb0 up txqueuelen 1000
把veth-pod-a
的接收重定向到ifb网卡上:
tc qdisc add dev veth-pod-a ingress handle ffff:
tc filter add dev veth-pod-a parent ffff: protocol ip u32 match u32 0 0 flowid 1:1 action mirred egress redirect dev ifb0 //重定向流量到ifb
设置ifb0的发送速率:
tc qdisc add dev ifb0 root tbf rate 100mbit burst 100mbit limit 100mbit
此时再测:
ip netns exec pod-a iperf3 -c 192.168.6.160 -i 1 -t 10
Connecting to host 192.168.6.160, port 5201
[ 5] local 192.168.10.10 port 54762 connected to 192.168.6.160 port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 26.5 MBytes 223 Mbits/sec 0 700 KBytes
[ 5] 1.00-2.00 sec 11.2 MBytes 94.4 Mbits/sec 0 700 KBytes
[ 5] 2.00-3.00 sec 11.2 MBytes 94.4 Mbits/sec 0 700 KBytes
[ 5] 3.00-4.00 sec 11.2 MBytes 94.4 Mbits/sec 0 700 KBytes
[ 5] 4.00-5.00 sec 12.5 MBytes 105 Mbits/sec 0 700 KBytes
[ 5] 5.00-6.00 sec 11.2 MBytes 94.4 Mbits/sec 0 700 KBytes
[ 5] 6.00-7.00 sec 11.2 MBytes 94.4 Mbits/sec 0 700 KBytes
[ 5] 7.00-8.00 sec 11.2 MBytes 94.4 Mbits/sec 0 700 KBytes
[ 5] 8.00-9.00 sec 11.2 MBytes 94.4 Mbits/sec 0 700 KBytes
[ 5] 9.00-10.00 sec 11.2 MBytes 94.4 Mbits/sec 0 700 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 129 MBytes 108 Mbits/sec 0 sender
[ 5] 0.00-10.05 sec 126 MBytes 105 Mbits/sec receiver
iperf Done.
105mbit/秒,符合预期,改一下速率限制,从100mbit改为200mbit:
tc qdisc replace dev ifb0 root tbf rate 200mbit burst 200mbit limit 200mbit
然后再测:
ip netns exec pod-a iperf3 -c 192.168.6.160 -i 1 -t 10
Connecting to host 192.168.6.160, port 5201
[ 5] local 192.168.10.10 port 56044 connected to 192.168.6.160 port 5201
[ ID] Interval Transfer Bitrate Retr Cwnd
[ 5] 0.00-1.00 sec 48.0 MBytes 403 Mbits/sec 0 354 KBytes
[ 5] 1.00-2.00 sec 23.1 MBytes 194 Mbits/sec 0 354 KBytes
[ 5] 2.00-3.00 sec 23.1 MBytes 194 Mbits/sec 0 354 KBytes
[ 5] 3.00-4.00 sec 22.4 MBytes 188 Mbits/sec 0 354 KBytes
[ 5] 4.00-5.00 sec 23.1 MBytes 194 Mbits/sec 0 354 KBytes
[ 5] 5.00-6.00 sec 22.4 MBytes 188 Mbits/sec 0 354 KBytes
[ 5] 6.00-7.00 sec 23.1 MBytes 194 Mbits/sec 0 354 KBytes
[ 5] 7.00-8.00 sec 23.1 MBytes 194 Mbits/sec 0 354 KBytes
[ 5] 8.00-9.00 sec 22.4 MBytes 188 Mbits/sec 0 354 KBytes
[ 5] 9.00-10.00 sec 23.1 MBytes 194 Mbits/sec 0 354 KBytes
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-10.00 sec 254 MBytes 213 Mbits/sec 0 sender
[ 5] 0.00-10.04 sec 252 MBytes 210 Mbits/sec receiver
iperf Done.
210mbit/秒,都符合预期。
删除pod-a的发送限速:
tc qdisc del dev veth-pod-a ingress
tc qdisc del dev ifb0 root tbf rate 200mbit burst 200mbit limit 200mbit
ip link del ifb0
OK,pod的流量控制也就说完了。
到目前为止,我们已经解决了一个常规则的k8s的cni需要解决的一切问题,但直到现在,也没见过go语言的影子,所以说k8s都是负责粘合功能的胶水代码,真正工作的是linux系统,与其说学习k8s的网络,不如说在学习linux提供的各种虚拟网络设备及内核协议栈的工作机制。
下一章我们来详细介绍下flannel,然后再动手用go语言撸个cni出来。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。