1.Kubernetes网络模型及CNI插件
Docker的传统网络模型在应用至日趋复杂的实际业务场景时必将导致复杂性的几何级数上升,由此,Kubernetes设计了一种网络模型,它要求所有容器都能够通过一个扁平的网络平面直接进行通信(在同一IP网络中),无论它们是否运行于集群中的同一节点。不过,在Kubernetes集群中,IP地址分配是以Pod对象为单位,而非容器,同-Pod内的所有容器共享同一网络名称空间。
1.1 Docker容器的网络模型
Docker容器网络的原始模型主要有三种:Bridge(桥接)、Host(主机)及Container(容器)。
- Bridge模型借助于虚拟网桥设备为容器建立网络连接;
- Host模型则设定容器直接共享使用节点主机的网络名称空间;
- Container模型则是指多个容器共享同一个网络名称空间,从而彼此之间能够以本地通信的方式建立连接;
Docker守护进程首次启动时,它会在当前节点上创建一个名为dockerO的桥设备,并默认配置其使用172.17.0.0/16网络,该网络是Bridge模型的一种实现,也是创建Docker容器时默认使用的网络模型。
在生产环境中使用容器技术时,跨节点的容器间通信反倒更为常见,可根据网络类型将其实现方式简单划分为如下几种:
- 为各Docker节点创建物理网络桥接接口,设定各节点上的容器使用此桥设备从而直接暴露于物理网络中;
- 配置各节点上的容器直接共享使用其节点的网络名称空间;
- 将容器接入指定的桥设备,如docker0,并设置其借助NAT机制进行通信;
多节点上的Docker容器间通信依赖于NAT机制转发实现。这种解决方案在网络规模庞大时将变得极为复杂、对系统资源的消耗较大且转发效率低下。此外,docker host的端口也是一种稀缺资源,静态分配和映射极易导致冲突,而动态分配又很容易导致模型的进一步复杂化。
1.2 Kubernetes网络模型
Kubernetes的网络模型主要可用于解决四类通信需求:同-Pod内容器间的通信(Contamer to Container)、Pod间的通信(Pod to Pod)、Service到Pod间的通信(Service to Pod)以及集群外部与Service之间的通信(externalto Service)。
- 容器间通信:Pod对象内的各容器共享同一网络名称空间,它通常由构建Pod对象的基础架构容器所提供,由pause镜像启动的容器。所有运行于同一主机上的多个进程类似,彼此之间可通过lo接口完成交互;
- Pod间通信:各Pod对象需要运行于同一平面网络中,每个Pod对象拥有一个集群全局唯一的地址并可直接用于与其他Pod进行通信,另外,运行Pod的各节点也会通过桥接设备等持有此平面网络中的一个IP地址,这就意味着Node到Pod间的通信也可在此网络上直接进行;
- Service与Pod间的通信:Service资源的专用网络也称为集群网络(Cluster Network),需要在启动kube-apiserver时经由“—service-cluster-ip-range”选项进行指定,而每个Service对象在此网络中均拥有一个称为Cluster-IP的固定地址;
- 集群外部到Pod对象之间的通信:将集群外部的流量引入到Pod对象的方式有受限于Pod所在的工作节点范围的节点端口(nodePort)和主机网络(hostNetwork)两种,以及工作于集群级别的NodePort或LoadBalancer类型的Service对象;
1.3 Pod网络的实现方式
一个易于实现的方案是使用软件实现的伪网络接口及模拟线缆将其连接至物理接口。伪网络接口的实现方案常见的有虚拟网桥、多路复用及硬件交换三种:
- 虚拟网桥:创建一对虚拟以太网接口(veth),一个接入容器内部,另一个留置于根名称空间内并借助于Linux内核桥接功能关联至真实的物理接口;
- 多路复用:多路复用可以由一个中间网络设备组成,它暴露了多个虚拟接口,可使用数据包转发规则来控制每个数据包转到的目标接口;
- 硬件交换:现今市面上的大多数NIC都支持单根I/O虚拟化(SR-IOV),它是创建虚拟设备的一种实现方式;
大多数情况下,用户希望创建跨越多个L2或L3的逻辑网络子网,这就要借助于叠加封装协议来实现(最常见的是VXLAN,它将叠加流量封装到UDP数据包中)。不过,由于控制平面缺乏标准化,VXLAN可能会引人更高的开销,并且来自不同供应商的多个VXLAN网络通常无法互操作。而K8s还将大量使用iptables和NAT来拦截进入逻辑/虚拟地址的流量并将其路由到适当的物理目的地。
目前Kubernetes支持使用CNI插件来编排网络,以实现Pod及集群网络管理功能的自动化。每次Pod被初始化或删除时kubelet都会调用默认的CNI插件创建一个虚拟设备接口附加到相关的底层网络,为其设置IP地址、路由信息并将其映射到Pod对象的网络名称空间。
配置Pod的网络时,kubelet首先在默认的/etc/cni/net.d目录中查找CNI JSON配置文件,接着基于type属性到/opt/cni/bin/中查找相关的插件二进制文件,如下面示例中的“portmap”。随后,由CNI插件调用IPAM插件(IP地址管理)来设置每个接口的IP地址,如host-local或dhcp等:
cat /etc/cni/net.d/10-calico.conflist{"name": "k8s-pod-network","cniVersion": "0.3.1","plugins": [{"type": "calico","log_level": "info","datastore_type": "kubernetes","nodename": "k8s-master","mtu": 1440,"ipam": {"type": "calico-ipam"},"policy": {"type": "k8s"},"kubernetes": {"kubeconfig": "/etc/cni/net.d/calico-kubeconfig"}},{"type": "portmap","snat": true,"capabilities": {"portMappings": true}}]}
1.4 CNI插件及其常见的实现
Kubernetes设计了网络模型,但将其实现交给了网络插件。于是,各种解决方案不断涌现。为了规范及兼容各种解决方案,CoreOS和Google联合制定了CNI(Container Network Interface)标准,旨在定义容器网络模型规范。CNI的基本思想是:容器运行时环境在创建容器时,先创建好网络名称空间(netns),然后调用CNI插件为这个netns配置网络,而后再启动容器内的进程。
CNI本身只是规范,CNI提供的插件分为三类:
- main:实现某种特定的网络功能,如loopback、bridge、macvaln和ipvlan等;
- meta:用于调用其他插件,如调用flannel;
- ipam:分配Ip地址,不提供网络实现;
常见的CNI网络插件包含如下:
- Flannel:一个为Kubernetes提供叠加网络的网络插件,它基于Linux TUN/TAP,使用UDP封装IP报文来创建叠加网络,并借助etcd维护网络的分配情况;
- Calico:一个基于BGP的三层网络插件并且也支持网络策略来实现网络的访问控制;它在每台机器上运行一个vRouter,利用Linux内核来转发网络数据包,并借助iptables实现防火墙等功能;
- Canal:由Flannel和Calico联合发布的一个统一网络捅件,提供CNI网络插件,并支持网络策略;
2.flannel网络插件
各Docker主机在dockerO桥上默认使用同一个子网,不同节点的容器很可能会得到相同的地址,于是跨节点的容器问通信会面临地址冲突的问题。另外,即使人为地设定多个节点上的dockerO桥使用不同的子网,其报文也会因为在网络中缺乏路由信息而无法准确送达。事实上,各种CNI插件都至少要解决这两类问题。
对于第一个问题,flannel的解决办法是,预留使用一个网络,如10.244.0.0/16,而后自动为每个节点的Docker容器引擎分配一个子网,如10.244.1.0/24和10.244。20/24,并将其分配信息保存于etcd持久存储。对于第二个问题,flannel有着多种不同的处理方法,每一种处理方法也可以称为一种网络模型。
- Vxlan:Linux内核自3.7.0起支持VxLAN,flannel的此种后端意味着使用内核中的VxLAN模块封装报文,这也是flannel较为推荐使用的方式;
- host-gw:它通过在节点上创建到达目标容器地址的路由直接完成报文转发,因此这种方式要求各节点本身必须在同一个二层网络中,故该方式不太适用于较大的网络规模(大二层网络除外)。host-gw有着较好的转发性能,且易于设定,推荐对报文转发性能要求较高的场景使用;
2.1 flannel的配置参数
flannel使用etcd来存储虚拟IP和主机IP之间的映射,各个节点上运行的flanneld守护进程负责监视etcd中的信息并完成报文路由。默认情况下,flannel的配置信息保存于etcd的键名/coreos.com/network/config之下,可以使用etcd服务的客户端工具来设定或修改其可用的相关配置。config的值是一个JSON格式的字典数据结构:
- Network: flannel于全局使用的CIDR格式的IPv4网络,字段串格式;
- SubnetLen:将Network属性指定的IPv4网络基于指定位的掩码切割为供各点使用的子网,此网络的掩码小于24时,切割子网时使用的掩码默认为24位;
- SubnetMin:可用作分配给节点使用的起始子网,默认为切分完成后的第一个子网;
- SubnetMax:可用作分配给节点使用的最大子网,默认为切分完成后最大的一个子网;
- Backend:flannel要使用的后端类型,以及后端的相关配置,字典格式;
如下面的配置示例中,全局网络为”10.244.0.0/16”,切分子网时用到的掩码长度为24,将相应的子网10.244.0.0/24-10.244.255.0/24分别分配给每一个工作节点使用,选择VxLAN作为使用的后端类型,并监听于8472端口:
{"Network": "10.244.0.0/16","SubnetLen": 24,"Backend": {"Type": "VxLAN","Port": 8472}}
2.2 VxLAN后端和direct routing
VxLAN,全称Virtual extensible Local Area Network(虚拟可扩展局域网),是VLAN扩展方案草案,采用的是MAC in UDP封装方式是NVo3(Network Virtualization over Layer3)中的一种网络虚拟化技术。其具体实现方式为:将虚拟网络的数据帧添加到VxLAN首部后,封装在物理网络的UDP报文中,然后以传统网络的通信方式传送该UDP报文,待其到达目的主机后,去掉物理网络报文的头部信息以及VxLAN首部,然后将报文交付给目的终端。
传统的VxLAN后端使用隧道网络转发叠加网络的通信报文会导致不少的流量开销,于是flannel的VxLAN后端还支持DirectRouting模式,它通过添加必要的路由信息使用节点的二层网络直接发送Pod的通信报文,仅在跨IP网络时,才启用传统的隧道方式转发通信流量。由于大部分场景中都省去了隧道首部开销,因此DirectRouting通信模式的性能基本接近于直接使用二层物理网络。
将flannel项目官方提供的配置清单下载至本地,而后将其ConfigMap资源kube-flannel-cfg的data字段中的网络配置部分修改为如下内容所示,并使用”kubectl apply
“命令重新应用于集群中即可:
net-conf.json: |{"Network": "10.244.0.0/16","Backend": {"Type": "VxLAN","Directrouting": true}}
VxLAN Directrouting后端转发模式同时兼具了VxLAN后端和host-gw后端的优势,既保证了传输性能,又具备了跨二层网络转发报文的能力,另外,VxLAN后端的可用配置参数除了Type之外还如下几个:
- Type: VxLAN,字符串;
- VNI:VxLAN的标识符,默认为1;
- Port:用于发送封装的报文的UDP端口,默认为8472;
- GBP:全称为Group Based Policy,配置是否启用VxLAN的基于组的策略机制,默认为否;
- DirectRouting: 是否为同一个二层网络中的节点启用直接路由机制,类似于host-gw后端的功能;此种场景下,VxLAN仅用于为那些不在同一个二层网络中的节点封装并转发报文;
3.网络策略
网络策略(Network Policy)是用于控制分组的Pod资源彼此之间如何进行通信,以及分组的Pod资源如何与其他网络端点进行通信的规范。它用于为k8s实现更为精细的流量控制,实现租户隔离机制。K8s使用标准的资源对象”NetworkPolicy”供管理员按需定义网络访问控制策略。
3.1 配置网络策略
Kubernetes系统中,报文流入和流出的核心组件是Pod资源,因此它们也是网络策略功能生效的主要目标,因此,NetworkPolicy对象也要使用标签选择器事先选择出一组 Pod资源作为控制对象。一般来说,NetworkPolicy是定义在一组Pod资源上的用于管控入站流量的”Ingress规则”,或者说是管理出站流量的”Egress规则”,也可以是二者组合定义,而仅部分生效还是全部生效则需要由spec.policyTypes予以定义。
就特定的Pod集合来说,入站和出站流量默认均处于放行状态,除非有规则能够明确匹配到它。然而,一旦在spec.policyTypes中指定了生效的规则类型,却在networkpolicy.spec字段中嵌套定义了没有任何规则的Ingress或Egress字段时,则表示拒绝相关方向上的一切流量。
定义网络策略时常用到的术语:
- Pod组:由网络策略通过Pod选择器选定的一组Pod的集合,它们时规则生效的目标Pod;可由NetworkPolicy对象通过matchLabel或matchExpression选定;
- Egress:出站流量,即由特定的Pod组发往其他网络端点的流量,通常由流量的目标网络端点(to)和端口(ports)来进行定义;
- Ingress:入站流量,即由其他网络端点发往特定Pod组的流量,通常由流量发出的源站点(from)和流量的目标端口所定义;
- 端口(ports):TCP或UDP的端口号;
- 端点(to,from):流量目标和流量源相关的组件,它可以是CIDR格式的IP地址块(ipBlock)、网络名称空间选择器(namespaceSelector)匹配的名称空间,或Pod选择 器(podSelector)匹配的Pod组;
3.2 管控入站流量
以提供服务为主要目的的Pod对象通常是请求流量的目标对象,但它们的服务未必应该为所有网络端点所访问,这就有必要对它们的访问许可施加控制。networkpolicy.spec中嵌套的Ingress字段用于定义入站流量规则。
Ingress字段的值是一个对象列表,主要由一下两个字段:
- from <[]Object>:可访问当前策略匹配到的Pod对象的源地址对象列表,多个项目之间的逻辑关系为“逻辑或”的关系;若未设置此字段或其值为空,则匹配一切源地址(默认的访问策略为不限制);如果此字段至少有一个值,那么它将成为放行的源地址白名单,仅来源于此地址列表中的流量允许通过。
- ports <[]Object>:当前策略匹配到的Pod集合的可被访问的端口对象列表,多个项目之间的逻辑关系为“逻辑或”的关系;若未设置此字段或其值为空,则匹配Pod集合上的所有端口(默认的访问策略为不限制);如果此字段至少有一个值,那么它将成为允许被访问的Pod端口白名单列表,仅入站流量的目标端口处于此列表中方才准许通过。
需要注意的是,NetworkPolicy资源属于名称空间级别,它的有效作用范围为其所属的名称空间;
3.2.1 设置默认的Ingress策略
必要时,用户可以创建一个NetworkPolicy来为名称空间设置一个“默认”的隔离策略,该策略选择所有的Pod对象,而后允许或拒绝任何到达这些Pod的入站流量。例如下面的 策略示例,其通过policyTypes字段指明要生效Ingress类型的规则 ,但未定义任何Ingress字段,因此不能匹配到任一源端点,从而拒绝所有入站流量:
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:name: deny-all-ingressspec:podSelector: {}policyTypes: ["Ingress"]
若要将默认策略设置为允许所有的入站流量,则只需要显式定义Ingress字段,并将其值设置为空以匹配所有源端点即可,如下面示例中的定义。不过,没有为入站流量定义任何规则时,本身的默认规则即为允许访问,因此允许所有相关的入站流量时,本身无须定义默认规则,下面的示例只是为说明规则的定义格式:
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:name: deny-all-ingressspec:podSelector: {}policyTypes: ["Ingress"]ingress:- {}
实践中,通常将默认策略设置为拒绝所有的入站流量,而后显式放行允许的源端点的入站流量。
3.2.2 放行特定的入站流量
在Ingress规则中嵌套from和ports字段即可匹配特定的入站流量,仅定义from字段时将隐含本地Pod资源组的所有端口,而仅定义ports字段时则表示隐含所有的源端点。 from和ports同时定义时表示隐含“逻辑与”关系,即那些来自from指定的源端点,访问由当前NetworkPolicy的podSelector匹配的当前名称空间的Pod上所指定的ports 的请求。
from宇段的值是一个对象列表,它可嵌套使用ipBlock、namespaceSelector和podSelector字段来定义流量来源,此三个字段匹配Pod资源的方式各有不同,同时使用两个或以上的字段,彼此之间隐含“逻辑或”关系:
- ipBlock:根据IP地址或网络地址块选择流量源端点;
- namespaceSelector: 基于集群级别的标签挑选名称空间,它将匹配由此标签选择器选出的所有名称空间内的所有Pod对象;赋予字段以空值来表示挑选所有的名称空间,即源站点为所有名称空间内的所有Pod对象;
- podSelector:于NetworkPolicy所在的当前名称空间内基于标签选择器挑选Pod资源,赋予字段以空值来表示挑选当前名称空间内的所有Pod对象;
ports字段的值也是一个对象列表,它嵌套port和protocol来定义流量的目标端口,即由NetworkPolicy匹配到的当前名称空间内的所有Pod资源上的端口:
- port:端口号或在Container上定义的端口名称,未定义时匹配所有端口;
- protocol:传输层协议的名称,TCP或UDP,默认为TCP;
下面配置清单中的网络策略示例定义了如何开放myapp pod资源给相应的源站点访问:
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:name: allow-myapp-ingressnamespace: defaultspec:podSelector:matchLabels:app: myapppolicyType: ["Ingress"]ingress:- from:- ipBlock:cidr: 10.244.0.0/16except:- 10.244.3.0/24- podSelector:matchLabels:app: myappports:- protocol: TCPport: 80
它将default名称空间中拥有标签”app=myapp”的Pod资源的80/TCP端口开放给10.244.0.0/16网络内除10.244.3.0/24子网中的所有源端点,以及当前名称空间中拥有标签”app=myapp”的所有Pod资源访问,其他未匹配到的源端点的流量则取决于其他网络策略的定义,若没有任何匹配策略,则默认为允许访问。
3.3 管控出站流量
除非是仅于当前名称空间中即能完成所有的目标功能,否则,大多数情况下,一个名称空间中的Pod资源总是有对外请求的需求,如向CoreDNS请求解析名称等。因此,通常应该将出站流量的默认策略设置为准许通过。但如果有必要对其实施精细管理,仅放行那些有对外请求需要的Pod对象的出站流量,则也可先为名称空间设置“禁止所有”默认策略,而后定义明确的“准许”策略。
networkpolicy.spec中嵌套的Egress宇段用于定义入站流量规则,就特定的Pod集合来说,出站流量一样默认处于放行状态,除非在所有入站策略中至少有一条规则能够明确匹配到它。
Egress字段的值是一个宇段列表,它主要由以下两个字段组成:
- to:由当前策略匹配到的Pod资源发起的出站流量的目标地址列表,多个项目之间为“或”(OR)关系;若未定义或字段值为空则意味着应用于所有目标地址(默认为不限制);若明确给出了主机地址列表,则只有目标地址匹配列表中的主机地址的出站流量被放行;
- ports:出站流量的目标端口列表,多个端口之间为“或”(OR)关系;若未定义或字段值为空则意味着应用于所有端口(默认为不限制);若明确给出了端口列表,则只有目标端口匹配列表中的端口的出站流量被放行;
3.3.1 设置默认Egress策略
类似于使用Ingress的使用方式,用户也可以通过创建一个NetworkPolicy对象来为名称空间设置一个默认的隔离策略,该策略选择所有的Pod对象,而后允许或拒绝由这些Pod发出的所有出站流量。例如下面的策略示例,它通过policyTypes字段指明要生效Egress类型的规则,但未定义任何Egress字段,因此不能匹配到任何目标端点,从而拒绝所有的入站流量:
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:name: deny-all-egressspec:podSelector: {}policyTypes: ["Egress"]
3.3.2 放行特定的出站流量
下面的配置清单示例中定义了一个Egress规则,它对来自拥有”app=tomcat”的Pod对象的,到达标签为”app=nginx”的Pod对象的80端口,以及到达标签为”app=mysql”的Pod对象的3306端口的流量给予放行:
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:name: allow-tomcat-egressnamespace: defaultspec:podSelector:matchLabels:app: tomcatpolicyTypes: ["Egress"]egress:- to:- podSelector:matchLabels:app: nginxports:- protocol: TCPport: 80- to:- podSelector:matchLabels:app: mysqlports:- protocol: TCPport: 3306
3.4 隔离名称空间
实践中,通常需要彼此隔离所有的名称空间,但应该允许它们都能够与kube-system名称空间中的Pod资源进行流量交换,以实现监控和名称解析等各种管理功能。下面的配置清单示例为default名称空间定义了相关的规则,在出站和入站流量默认均为拒绝的情况下,它用于放行名称空间内部的各Pod对象之间的通信,以及与kube-system名称空间内各Pod间的通信:
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:name: namespace-deny-allnamespace: defaultspec:policyTypes: ["Ingress","Egress"]podSelector: {}---apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata:name: namespace-allownamespace: defaultspec:policyTypes: ["Ingress","Egress"]podSelector: {}ingress:- from:- namespaceSelector:matchExpressions:- key: nameoperator: Invalues: ["default","kube-system"]egress:- to:- namespaceSelector:matchExpressions:- key: nameoperator: Invalues: ["default","kube-system"]
4. Calico网络插件
Calico是一个开源虚拟化网络方案,用于为云原生应用实现互联及策略控制。与Flannel相比,Calico的一个显著优势是对网络策略(network policy)的支持,它允许用户动态定义ACL规则控制进出容器的数据报文,实现为Pod间的通信按需施加安全策略。
Calico本身是一个三层的虚拟网络方案,它将每个节点都当作路由器(router),将每个节点的容器都当作是“节点路由器”的一个终端并为其分配一个IP地址,各节点路由器通过BGP(Border Gateway Protocol)学习生成路由规则,从而将不同节点上的容器连接起来。
因此,Calico方案其实是一个纯三层的解决方案,通过每个节点协议栈的三层(网络层)确保容器之间的连通性,这摆脱了flannel host-gw类型的所有节点必须位于同一二层网络的限制,从而极大地扩展了网络规模和网络边界。
BGP是互联网上一个核心的去中心化自治路由协议,它通过维护IP路由表或“前缀”表来实现自治系统(AS)之间的可达性,属于矢量路由协议。不过,考虑到并非所有的网络都能支持BGP,以及Calico控制平面的设计要求物理网络必须是二层网络,以确保vRouter间均直接可达,路由不能够将物理设备当作下一跳等原因,为了支持三层网络,Calico还推出了IP-in-IP叠加的模型,它也使用Overlay的方式来传输数据。IPIP的包头非常小,而且也是内置在内核中,因此理论上它的速度要比VxLAN快一点,但安全性更差。 Calico 3.x的默认配置使用的是IPIP类型的传输方案而非BGP。
4.1 Calico工作特性
Calico利用Linux内核在每一个计算节点上实现了一个高效的vRouter(虚拟路由器)进行报文转发,而每个vRouter都通过BGP负责把自身所属的节点上运行的Pod资源的IP地址信息基于节点的agent程序(Felix)直接由vRouter生成路由规则向整个Calico网络内进行传播,不过,尽管小规模部署可以直接互联,但大规模网络还是建议使用BGP路由反射器(route reflector)来完成。Felix也支持在每个节点上按需生成ACL(Access Control List)从而实现安全策略,如隔离不同的租户或项目的网络通信。vRouter利用BGP通告本节点上现有的地址分配信息,每个vRouter均接入BGP路由反射器以实现控制平面扩展。
Calico承载的各Pod资源直接通过vRouter经由基础网络进行互联,它非叠加、无隧道、不使用VRF,也不依赖于NAT,因此每个工作负载都可以直接配置使用公网IP接入互联网,当然,也可以按需使用网络策略控制它的网络连通性。
- 经IP路由直连:Calico中,Pod收发的IP报文由所在节点的Linux内核路由表负责转发,并通过iptables规则实现其安全功能;
- 简单、高效、易扩展:Calico未使用额外的报文封装和解封装,从而简化了网络拓扑,这也是Calico高性能、易扩展的关键因素;Calico出色的扩展性缘于与互联网架构设计原则别无二致的方式,它们都使用了BGP作为控制平面。BGP以高效管理百万级的路由设备而闻名于世,Calico自然可以游刃有余地适配大型IDC网络规模;
较好的安全性:Calico操纵节点上的iptables规则以管控工作负载的互联许可。此种iptables规则操纵功能是节点间的警戒哨,负责阻挡任何非许可流量,并防止通过工作负载危及节点自身;
- 严格的域间流量分隔:运行于某个租户虚拟网络内的应用应严禁访问其他租户的应用,这种流量分隔是由久经考验的Linux内核中的ACL子系统予以实现的;
- 精细的策略规则:Calico通过使用Linux内建的ACL扩展来支持一众安全规则,任何可由ACL支持的功能均能通过Calico实现;
- 简介而不简单:Calico直接使用IP网络,无须任何地址转换或隧道承载的机制实现了一个简洁的“WYSIWYG”(What You See Is What You Get)网络模型,它可以清晰地标识出每个报文从哪儿来,到哪儿去。
4.2 Calico的核心组件
- Felix:Calico agent,跑在每台需要运行workload的节点上,主要负责配置路由及ACLs等信息来确保endpoint的连通状态;
- etcd:分布式键值存储,主要负责网络元数据一致性,确保Calico网络状态的准确性;
- BGPClient(BIRD):主要负责把Felix写入kernel的路由信息分发到当前Calico网络,确保workload间的通信的有效性;
- BGP Route Reflector(BIRD):大规模部署时使用,摒弃所有节点互联的mesh模式,通过一个或者多个BGP Route Reflector来完成集中式的路由分发;l
通过将整个互联网的可扩展IP网络原则压缩到数据中心级别,Calico在每一个计算节点利用Linux kernel实现了一个高效的vRouter来负责数据转发而每个vRouter通过BGP协议负责把自己上运行的workload的路由信息像整个Calico网络内传播--小规模部署可以直接互联,大规模下可通过指定的BGP route reflector来完成。这样保证最终所有的workload之间的数据流量都是通过IP包的方式完成互联的。
与K8s集群进行整合时,Calico需要提供三个组件:
- calico/node: 为集群中每个节点上运行的容器提供Felix Agent和BGP客户端;
- cni-plugin: CNI网络插件,用于整合Calico和kubelet,发现Pod资源,并将其添加进Calico网络;
- calico/kube-controllers: Calico网络策略控制器;
