概述
容器是相比于虚拟机(Virtual Machine,VM)更轻便,部署更方便快捷的一种虚拟化技术。 Docker是目前主流的容器引擎,支持Linux,Windows等多种平台,同时支持Kubernetes(K8S), Swarm及Rocket(RKT)等主流Docker编排系统。常见的容器网络支持Bridge,Overlay,Host及用户自定义网络等多种模型,K8S等系统依赖于容器网络接口(Container Network Interface, CNI)插件来完成网络管理,常见的有Calico/Flannel等知名CNI插件。本文将介绍一些容器网络的基本知识,基于阿里云的弹性网络接口(Elastic Network Interface, ENI)技术实现了ECS容器网络的高性能,易部署及维护, 具有强隔离的高安全容器网络。
多网卡容器网络
当VM拥有多张网络接口卡(Network Interface Card, NIC),而且这些NIC是能够动态热插拔时,NIC就能够用于容器网络, 这样容器网络将不再需要利用Linux VETH及Bridge等技术,同时报文转发下移到了位于宿主机上的虚拟交换机(Virtual Switch,vSwitch),通过减少流程提升网络性能。
方案介绍
如下图所示,在宿主机上运行有vSwitch,用于转发VM及容器的流量, 在vSwitch上连接有多张虚拟NIC。在VM内启动容器时,在宿主机上动态地将虚拟NIC绑定容器所在的VM,然后在VM内部将NIC绑定到容器所在的网络命名空间,容器内的网络流量能够直接通过这块NIC直接发送到位于宿主机上的vSwitch(容器网络直通vSwitch), vSwith内应用ACL/QoS/Session等规则过后将流量进行转发。当位于宿主机1上的VM内运行的容器访问位于宿主机2上的VM内运行的容器时,流量大致会经历如下流程:
1> 网络报文经过容器内核网络协议栈,查找路由后通过eth0网卡发送报文;
2> 宿主机上的vSwitch从虚拟端口收到来自容器的报文,运行vSwitch的转发逻辑,将报文通过物理网络端口发送到ToR交换机 (Top of Rack Switch, ToR Switch),如果针对容器或者虚拟机网络建立了虚拟私有云 (Virtual Private Cloud, VPC)则需要对报文使用VxLAN等隧道技术进行封装;
3> ToR Switch通过查询路由信息,通过连接宿主机2的物理端口将报文转发;
4> 位于宿主机2上的vSwitch接收物理端口报文,经转发逻辑发送到连接容器的虚拟端口;
5> 容器内协议栈eth0收到由另一端发送来的报文,由容器内网络协议栈进行处理。
2> 宿主机上的vSwitch从虚拟端口收到来自容器的报文,运行vSwitch的转发逻辑,将报文通过物理网络端口发送到ToR交换机 (Top of Rack Switch, ToR Switch),如果针对容器或者虚拟机网络建立了虚拟私有云 (Virtual Private Cloud, VPC)则需要对报文使用VxLAN等隧道技术进行封装;
3> ToR Switch通过查询路由信息,通过连接宿主机2的物理端口将报文转发;
4> 位于宿主机2上的vSwitch接收物理端口报文,经转发逻辑发送到连接容器的虚拟端口;
5> 容器内协议栈eth0收到由另一端发送来的报文,由容器内网络协议栈进行处理。
方案特点
与传统的在VM内运行容器的方案对比,本方案具有高性能,易管理及强隔离等特点。
VPC直通
让容器通过多网卡直通的方案,直接接入VPC网络平面,能让每个容器具备全量的VPC网络功能,包括:EIP、SLB、高防、安全组、HAVIP、NAT、用户路由等众多高级功能。
跨VPC
容器多网卡直通方案,直接接入了VPC网络平面,因此可以可以使用VPC的一些高级功能,例如能够使用peer 功能,也可以使用跨VPC的弹性网卡访问云产品, 也可以给容器分配多个不通VPC内的弹性网卡,使其能够同时跨多个VPC。
高性能
在多网卡方案中,容器内的网络流量不再需要经过在VM内部的Iptables/Bridge等进行转发,而是直通到位于宿主机上的vSwitch,这样既省去VM内的数据报文转发逻辑,同时也减少了数据拷贝过程,可以在很大程度上提高容器网络的性能。如下表是单次测试的基本性能数据对比:
单线程 (Mbps) |
单线程(pps) |
多线程 (pps) |
tbase实测 1kB(QPS) |
|
LinuxBridge |
32.867 |
295980 |
2341669 |
36.33W |
多网卡方案 | 51.389 |
469865 |
3851922 |
47.09W |
性能提升 | 56.35 % |
58.7 % |
64.49 % |
29.6% |
强隔离
传统的Bridge方案,所有的容器实例都在同一个大二层网络里,存在各种广播、多播、未知单播泛滥的情况。而通过多网卡直通配合ECS网络提供的ACL和安全组功能,就能够做到有效的安全隔离,甚至容器的管理平面都看不到容器的网络流量; 安全规则的力度也可以达到容器而不再是虚拟机级别。
易管理
当管控系统将容器调度到某个VM时, 管控系统在VM所在宿主机上的vSwitch上创建一个NIC,通过热插拔技术将NIC插入到VM,在VM部将NIC配置到容器网络命名空间,通过配置vSwitch的流量转发规则,然后在xGW上配置HaVIP,外部应用及客户端就能够访问容器提供的服务。
多网卡方案还利于容器的迁移。以迁移到同一台宿主机上的另一个VM为例,K8S的kubelet模块迁移应用,然后通过CNI插件重新配置网络,管理容器IP及VIP, 配置访问容器应用的方式,这个一个复杂的过程;多网卡方案能够很好的解决这个问题,容器被调度到一个VM后,将旧的容器绑定的NIC从旧的VM拔出,然后插入新的容器所在的VM,在VM内部将NIC绑定到容器网络命名空间,新的容器就能够正常进行通信了,不再需要重新进行网络配置。
支持DPDK
由于优越的性能优势,DPDK变得流行起来,越来越多的应用开始基于DPDK开发。 传统的容器网络使用VETH作为网络设备,目前无法直接使用DPDK的PMD驱动,所以无法直接在容器内部使用基于DPDK的应用。 而在多网卡的方案中,容器使用ECS的网络设备,网络设备为常见的E1000 或者 virtio_net设备, 这两种设备都有PMD驱动,容器使用这个设备就能够直接运行基于DPDK的应用,能够在一定程度上提升容器内应用的网络性能。
背景介绍
本章节会简介传统容器网络的工作原理及多网卡容器网络方案用到的虚拟机多网卡技术。
容器网络
CNI是CNCF(Cloud Native Computing Foundation)基金会管理的一个开源项目,制定标准及提供源码库用于各大厂商开发Linux容器网络管理的插件。知名的CNI插件有Calico和Flannel,Calico通过Flex/Bird实现BGP等协议,存储到分布式的内存数据库实现了一个大三层网络,使不同宿主机上的容器在不发送ARP的前提下能够与不同子网的容器进行通信。Flannel基于VxLAN等隧道技术实现了容器Overlay网络。Calico/Flannel等CNI配置容器网络就是利用的VETH成对出现的特性,在配置容器网络时创建一对VETH设备,将一端绑定到容器, 另一端保留在VM,VM内通过网络协议栈(Overlay网络),Iptables(Calico插件)或者Linux Bridge等技术来实现容器网络的转发(容器网络通过ECS内的网桥连接到虚拟交换机, VPC 只能达到ECS级别,容器网络为网桥上的私有网络)。
目前主流容器网络工作流如下图所示,与多网卡容器网络存在如下的差异:
1> 宿主机1上容器发送出报文通过VETH传输到VM内部的Linux Bridge上, LinuxBridge运行转发逻辑将报文VM内的NIC发送到位于宿主机上的vSwitch。
2> 宿主机2上VM收到vSwitch发送的报文,通过LinuxBridge的转发逻辑后由VETH发送给容器
2> 宿主机2上VM收到vSwitch发送的报文,通过LinuxBridge的转发逻辑后由VETH发送给容器
在整个网络系统中,VM内部需要K8S等编排系统的CNI插件进行网络配置,vSwitch支持Openflow或Netconf等通信协议,通过软件定义网络(Software Defined Network, SDN)控制器进行管理配置。主流ToR Switch 使用Netconf协议进行远程配置,市面也有支持Openflow的SDN物理交换机。
要管理整个网络,就需要两个不同的网络控制系统进行控制,配置相对复杂,且因实现机制等因素导致整个系统存在一定的性能瓶颈,宿主机上的安全策略也不能做到容器应用级别。
虚拟机多网卡
在为了让物理主机实现跨域,需要在物理机上插入多张NIC,由于受限于PCI插槽数目及成本控制,物理机上很少部署多于两张NIC;硬件设备的上下电都或多或少的给整个系统增加脉冲,影响到整机的稳定性,设备热插拔收到一定的限制,比较常见的热插拔设备是USB设备,PCI设备由于需要进行2次枚举及功率影响等原因,热插拔在最近几年才得以受限支持。
在虚拟化环境中,虚拟NIC的低成本及灵活性大大增加了VM的可用性,用户能够根据需求动态的分配或者释放NIC,能够将NIC在不影响VM正常运行的前提下动态的插入或者拔出VM, libvirt/qemu模拟虚拟设备的方式具有物理主机无法比拟的优势:
资源限制
针对NIC,只要系统有足够的内存等资源,就能够模拟出多张NIC分配给同一个VM,可以实现一个VM里面安装有64张甚至128张NIC。由于这些NIC都是软件模拟的,相对于物理硬件环境可以极大的节约成本,而针对主流硬件支持的多队列及一些卸载功能也有很好的支持,增加了系统的灵活性。
动态热插拔
VM上NIC是通过软件模拟的,因此在需要NIC的时候,通过软件分配一些基本的资源后模拟出NIC设备,通过libvirt/qemu的热插拔框架能够很轻松的绑定到某个已经正在运行的VM,VM就能够使立即使用这个NIC发送网络报文;当不再需要这个NIC后,在不停止VM的情况下,通过libvirt/qemu接口调用就能“拔”掉这个NIC并将NIC的资源进行销毁,回收重利用其所占用的内存,中断等。
配置实践
本章节将介绍如何一步步通过使用虚拟机多网卡实现容器网络通信。
-
在阿里云控制台部署创建虚拟机实例,创建实例时候选择支持多网卡, 在虚拟机内部能够看到多张网卡:
-
在虚拟机内部部署容器应用:
~# docker run -itd --net none ubuntu:16.04
注: 在启动docker 的时候指定容器的netowrk 类型为none
-
登录到VM内部, 将其中一个NIC绑定到容器命名空间, 在下面的例子中,新动态插入的网络接口卡为eth2, 容器的网络命名空间为2017(为方便区分,以docker inspect看到的PID作为网络命名空间名)
~# mkdir /var/run/netns
~# ln -sf /proc/2017/ns/net /var/run/netns/2017
~# ip link set dev eth2 netns 2017
~# ip netns exec 2017 ip link set eth2 name eth0
~# ip netns exec 2017 ip link set eth0 up
~# ip netns exec 2017 dhclient eth0
注: 根据发行版的不同,可能不需要用户自己手动创建连接来“创建” 容器的网络命名空间, 在将eth2绑定到容器的网络命名空间后,将其重命名为eth0.
-
在VM内及容器内查看NIC配置状态:
虚拟机内查看NIC是否还继续存在:
~# ifconfig -a
容器内查看是否有新配置的NIC:
/# ifcofig -a
可以看到,eth2已经从VM内部移除,应用到了容器内部。
-
重复步骤1~4启动另一个VM及容器。
-
使用sockperf等工具进行性能测试及对比。
$ cat server.sh
#!/bin/bash
for i in $(seq 1 $1)
do
sockperf server --port 123`printf "%02d" $i` &
done
$ sh server.sh 10
$ cat client.sh
#!/bin/bash
for i in $(seq 1 $1)
do
sockperf tp -i 192.168.2.35 --pps max --port 123`printf "%02d" $i` -t 300 &
done
$ sh client 10
总结
本文介绍了一种基于虚拟机多网卡热插拔的容器网络解决方案,通过给虚拟机动态热插拔网卡并应用到容器中用于容器网络数据报文的收发,借助运行在虚拟机内的虚拟软件交换机对网络报文的转发,大大简化了容器网络的管理控制系统的复杂度,同时也大幅提高了网络性能,同时增强了容器网络的安全性。
附录1. 业务实测
Tier-Base(tbase) 是一个跟Redis类似的分布式KV数据库,使用C++语言编写,几乎支持所有的Redius数据结构,同时支持 RocksDB作为后端. tbase在蚂蚁金服里面比较常用,本章节将介绍本方案tbase业务实测情况。
传统LinuxBridge测试
测试环境
server: 16C60G x 1(half A8)
client: 4C8G x 8
tbase server部署方式:7G x 7个实例
tbase client部署方式:8 x (16threads + 1 client) => 128 threads + 8 clients
测试报告
操作 | 包大小 | clients |
网卡 |
load1 |
cpu |
QPS |
avg rt |
99th rt |
set | 1KB | 8 | 424MB | 7.15 | 44% | 36.33W | 0.39ms | <1ms |
get | 1KB | 8 | 421MB | 7.06 | 45% | 35.70W | 0.39ms | <1ms |
set |
64KB | 1 |
1884MB |
2.3 |
17% |
2.90W |
0.55ms |
<5ms |
set |
128KB | 1 |
2252MB |
2.53 |
18% |
1.82W |
0.87ms |
<6ms |
set |
256KB | 1 |
2804MB |
2.36 |
20% |
1.11W |
1.43ms |
<5ms |
set |
512KB | 1 |
3104MB |
2.61 |
20% |
0.60W |
2.62ms |
<10ms |
ENI 多网卡测试
测试环境
server: 16C60G x 1(half A8)
client: 4C8G x 8
tbase server部署方式:7G x 7个实例
tbase client部署方式:16 x (16threads + 1 client) => 256 threads + 16 clients
测试报告
操作 |
包大小 |
clients |
网卡 |
load1 |
cpu |
QPS |
avg rt |
99th rt |
set/get |
1KB | 16 |
570MB |
6.97 |
45% |
47.09W |
0.30ms |
<1 |
测试结论
基于ENI多网卡方案,整体性能和延迟情况比Bridge方案有较明显提升(QPS提升30%,平均延迟降低23%),16C60G的情况下,QPS 47.09W左右,avg / p99 = 0.30ms / <1ms,CPU消耗分别为user 45% + sys 29% + si 18% + st 2%,si消耗比Bridge有明显下降,通过内核队列打散,st消耗分散到了多个不同的core上,处理资源使用更均衡。
对于VPC路由表的的解决方案flannal/canal来看,带宽和吞吐基本上没有损失,延迟会相对宿主机的有0.1ms左右的延迟,使用nginx测试qps,在页面较小时的损失在10%左右;对于ENI方案看,带宽,吞吐,相对宿主机基本上没损失,在延迟上比宿主机稍微低一些,在应用测试上性能优于宿主机网络10%左右,原因应该是POD内部基本上没有经过iptables;对于默认的flannel vxlan,带宽和吞吐损失5%左右,而在nginx测试小页面的最大qps中,相对宿主机损失大概30%的性能。