- 过去IaaS层在网络方便作了不少工做,已经造成了成熟的环境,若是和容器环境适配,两边都须要作不少改造
- 容器时代提倡微服务,致使容器的粒度之小,离散程度之大,原有IaaS层网络解决方案很难承载如此复杂的需求
咱们来看下一些主流的容器网络接入方案:
Host network
最简单的网络模型就是让容器共享Host的network namespace,使用宿主机的网络协议栈。这样,不须要额外的配置,容器就能够共享宿主的各类网络资源。
共享物理网卡
这种接入方式主要使用SR-IOV技术,每一个容器被分配一个VF,直接经过PCIe网卡与外界通讯。SR-IOV 技术是一种基于硬件的虚拟化解决方案,可提升性能和可伸缩性。
SR-IOV 标准容许在虚拟机之间高效共享 PCIe设备,而且它是在硬件中实现的,能够得到可以与本机性能媲美的 I/O 性能。启用了 SR-IOV 而且具备适当的硬件和 OS 支持的 PCIe 设备(例如以太网端口)能够显示为多个单独的物理设备,每一个都具备本身的 PCIe 配置空间。
SR-IOV主要用于虚拟化中,固然也能够用于容器:
SR-IOV配置(须要网卡支持,线上主流万兆卡Intel 540能够支持128个VF(Virtual Function)):
modprobe ixgbevf
lspci -Dvmm|grep -B 1 -A 4 Ethernet
echo 2 > /sys/bus/pci/devices/0000:82:00.0/sriov_numvfs
# check ifconfig -a. You should see a number of new interfaces created, starting with “eth”, e.g. eth4
Intel也给Docker实现了一个[SR-IOV network plugin](
https://github.com/clearcontainers/sriov "SR-IOV network plugin"),一样也有相应的[CNI(后面会提到)Plugin](
https://github.com/Intel-Corp/sriov-cni "CNI(后面会提到)Plugin")。SR-IOV的接入方式能够达到物理网卡的性能,可是须要硬件支持,并且VF的数量是有限的。
共享容器网络
多个容器共享同一个netns,只须要第一个容器配置网络。好比Kubernetes Pod就是全部容器共享同一个pause容器的网络。
VSwitch/Bridge
容器拥有独立的network namespace,经过veth-pair链接到VSwitch或者Bridge上。
容器网络技术突飞猛进,有些至关复杂,并且提供某种功能的方式也多种多样。这样就给容器runtime与调度系统的实现与各类接入方式的适配带来的很大的困难。为了解决这些问题,Docker与CoreOS相继发布了容器网络标准,即是CNM与CNI,为Linux容器网络提供了统一的接口。
先来看下CNM:
Libnetwork是CNM的原生实现。它为Docker daemon和网络驱动程序之间提供了接口。网络控制器负责将驱动和一个网络进行对接。每一个驱动程序负责管理它所拥有的网络以及为该网络提供的各类服务,例如IPAM等等。由多个驱动支撑的多个网络能够同时并存。网络驱动能够按提供方被划分为原生驱动(libnetwork内置的或Docker支持的)或者远程驱动 (第三方插件)。原生驱动包括 none、bridge、overlay 以及 MACvlan。驱动也能够被按照适用范围被划分为本地(单主机)的和全局的 (多主机)。
- Sandbox:一个Sandbox对应一个容器的网络栈,可以对该容器的interface、route、dns等参数进行管理。一个Sandbox中能够有多个Endpoint,这些Endpoint能够属于不一样的Network。Sandbox的实现能够为linux network namespace、FreeBSD Jail或其余相似的机制。
- Endpoint: Sandbox经过Endpoint接入Network,一个Endpoint只能属于一个Network。Endpoint的实现能够是veth pair、Open vSwitch internal port或者其余相似的设备。
- Network:一个Network由一组Endpoint组成,这些Endpoint彼此间能够直接进行通讯,不一样的Network间Endpoint的通讯彼此隔离。Network的实现能够是linux bridge、Open vSwitch等。
咱们能够以Docker操做为例,简单看下CNM建立容器的工做流:
- Create Network(docker network create)
- IpamDriver.RequestPool:建立subnetpool用于分配IP
- IpamDriver.RequestAddress:为gateway获取IP
- NetworkDriver.CreateNetwork:建立network和subnet
- Create Container(docker run / docker network connect)
- IpamDriver.RequestAddress:为容器获取IP
- NetworkDriver.CreateEndpoint:建立port
- NetworkDriver.Join:为容器和port绑定
- NetworkDriver.ProgramExternalConnectivity
- NetworkDriver.EndpointOperInfo
CNM与libnetwork相对比较复杂,这里不作详细介绍,相关内容可参考libnetwork的[Design Doc](
https://github.com/docker/libn ... gn.md "Design Doc")。
再看下CNI:
其基本思想为:Container Runtime在建立容器时,先建立好network namespace,而后调用CNI插件为这个netns配置网络,其后再启动容器内的进程。
CNI插件包括两部分:
- CNI Plugin负责给容器配置网络,它包括两个基本的接口
- 配置网络:AddNetwork(net NetworkConfig,rt RuntimeConf)(types.Result,error)
- 清理网络:DelNetwork(net NetworkConfig, rt RuntimeConf) error
- IPAM Plugin负责给容器分配IP地址,实现包括host-local和dhcp等
再来看下CNI建立容器的工做流:
- 容器runtime分配一个网络命名空间以及一个容器ID
- 连同一些CNI配置参数传给网络驱动
- 网络驱动会将该容器链接到网络并将分配的IP地址以JSON的格式返回给容器runtime
全部CNI插件均支持经过环境变量和标准输入传入参数:
$ echo '{"cniVersion": "0.3.1","name": "mynet","type": "macvlan","bridge": "cni0","isGateway": true,"ipMasq": true,"ipam": {"type": "host-local","subnet": "10.244.1.0/24","routes": [{ "dst": "0.0.0.0/0" }]}}' | sudo CNI_COMMAND=ADD CNI_NETNS=/var/run/netns/a CNI_PATH=./bin CNI_IFNAME=eth0 CNI_CONTAINERID=a CNI_VERSION=0.3.1 ./bin/bridge
$ echo '{"cniVersion": "0.3.1","type":"IGNORED", "name": "a","ipam": {"type": "host-local", "subnet":"10.1.2.3/24"}}' | sudo CNI_COMMAND=ADD CNI_NETNS=/var/run/netns/a CNI_PATH=./bin CNI_IFNAME=a CNI_CONTAINERID=a CNI_VERSION=0.3.1 ./bin/host-local
直观的来看,CNI 的规范比较小巧。它规定了一个容器runtime和网络插件之间的简单的契约(详细内容请参考[SPEC](
https://github.com/containerne ... EC.md "SPEC"))。
在斟酌某项技术的时候,咱们须要考虑采纳的成本;是否会增长对供应商的依赖。社区的采纳程度和支持程度也是比较重要的考量。插件开发者还须要考虑哪一种方式相对比较容易实现。
目前,主流容器调度框架(Mesos、Kubernetes)与网络解决方案(Calico、Flannel等)都对CNI有良好的支持,另外其简单的接口使插件的开放成本很低;而且不依赖docker runtime,使容器解决方案有了更多选择(例如Mesos Containerizer)。因此,咱们选择了CNI。
在对比业内支持CNI接口的主流网络解决方案以前,须要对多主机条件下,容器的组网方案作下分类,来方便你们理解不一样解决方案之间的区别:
Flat
- L2 Flat
- 各个host中容器在虚拟与物理网络造成的VLAN(大二层)中
- 容器能够在任意host间迁移不用改变其IP
- 不一样租户IP不可Overlap
- L3 Flat
- 各个host中容器在虚拟与物理网络中可路由,且为32位路由
- 由于是32位路由,因此容器在任意host间迁移不用改变IP
- 不一样租户IP不可Overlap,容器与物理网络IP也不可Overlap
L3 Hierarchy
- 各个host中容器在虚拟与物理网络中可路由
- 路由在不一样层次上(VM/Host/Leaf/Spine)以聚合路由的形式存在
- 相同CIDR的容器需在物理上被组织在一块儿
- 容器在host间迁移须要改变IP
- 不一样租户IP不可Overlap,容器与物理网络IP也不可Overlap
Overlay
- L2 over L3
- 容器可跨L3 Underlay进行L2通讯
- 容器可在任意host间迁移而不改变其IP
- L3 over L3
- 容器可跨L3 Underlay进行L3通讯
- 容器可在任意host间迁移可能须要改变其IP
- 不一样租户IP可Overlap,容器IP也可与Underlay网络Overlap
下面咱们来看下社区比较承认的几种容器网络解决方案。
Flannel
经过给每台宿主机分配一个子网的方式为容器提供虚拟网络,它基于Linux TUN/TAP,使用UDP封装IP包来建立overlay网络,并借助etcd维护网络的分配状况。
控制平面上host本地的flanneld负责从远端的ETCD集群同步本地和其它host上的subnet信息,并为POD分配IP地址。数据平面flannel经过Backend(好比UDP封装)来实现L3 Overlay,既能够选择通常的TUN设备又能够选择VxLAN设备。
从上图能够看出,Flannel是典型的L3 over L3组网方案。
支持的Backend:
- udp:使用udp封装,默认使用8285端口
- vxlan:vxlan封装,须要配置VNI,Port(默认8472)和GBP
- host-gw:直接路由的方式
- 公有云vpc:aws-vpc、gce、ali-vpc
如上图,能够方便的与Docker集成:
source /run/flannel/subnet.env
docker daemon --bip=${FLANNEL_SUBNET} --mtu=${FLANNEL_MTU} &
也提供CNI接口:
CNI插件会将flannel网络配置转换为bridge插件配置,并调用bridge插件给容器netns配置网络。好比下面的flannel配置:
{
"name": "mynet",
"type": "flannel",
"delegate": {
"bridge": "mynet0",
"mtu": 1400
}
}
会被插件转换为:
{
"name": "mynet",
"type": "bridge",
"mtu": 1472,
"ipMasq": false,
"isGateway": true,
"ipam": {
"type": "host-local",
"subnet": "10.1.17.0/24"
}
}
Flannel即是经过CNI与Mesos、Kubernetes集成。因为历史缘由,咱们线上Kubernetes集群是经过前一种方式直接与Docker集成的。
优势:
- 配置安装简单,使用方便
- 与公有云集成方便,VPC方式无Overhead
缺点:
- Vxlan模式对平滑重启支持很差(重启须要数秒来刷新ARP表,且不可变动配置,例如VNI、iface)
- 功能相对简单,不支持Network Policy
- Overlay存在必定Overhead
Weave
不一样于其它的multi-host方案,其支持去中心化的控制平面,各个host上的wRouter间经过创建Full Mesh的TCP连接,并经过Gossip来同步控制信息。这种方式省去了集中式的K/V Store,可以在必定程度上减低部署的复杂性。不过,考虑到docker libnetwork是集中式的K/V Store做为控制平面,所以Weave为了集成docker,它也提供了对集中式控制平面的支持,可以做为docker remote driver与libkv通讯。
数据平面上,Weave经过UDP封装实现L2 Overlay,封装支持两种模式:
- 运行在user space的sleeve mode:经过pcap设备在Linux bridge上截获数据包并由wRouter完成UDP封装,支持对L2 traffic进行加密,还支持Partial Connection,可是性能损失明显。
- 运行在kernal space的 fastpath mode:即经过OVS的odp封装VxLAN并完成转发,wRouter不直接参与转发,而是经过下发odp 流表的方式控制转发,这种方式能够明显地提高吞吐量,可是不支持加密等高级功能。
- 全部容器都链接到Weave网桥
- weave网桥经过veth pair连到内核的OpenVSwitch模块
- 跨主机容器经过openvswitch vxlan通讯
- policy controller经过配置iptables规则为容器设置网络策略
关于Service的发布,Weave作的也比较完整。首先,wRouter集成了DNS功能,可以动态地进行服务发现和负载均衡,另外,与libnetwork 的overlay driver相似,weave要求每一个容器有两个网卡,一个就连在lb/ovs上处理L2 流量,另外一个则连在docker0上处理Service流量,docker0后面仍然是iptables做NAT。同事,提供了一个容器监控和故障排查工具Weave Scope,能够方便的生成整个集群的拓扑并智能分组。
优势:去中心化、故障自愈、加密通信、支持组播
缺点:
- sleeve mode性能损耗巨大
- full mesh模型限制了集群规模
- Partial Connection在fastpath未实现
Contiv
思科开源的容器网络方案,是一个用于跨虚拟机、裸机、公有云或私有云的异构容器部署的开源容器网络架构,并与主流容器编排系统集成。Contiv最主要的优点是直接提供了多租户网络,并支持L2(VLAN),L3(BGP),Overlay(VXLAN)以及思科自家的ACI。
主要由两部分组件组成:
- Netmaster
- 对外提供REST API
- 学习并分发路由到Netplugin
- 管理集群资源(IP、VLAN、VXLAN ID等)
- Netplugin
- 运行在集群中每一个节点上
- 实现了CNI与CNM接口
- 经过REST方式与Netmaster通信,监听事件,并作相应网络配置
- 数据平面使用OVS实现(插件架构,可替换,例如VPP、BPF)
优势:
- 支持多种组网方式,集成现有SDN方案
- 多租户网络混部在同一台主机上
- 插件化的设计,可替换的数据平面
- 即时生效的容器网络Policy/ACL/QoS规则
- 基于OVS的高性能Load Balancer的支持
- 清晰的设计,方便二次开发
缺点:
- 功能丰富带来的反作用即是配置和使用比较复杂
- 由于发展时间较短,社区和文档都不怎么成熟,也存在一些bug,稳定性有待商榷
- CNI plugin对Mesos支持不友好(跟Cisco官方沟通后,对Mesos支持仍在POC阶段)
OpenContrail
是Juniper推出的开源网络虚拟化平台,其商业版本为Contrail。
OpenContrail主要由控制器和vRouter组成:
- 控制器提供虚拟网络的配置、控制和分析功能
- vRouter提供分布式路由,负责虚拟路由器、虚拟网络的创建以及数据转发
vRouter支持三种模式:
- Kernel vRouter:相似于ovs内核模块
- DPDK vRouter:相似于ovs-dpdk
- Netronome Agilio Solution (商业产品):支持DPDK, SR-IOV and Express Virtio (XVIO)
从上图能够看出,OpenContrail的架构是至关复杂的。相应的,其提供的feature基本覆盖了前全部提到的方案;甚至包括基于Cassandra后端的流量采样与分析平台;因为篇幅所限,这里不作详细介绍,相关内容可参考[Arch Doc](
http://www.opencontrail.org/op ... tion/ "Arch Doc")。
计算机科学的先驱David Wheeler曾经说过——“Any problem in computer science can be solved with another level of indirection.”, SDN便是Network的indirection。容器网络解决方案多不胜数,咱们在这里就不一一介绍了。可是咱们能够看到,每种方案其实都是对network作了不一样程度的indirection,可是不要忘了后面还有一句——“except of course for the problem of too many indirections”,因此你们在不一样的方向上作出了取舍与权衡。正如前面所提到的,在斟酌某项技术的时候,咱们须要考虑采纳成本、对供应商的依赖、社区的支持程度等等。咱们正在如此权衡以后,并没选择上面的方案,而是选择了Calico,下面我会详细说明。
Calico
[Calico](
https://www.projectcalico.org/ "Calico") 是一个纯三层的数据中心网络方案(不须要Overlay),而且与OpenStack、Mesos、Kubernetes、AWS、GCE等IaaS和容器平台都有良好的集成。
Calico是一个专门作数据中心网络的开源项目。当业界都痴迷于Overlay的时候,Calico实现multi-host容器网络的思路确能够说是返璞归真——pure L3,pure L3是指容器间的组网都是经过IP来完成的。这是由于,Calico认为L3更为健壮,且对于网络人员更为熟悉,而L2网络因为控制平面太弱会致使太多问题,排错起来也更加困难。那么,若是可以利用好L3去设计数据中心的话就彻底没有必要用L2。
不过对于应用来讲,L2无疑是更好的网络,尤为是容器网络对二层的需求则更是强烈。业界广泛给出的答案是L2 over L3,而Calico认为Overlay技术带来的开销(CPU、吞吐量)太大,若是能用L3去模拟L2是最好的,这样既能保证性能、又能让应用满意、还能给网络人员省事,看上去是件一举多得的事。用L3去模拟L2的关键就在于打破传统的Hierarchy L3概念,IP再也不之前缀收敛,你们干脆都把32位的主机路由发布到网络上,那么Flat L3的网络对于应用来讲即和L2如出一辙。
这个思路不简单,刨了L2存在必要性的老底儿,实际上若是不用考虑可扩展性、隔离性,IP地址和MAC地址标识endpoint的功能上确实是彻底冗余的,即便考虑可扩展性,一个用L3技术造成的大二层和一个用L2技术造成的大二层并无本质上的差距。并且,L3有成熟的、完善的、被广泛承认接受的控制平面,以及丰富的管理工具,运维起来要容易的多。
因而,Calico给出了下面的设计:
Calico在每个计算节点利用Linux Kernel实现了一个高效的vRouter来负责数据转发,而每一个vRouter经过BGP协议负责把本身上运行的workload的路由信息向整个Calico网络内传播—,路由条目全是/32的v4或者/128的v6。小规模部署能够直接互联,大规模下可经过指定的BGP route reflector来完成。 这样保证最终全部的workload之间的数据流量都是经过IP路由的方式完成互联的。Calico节点组网能够直接利用数据中心的网络结构(不管是L2或者L3),不须要额外的NAT,隧道或者Overlay Network。
此外,Calico基于iptables还提供了丰富而灵活的网络Policy,保证经过各个节点上的ACLs来提供Workload的多租户隔离、安全组以及其余可达性限制等功能。另外,对CNM与CNI的支持使其能够方便适配绝大多数场景。
这里有必要说明下,CNI和CNM并不是是彻底不可调和的两个模型。两者能够进行转化的,calico项目最初只支持CNI,后面才加入CNM的支持。从模型中来看,CNI中的container应与CNM的sandbox概念一致,CNI中的network与CNM中的network一致。在CNI中,CNM中的endpoint被隐含在了ADD/DELETE的操做中。CNI接口更加简洁,把更多的工做托管给了容器的管理者和网络的管理者。从这个角度来讲,CNI的ADD/DELETE接口其实只是实现了docker network connect和docker network disconnect两个命令。相似的,[kubernetes/contrib](
https://github.com/kubernetes/ ... ocker "kubernetes/contrib")项目提供了一种从CNI向CNM转化的过程,这里就不作详细介绍了。
咱们详细看下Calico中的各个组件:
- Felix:Calico Agent,跑在每台须要运行Workload的节点上,主要负责经过iptables来配置ACLs
- etcd:分布式键值存储,主要负责网络元数据一致性,确保Calico网络状态的准确性
- confd:根据etcd上状态信息,与本地模板,生成并更新BIRD配置
- BGP Client(BIRD):主要负责容器的路由信息分发到当前Calico网络,确保Workload间的通讯的有效性
- BGP Route Reflector(BIRD):大规模部署时使用,摒弃全部节点互联的 mesh 模式,经过一个或者多个BGP Route Reflector来完成集中式的路由分发
这个架构,技术成熟、高性能、易维护,看起来是生产级别的容器网络环境最好的选择。可是,也有不如意的地方:
- 没有了外面的封装,就谈不上VRF,多租户的话地址无法Overlap
- L2和L3的概念模糊了,那么network级别的安全就难以实现,port级别的安全实现难是由于须要的规则都是1:1的,数量太多
上面并非什么严重的问题。可是有一点,Calico控制平面的上述设计中,物理网络最好是L2 Fabric,这样vRouter间都是直接可达的,路由不须要把物理设备当作下一跳。若是是L3 Fabric,控制平面的问题就来了:物理设备若是要存32位的路由,路由表将变得巨大无比。
所以,为了解决以上问题,Calico不得不采起了妥协,为了支持L3 Fabric,Calico推出了IPinIP的选项,用做cross-subnet下容器间通信,可是有必定性能损耗。另外一种方案是回退到L3 Hierarchy(calico目前支持),若是对容器迁移保持IP不变的需求不大,能够选择这种,这也是咱们最后选择的方案。不过,对于使用L2 Fabric、没有多租户需求的企业来讲,用calico上生产环境应该最理想选择。
Calico最理想的部署方式是基于L2 Fabrics的,官方也有相应说明([Calico over an Ethernet interconnect fabric](
http://docs.projectcalico.org/ ... abric "Calico over an Ethernet interconnect fabric")),这里就不作详细说明了。因为咱们数据中心规模比较大,显然L2 Fabrics不是理想的选择。事实也是如此,咱们多个数据中心组成的网络是以BGP为控制层面组成的L3 Fabrics。每一个数据中心为一个AS。这里不讨论跨数据中心的问题,你们只需知道咱们跨数据中心在L3上是互通的即可以。
官方给出了3种基于L3 Fabrics的组网建议(详细介绍可点击进去查看):
AS per Rack这种模型在每一个ToR下加入了RR(Route Reflector),关于RR,前面有提到过,这里有必要解释一下:
首先说下BGP的水平分割原则(这里只谈IBGP),从IBGP学习到的路由毫不对再传播给其它的IBGP邻居(能够传给EBGP邻居)。IBGP水平分割主要是为了防止在AS内部产生路由环路。因此,为了AS内路由正确收敛,须要全部节点组成full mesh结构(也就是两两之间创建邻居)。可是,这种架构在大型网络中显然行不通。
另外一种解决方案即是RR,一种C/S模型。在同AS内,其中一台设备做为RR,其余设备做为Client。Client与RR之间创建IBGP链接。路由反射器和它的客户机组成一个Cluster。这里不详细讨论RR的工做原理。显然,路由反射破坏了水平分割的原则,因此便要许多额外的配置来防止环路,这会给实际部署过程带来不少负担。另外,咱们同一数据中心内运行的是IBGP,也就是都在同AS内。这使的配置改造更为复杂,并且,显然不符合AS per Rack。
另外两种模型一样要求每一个ToR在不一样的AS,也面临上述问题。另外,若是不在ToR上作路由聚合,AS per Compute Server会形成Linux服务器上有全网的路由,庞大的路由表也会成为性能的瓶颈。
通过与网络部门的认真研讨,咱们最终选择了下面的部署模型:
- 每一个Server选择不一样的AS号,且与数据中心AS不一样,与ToR创建EBGP邻居
- Server使用原有网关(默认路由),ToR不向Server宣告任何路由条目
- Server本地聚合路由到28位,并宣告给ToR
- 与网络部门制定必定算法(方便自动化),用Server的IP地址计算出AS号
能够看到,上述方案,几乎不用对数据中心现有网络架构作出改变;并且,也大大减小了Server与ToR上路由条目数。
实际部署Calico的Agent时,咱们还遇到了一些问题:官方默认只提供基于Docker的部署方式,也就是以前提到的几个组件都部署在一个Docker容器中。calicoctl也是基于Docker容器作部署操做。可是,实际环境中,咱们线上的Mesos集群有部分是只是用Mesos Containerizer的,也就是并无Docker环境;并且,咱们认为,Calico Agent加入对Docker daemon的依赖,考虑到稳定性,显然不是一个良好的选择。
因此,咱们对Calico组件进行拆解,从新拼装,并去掉咱们用不到IPv6模块;最终经过systemd在每台server上运行bird、confd、felix这三个模块,实现了去Docker化的部署(实际上对confd的模板也作了些改造,去掉了IPinIP的支持,而且适配的非Docker环境)。整个过程基于Ansible,实现了一键部署。
关于性能问题,虽然Calico在众多基于软件的容器网络解决方案里是性能最好的,可是也不是彻底没有性能损耗。前面说过,Calico实现namespace内外通信是经过linux内核的veth pair来实现的(就是以前提到的接入方式中的VSwitch/Bridge)。确实增长了一层overhead,可是好在veth pair的实现比较简单,性能损耗几乎能够忽略(无policy条件下)。咱们实际测试中,对于echo server(小报文测试),请求延迟会增长0.02到0.04ms;对于bandwidth(大报文测试,万兆环境),很难看到差异。
到了这一步,你可能以为咱们大功告成,实则否则。其实咱们还有不少事情能够作,也须要去作。Calico支持丰富的安全策略,可是在server上是用kernel的netfilter实现的(iptables),大量policy下的性能仍有待考察。
业内也有一些其余的实现思路,例如Google大力支持cilium方案,其policy的实现就用了BPF(Berkeley Packet Filter,最先在kernel 3.15中加入,4.8版本之后标为stable)。BPF的基本思路是把用做包处理的代码经过llvm编译成ir,而后插入到正在运行的kernel网络栈中,经过jit方式执行,大幅度提升了处理能力。这里不作详细介绍,相关信息能够看这里。cilium还加入了对XDP的支持(一种相似DPDK的技术,可是目前集成在kernel中),来加速网络栈的处理。这些都不失为将来改造calico的一个方向。
另一个问题是,calico目前并无对traffic shaping的支持。试想,当大量容器运行与同一物理机,其进出流量都共享物理网卡,若是不对容器流量进行限制,单个容器过度使用网络资源就会影响其余容器提供服务(磁盘IO也有一样问题,可是不在本文讨论范围)。Calico社区目前也在讨论这个问题,详情可见这个issue,目前还在API制定的阶段。
上述问题,底层实现目前可行选择是linux自带的tc(OVS一样用到了tc)。上层切入点却有两个选择,一是拓展Calico的API,在felix上实现应用tc规则的逻辑;另外一种是在CNI plugin上hook,经过相应的调度系统,传参数进来,由plugin来应用tc规则。因为,calico社区已经在讨论这个问题,并且第二种方案实现起来成本也比较低。现阶段,咱们只考虑方案二,也作了相应的POC。
实践过程当中,因为veth pair两端的流量是相反的(ingress与egress),理论上能够在两端的egress上应用tc规则(tc只支持egress方向的shaping),来实现两个方向上的QoS。可是,咱们仍是遇到了一些问题,namespace内的虚拟网卡上的tc规则虽然可配置,可是并不生效。并且,这些规则对容器内应用是可见、可修改的,这样作也并不安全。因此,咱们用到了ifb内核模块,把物理机上虚拟网卡的ingress流量变成egress,最终在在ifb网卡上应用tc规则,来实现容器egress方向的QoS。关于ifb,功能比较简单,你们能够看下kernel代码中相关的介绍:
/* drivers/net/ifb.c:
The purpose of this driver is to provide a device that allows
for sharing of resources:
1) qdiscs/policies that are per device as opposed to system wide.
ifb allows for a device which can be redirected to thus providing
an impression of sharing.
2) Allows for queueing incoming traffic for shaping instead of
dropping.
The original concept is based on what is known as the IMQ
driver initially written by Martin Devera, later rewritten
by Patrick McHardy and then maintained by Andre Correa.
You need the tc action mirror or redirect to feed this device
packets.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version
2 of the License, or (at your option) any later version.
Authors: Jamal Hadi Salim (2005)
*/
迫于篇幅限制,此次就先到这里吧,后续我会把一些详细实现拿出来给你们分享,欢迎共同交流。
看了上面这么多,有人可能会问,咱们作了这么多工做,到底是为了什么。回过头来看今天,咱们理所固然的用着方便的跨数据中心环网、接入负载均衡、共享存储、对象存储,甚至到上层的云平台等等。正是由于有了这些,业务们拿出编译好的代码,快的不到几分钟就能够被线上的用户所使用。
这就是基础设施,这些是这座大楼的地基。正由于如此,基础设施的改造才异常困难。咱们须要用合理的抽象来解决顽疾,更须要对上层建筑的兼容。正如咱们在网络方面所作的工做,让容器和现有物理设备在三层上的互通,实现了网络上对业务的透明;同时又提供了良好的性能,消除了业务迁移到云平台上的顾虑;在提升资源利用率的混部环境下,同时提供可配置的策略,为服务质量提供了有效的保障。