一. 容器平台做容器网络限流的意义
无论我们的目标是搭建公有云的容器云平台还是为客户提供容器平台的私有部署或解决方案,我们都会面临一个问题:容器网络限流的问题。在我们实验室的环境下,如果没有对容器带宽进行限制,单Pod使用的最大吞吐量将可以独占整个带宽,从而导致其他应用不能被访问。
单用户单Pod在没有做任何网络带宽限制情况下,网络吞吐量测试结果如下:
我们需要为“上云“的应用提供流量带宽保证,使其不受到其他应用或其他用户的应用的影响。我们需要提供租户级别或者应用级别的有效隔离。今天将分享一下我们为了达到这个目标做了哪些实践工作。
在Kubernetes众多的网络插件中,我们选择使用Open vSwitch容器网络插件。通常情况下,我们根据VNID实现租户间的网络隔离,并且在此基础上我们还可以根据Networkpolicy网络策略提供更细粒度的网络隔离,可以实现Pod之间、跨Namespace的网络访问控制。
二. 使用Open vSwitch容器网络插件如何进行容器网络限流
那么使用Open vSwitch容器网络插件如何进行容器网络限流呢?Open vSwitch本身不实现QoS,它使用Linux内核自带的traffic-control机制进行流量控制。主要两种实现方式:
- Policing管制:Policing用于控制接口上接收分组(ingress)的速率,是一种简单的QoS的功能,通过简单的丢包机制实现接口速率的限制,它既可以作用于物理接口,也可以作用于虚拟接口。
- Shaping整形:Shaping是作用于接口上的出口流量(egress)策略,可以实现QoS队列,不同队列里面处理不同策略。
policing的实现
policing在OVS中采用ingress_policing_rate和ingress_policing_burst两个字段完成ingress入口限速功能,该两个字段放在Interface表中。
入口限速直接配置在网络接口上,命令示例如下:
# ovs-vsctl set interface eth1 ingress_policing_rate=1000
# ovs-vsctl set interface eth1 ingress_policing_burst=100
- eth1:加入ovs桥端口的网络接口名称;
- ingress_policing_rate:为接口最大收包速率,单位kbps,超过该速度的报文将被丢弃,默认值为0表示关闭该功能;
- ingress_policing_burst:为最大突发流量大小,单位kb。默认值0表示1000kb,这个参数最小值应不小于接口的MTU,通常设置为ingress_policing_rate的10%更有利于tcp实现全速率;
通过命令ovs-vsctl list interface eth1可以查看配置
shaping的实现
shaping用于实现出口流量的控制,使用了队列queue,可以缓存和调度数据包发送顺序,比policing更加精确和有效,在OVS的数据表中主要使用QoS和Queue两张表
QoS创建命令
ovs-vsctl set port eth1 qos=@newqos--
--id=@newqoscreate qos type=linux-htb queues=0=@q0--
--id=@q0create queue other-config:max-rate=100000000
创建Queue
--id=@q0 create queue other-config:max-rate=100000000
创建q0队列,设置最大速率100M,通过ovs-vsctl查看配置的Queue表内容如下:
创建Qos规则
--id=@newqos create qos type=linux-htb queues=0=@q0
创建qos规则newqos,类型为linux-htb,并连接key值为0的队列q0,通过ovs-vsctl查看配置的Qos表内容如下:
创建接口Qos
set port eth1 qos=@newqos
设置接口eth1的qos为newqos,通过ovs-vsctl list port查看配置的port表内容如下:
我们采用的技术栈是Golang。我们也可以使用红帽提供的ovs的库http://github.com/openshift/origin/pkg/util/ovs实现上面Policing和shaping功能。
代码示意如下:
在Kubernetes上,如果使用Open vSwitch CNI插件,我们可以在创建Pod资源的时候为其配置速率限制。
- 创建iperf-pod.yaml,并为其配置速率限制:
cat <<EOF >iperf-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: iperf
annotations:
kubernetes.io/ingress-bandwidth: 3M
kubernetes.io/egress-bandwidth: 3M
spec:
containers:
- name: iperf
image: yadu/hello-openshift-iperf
imagePullPolicy: IfNotPresent
nodeSelector:
zone: default
EOF
- 流入测试:
pod容器里启动iperf server
$ iperf -s -p 12345 -i 1
------------------------------------------------------------
Server listening on TCP port 12345
TCP window size: 85.3 KByte (default)
------------------------------------------------------------
在任意节点向iperf server产生流量
[root@a-node4 ~]# iperf -c 10.130.2.103 -p 12345 -i 1 -t 10 -w 5m
------------------------------------------------------------
Client connecting to 10.130.2.103, TCP port 12345
TCP window size: 416 KByte (WARNING: requested 4.77 MByte)
------------------------------------------------------------
[ 3] local 10.130.2.1 port 58162 connected with 10.130.2.103 port 12345
[ ID] Interval Transfer Bandwidth
[ 3] 0.0- 1.0 sec 773 KBytes 6.33 Mbits/sec
[ 3] 1.0- 2.0 sec 316 KBytes 2.59 Mbits/sec
[ 3] 2.0- 3.0 sec 314 KBytes 2.57 Mbits/sec
(omitted)
查看pod里流入的日志
iperf -s -p 12345 -i 1
------------------------------------------------------------
Server listening on TCP port 12345
TCP window size: 85.3 KByte (default)
------------------------------------------------------------
[ 4] local 10.130.2.103 port 12345 connected with 10.130.2.1 port 58162
[ ID] Interval Transfer Bandwidth
[ 4] 0.0- 1.0 sec 354 KBytes 2.90 Mbits/sec
[ 4] 1.0- 2.0 sec 351 KBytes 2.87 Mbits/sec
[ 4] 2.0- 3.0 sec 349 KBytes 2.86 Mbits/sec
(omitted)
- 流出测试:
在任意节点上启动iperf server作为测试
iperf -s -p 12345 -i 1
------------------------------------------------------------
Server listening on TCP port 12345
TCP window size: 85.3 KByte (default)
------------------------------------------------------------
pod中用iperf client进行测试
iperf -c 172.18.143.117 -p 12345 -i 1 -t 10 -w 5m
------------------------------------------------------------
Client connecting to 172.18.143.117, TCP port 12345
TCP window size: 416 KByte (WARNING: requested 5.00 MByte)
------------------------------------------------------------
[ 3] local 10.130.2.103 port 44602 connected with 172.18.143.117 port 12345
[ ID] Interval Transfer Bandwidth
[ 3] 0.0- 1.0 sec 1.62 MBytes 13.6 Mbits/sec
[ 3] 1.0- 2.0 sec 384 KBytes 3.15 Mbits/sec
[ 3] 2.0- 3.0 sec 384 KBytes 3.15 Mbits/sec
(omitted)
查看node4上流入的日志
iperf -s -p 12345 -i 1
------------------------------------------------------------
Server listening on TCP port 12345
TCP window size: 85.3 KByte (default)
------------------------------------------------------------
[ 4] local 172.18.143.117 port 12345 connected with 10.130.2.103 port 44602
[ ID] Interval Transfer Bandwidth
[ 4] 0.0- 1.0 sec 1.28 MBytes 10.7 Mbits/sec
[ 4] 1.0- 2.0 sec 373 KBytes 3.05 Mbits/sec
[ 4] 2.0- 3.0 sec 380 KBytes 3.11 Mbits/sec
(omitted)
查看tc的配置
[root@a-node4 ~]# tc -s -d class show dev vethddb66bac
class htb 1:1 parent 1:fffe prio 0 quantum 1450 rate 11600bit ceil 3000Kbit burst 1513b/1 mpu 0b overhead 0b cburst 1513b/1 mpu 0b overhead 0b level 0
Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0)
rate 0bit 0pps backlog 0b 0p requeues 0
lended: 0 borrowed: 0 giants: 0
tokens: 16314655 ctokens: 63083
class htb 1:fffe root rate 3000Kbit ceil 3000Kbit burst 1449b/1 mpu 0b overhead 0b cburst 1449b/1 mpu 0b overhead 0b level 7
Sent 4187548 bytes 2872 pkt (dropped 0, overlimits 0 requeues 0)
rate 0bit 0pps backlog 0b 0p requeues 0
lended: 998 borrowed: 0 giants: 0
tokens: 60416 ctokens: 6041
三.实现AdmissionWebhook组件: bandwidth-inject-webhook
在公有云平台中,我们可以根据租户等级,为其创建的Pod设置不同级别的限流限制。我们通过自研开发实现了AdmissionWebhook组件:bandwidth-inject-webhook。当创建pod的配置文件yaml提交给apiserver时,可以为其增加网络限流配置。
上面是我们基于Open vSwitch的CNI做的一些研究和开发工作。当然,我们也可以借鉴华为自研的CNI插件,去支持任何网络插件的情景。
参考文章:
- Open vSwitch之QoS的实现