网络模型与网络策略
另一篇讲述的文章地址:
NetworkPolicy网络策略以及举例说明 https://www.cnblogs.com/sanduzxcvbnm/p/14779916.html
Kubernetes网络模型及CNI插件
Kubernetes设计了一种网络模型,它要求所有容器都能够通过一个扁平的网络平面直接进行通信(在同一IP网络中),无论它们是否运行于集群中的同一节点。不过,在Kubernetes集群中,IP地址分配是以Pod对象为单位,而非容器,同一Pod内的所有容器共享同一网络名称空间。
Docker容器的网络模型
Docker容器网络的原始模型主要有三种:Bridge(桥接)、Host(主机)及Container(容器)。
- Bridge模型借助于虚拟网桥设备为容器建立网络连接
- Host模型则设定容器直接共享使用节点主机的网络名称空间
- Container模型则是指多个容器共享同一个网络名称空间,从而彼此之间能够以本地通信的方式建立连接
Docker守护进程首次启动时,它会在当前节点上创建一个名为docker0的虚拟网桥设备,并默认配置其使用172.17.0.0/16网络,该网络是Bridge模型的一种实现,也是创建Docker容器时默认使用的网络模型。
创建Docker容器时,默认有四种网络可供选择使用,从而表现出了四种不同类型的容器
然而,在生产环境中使用容器技术时,跨节点的容器间通信反倒更为常见,可根据网络类型将其实现方式简单划分为如下几种。
- 为各Docker节点创建物理网络桥接接口,设定各节点上的容器使用此桥设备从而直接暴露于物理网络中。
- 配置各节点上的容器直接共享使用其节点的网络名称空间。
- 将容器接入指定的桥设备,如docker0,并设置其借助NAT机制进行通信。
第三种方案是较为流行且默认的解决方案。不过,此种方案的IPAM(IP Address Management)是基于容器主机本地范围进行的,每个节点上的容器都将从同一个网络172.17.0.0/16中获取IP地址,这就意味着不同Docker主机上的容器可能会使用相同的地址,因此它们也就无法直接通信。
解决此问题的通行方式是为NAT。所有接入到此桥设备上的容器均会被NAT隐藏,它们发往Docker主机外部的所有流量都会在执行过源地址转换后发出,并且默认是无法直接接收节点之外的其他主机发来的请求的。若要接入Docker主机外部的流量,则需要事先通过目标地址转换甚至额外的端口转换将其暴露于外部网络中.
故此,传统的解决方案中,多节点上的Docker容器间通信依赖于NAT机制转发实现。这种解决方案在网络规模庞大时将变得极为复杂、对系统资源的消耗较大且转发效率低下。此外,docker host的端口也是一种稀缺资源,静态分配和映射极易导致冲突,而动态分配又很容易导致模型的进一步复杂化。
Kubernetes网络模型
Kubernetes的网络模型主要可用于解决四类通信需求:同一Pod内容器间的通信(Container to Container)、Pod间的通信(Pod to Pod)、Service到Pod间的通信(Service to Pod)以及集群外部与Service之间的通信(external to Service)。
(1)容器间通信
Pod对象内的各容器共享同一网络名称空间,它通常由构建Pod对象的基础架构容器所提供,所有运行于同一个Pod内的容器彼此之间可通过lo接⼜完成交互.
(2)Pod间通信
各Pod对象需要运行于同一个平面网络中,每个Pod对象拥有一个集群全局唯一的地址并可直接用于与其他Pod进行通信,此网络也称为Pod网络。
另外,运行Pod的各节点也会通过桥接设备等持有此平面网络中的一个IP地址,这就意味着Node到Pod间的通信也可在此网络上直接进行。因此,Pod间的通信或Pod到Node间的通信比较类似于同一IP网络中主机间进行的通信。
(3)Service与Pod间的通信
Service资源的专用网络也称为集群网络(Cluster Network),需要在启动kube-apiserver时经由“--service-cluster-ip-range”选项进行指定,而每个Service对象在此网络中均拥一个称为Cluster-IP的固定地址。管理员或用户对Service对象的创建或更改操作由API Server存储完成后触发各节点上的kube-proxy,并根据代理模式的不同将其定义为相应节点上的iptables规则或ipvs规则,借此完成从Service的Cluster-IP与Pod-IP之间的报文转发。
(4)集群外部到Pod对象之间的通信
将集群外部的流量引入到Pod对象的方式有受限于Pod所在的工作节点范围的节点端口(nodePort)和主机网络(hostNetwork)两种,以及工作于集群级别的NodePort或LoadBalancer类型的Service对象。不过,即便是四层代理的模式也要经由两级转发才能到达目标Pod资源:请求流量首先到达外部负载均衡器,由其调度至某个工作节点之上,而后再由工作节点的netfilter(kube-proxy)组件上的规则(iptables或ipvs)调度至某个目标Pod对象。
Pod网络的实现方式
每个Pod对象内的基础架构容器均使用一个独立的网络名称空间,并共享给同一Pod内的其他容器使用。每个名称空间均有其专用的独立网络协议栈及其相关的网络接口设备。一个网络接口仅能属于一个网络名称空间,于是,运行多个Pod必然要求使用多个网络名称空间,也就需要用到多个网络接口设备。不过,一个易于实现的方案是使用软件实现的伪网络接口及模拟线缆将其连接至物理接口。伪网络接口的实现方案常见的有虚拟网桥、多路复用及硬件交换三种。
- 虚拟网桥:创建一对虚拟以太网接口(veth),一个接入容器内部,另一个留置于根名称空间内并借助于Linux内核桥接功能或OpenVSwitch(OVS)关联至真实的物理接口。
- 多路复用:多路复用可以由一个中间网络设备组成,它暴露了多个虚拟接口,可使用数据包转发规则来控制每个数据包转到的目标接口。MAC VLAN为每个虚拟接口配置一个MAC地址并基于此地址完成二层报文收发,而IP VLAN是基于IP地址的并使用单个MAC,从而使其更适合VM。
- 硬件交换:现今市面上的大多数NIC都支持单根I/O虚拟化(SR-IOV),它是创建虚拟设备的一种实现方式。每个虚拟设备自身均表现为一个独立的PCI设备,并有着自己的VLAN及与硬件强制关联的QoS。SR-IOV提供了接近硬件级别的性能,但在公共云中通常是不可用的。
无论上述哪种方式应用于容器环境中,其实现过程都需要大量的操作步骤。不过,目前Kubernetes支持使用CNI插件来编排网络,以实现Pod及集群网络管理功能的自动化。每次Pod被初始化或删除时,kubelet都会调用默认的CNI插件创建一个虚拟设备接口附加到相关的底层网络,为其设置IP地址、路由信息并将其映射到Pod对象的网络名称空间。
配置网络接口时,kubelet将Pod对象的名称和名称空间作为CNI_ARGS变量的一部分进行传递(如“K8S_POD_NAMESPACE=default;K8S_POD_NAME=myapp-6d9f48c5d9-n77qp;”)。它可以定义每个Pod对象或Pod网络名称空间的网络配置
网络策略
Kubernetes的网络策略功能由其所使用的网络插件实现,因此,仅在使用那些支持网络策略功能的网络插件时才能够配置网络策略,如Calico、Canal及kube-router等。
Calico的calico/kube-controllers即为Calico项目中用于将用户定义的网络策略予以实现的组件,它主要依赖于节点的iptables来实现访问控制功能
策略控制器用于监控创建Pod时所生成的新API端点,并按需为其附加网络策略。当发生需要配置策略的事件时,侦听器会监视到变化,控制器随即响应以进行接口配置和策略应用。
Pod的网络流量包含“流入”(Ingress)和“流出”(Egress)两种方向,每种方向的控制策略则包含“允许”和“禁止”两种。默认情况下,Pod处于非隔离状态,它们的流量可以自由来去。一旦有策略通过选择器规则将策略应用于Pod,那么所有未经明确允许的流量都将被网络策略拒绝,不过,其他未被选择器匹配到的Pod不受影响。
Kubernetes自1.8版本起才支持Egress网络策略,此前的版本仅支持Ingress网络策略。
配置网络策略
Kubernetes系统中,报文流入和流出的核心组件是Pod资源,因此它们也是网络策略功能生效的主要目标,因此,NetworkPolicy对象也要使用标签选择器事先选择出一组Pod资源作为控制对象。一般来说,NetworkPolicy是定义在一组Pod资源上的用于管控入站流量的“Ingress规则”,或者说是管理出站流量的“Egress规则”,也可以是两者组合定义,而仅部分生效还是全部生效则需要由spec.policyTypes予以定义
默认情况下,Pod对象既可以接受来自任何来源的流量,也能够向外部发出期望的所有流量。
而附加网络策略机制后,Pod对象会因NetworkPolicy对象的选定而被隔离:一旦名称空间中有任何NetworkPolicy对象匹配了某特定的Pod对象,则该Pod将拒绝Network-Policy所不允许的一切连接请求,而那些未被任何NetworkPolicy对象匹配到的其他Pod对象仍可接受所有流量。
因此,就特定的Pod集合来说,入站和出站流量默认均处于放行状态,除非有规则能够明确匹配到它。然而,一旦在spec.policyTypes中指定了有效的规则类型,却在networkpolicy.spec字段中嵌套定义了没有任何规则的Ingress或Egress字段时,则表示拒绝相关方向上的一切流量。
定义网络策略时常用到的术语及说明具体如下:
- Pod组:由网络策略通过Pod选择器选定的一组Pod的集合,它们是规则生效的目标Pod;可由NetworkPolicy对象通过macthLabel或matchExpression选定。
- Ingress:入站流量,即由其他网络端点发往特定Pod组的流量,通常由流量发出的源站点(from)和流量的目标端口所定义。
- Egress:出站流量,即由特定的Pod组发往其他网络端点的流量,通常由流量的目标网络端点(to)和端口(ports)来进行定义。
- 端点(to,from):流量目标和流量源相关的组件,它可以是CIDR格式的IP地址块(ipBlock)、网络名称空间选择器(namespaceSelector)匹配的名称空间,或Pod选择器(podSelector)匹配的Pod组。
- 端口(ports):TCP或UDP的端口号。
无论是Ingress还是Egress流量,与选定的某Pod组通信的另一方都可使用“网络端点”予以描述,它通常是某名称空间中的一个或一组Pod资源,由namespaceSelector选定名称空间后,经由ipBlock或podSelector进行指定。
在Ingress规则中,网络端点也称为“源端点”,它们用from字段进行标识,而在Egress规则中,网络端点也称为“目标端点”,它们用to字段进行标识。不过,在未定义Ingress或Egress规则时,相关方向的流量均为“允许”,即默认为为隔离状态。而一旦在networkpolicy.spec中明确给出了Ingress或Egress字段,则它们的from或to字段的值就成了白名单列表,而空值意味着所有端点,即不限制访问。
管控入站流量
以提供服务为主要目的的Pod对象通常是请求流量的目标对象,但它们的服务未必应该为所有网络端点所访问,这就有必要对它们的访问许可施加控制。networkpolicy.spec中嵌套的Ingress字段用于定义入站流量规则,就特定的Pod集合来说,入站流量默认处于放行状态,除非在所有入站策略中,至少有一条规则能够明确匹配到它。Ingress字段的值是一个对象列表,它主要由以下两个字段组成。
Ingress字段的值是一个对象列表,它主要由以下两个字段组成。
- from<[]Object>:可访问当前策略匹配到的Pod对象的源地址对象列表,多个项目之间的逻辑关系为“逻辑或”的关系;若未设置此字段或其值为空,则匹配一切源地址(默认的访问策略为不限制);如果此字段至少有一个值,那么它将成为放行的源地址白名单,仅来源于此地址列表中的流量允许通过。
- ports<[]Object>:当前策略匹配到的Pod集合的可被访问的端口对象列表,多个项目之间的逻辑关系为“逻辑或”的关系;若未设置此字段或其值为空,则匹配Pod集合上的所有端口(默认的访问策略为不限制);如果此字段至少有一个值,那么它将成为允许被访问的Pod端口白名单列表,仅入站流量的目标端口处于此列表中方才准许通过。
NetworkPolicy资源属于名称空间级别,它的有效作用范围为其所属的名称空间。
1.设置默认的Ingress策略(拒绝/允许所有入口流量)
用户可以创建一个NetworkPolicy来为名称空间设置一个“默认”的隔离策略,该策略选择所有的Pod对象,而后允许或拒绝任何到达这些Pod的入站流量。
下面的策略示例,其通过policyTypes字段指明要生效Ingress类型的规则,但未定义任何Ingress字段,因此不能匹配到任一源端点,从而拒绝所有入站流量:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress # 没有设置命令空间则默认命令空间为default
spec:
podSelector: {} # 空的 podSelector 表示选择命名空间下的所有pod
policyTypes: ["Ingress"] # 入口网络策略
# 未设置进入条件,也就是不允许任何进入
若要将默认策略设置为允许所有的入站流量,则只需要显式定义Ingress字段,并将其值设置为空以匹配所有源端点即可,没有为入站流量定义任何规则时,本身的默认规则即为允许访问,因此允许所有相关的入站流量时,本身无须定义默认规则.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-ingress
spec:
podSelector: {}
policyTypes: ["Ingress"]
ingress:
- {}
实践中,通常将默认策略设置为拒绝所有的入站流量,而后显式放行允许的源端点的入站流量。
2.放行特定的入站流量
在Ingress规则中嵌套from和ports字段即可匹配特定的入站流量,仅定义from字段时将隐含本地Pod资源组的所有端口,而仅定义ports字段时则表示隐含所有的源端点。from和ports同时定义时表示隐含“逻辑与”关系,它将匹配那些同时满足from和ports的定义的入站流量,即那些来自from指定的源端点,访问由当前NetworkPolicy的podSelector匹配的当前名称空间的Pod资源组上所指定的ports的请求。
from字段的值是一个对象列表,它可嵌套使用ipBlock、namespaceSelector和podSelector字段来定义流量来源,此三个字段匹配Pod资源的方式各有不同,同时使用两个或以上的字段,彼此之间隐含“逻辑或”关系。
- ipBlock