分享摘要:
我们需要为“上云“的应用提供流量带宽保证,使其不受到其他应用或其他用户的应用的影响。我们需要提供租户级别或者应用级别的有效隔离。今天将分享一下我们为了达到这个目标做了哪些实践工作。
主要内容:
容器平台做容器网络限流的意义。
使用Open vSwitch容器网络插件如何做容器网络限流。
实现AdmissionWebhook组件: bandwidth-inject-webhook。

一、容器平台做容器网络限流的意义

无论我们的目标是搭建公有云的容器云平台还是为客户提供容器平台的私有部署或解决方案,我们都会面临一个问题:容器网络限流的问题。在我们实验室的环境下,如果没有对容器带宽进行限制,单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

创建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的库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
[  4]  3.0- 4.0 sec 339 KBytes 2.77 Mbits/sec
(omitted)

三、实现AdmissionWebhook组件: bandwidth-inject-webhook

在公有云平台中,我们可以根据租户等级,为其创建的Pod设置不同级别的限流限制。我们通过自研开发实现了AdmissionWebhook组件:bandwidth-inject-webhook。当创建pod的配置文件yaml提交给apiserver时,可以为其增加网络限流配置。

上面是我们基于Open vSwitch的CNI做的一些研究和开发工作。当然,我们也可以借鉴华为自研的CNI插件,去支持任何网络插件的情景。

Q&A

Q:网络插件为什么没用flannel?
A: 如果选择flannel cni网络插件,可以采用华为开发的限流cni插件。
Q:您好,我想请问下对于容器限速为什么不考虑用动态方式去声明呢?定义在pod里面会不会不太灵活。
补充:不好意思,可能描述不清楚,我的意思是如果pod使用期间,要对其带宽限制做变更,需要重新patch pod的annotation,然后滚动更新生效?
A: 是的。
Q:用openvsitch跟 用calico 有什么区别?
A: calico是基于iptables实现节点间通信。ovs是vxlan方式。
Q:基于contiv或者ovn开发的吗?
A: 不是。
Q: 目前istio也具有限速功能,这与tc限速共存吗?
A: sdn控制器,红帽开源的openshift有相应的实现。ovs选择是vxlan方式解决节点间通信。
istio的限流方式暂时我还没有了解。

分享嘉宾:范彬,现电信云容器项目研发组长。对技术保有一颗热衷的心。自2016年起开始一直从事容器、微服务等方面的研究和开发工作。熟悉Golang技术栈、Kubernetes分布式系统架构和工作原理。