原文:https://system51.github.io/2020/05/27/using-calico/#
https://blog.csdn.net/weixin_43266367/article/details/128018625
https://www.bladewan.com/2020/11/18/calico_ops/
Calico基本概念
- Calico是针对容器,虚拟机和基于主机的本机工作负载的开源网络和网络安全解决方案。
- Calico支持广泛的平台,包括Kubernetes,OpenShift,Docker EE,OpenStack和裸机服务。
- Calico将灵活的网络功能与无处不在的安全性实施相结合,以提供具有本地Linux内核性能和真正的云原生可扩展性的解决方案。
- Calico为开发人员和集群运营商提供了一致的经验和功能集,无论是在公共云中还是本地运行,在单个节点上还是在数千个节点集群中运行。
Calico架构
calico-cni
Calico网络模型主要工作组件:
-
Felix:运行在每一台 Host 的 agent 进程,Felix负责刷新主机路由和ACL规则等,以便为该主机上的
Endpoint
正常运行提供所需的网络连接和管理。进出容器、虚拟机和物理主机的所有流量都会遍历Calico,利用Linux内核原生的路由和iptables生成的规则。- Felix一般负责以下工作:
- 管理网络接口:Felix将有关网络接口的一些信息编程到内核中,使内核能够正确处理该Endpoint发出的流量。Felix将确保主机正确响应来自每个工作负载的ARP请求,并将其管理的网卡启用IP Forward;
- 编写路由:Felix负责将到其主机上Endpoint的路由编写到Linux内核FIB(转发信息库)中。这可以确保那些发往目标主机的Endpoint的数据包被正确地转发;
- 编写ACL:Felix还负责将ACL编程到Linux内核中,即iptables规则。这些ACL用于确保只在Endpoints之间发送有效的网络流量,并确保Endpoint无法绕过Calico的安全措施;
- 报告状态:Felix负责提供有关网络健康状况的数据。例如,它将报告配置其主机时发生的错误和问题。该数据会被写入etcd,并对网络中的其他组件可见。
- Felix一般负责以下工作:
-
etcd:分布式键值存储,主要负责网络元数据一致性,确保Calico网络状态的准确性,可以与kubernetes共用;
-
bird(BGP Client):
Calico
为每一台Host
部署一个BGP Client
,当Felix
将路由写入kernel
FIB中时BGP Client
将通过BGP
协议广播告诉剩余calico
节点,从而实现网络互通。 -
confd:通过监听etcd以了解BGP配置和全局默认值的更改(例如:AS number、日志级别、IPAM信息)。Confd根据ETCD中数据的更新,动态生成BIRD配置文件。当配置文件更改时,confd触发BIRD重新加载新文件。
Calico两种网络模式
  Calico本身支持多种网络模式,从overlay
和underlay
上区分。Calico overlay
模式,一般也称Calico IPIP或VXLAN模式,不同Node间Pod使用IPIP或VXLAN隧道进行通信。Calico underlay
模式,一般也称calico BGP模式,不同Node Pod使用直接路由进行通信。在overlay和underlay都有nodetonode mesh
(全网互联)和Route Reflector
(路由反射器)。如果有安全组策略需要开放IPIP协议;要求Node允许BGP协议,如果有安全组策略需要开放TCP 179端口;官方推荐使用在Node小于100的集群,我们在使用的过程中已经通过IPIP模式支撑了100-200规模的集群稳定运行。
路由更新速率问题
  为什么要考虑路由更新速率?在Calico默认的使用模式中,Calico每个Node一个分配一个Block,每个Block默认为64个IP,当单个Node启动的Pod超过64时,才会分配下一个Block。Calico BGP client默认只向外通告聚合后的Block的路由,默认配置,只有在Node上下线、Node上Pod数量超过Block size的倍数才会出现路由的更新,路由的条目数量是Node级别的。
  而实际业务在使用的过程中,会针对一个服务或者一个deployment分配一个IP Pool,这种使用模式会导致Calico的IP Pool没有办法按照Node聚合,出现一些零散的无法聚合的IP地址,最差的情况,会导致每个Pod产生一条路由,会导致路由的条目变为Pod级别。
  在默认情况下,交换机设备为了防止路由震荡,会对BGP路由进行收敛保护。但是Kubernetes集群中,Pod生命周期短,变化频繁,需要关闭网络设备的路由变更保护机制才能满足Kubernetes的要求;对于不同的网络设备,路由收敛速度也是不同的,在大规模Pod扩容和迁移的场景,或者进行双数据中心切换,除了考虑Pod的调度时间、启动时间,还需要对网络设备的路由收敛速度进行性能评估和压测。
路由黑洞问题
  使用Calico Downward Default模型组网时,Node使用EBGP模式与Node建立邻居关系。当Pod使用的IP地址为内部统一规划的地址,出现Pod IP地址紧张的时候,会出现Pod之间不能正常访问的情况。(注:只会在EBGP模式下才会出现)
  Calico分配IP地址的原则为,将整个IPPool分为多个地址块,每个Node获得一个Block,当有Pod调度到某个Node上时,Node优先使用Block内的地址。如果每个新增的Node分不到一个完整的地址块(也就是说Node无法获得整个网段64个IP),Calico IP地址管理功能会去使用其他Node的Block的IP,此时,就会出现Pod无法访问的现象。
如下图所示,Pod 10.168.73.82无法访问Pod 10.168.73.83。
calico-cni-4
  查看Node 10.0.0.70的路由表,其中“blackhole 10.168.73.80/28 proto bird”为黑洞路由。如果没有其他优先级更高的路由,主机会将所有目的地址为10.168.73.80/28的网络数据丢弃掉。所以在Node 10.0.0.70上ping Pod 10.168.73.84会报“参数不合法”的错误。此时,在Downward Default模式下,Calico配置的这一条黑洞路由使得Node 10.0.0.70不能够响应其他Node上PodIP在10.168.73.80/28网段发起的网络请求。
  要解决路由黑洞问题问题,首先,除了对整个Calico 的IP Pool总量进行监控外,还需要对可用的IP Block进行监控,确保不会出现IP Block不够分的情况,或者或者IP地址Block借用的情况;也可以通过在规划时计算IP地址总量,以及在kubelet配置参数中指定maxPods来规避这个问题;(注:一般我们会配置子网为/16
也就是说能容纳65536
个POD。如果一台主机一个block块(64个IP)那么也要1024个主机。为什么是1024,因为65536/64=1024,用总的IP数除以一个block块的IP数可以计算出总共有多少个block,上面也提到过当一个新的主机无法获得一个完整的block的时候才会出现路由黑洞。所以只要主机不超过1024台或者block块不超过1024个就不会出现。)
IPIP网络:
流量:tunl0设备封装数据,形成隧道,承载流量。
适用网络类型:适用于互相访问的pod不在同一个网段中,跨网段访问的场景。外层封装的ip能够解决跨网段的路由问题。
效率:流量需要tunl0设备封装,效率略低。
calico-cni-2
BGP网络:
流量:使用主机路由表信息导向流量
适用网络类型:适用于互相访问的pod在同一个网段,适用于大型网络。
效率:原生hostGW,效率高。
calico-cni-3
Calico 数据流向
由于个人环境中使用的是 IPIP
模式,因此接下来这里分析一下这种模式。首先是准备两个准备两个Pod分别在不同的主机上。
1
|
|
这里在 nginx-7fb7fd49b4-94694
这个Pod中ping busybox
这个Pod
1
|
|
进入pod nginx-7fb7fd49b4-94694
中查看这个pod中的路由信息
1
|
|
根据路由信息 ping 10.244.215.68
会匹配到第一条。
第一条路由的意思是:去往任何网段的数据包都发往网关169.254.1.1,然后从eth0网卡发送出去。
nginx-7fb7fd49b4-94694
所在的 k8s-n2
宿主机上路由信息如下:
1
|
|
可以看到一条Destination为 10.244.215.64
的路由。
意思是:当ping包来到 k8s-n2
节点上,会匹配到路由tunl0。该路由的意思是:去往10.244.215.64/26
的网段的数据包都发往网关 192.168.28.13
。因为 nginx-7fb7fd49b4-94694
的pod在192.168.28.14
上,busybox
的pod在192.168.28.13
上。所以数据包就通过设备tunl0发往到 k8s-n1
节点上。
busybox
所在的 k8s-n1
宿主机上路由信息如下:
1
|
|
当 k8s-n1
节点网卡收到数据包之后,发现发往的目的ip为 10.244.215.68
,于是匹配到Destination为 10.244.215.68
的路由。
该路由的意思是:10.244.215.68
是本机直连设备,去往设备的数据包发往 cali12d4a061371
为什么这么奇怪会有一个名为 cali12d4a061371
的设备呢?
简单来说,Calico 在主机上创建了一堆的 veth pair
,其中一端在主机上,另一端在容器的网络命名空间里,然后在容器和主机中分别设置几条路由,来完成网络的互联。
接着验证一下。我们进入 busybox
的pod,查看到 4 号设备后面的编号是:66
1
|
|
然后我们登录到 busybox
这个pod所在的宿主机查看
1
|
|
发现pod busybox
中的另一端设备编号和k8s-n1
宿主机上看到的 cali12d4a061371
编号 66
是一样的
所以,k8s-n1
上的路由,发送 cali12d4a061371
网卡设备的数据其实就是发送到了 busybox
的这个pod中去了。到这里ping包就到了目的地。最粗暴的办法是使用 ip link del cali12d4a061371@if4
删除网卡看是否还能ping通就知道了。
  我们看到不管是容器也好还是主机也好都有一些奇怪的地方,从容器路由表可以知道 169.254.1.1
是容器的默认网关,MAC地址也是一个无效的MAC地址ee:ee:ee:ee:ee:ee
为什么会这这样呢,其实这些都是calico写死了的。Calico利用了网卡的proxy_arp功能,具体的,是将/proc/sys/net/ipv4/conf/calic9aa42a3793/proxy_arp
置为1
。当设置这个标志之后,就开启了proxy_arp功能。主机就会看起来像一个网关,会响应所有的ARP请求,并将自己的MAC地址告诉客户端。
  也就是说,当容器发送ARP请求时,calic9aa42a3793
网卡会告诉容器,我拥有169.254.1.1这个IP,我的MAC地址是XXX,这样当容器去访问外部服务时其实是访问的是calic9aa42a3793
。然后在由calic9aa42a3793
代替容器去访问外部服务然后把结果返回给容器这样就看起来网络就通了。
  通过tcpdump抓包可以看到首先容器会发送一个arp广播问169.254.1.1
的MAC地址是多少,告诉10.244.215.81
这IP。其实这个IP就是当前Pod自己的IP,也就是告诉自己。然后cali03d85d58f77
这个ARP请求,并回复告诉容器我拥有这个IP的MAC,他的MAC地址是ee:ee:ee:ee:ee:ee
。如果你想验证你可以使用ip link set dev cali03d85d58f77 address ee:ee:ee:ee:11:11
修改cali03d85d58f77
网卡的MAC地址,然后你在抓包看看效果。
1
|
|
修改MAC地址后
1
|
|
部署安装
1)确保Calico可以在主机上进行管理cali和tunl接口,如果主机上存在NetworkManage,请配置NetworkManager。
NetworkManager会为默认网络名称空间中的接口操纵路由表,在该默认名称空间中,固定了Calico veth对以连接到容器,这可能会干扰Calico代理正确路由的能力。
在以下位置创建以下配置文件,以防止NetworkManager干扰接口:
1
|
|
选择数据存储方式
  Calico同时支持Kubernetes API数据存储(kdd)和etcd数据存储。建议在本地部署中使用Kubernetes API数据存储,它仅支持Kubernetes工作负载。etcd是混合部署的最佳数据存储。(注意:使用Kubernetes API数据存储安装Calico时calico超过50个节点)需要做如下设置。
  在calico.yaml文件中将名为calico-typha
的deployments
的replicas
修改为当前节点的10/1。假如有200个节点就设置20个replicas
。
1
|
|
2)然后更改 CALICO_IPV4POOL_IPIP
为 Never
使用 BGP
模式,另外增加 IP_AUTODETECTION_METHOD
为 interface
使用匹配模式,默认是first-found模式,在复杂网络环境下还是有出错的可能,还有CALICO_IPV4POOL_CIDR
设置为kubeadm初始化时设置的podSubnet
参数。
1
|
|
1
|
|
3)应用calico文件
1
|
|
1
|
|
安装calicoctl
下载calicoctl客户端
1
|
|
验证是否可用
1
|
|
BGP两种模式
-
全互联模式(node-to-node mesh)
  全互联模式,每一个BGP Speaker都需要和其他BGP Speaker建立BGP连接,这样BGP连接总数就是N^2,如果数量过大会消耗大量连接。如果集群数量超过100台官方不建议使用此种模式。 -
路由反射模式Router Reflection(RR)
  RR模式中会指定一个或多个BGP Speaker为RouterReflection,它与网络中其他Speaker建立连接,每个Speaker只要与Router Reflection建立BGP就可以获得全网的路由信息。在calico中可以通过Global Peer实现RR模式。
1
|
|
使用calicoctl命令查看calico当前使用模式为node-to-node mesh
全互联模式(full mesh)会造成路由条目过大,无法在大规模集群中部署。使用BGP RR(中心化)的方式交换路由,能够有效降低节点间的连接数。
配置BGP RR模型(使用node充当路由反射器)
我们将建立两个路由反射器,这意味着即使我们取消一个路由反射器节点进行维护,也可以避免单点故障。
选择两个节点,并对每个节点执行以下操作:
1
|
|
编辑YAML以添加:
1
|
|
重新应用YAML
1
|
|
配置 BGPPeer
将所有非反射器节点配置为与所有路由反射器对等
1
|
|
将所有路由反射器配置为彼此对等
1
|
|
1
|
|
禁用默认的node-to-node mesh模式
1
|
|
1
|
|
此时在反射器节点上使用 calicoctl node status 应该能看到类似如下输出
1
|
|
在非反射器节点上,您应该只看到两个对等体。
1
|
|
Calico BGP跨网段(大型网络)
calico-cni-6
calico-cni-5
当节点位于不同的网络段时,我们需要在交换机或路由器上开启BGP协议,并配置BGPPeer将peerIP设置为路由器或交换机IP,我们需要做如下操作。
为机架1上的节点设置AS号
1
|
|
为机架1上的节点打标签
1
|
|
设置机架1上的node节点与tor交换机做IBGP
1
|
|
关闭BGP的node-to-node模式(对全局生效)
1
|
|
Calico Overlay网络
在Calico Overlay网络中有两种模式可选(仅支持IPV4地址)
- IP-in-IP (使用BGP实现)
- Vxlan (不使用BGP实现)
两种模式均支持如下参数
- Always: 永远进行 IPIP 封装(默认)
- CrossSubnet: 只在跨网段时才进行 IPIP 封装,适合有 Kubernetes 节点在其他网段的情况,属于中肯友好方案
- Never: 从不进行 IPIP 封装,适合确认所有 Kubernetes 节点都在同一个网段下的情况(配置此参数就开启了BGP模式)
在默认情况下,默认的 ipPool 启用了 IPIP 封装(至少通过官方安装文档安装的 Calico 是这样),并且封装模式为 Always
;这也就意味着任何时候都会在原报文上封装新 IP 地址,在这种情况下将外部流量路由到 RR 节点,RR 节点再转发进行 IPIP 封装时,可能出现网络无法联通的情况(没仔细追查,网络渣,猜测是 Pod 那边得到的源 IP 不对导致的);此时我们应当调整 IPIP 封装策略为 CrossSubnet
导出 ipPool 配置
1
|
|
修改 ipipMode
值为 CrossSubnet
1
|
|
重新使用 calicoctl apply -f ippool.yaml
应用既可