在腾讯云基于VPC(虚拟私有网络)的CVM上使用k8s集群,有两种方式:
使用腾讯云官方容器服务CCS时, 会使用专有的VPC容器网络, 节点上会给容器分配一个跟VPC CIDR不重叠的容器网段(用户可配置),由CCS负责向VPC网络注册容器网段的路由,容器之间,容器跟节点之间,在用户看来都是直接路由的。
自己搭建k8s集群的话, 常用的容器网络方案是vxlan。
为了弄清楚这两种网络方案下, 容器网络的性能情况,笔者设计并执行了一个简单的对比测试, 对比了吞吐量和时延。
本文不介绍vxlan和vpc网络原理,只呈现一个可复现的详细测试流程,并附上典型测试结果。 只关心测试结果的话,直接看最后一节。
通过腾讯云容器服务(CCS)创建一个两节点的集群。
通过CCS(或kubectl)创建两个POD, 分布在两个节点上。这两个POD使用的是VPC容器网络。后面简称这两个POD为VPC POD。
通过flannel给两个节点分配另一个虚拟网段,并创建vxlan设备。通过本地docker run命令在两个节点个运行一个docker container, 这两个docker container使用的是flannel管理的vxlan虚拟网络,后面简称这两个容器为vxlan容器。
在两个VPC POD间运行qperf测试(走的是VPC容器网络), 在两个vxlan 容器之间运行qperf测试(走的是vxlan网络), 然后进行对比。
从节点二的VPC POD和vxlan容器分别访问节点一, 通过qperf测试性能对比。
测试使用的节点是CVM, 不可避免收到母机上其他CVM的影响。
为了尽量排除这一影响, 让两个网络下的qperf测试,在同样两台CVM节点上,先后进行(间隔在一分钟之内)。
为了排除两次测试收到母机网络负载的波动影响,每间隔半小时,重复执行一遍对比测试,测试多次。最后取平均值来对比。
参考上一问题的答案。
https://console.cloud.tencent.com/vpc
CIDR选择 172.31.0.0/16
https://console.cloud.tencent.com/ccs/cluster
容器网络CIDR选择 10.0.0.0/14
节点配置选择1核CPU, 1G内存
操作系统选择Ubuntu 16.04 64位
等几分钟,两个节点的状态为“健康”以后,说明节点和集群都已经初始化完成了。
两个节点分配到的IP分别为(后面分别记为节点一和节点二):
查看节点的内网IP和状态
- root@VM-0-5-ubuntu:~# kubectl get nodes
- NAME STATUS AGE VERSION
- 172.31.0.16 Ready 14d v1.7.8-qcloud
- 172.31.0.5 Ready 12d v1.7.8-qcloud
查看CCS(腾讯云容器服务)给每个节点分配的容器网段
- # kubectl get node/172.31.0.5 -o jsonpath={.spec.podCIDR}
- 10.0.1.0/24
- kubectl get node/172.31.0.16 -o jsonpath={.spec.podCIDR}
- 10.0.0.0/24
- wget https: //github.com/coreos/flannel/releases/download/v0.9.1/flannel-v0.9.1-linux-amd64.tar.gz
- tar - xzf flannel - v0.9.1 - linux - amd64.tar.gz
为了简单,只在节点172.31.0.5上安装和运行etcd:
- apt install etcd
- systemctl start etcd
这样两个节点上都可以通过 http://172.31.0.5:4001 来访问这个只有一个实例的etcd集群
- etcdctl--endpoint http: //
- 172.31.0.5 : 4001 set / flannel / net / config '{
- "NetWork"
- :
- "192.168.0.0/16"
- ,
- "SubnetLen"
- :
- 24
- ,
- "Backend"
- : {
- "Type"
- :
- "vxlan"
- }}'
./flanneld -etcd-endpoints=http://172.31.0.5:4001 -etcd-prefix=/flannel/net -iface=eth0 &
查看etcd数据可以看到两个节点上的flanneld各自分配到的网段:
- # etcdctl --endpoint http://172.31.0.5:4001 ls /flannel/net/subnets
- /flannel/net/subnets/192.168.24.0-24
- /flannel/net/subnets/192.168.81.0-24
- # etcdctl --endpoint http://172.31.0.5:4001 get /flannel/net/subnets/192.168.24.0-24
- {"PublicIP":"172.31.0.5","BackendType":"vxlan","BackendData":{"VtepMAC":"6a:7d:68:63:b4:d1"}}
- # etcdctl --endpoint http://172.31.0.5:4001 get /flannel/net/subnets/192.168.81.0-24
- {"PublicIP":"172.31.0.16","BackendType":"vxlan","BackendData":{"VtepMAC":"7e:f8:6a:92:eb:a8"}}
flanneld还会在本地生成一个文件/run/flannel/subnet.env。例如在节点172.31.0.5上:
- # cat /run/flannel/subnet.env
- FLANNEL_NETWORK=192.168.0.0/16
- FLANNEL_SUBNET=192.168.24.1/24
- FLANNEL_MTU=1450
- FLANNEL_IPMASQ=false
停止dockerd
- systemctl stop dockerd
修改/usr/lib/systemd/system/dockerd.service, 如下所示,增加"EnvironmentFile=-/run/flannel/subnet.env", 并修改ExecStart定义
- EnvironmentFile=-/run/flannel/subnet.env
- ExecStart=/usr/bin/dockerd ${LOG_LEVEL} --bip=${FLANNEL_SUBNET} --mtu=${FLANNEL_MTU} ${STORAGE_DRIVER} ${REGISTRY_MIRROR}
重新启动dockerd
- systemctl start dockerd
通过kubectl create -f命令创建一个deployment。 所用yaml文件内容如下:
- apiVersion: extensions/v1beta1
- kind: Deployment
- metadata:
- labels:
- qcloud-app: nginx
- name: nginx
- namespace: default
- spec:
- replicas: 2
- selector:
- matchLabels:
- qcloud-app: nginx
- template:
- metadata:
- labels:
- qcloud-app: nginx
- spec:
- affinity:
- podAntiAffinity:
- requiredDuringSchedulingIgnoredDuringExecution:
- - labelSelector:
- matchExpressions:
- - key: "qcloud-app"
- operator: In
- values:
- - nginx
- topologyKey: "kubernetes.io/hostname"
- containers:
- - image: nginx:alpine
- imagePullPolicy: Always
- name: nginx
- imagePullSecrets:
- - name: qcloudregistrykey
- restartPolicy: Always
这个yaml文件中通过podAntiAffinity来确保两个POD被调度到两个不同的节点。
(这一步也可以通过控制台来完成: https://console.cloud.tencent.com/ccs/service)
然后检查pod状态
- # kubectl get pod -o wide
- NAME READY STATUS RESTARTS AGE IP NODE
- nginx-2438677318-d9jv7 1/1 Running 0 3m 10.0.1.10 172.31.0.5
- nginx-2438677318-jc4b1 1/1 Running 0 3m 10.0.0.26 172.31.0.16
可以看到,两个POD分布在两个节点, IP分别为10.0.1.10 和 10.0.1.26, 属于之前配置的VPC容器网络10.0.0.0/14。
- docker run - d nginx: alpine
获取POD和vxlan容器的IP。 以节点一上的VPC POD为例:
- # kubectl get pod/nginx-2438677318-d9jv7 -o jsonpath={.status.containerStatuses[0].containerID}
- docker://a1173da42e8b395a1b72e9ffe9dad43a135826e32dedc9202acc717d63b21967
- # docker inspect --format '{{.State.Pid}}' a1173da42e8b395a1
- 17694
最终两个VPC POD和vxlan容器的IP, pid如下表所示:
节点 | POD | 容器 | 容器IP | pid |
---|---|---|---|---|
172.31.0.5 | nginx-2438677318-d9jv7 | a1173da42e8b | 10.0.1.11 | 17694 |
172.31.0.5 | --- | 8b610711dc45 | 192.168.24.2 | 28611 |
172.31.0.16 | nginx-2438677318-jc4b1 | 461b98c72697 | 10.0.1.30 | 6512 |
172.31.0.16 | --- | 5a56b09429b | 192.168.81.2 | 18172 |
ubuntu下安装qperf:
- wget https://www.openfabrics.org/downloads/qperf/qperf-0.4.9.tar.gz
- tar -xzvf qperf-0.4.9.tar.gz
- cd qperf-0.4.9
- /config
- make
centos下直接
即可
- yum install qperf
在host, VPC POD, vxlan容器三个不同的网络namespace中启动qperf server。
1) host网络
- qperf &
2) VPC POD
- # 其中17694是节点一上VPC POD的pid
- nsenter -t 17694 -n qperf &
3) vxlan容器
- # 其中28611是节点一上vxlan容器的pid
- nsenter -t 28611 -n qperf &
在172.31.0.16上,运行
- #其中10.0.1.11是远端POD的IP nsenter - t 6512 - n qperf 10.0.1.11 - oo msg_size: 64 : 64K: *2 - vu - vvc tcp_bw tcp_lat & gt;
- vpc - c2c.raw
在172.31.0.16上,运行
- #其中192.168.24.2是远端vxlan容器的IP nsenter - t 18172 - n qperf 192.168.24.2 - oo msg_size: 64 : 64K: *2 - vu - vvc tcp_bw tcp_lat & gt;
- vxlan - c2c.raw
在172.31.0.16上,运行
- nsenter
- -t
- 6512
- -n
- qperf
- 172
- .31
- .0
- .5
- -oo
- msg_size
- :64
- :64K
- :*2
- -vu
- -vvc
- tcp_bw
- tcp_lat
- &
- gt
- ;
- vpc-c2n
- .raw
在172.31.0.16上,运行
- nsenter
- -t
- 18172
- -n
- qperf
- 172
- .31
- .0
- .5
- -oo
- msg_size
- :64
- :64K
- :*2
- -vu
- -vvc
- tcp_bw
- tcp_lat
- &
- gt
- ;
- vxlan-c2n
- .raw
- qperf
- 172
- .31
- .0
- .5
- -oo
- msg_size
- :64
- :64K
- :*2
- -vu
- -vvc
- tcp_bw
- tcp_lat
- &
- gt
- ;
- n2n
- .raw
下载qperf-plot工具: https://github.com/honkiko/qperf-plot
把原始qperf报告转换为plot数据文件
- cat vxlan-c2c.raw | ./qperf-parse.py > vxlan-c2c
- cat vpc-c2c.raw | ./qperf-parse.py > vpc-c2c
- cat vxlan-c2n.raw | ./qperf-parse.py > vxlan-c2n
- cat vpc-c2n.raw | ./qperf-parse.py > vpc-c2n
- cat n2n.raw | ./qperf-parse.py > n2n
生成对比图表
- gnuplot - c qperf - plot.plt vpc - c2c vxlan - c2c
会在当前目录生成bw-[vpc-c2c]-vs-[vxlan-c2c].png和lat-[vpc-c2c]-vs-[vxlan-c2c].png
- gnuplot - c qperf - plot.plt vpc - c2n vxlan - c2n
会在当前目录生成bw-[vpc-c2n]-vs-[vxlan-c2n].png和lat-[vpc-c2n]-vs-[vxlan-c2n].png
节点配置: 腾讯云北京一区 标准型S1 1核1G
节点操作系统: Ubuntu 16.04.1 LTS
节点内核版本: 4.10.0-32-generic
qperf版本: qperf 0.4.9
flannel版本: v0.9.1
容器到容器(c2c)
以上5个qperf测试(vpc-c2c, vxlan-c2c, vpc-c2n, vxlan-c2n, n2n),重复5轮, 每轮之间间隔两分钟。5轮测试结果取平均值。结果如下。
加入节点到节点(n2n)的曲线(图中紫色曲线)作为参照
加入节点到节点(n2n)的曲线(图中紫色曲线)作为参照
吞吐率方面,VPC容器网络要比vxlan高40%, 是不是消耗了特别多的CPU资源换来的呢。为此笔者专门做了一个对比测试。
两种网络模式下,使用固定msg_size=1440, 执行qperf tcp_bw测试, 时长60秒, 同时通过mpstat每秒采集一次CPU利用率指标。 重复执行10轮测试,取平均值。结果如下:
同样的msg_size, 吞吐率大40%, 也意味着同样时间的发包数量多40%, 从而send系统调用次数也多40%。 因此mpstat统计出来的VPC容器网络下sys要高。 vxlan的封包解包是在软中断中执行的,所以即使多发送了40%的报文, VPC容器网络模式下, mpstat统计出来的softirq CPU占用反而还比vxlan要低。
进行吞吐率测试时, usr和sys这两个CPU消耗,肯定是随着每秒发包数量(pps, PacketPerSecond)增加而增加的。但是softirq的消耗对比, 则体现出了vxlan模式下额外的封包解包带来的额外CPU开销。
容器到容器,VPC容器网络是直接路由,无论是吞吐率还是时延,都很接近节点到节点通信的性能;
而vxlan需要多一层隧道封装,因此带来了很大的开销。
吞吐率方面,VPC容器网络要比vxlan高40%,时延要小5%~10%。
容器到其他节点, VPC容器网络仍然是直接路由,而vxlan容器则是通过iptables规则实现的NAT来完成通信,因此性能损耗不算大。但是不得不面对NAT带来的各种复杂性。
因此,想要在腾讯云上使用k8s, 建议还是选择官方CCS。官方CCS由专业团队来提供保障和服务,在网络性能上也有巨大的优势。
来源: https://www.qcloud.com/community/article/577511