本篇主要是简单了解CNI的插件,代码链接在此。
概览
containernetworking团队维护了一些CNI插件。我们就叫他官方插件吧。接下来我们看看官方插件以及比较知名第三方插件都有哪些。
官方插件
Main: interface-creating
- bridge: 创建一个bridge,给他添加host以及container。
- ipvlan: 在容器中添加ipvlan的端口。
- loopback: 将loopback接口的状态设置为up。
- macvlan: 创建一个新的MAC地址,将所有目的mac对应的流量都转发到该容器。
- ptp: 创建一个veth pair的接口。
- vlan: 申请一个vlan设备。
- host-device: 将一个已经存在的设备转移到容器。
- win-bridge: win下的插件,功能类似上述的bridge。
- win-overlay: 给container创建一个overlay端口。
IPAM: IP address allocation
- dhcp: 在宿主机上运行一个daemon程序,代表container生成DHCP请求。
- host-local: 维护一个本地的数据库来存储已经申请的IP。
- static: 申请一个固定的IPv4/IPv6地址,他对debug很有用处。
Meta: other plugins
- flannel: 生成和flannel配置对应的接口。
- tuning: 调整已有接口的sysctl参数。
- portmap: 一个基于iptables的portmapping插件。将端口从宿主机地址空间映射到容器。
- bandwidth: 允许通过使用流量控制tbf(ingress/egress)限制宽带。
- sbr: 用于为接口配置基于源的路由。
- firewall: 一种防火墙插件,使用iptables或者firewalld添加规则,以允许金除容器的流量。
知名第三方插件
- Calico: Calico使用Linux内核的转发和访问控制功能来提供数据平面的功能,并且优化性能。功能很强大,值得后续学习。
- Weave: Weave Net创建一个虚拟网络,将多个容器连接起来,不论是同一台宿主机,跨宿主机或者跨机房和云提供商,都好像是连接在同一个交换上。
- Contiv: 各种网络场景中使用的策略网络。
- SR-IOV: 具有SR-IOV功能的网卡可以使用,PF由主机使用,每个VF都看作是一个物理网卡给容器使用,配置单独的MAC、VLAN和IP等信息。
- Cilium: 基于ebpf和xdp的容器网络。
- Infoblox: 容器的IP地址管理插件,是IPAM类的插件。
- Multus: 将多个网络接口添加到容器。
- Romana: Kubernetes的特定组件,支持网络策略的3层CNI插件。
- CNI_Genie: 华为推出的通用插件,可以对接大部分的插件,并且同事存在,否则只能对接一种插件。
- Nuage CNI: Kubernets支持的网络策略插件。
- Silk: 为CloudFoundry提供的cni插件。
- Linen: 供ovs使用的overlay网络。
- Vhostuser: 支持ovs-dpdk和vpp的容器网络。
- Amazon ECS CNI Plugins: AWS的ENIs(elastic network interfaces)专用插件。
- Bonding CNI: 为高可用和聚合网络使用的插件。
- ovn-kubernetes: 基于ovs和ovn的容器网络插件。
- Juniper Contrail/TungstenFabric: 提供overlay SDN解决方案,多云网络,混合云网络等等,反正是一堆,比较复杂。
- Knitter: 支持多种网络的插件,中兴推出的。
- DANM: 电信级的网络管理插件,诺基亚推出的。
- VMware NSX: 为VMware NSX提供的插件,可以实现L2/L3网络和L4/L7负载均衡,pod、node和集群级别的网络隔离以及零信任安全策略。
- cni-route-override: META类型的插件,覆盖路由信息。
- Terway: 阿里云的网络插件。
插件了解
我们每种类型都选一种进行了解,首先是内置的插件,第三方插件我们会单开一篇学习,因为比较复杂。
bridge插件
使用bridge插件,所有容器都会连接bridge,使用到veth pair虚拟网卡对,一端连接到容器,另一端连接到bridge。IP地址分配给容器一段的端口,网桥上也可以配置IP作为容器的网关。网络配置指定了使用的网桥名称,如果网桥不存在,插件将县创建一个。如果配置网关模式,则通过IPAM插件的gateway字段分配IP。
#normal mode
{
"name": "mynet",
"type": "bridge",
"bridge": "mynet0",
"isDefaultGateway": true,
"forceAddress": false,
"ipMasq": true,
"hairpinMode": true,
"ipam": {
"type": "host-local",
"subnet": "10.10.0.0/16"
}
}
# L2-only
{
"cniVersion": "0.3.1",
"name": "mynet",
"type": "bridge",
"bridge": "mynet0",
"ipam": {}
}
- name (string, required): 网络名称。
- type (string, required): 类型是bridge。
- bridge (string, optional): bridge的名称。默认是cni0。
- isGateway (boolean, optional): 为网桥分配IP地址。默认是false。
- isDefaultGateway (boolean, optional): 将isGateway设置为true,并将分配的IP设置为默认路由。默认为false。
- forceAddress (boolean, optional): 只是如果先前的值已经更改,是否应该设置新的IP地址。默认为false。
- ipMasq (boolean, optional): 在主机上设置IP伪装,以接收来自该网络并发往其外部的流量。默认为false。
- mtu (integer, optional): 将MTU显示设置为指定值。默认为内核选择值。
- hairpinMode (boolean, optional): 网桥上的接口设置为hairpin模式。默认值是false。
- ipam (dictionary, required): 用于网络的IPAM配置。对于L2-only网络,需要创建空的。
- promiscMode (boolean, optional): 设置网桥的混杂模式。默认为false。
- vlan (int, optional): 分配Vlan标签。默认为none。
cmdAdd操作
loadNetConf加载上述的配置文件。
- json.Unmarshall解析数据。
- 判定是L2模式还是L3模式。
- 判定是否配置网关。
- 判定hairpin和promisc模式。
setupBridge创建bridge。
调用ensureBridge创建bridge,然后返回bridge信息。
- 创建netlink.Bridge类型的bridge。
- 然后调用netlink.LinkAdd添加bridge。
- 如果配置了promisc,则调用netlink.SetPromiscOn来配置混杂模式。
- 调用bridgeByName重新获取bridge信息。
- 调用netlink.LinkSetUp启动bridge。
- ns.GetNS获取当前的namespace。
setupVeth创建veth pair。
- 在容器中调用ip.SetupVeth创建veth pair,并且将一端转移到宿主机。
- 调用netlink.LinkByName重新获取宿主机端的veth,因为移动后有些信息变化。
- 调用netlink.LinkSetMaster将宿主机端的veth连接到bridge。
- 调用netlink.LinkSetHairpin设置hairpin模式。
- 如果有vlan,则调用netlink.BridgeVlanAdd给宿主机的veth配置vlan id。
L3模式下需要做的操作。
- ipam.ExecAdd调用IPAM插件,并且返回配置信息。
- calcGateways收集每个IP的网关信息。
- 在namespace中获取端口,并且调用ipam.ConfigureIface配置相应的IP,然后发送免费arp通告。
网关模式下,进行一系列的操作。
配置Vlan的话,调用ensureVlanInterface创建vlan接口,并且调用ensureAddr配置IP。
- ensureVlanInterface主要是拿到br对应vlan的端口,然后调用setupVeth创建veth pair,然后返回veth pair对应的信息。
- ensureAddr主要是调用netlink.AddrAdd添加IP地址,然后调用netlink.LinkSetHardwareAddr设置bridge的mac。
- 配置网关地址的话,调用enableIPForward配置网络转发。
- 配置IPMasq的话,调用ip.SetupIPMasq设置IP伪装。
- 调用bridgeByName重新获取bridge信息,用于返回值,因为前面添加veth pair和配置ip之后,可能导致bridge信息发生变化。
cmdDel操作
loadNetConf加载上述的配置文件。
- json.Unmarshall解析数据。
- 判定是L2模式还是L3模式,L3模式调用ipam.ExecDel删除申请的IP
- 在容器中执行ip.DelLinkByNameAddr,清除端口以及IP地址
- 如果是L3模式,并且配置了IPMasq,那么调用ip.TeardownIPMasq删除IP伪装。
cmdCheck操作
loadNetConf加载上述的配置文件。
- json.Unmarshall解析数据。
- ns.GetNS获取对应的namespace。
- ipam.ExecCheck调用ipam插件的check
- version.ParsePrevResult将netconf内的RawPrevResult解析到PrevResult
- current.NewResultFromResult对上面解析的result进行转换
- 解析端口bridge以及namespace中的接口等信息
validateCniBrInterface检测bridge的合法性
validateInterface主要是判断bridge是否存在
- netlink.LinkByName主要判断端口是否存在,并且返回link信息
- 根据上面获取到的link信息判断是不是bridge
- 判断配置中的mac信息是否和实际的mac地址匹配
- 判断是否配置了promisc,如果配置了则判断promisc模式是否对
- 判断result信息中的namespace和参数namespace是否一致
在namespace中调用validateCniContainerInterface检测接口的合法性
- validateInterface主要判断container一侧的veth是否存在,并且返回link信息
- 根据link信息判断是否为veth类型
- ip.GetVethPeerIfindex获取对端的link信息
- 判断mac地址是否和实际mac地址匹配
validateCniVethInterface检测bridge上的veth和namespace中的veth是否一个pair
- validateInterface主要是判断veth是否存在,并且返回link信息
- 根据link信息判断是不是veth
- ip.GetVethPeerIfindex获取对端的link信息
- 各种方式判断是不是一个pair,具体看代码
- 在namespace中调用ip.ValidateExpectedInterfaceIPs检测ip是否符合预期
- 在namespace中调用ip.ValidateExpectedRoute检测route是否符合预期
host-local插件
host-local IPAM插件在限定的地址范围内分配IP地址,他将状态本地的文件系统上,从而确保单个主机上IP地址的唯一性。
分配器可以分配多个范围,并且支持多个不相交的子网的集合。在每个范围集内,分配策略都是松散循环的。
{
"ipam": {
"type": "host-local",
"ranges": [
[
{
"subnet": "10.10.0.0/16",
"rangeStart": "10.10.1.20",
"rangeEnd": "10.10.3.50",
"gateway": "10.10.0.254"
},
{
"subnet": "172.16.5.0/24"
}
],
[
{
"subnet": "3ffe:ffff:0:01ff::/64",
"rangeStart": "3ffe:ffff:0:01ff::0010",
"rangeEnd": "3ffe:ffff:0:01ff::0020"
}
]
],
"routes": [
{ "dst": "0.0.0.0/0" },
{ "dst": "192.168.0.0/16", "gw": "10.10.5.1" },
{ "dst": "3ffe:ffff:0:01ff::1/64" }
],
"dataDir": "/run/my-orchestrator/container-ipam-state"
}
}
- type (string, required): "host-local".
- routes (string, optional): 需要添加到容器namespace的路由列表,如果没有gw配置,那么就使用gateway字段的值。
- resolvConf (string, optional): resove.conf的路径。
- dataDir (string, optional): 记录IP申请记录的路径。
- ranges, (array, required, nonempty) 一组数组。
- subnet (string, required): 要分配的IP段。
- rangeStart (string, optional): IP分配的起始地址,默认从.2开始。
- rangeEnd (string, optional): IP分配的终止地址,IPv4默认值是.254,IPv6默认值是.255。
- gateway (string, optional): IP网段的网关。默认是.1。
cmdAdd操作
- allocator.LoadIPAMConfig加载IPAM配置
- 如果resolvConf配置了,则调用parseResolvConf解析配置
- disk.New申请锁
- 查看申请的IP是否在range范围内,如果找到的话,将ip和容器以及其内的端口挂钩,并且调用allocator.Get存储起来,然后存入result.IPs
- 返回result信息
cmdDel操作
- allocator.LoadIPAMConfig加载IPAM配置
- disk.New申请锁
- 调用ipAllocator.Release释放存储的IP信息
cmdCheck操作
- allocator.LoadIPAMConfig加载IPAM配置
- disk.New申请锁
- store.FindByID查找IP是否能找到,找不到则检测失败,报错
bandwidth插件
插件使用了Linux的TC(traffic control)子系统。在ingress和egress流量上配置tbd(token bucket filter) qdisc(queuing discipline),导致出入流量都会受到影响。
犹如ingress的tc整形规则限制,此插件创建了一个ifb(Intermediate Functional Block)设备来重定向来自宿主机网卡的报文,然后将tc tbf应用于ifb设备。重定向到ifb设备的报文被整形并写上OUT到宿主机网卡。
该插件需要和其他插件一起使用时才有效。
以下是一个示例json配置列表,用于通过veth接口在host-> container之间创建ptp,流量由带宽插件决定:
{
"cniVersion": "0.3.1",
"name": "mynet",
"plugins": [
{
"type": "ptp",
"ipMasq": true,
"mtu": 512,
"ipam": {
"type": "host-local",
"subnet": "10.0.0.0/24"
},
"dns": {
"nameservers": [ "10.1.0.1" ]
}
},
{
"name": "slowdown",
"type": "bandwidth",
"ingressRate": 123,
"ingressBurst": 456,
"egressRate": 123,
"egressBurst": 456
}
]
}
- ingressRate: 入流量bps。
- ingressBurst: 最大的bits。
- egressRate: 出流量bps。
- egressBurst: 最大的bits。
必须同时设置ingrssRate和ingressBurst才能限制入口宽带,缺一不可,egress同理。
cmdAdd操作
调用parseConfig解析配置文件
- 解析配置文件,从中提取带宽限制信息,并检测rate和burst的
- 判断是否有prevResult信息,有的话也进行解析
- 将配置文件的带宽限制信息存入result中
- 获取当前容器的namespace
- 调用getHostInterface获取宿主机的网口
调用CreateIngressQdisc配置ingress相关的tc
- 调用netlink.LinkByName获取网口
调用createTBF配置策略
- 对带宽限制信息进行转换成TC能识别的数据,存入netlink.Tbf。
- 然后调用netlink.QdiscAdd下发规则
- 调用getMTU获取端口的mtu
- 调用getIfbDeviceName-->utils.MustFormatHashWithPrefix生成ifb设备的名字
- 调用CreateIfb-->netlink.LinkAdd创建ifb设备
- 调用netlink.LinkByName获取ifb设备的句柄
- 将ifb设备信息存入result
调用CreateEgressQdisc配置egress相关的tc
- 获取ifb设备的句柄
- 获取宿主机设备的句柄
- 将策略信息存入netlink.Ingress
- 调用netlink.QdiscAdd给宿主机的ingress下发规则
- 给宿主机设备镜像到ifb设备的流量生成过滤规则
- 调用netlink.FilterAdd设置过滤规则
调用createTBF给ifb设备配置策略
- 对带宽限制信息进行转换成TC能识别的数据,存入netlink.Tbf。
- 然后调用netlink.QdiscAdd下发规则
- 返回result
cmdDel操作
调用parseConfig解析配置文件
- 解析配置文件,从中提取带宽限制信息,并检测rate和burst的
- 判断是否有prevResult信息,有的话也进行解析
- 调用getIfbDeviceName-->utils.MustFormatHashWithPrefix生成ifb设备的名字
- 调用TeardownIfb-->ip.DelLinkByNameAddr删除ifb设备
cmdCheck操作
调用parseConfig解析配置文件
- 解析配置文件,从中提取带宽限制信息,并检测rate和burst的
- 判断是否有prevResult信息,有的话也进行解析
- 将配置文件的带宽限制信息存入result中
- 获取当前容器的namespace
- 调用getHostInterface获取宿主机的网口
- 解析带宽信息
- 将上述信息进行转换,然后调用SafeQdiscList获取qdisc信息
- 检测qdisc信息和配置文件中的配置是否一致
- 调用getIfbDeviceName-->utils.MustFormatHashWithPrefix生成ifb设备的名字
- 获取ifb设备的句柄,调用SafeQdiscList获取qdisc信息
- 检测qdisc信息和配置文件中的配置是否一致
4 条评论
每次看到你的文章,我都觉得时间过得好快。 https://www.yonboz.com/video/63003.html
stumv.com
想想你的文章写的特别好www.jiwenlaw.com
怎么收藏这篇文章?