一、CNI网络插件基础

CNI(Container Nerwork Interface,容器网络接口),它是容器引擎与遵循该规范网络插件的中间层,专用于为容器配置网络子系统。

简单来说,目前的CNI规范主要由NetPlugin(网络插件)和IPAM两类插件API组成。

1.1 网络插件

网络插件也称Main插件,负责创建/删除网络以及向网络添加/删除容器。它专注于连通容器与容器之间以及容器与宿主机之间的通信,同容器相关网络设备通常都由该类插件所创建,例如 bridge、ipvlan、macvlan、loopback、ptp、veth以及vlan等虚拟设备。

1.2 IPAM

IPAM的全称“IP Address Management”,该类插件负责创建/删除地址池以及分配/回收容器的IP地址。目前,该类型插件的实现主要有host-local和dhcp两个,前一个基于预置的地址范围进行地址分配,而后一个通过dhcp 协议获取地址。

对地址池的理解:以Flannel网络插件为例,其Pod网络为10.244.0.0/16,它会为集群上每个节点分配子网(默认使用24位掩码),当有Pod调度到节点时,Pod获得的IP一定在Pod所在节点的子网的范围内。比如Node1分配子网为10.244.0.0/24,Node2分配子网为10.244.1.0/24,以此类推。

二、Kubernetes的网络解决方案

NetPlugin目前常用的实现方案有叠加网络(Overlay Network)和承载网络(Underlay Network)两类。

叠加网络借助于VXLAN、UDP、IPIP或GRE等隧道协议,通过隧道协议报文封装Pod间的通信报文(IP报文或以太网帧)来构建虚拟网络。

承载网络通常使用direct routing(直接路由)技术在Pod的各子网间路由Pod的IP报文,或使用bridge、macvlan或ipvlan等技术直接将容器暴露至外部网络中。简单地理解就是,把物理机的网卡虚拟为多个网络设备,一个留在宿主机使用,其它的留给其它Pod使用。就好像是Pod直接接入了宿主机所在的网络当中。

其实,叠加网络的底层网络也就是承载网络。承载网络的解决方案也就是一类非借助于隧道协议而构建的容器通信网络。相较于承载网络,叠加网络由于存在额外的隧道报文封装,会存在一定程度的性能开销。而在承载网络中,报文要么直接接入物理节点所在的网络当中,要么把物理节点所在的网络当做路由网络(路由模式)来使用,因此不存在额外的网络开销,性能更好,但承载网络会对底层网络有更多的限制条件(比如,路由模式需要节点支持BGP协议)。

三、Flannel

3.1 简介

Flannel由CoreOS研发,使用”虚拟网桥和veth设备”的方式为Pod创建虚拟网络接口,通过可配置的后端(backend)定义Pod间的通信网络。它支持基于VXLAN和UDP的Overlay网络,以及基于三层路由的Underlay网络。
       对于每一个容器而言,在加入网络时,在每个节点创建一个虚拟交换机,并为每一个Pod/容器创建一对虚拟网卡(veth pair),一端插入容器的网络名称空间,另一端插入节点(宿主机)的网桥(cni0接口)。而Pod如何接入物理网络,则取决于后端的定义。
       Flannel用于解决Kubernetes集群中各节点Pod之间的通信问题,它的主要思路是:预先留出一个网段,每个主机使用其中一部分,然后每个容器被分配不同的IP;让所有的容器认为它们都在同一个直连的网络,底层通过UDP/VxLAN/Host-GW等数据转发方式(后端)进行报文的封装和转发。
       在IP地址分配方面,它将预留的一个专用网络(默认为10.244.0.0/16)划分为多个子网作为每个节点的IP地址范围(podCIDR),使得每个Pod拥有一个独立的IP地址。具体由IPAM插件host-local进行IP地址分配,并将子网分配信息保存于etcd。
       Flannel在每个节点上运行一个名为flanneld的二进制代理程序,它负责从预留的网络中按照指定或者默认的掩码长度为当前节点申请分配一个子网,并将网络配置、已分配的子网和辅助数据(比如主机的公网IP等)存储在Kubernetes API或独立的etcd中。

3.2 Flannel支持的后端

Flannel使用称为后端(backend)的容器网络机制转发跨节点的Pod报文。Flannel目前支持的主流backend如下:

  • vxlan

使用Linux内核中的vxlan模块封装隧道报文,以叠加(overlay)网络模型支持跨节点的Pod间的互联互通。同时支持直接路由模式,在此模式下,位于同一个二层网络内的节点之上的Pod间通信可通过路由模式直接发送,而跨二层网络的节点(两个节点不在同一网段,要经过路由器)之上的Pod间通信仍要使用VXLAN隧道协议转发(即兼具vxlan和host-gw两种后端的功能)。使用直接路由模式可以节省一部分隧道网络的开销。下图为VXLAN后端使用直接路由模式的示意图。

Kubernetes主流网络插件介绍_Flannel

vxlan模型中,flanneld监听udp协议的8472端口用于接收和发送封装的数据包。

  • host-gw

类似于VXLAN后端的直接路由模式,但不支持跨二层网络的节点,因此这种模式要求各节点处于同一个二层网络中,不太适用于规模较大的环境,但转发性能较好。在host-gw这个后端模式下,每个物理节点(Pod所在的主机)可视为路由器,节点内建的路由表决定报文该如何发送。下图为host-gw后端的示意图。

Kubernetes主流网络插件介绍_Flannel_02

  • udp

用常规UDP报文封装完成隧道转发,性能比vxlan和host-gw相比较差,仅在不支持vxlan和host-gw时使用;UDP后端模式中,flanneld监听UDP 8285端口发送报文。

3.3 VXLAN后端

Flannel的配置文件默认使用VXLAN后端,相关配置定义在kube-flannel名称空间下configmap/kube-flannel-cfg资源对象中。相关配置如下:

net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "Backend": {
        "Type": "vxlan"
      }
}

相关的键说明如下:

  • Network:Flannel全局使用的子网,即pod cidr的值
  • SubnetLen:子网分割的长度,在全局子网掩码小于24时(eg:16),默认为24
  • SubnetMin:分配给节点使用的起始子网,默认为切割完成后的第一个子网
  • SubnetMax:分给给节点使用的最大子网,默认为切割完成后的最后一个子网
  • Backend:Flannel使用的后端,以及后端的配置

这里可以通过抓包进行分析。这里让两个Pod进行通信,其中src-ip为10.244.1.35(nginx Pod1),dst-ip为10.244.2.31(nginx Pod2),二者对应的Node节点的IP分别为192.168.131.14(K8S-Node1)和192.168.131.15(K8S-Node2)。

用kubectl exec命令进入nginx Pod1,执行while true;do curl 10.244.2.31;sleep 1;done命令,之后在Node1节点的物理接口抓包。

tcpdump -i ens33 -nn -X udp port 8472

Kubernetes主流网络插件介绍_Kubernetes_03

可以看到,报文在外部会封装两层首部。外层为192.168.131.15.47961 > 192.168.131.14.8472(物理IP,包含隧道封装),内层为10.244.2.31.80 > 10.244.1.35.44014(Pod IP)。事实上,任何报文只要进入了flannel.1接口即相当于进入了隧道接口,之后报文会发到对端的隧道接口。如果是本机以内的Pod之间的通信(eg:两个Pod均属于10.244.3.0子网段),则无需做任何复杂的转发,直接通过cni0接口(本地的虚拟交换机接口)发送。反之,如果是跨节点的Pod间通信,则需要用到flannel.1这个隧道接口来对外通信。

Kubernetes主流网络插件介绍_Flannel_04

此外,flannel还会在运行的节点上生成一个环境变量文件,默认是/run/flannel/subnet.env,其包含本节点使用的子网、mtu等信息。

cat /run/flannel/subnet.env
FLANNEL_NETWORK=10.244.0.0/16  #全局网段
FLANNEL_SUBNET=10.244.3.1/24  #本节点的子网
FLANNEL_MTU=1450  #容器接口mtu值
FLANNEL_IPMASQ=true  #地址映射

3.4 直接路由

如果是VXLAN添加直接路由模式,则修改kube-flannel名称空间下configmap/kube-flannel-cfg资源对象,为VXLAN后端添加Directrouting: true键值对就可以开启DirectRouting模式(见下图)。

Kubernetes主流网络插件介绍_Calico_05

以K8S-Node2为例,启用Directrouting模式后,节点上的路由变成如下图

Kubernetes主流网络插件介绍_Flannel_06

可以看到,Pod子网的下一跳地址由对端flannel.1接口地址变成了宿主机物理接口的地址,本地用于发出报文的接口由flannel.1变成了本地的物理接口(eg:ens33)。

显然,这种模式无法满足跨二层网络节点上Pod的通信需求,因为到达Pod子网的下一跳地址无法指向另一个二层网络中的节点地址。所以节点上依然保留与VXLAN隧道相关的flannel.1接口用于跨二层网络节点上Pod的通信需求。

3.5 host-gw后端

如果想修改后端类型,则修改kube-flannel名称空间下configmap/kube-flannel-cfg资源对象,将Backend的Type字段修改为指定类型(eg:host-gw)。

当修改为host-gw后端后,使用tcpdump -i ens33 -nn -X tcp port 80命令再次抓包,就会发现报文内已经没有了隧道封装。说明此时直接路由功能已经在发挥作用了。

Kubernetes主流网络插件介绍_Flannel_07

此时查看路由信息,发现Pod的子网对应的网关正好是Node节点物理网卡的IP地址,同时也不再需要flannel.1接口(因为没有了隧道封装的开销)。比如,位于10.244.1.0网段的某个Pod和其它网段的某个Pod通信通过192.168.131.14(即本机物理接口ens33的地址)直接把对端的物理网卡当作下一跳路由,完全通过路由的方式完成报文转发。

Kubernetes主流网络插件介绍_Flannel_08

当节点处于不同的二层网络时,host-gw后端就不能实现Pod间的通信(由于不具备隧道转发的能力)。相对来说,VXLAN的DirectRouting模式兼具VXLAN后端和host-gw后端的优势,既能保证传输性能,又可以跨二层网络转发Pod报文。

此外,Flannel并不支持为Pod网络添加网络策略以控制Pod间通信的能力,它只能借助额外的支持网络策略的插件实现此功能,Calico网络插件就是为此目的而设立的。

四、Calico

4.1 简介

Calico是一个三层的虚拟网络解决方案,它将每个节点都当做虚拟路由器(vRouter),把每个节点上的Pod都当做是”节点路由器”后的一个终端设备并为其分配一个IP地址。各节点路由器通过BGP协议(Border Gateway Protocol,边界路由协议)学习生成路由规则从而实现不同节点上的Pod间的互联互通。

Kubernetes主流网络插件介绍_Flannel_09

与Flannel相比,Calico的一个显著优势是对网络策略的支持,它允许用户定义访问控制规则以管控进出Pod的数据报文,从而为Pod间的通信施加安全策略。

实际上,Calico提供的网络解决方案与Flannel的host-gw模式几乎一样,也是基于路由表实现容器的数据包转发。但不同于Flannel使用flanneld进程来维护路由信息的做法,Calico使用BGP协议来维护集群的路由信息。

Calico在每一个计算节点通过Linux内核实现了一个高效的vRouter(虚拟路由器,简单的理解为将节点内核视为路由器使用)进行报文转发,而每个vRouter通过BGP协议负责把自身所属的节点上运行的Pod资源的IP地址信息基于节点的agent程序(Felix)直接由vRouter生成路由规则向整个Calico网络内传播。

BGP是互联网上一个核心的去中心化自治路由协议,它通过维护IP路由表或”前缀”表来实现自治系统(AS,Autonomous System)之间的可达性,通常作为大规模数据中心维护不同的自治系统之间路由信息的矢量路由协议。Linux内核原生支持BGP,因而可以将Linux主机配置成为边界网关。

Calico把Kubernetes集群环境中的每个节点上Pod组成的网络视为一个自治系统(AS),而每个节点就相当于自治系统的边界网关。各节点之间通过BGP协议交换路由信息并生成路由规则。但并非所有的网络环境都能支持BGP,而且BGP路由模型要求所有节点位于同一个二层网络中,所以Calico还支持基于IPIP或VXLAN的Overlay网络模型(默认使用IPIP隧道网络。IPIP和VXLAN二者为互斥关系,只能用其一)

与Flannel在VXLAN后端使用直接路由(DirectRouting)的网络模型类似,Calico也支持混合使用路由和叠加(Overlay)网络模型,BGP路由模型用于二层网络通信,IPIP或VXLAN用于跨子网(Cross Subnet)的节点间报文转发。

Kubernetes主流网络插件介绍_Flannel_10


4.2 Calico架构

Kubernetes主流网络插件介绍_Flannel_11

Calico主要由Felix、Orchestrator Plugin(编排系统插件)、etcd、BIRD和BGP Router Reflector(BGP路由反射器)等组件组成。

  • Felix:Calico的核心组件,运行在每个节点上。主要负责完成接口管理、路由规则、ACL规划和状态报告这几个核心任务,从而为各端点(VM或Container)生成连接机制。
  • 接口管理:负责创建网络接口,将接口信息配置到内核,以确保内核能够处理各端点的流量,尤其是要确保能用节点自身的MAC来响应当前工作节点上每个工作负载MAC地址的ARP请求,以及为Felix管理的接口打开转发功能。另外,接口管理还要监控各接口的变动以确保规则能够得到正确应用。
  • 路由规划:负责为当前工作节点上的各端点在内核FIB(Forwarding Information Base,转发信息库)中生成路由信息,以保证到达当前节点的报文可以正确转发给端点。
  • ACL规划:负责在Linux内核中生成ACL规则,以实现仅放行端点间的合规流量,并确保流量不能绕过Calico的安全措施。
  • 状态报告:Felix负责提供关于网络健康状况的相关数据,尤其是报告由Felix管理的节点上出现的错误和问题。这些报告和数据会存储在etcd中供其它组件或管理员使用。
  • Orchestrator Plugin:编排系统插件的主要功能是将Calico整合进所在的编排系统(eg:Kubernetes、Openstack等云原生平台)中。可以通过各自的API来配置Calico网络实现无缝集成。Eg:Kubernetes的cni网络插件。
  • etcd:用于持久存储Calico相关配置数据(eg:使用的子网、网络策略等)的存储管理系统。
  • BIRD:Calico在每个运行着Felix的节点上同时运行一个名为BIRD的守护进程,它是BGP协议的客户端,负责将Felix生成的路由信息载入内核并通告到整个网络中,同时从其它节点学习到新的路由信息。学习到的路由信息由Felix生成路由表。
  • BGP路由反射器:可选组件。Calico的BGP路由模型默认采用节点网格模式(node-to-node mesh),随着节点数量的增加,节点之间的连接数量会快速增长,给集群网络带来较大的压力。因此,一般建议大规模集群使用BGP路由反射器模式进行路由学习,BGP的点到点通信也就转换为与中心节点的单路通信模型。另外,基于冗余考虑,生产环境应该配置多个BGP路由反射器。对于Calico来说,BGP客户端程序除了作为客户端使用,也可以配置为路由反射器。

4.3 Calico程序组件介绍

4.3.1 calico-node

运行于集群中的每个节点,负责路由编程(Felix)和路由分发(BIRD)。

  • Felix:负责生成路由规则和iptables规则,前者用于完成Pod报文路由,后者用于支撑NetworkPolicy。
  • BIRD:读取并分发由同一节点上的Felix生成的路由规则,支持多种分发拓扑。

4.3.2 calico-kube-controller

负责监视Kubernetes对象中(包括NetworkPolicy、 Pod、Namespace、ServiceAccount和Node等)会影响到路由的相关变更,将变更带来的影响生成Calico配置,并保存于Calico Datastore中。

4.3.3 Typha

各calico-node实例同Calico Datastore通信的中间层,由它负责将Calico Datastore中生成的更改信息分发给各 calico-node,以减轻50个节点以上规模集群中的Calico Datastore的负载。具有缓存功能,且能够通过删除重复事件,降低系统负载。

4.3.4 Calico DataStore

通用术语,是指存储的Calico配置、路由、策略及其它信息,它们通常表现为Calico CRD资源对象。支持的CRD包括BGPConfiguration、BGPFilter、BGPPeer、BlockAffinity、CalicoNodeStatus、ClusterInformation、FelixConfiguration、GlobalNetworkPolicy、GlobalNetworkSet、HostEndpoint、IPAMBlock、IPAMConfig、 IPAMHandle、IPPool、IPReservation、NetworkPolicy、NetworkSet和KubeControllersConfiguration等。

常用的CRD功能说明如下:

  • BGPConfiguration:全局BGP配置,用于设定AS(自治系统)编号、node mesh,以及用于通告ClusterIP的设置
  • FelixConfiguration:Felix相关的低级别配置,包括iptables、MTU和路由协议等
  • GlobalNetworkPolicy:全局网络策略,生效于整个集群级别;
  • GlobalNetworkSet:全局网络集,是指可由GlobalNetworkPolicy引用的外部网络IP列表或CIDR列表
  • IPPool:IP地址池及相关选项,包括要使用的路由协议(IPIP、VXLAN或Native);一个集群支持使用多个Pool;

4.4 Calico数据存储模式

数据可以存储于Kubernetes数据存储(内置的etcd,建议使用),也可存储于独立的etcd之上。

4.4.1 Kuernetes数据存储

数据通过CRD存储于kube-apiserver,对calico资源的访问可由Kubernetes RBAC控制。但成百上千个Felix实例同时与kube-apiserver交互,会带来负面影响,因而必须要使用typha中间层。

4.4.2 etcd

使用独立的etcd存储Calico的相关信息可减轻kube-apiserver的压力,但也会引入不必要的复杂性和安全风险。

4.5 Calico下的Pod网络接口

Calico网络插件如何为Pod配置网络接口?

Calico网络插件为每个Pod创建一组veth pair,一端注入Pod网络名称空间,另一端驻留在宿主机上。Pod内的一端,通常名称格式为“eth0@ifN”,其中的n是驻留在宿主机上的另一端的ip link编号。驻留宿主机的一端,名称格式为“caliXXXXXXXXXXX@ifN”,其中的11位X是经由函数计算生成,而N则是注入到 Pod网络名称空间中的对端的ip link编号(具体见以下两幅图)。

Kubernetes主流网络插件介绍_Calico_12

Kubernetes主流网络插件介绍_Flannel_13

同一节点上的Pod间通信依赖于为每个Pod单独配置的路由规则(见下图,借助每个Pod在宿主机上的对端网卡,以cali开头),不同节点上的Pod间通信则由Calico的路由模式决定。

Kubernetes主流网络插件介绍_Kubernetes_14

此外,宿主机为每个cali接口都开启了ARP Proxy功能,从而让宿主机扮演网关设备,并以自己的MAC地址代为应答对端Pod中发来的所有ARP请求。ARP Proxy的关键配置:/proc/sys/net/ipv4/conf/DEV/proxy_arp

4.6 IPIP隧道网络

从字面来理解,就是把一个IP数据包又套在一个IP包里,即把 IP 层封装到 IP 层的一个 tunnel,看起来似乎是浪费,实则不然。它的作用其实基本上就相当于一个基于IP层的网桥!一般来说,普通的网桥是基于mac层的,根本不需 IP,而这个 ipip 则是通过两端的路由做一个 tunnel,把两个本来不通的网络通过点对点连接起来。其工作原理见下图。

Kubernetes主流网络插件介绍_Kubernetes_15

wget https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml

相关配置说明如下:

- name: CALICO_IPV4POOL_IPIP
 value: "Always"

设置在IPv4类型的地址池上是否启用IPIP这个隧道网络。支持如下可用值

  • Always:总是使用。不论节点是否处在同一个二层网络中均使用IPIP隧道。
  • Cross-Subnet:跨子网流量。用于开启混合网络模型。当节点不在同一个二层网络时其流量才使用隧道封装,否则启用BGP路由网络。
  • Never:从不使用IPIP隧道网络。
- name: CALICO_IPV4POOL_VXLAN
 value: "Never"

是否启用VXLAN这个隧道网络。支持的可用值及其意义与IPIP类似。不过要注意的是,IPIP与VXLAN只能二者用其一。这里用了IPIP就不用VXLAN了。如果使用VXLAN,Calico会在节点多增加一个网络接口vxlan.calico作为隧道出入口用于封装VXLAN报文,它会监听UDP协议的4789端口,vni为4096。

这里先用IPIP模式(CALICO_IPV4POOL_IPIP:Always)进行抓包分析。先创建若干Pod做测试用。

kubectl create deploy demoapp --image ikubernetes/demoapp:v1.0 --replicas 3

工作在IPIP模式的Calico会在每个节点上创建一个tunl0接口作为隧道出入口来封装IPIP隧道报文。Calico会为每一个Pod资源创建一对veth设备,其中一端作为Pod的网络接口,另一端(名称以cali为前缀,后跟随机字串)留置在节点的网络名称空间,不使用虚拟网桥。

Kubernetes主流网络插件介绍_Flannel_16

IPIP隧道网络仍需借助BGP维护节点间的可达性。部署完成后,Calico会通过BGP协议在每个节点上生成到达Kubernetes集群中其它各节点的Pod子网路由信息。以K8S-Node1为例,使用IPIP模式后,节点的路由显示如下图。以去往Node2节点的Pod(192.168.12.0网段)为例说明,要到达192.168.12.0网段需要通过本机的tunl0接口去送给192.168.131.15(这是K8S-Node2节点IP地址)。这些是由各节点上的BIRD以点对点的方式(node-to-node mesh)向网络中的其它节点进行通告并学习其它节点的通告而得。

Kubernetes主流网络插件介绍_Calico_17

对于每个Pod,Calico都会在节点上为其生成一个专用路由条目,用于确保以Pod IP为目标的报文可以通过节点上的calixxx接口送达,这是因为Calico没有像Flannel一样使用虚拟网桥进行报文转发导致的。

Kubernetes主流网络插件介绍_Calico_18

这里进入Node1节点所在的Pod,去与Node2节点所在的Pod进行通信,其中src-ip为192.168.113.2(demoapp Pod1),dst-ip为192.168.12.2(demoapp Pod2),二者对应的Node节点的IP分别为192.168.131.14和192.168.131.15。另外在Node1节点执行tcpdump -i ens33 -nn host 192.168.131.15命令进行抓包。

Kubernetes主流网络插件介绍_Flannel_19

可以看到,这是IPIP报文,里面封装了两层IP,外层为192.168.131.14 > 192.168.131.15(节点IP),内层为192.168.113.2.36822 > 192.168.12.2.80(Pod IP)。IP包进入IP隧道设备之后,就会被Linux内核的IPIP驱动接管。IPIP驱动会将这个IP包直接封装在一个宿主机网络的IP包中。可以参考下图。

Kubernetes主流网络插件介绍_Calico_20

4.7 BGP

可以将IPIP的值设为Cross-Subnet(CALICO_IPV4POOL_IPIP:Cross-Subnet),当节点处在同一个子网时通过BGP进行路由学习生成路由信息。
       等到calico清单文件apply成功后,再检查路由信息,发现节点将同一网络内其它节点相关的路由条目经由IPIP模式的tunl0接口传输变为节点的物理接口(eg:ens33)传输,这就与Flannel的host-gw后端的工作逻辑非常相似了。如下路由信息截取自K8S-Node1主机上。

与之前的测试方式类似,只不过这次抓包要针对Pod的IP来抓。下面命令在Node1节点执行。其中,192.168.12.5为demoapp Pod2的IP,在Node2节点上。

tcpdump -i ens33 -nn host 192.168.12.5

Kubernetes主流网络插件介绍_Kubernetes_21

此时,就直接显示源Pod(192.168.113.4)到目标Pod(192.168.12.5)的通信,没有了外层节点IP的封装。

BGP网络相比较IPIP网络,最大的不同之处就是没有了隧道设备 tunl0。前面介绍过IPIP网络Pod之间的流量发送tunl0,然后tunl0发送对端设备。BGP网络中,Pod之间的流量直接从网卡发送目的地,减少了tunl0这个环节。

五、NetworkPolicy

5.1 简介

NetworkPolicy它是标准的API资源类型,由网络插件负责转换为节点上的iptables filter规则,以定义Pod间的通信权限。主要针对TCP、UDP和SCTP协议,实现在IP地址或Port层面进行流量控制。

5.2 功能

针对一组Pod,定义其同对端实体通信时,在入向(Ingress)或/和 出向(Egress)流量上的控制规则。描述对端实体的方法有如下几种:

  • 一组Pod对象,通常基于标签选择器定义筛选条件
  • 单个或一组名称空间
  • IP地址块(但Pod同其所在的节点间的通信不受限制)

5.3 NetworkPolicy的生效机制

默认情况下,一组Pod上的出向和入向流量均被允许。

同一方向上,适用于一组Pod的多个规则的生效遵循加法机制。

  • 一组Pod上,多个Ingress策略相加所生成的集合(并集)是为最终生效的结果。
  • 一组Pod上,多个Egress策略相同所生成的集合是为最终生效的结果策略。

若同时定义了Ingress和Egress规则,针对一个对端,双向策略都为“许可”,通信才能真正实现(控制入向流量较为常见)。

5.4 资源规范

apiVersion: networking.k8s.io/v1 
kind: NetworkPolicy 
metadata: 
  name: … 
  namespace: … 
spec: 
  egress: # 出向流量控制规则,默认为开放 
    to: # 出向流量控制规则,默认为开放;流量目标的表示方式与ingress.from相同;表示对端实体
    ports: # 出向流量的目标端口;流量目标端口的表示方式与ingress.ports相同 
  ingress: # 入向流量控制规则,默认为开放 
    from: # 入向流量源,多个列表项之间为“或”逻辑;未给定任何值或未定义该字段,将匹配所有 
          # 流量源;定义了该字段且至少存在一个item,则表示仅允许指定的流量源;  
    ipBlock: # 源IP地址段,通常是网络地址;不支持同namespaceSelector或podSelector同时使用; 
      cidr: # CIDR格式的网络地址 
      except: # 要排除的地址列表,可以是CIDR格式的子网地址; 
    namespaceSelector: # namespace标签选择器,用于表示源自匹配到的namespace内的所有流量 
    podSelector: # pod标签选择器,用于表示源自匹配到的Pod上的所有流量;可以同nameSelector # 同时使用,用于匹配选定namespace中选定的pod; 
    ports: # 入向流量的目标端口,即流量源要访问的目标端口,生效机制同from; 
      port: # 端口,同endPort字段同时使用时,表示端口范围的起始端口号 
      endPort: # 端口号,同port字段同时使用时,表示端口范围的结束端口号 
      protocol: # 协议,仅支持TCP、UDP和SCTP,默认为TCP; 
    podSelector: # Pod标签选择器,用于选定流量规则适用的对象,必选字段 
    policyTypes: # 规则类型,支持Ingress、Egress,默认在两个方向上同时生效

5.5 资源示例

例1

--- 
apiVersion: networking.k8s.io/v1 
kind: NetworkPolicy 
metadata: 
  name: allow-all-ingress 
spec: 
  podSelector: {} 
  ingress: 
  - {} 
  policyTypes: 
  - Ingress

此处,配置了ingress字段,且添加了一个列表元素,表示仅允许匹配到的流量源;而一个为空的元素则表示匹配所有流量源。

使用“kubectl describe”命令查看该对象,将得到如下信息,它表示将开放当前名称空间下的Pod给任何流量源。

Kubernetes主流网络插件介绍_Calico_22

例2

--- 
apiVersion: networking.k8s.io/v1 
kind: NetworkPolicy 
metadata: 
  name: deny-all-ingress 
spec: 
  podSelector: {} 
  ingress: 
  policyTypes: 
  - Ingress

不配置ingress字段,或使用如下两种方式之一,意义相同;它们表示不匹配任何流量源(对端)。

• ingress:

• ingress: []

用“kubectl describe”命令查看该对象,将得到如下信息。它表示,将禁止任何流量访问当前namespace中的任何Pod。

Kubernetes主流网络插件介绍_Calico_23

5.6 实例说明

vim example-base-env.yaml
---
apiVersion: v1
kind: Namespace
metadata:
  name: demo
---
apiVersion: v1
kind: Namespace
metadata:
  name: dev
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demoapp
  namespace: dev
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demoapp
  template:
    metadata:
      labels:
        app: demoapp
    spec:
      containers:
      - image: ikubernetes/demoapp:v1.0
        name: demoapp
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demoapp
  namespace: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demoapp
  template:
    metadata:
      labels:
        app: demoapp
    spec:
      containers:
      - image: ikubernetes/demoapp:v1.0
        name: demoapp
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sleep
  namespace: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sleep
  template:
    metadata:
      labels:
        app: sleep
    spec:
      containers:
      - image: ikubernetes/admin-box:v1.2
        name: sleep
        command: ["/bin/sh", "-c", "sleep 99999"]
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demoapp
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: demoapp
  template:
    metadata:
      labels:
        app: demoapp
    spec:
      containers:
      - image: ikubernetes/demoapp:v1.0
        name: demoappv10
      - image: ikubernetes/demoapp:v1.1
        name: demoappv11
        command: ["/bin/sh", "-c", "python3 /usr/local/bin/demo.py --port=8080"]

kubectl apply -f example-base-env.yaml

apply此文件后,会新创建demo、dev两个名称空间,在两个名称空间以及default名称空间下也有对应的Pod、Deployment。

Kubernetes主流网络插件介绍_Kubernetes_24

Kubernetes主流网络插件介绍_Calico_25

Kubernetes主流网络插件介绍_Flannel_26

此时访问default名称空间下的Pod,80和8080端口均可访问。80端口使用的是demoapp v1.0,8080端口使用的是demoapp v1.1。

Kubernetes主流网络插件介绍_Kubernetes_27

接下来的配置生效后,default名称空间下所有Pod都不允许任何客户端访问。

vim deny-all-ingress-traffic.yaml
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {}
  ingress: []
  policyTypes:
  - Ingress

kubectl apply -f deny-all-ingress-traffic.yaml

Kubernetes主流网络插件介绍_Flannel_28

此时,再去访问default名称空间下的Pod就无法访问。哪怕进入相关Pod的shell内(入向也为default名称空间下的Pod)也不能访问。

Kubernetes主流网络插件介绍_Flannel_29

Kubernetes主流网络插件介绍_Kubernetes_30

以下配置定义了针对default名称空间下的Pod的入向的两组匹配规则(from块)。两个from块定义的匹配规则,彼此间遵循“或”逻辑。在第一个from块中,说明允许default、kube-system、test名称空间下的Pod访问default名称空间下的所有Pod的所有端口。在第二个from块中,允许demo名称空间下的键为app、值为demoapp或nginx的Pod访问default名称空间下的所有Pod的80端口。

vim allow-selected-ingress-traffic.yaml
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-selected-ingresses
  namespace: default
spec:
  podSelector: {}
  ingress:
  - from:
    - namespaceSelector:
        matchExpressions:
        - key: kubernetes.io/metadata.name
          operator: In
          values: ["default", "kube-system", "test"]
    - ipBlock:
        cidr: 192.168.10.0/24
    ports: []
  - from:
    - namespaceSelector:
        matchLabels:
          kubernetes.io/metadata.name: demo
      podSelector:
        matchExpressions:
        - key: app
          operator: In
          values: ["demoapp", "nginx"]
    ports:
    - port: 80
     protocol: TCP
  policyTypes:
  - Ingress

kubectl apply -f allow-selected-ingress-traffic.yaml

test名称空间下的Pod可访问default名称空间的Pod,80和8080端口均正常访问。

Kubernetes主流网络插件介绍_Calico_31

default名称空间下的Pod可访问default名称空间的Pod,80和8080端口均正常访问。

Kubernetes主流网络插件介绍_Kubernetes_32

demo名称空间下拥有标签app=demoapp的Pod可访问default名称空间的Pod的80端口,却不能访问8080端口。此外,在demo名称空间下还有个标签为app=sleep的Pod,由于不满足上述策略(app=demoapp或nginx),故无法访问default名称空间下所有的Pod的所有端口。

Kubernetes主流网络插件介绍_Calico_33

Kubernetes主流网络插件介绍_Flannel_34

dev名称空间下的Pod无法访问default名称空间的Pod(80和8080端口)。

Kubernetes主流网络插件介绍_Kubernetes_35