calico简介

Calico是一个开源的虚拟化网络方案,用于为云原生应用实现互联及策略控制.相较于 Flannel 来说,Calico 的优势是对网络策略(network policy),它允许用户动态定义 ACL 规则控制进出容器的数据报文,实现为 Pod 间的通信按需施加安全策略.不仅如此,Calico 还可以整合进大多数具备编排能力的环境,可以为 虚机和容器提供多主机间通信的功能。 Calico 本身是一个三层的虚拟网络方案,它将每个节点都当作路由器,将每个节点的容器都当作是节点路由器的一个终端并为其分配一个 IP 地址,各节点路由器通过 BGP(Border Gateway Protocol)学习生成路由规则,从而将不同节点上的容器连接起来.因此,Calico 方案其实是一个纯三层的解决方案,通过每个节点协议栈的三层(网络层)确保容器之间的连通性,这摆脱了 flannel host-gw 类型的所有节点必须位于同一二层网络的限制,从而极大地扩展了网络规模和网络边界。

配置查看

calico的CNI配置文件:

cat /etc/cni/net.d/10-calico.conflist
{
  "name": "k8s-pod-network",
  "cniVersion": "0.3.1",
  "plugins": [
    {
      "type": "calico",
      "log_level": "info",
      "log_file_path": "/var/log/calico/cni/cni.log",
      "datastore_type": "kubernetes",
      "nodename": "master01",
      "mtu": 0,
      "ipam": {
          "type": "host-local",
          "subnet": "usePodCidr"
      },
      "policy": {
          "type": "k8s"
      },
      "kubernetes": {
          "kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
      }
    },
    {
      "type": "portmap",
      "snat": true,
      "capabilities": {"portMappings": true}
    },
    {
      "type": "bandwidth",
      "capabilities": {"bandwidth": true}
    }
  ]
}

calico部署完毕后,会生成一系列的自定义配置属性信息

kubectl api-versions  | grep crd
crd.projectcalico.org/v1
该api版本信息中有大量的配置属性
kubectl get ippools
NAME                  AGE
default-ipv4-ippool   15m

查看这里配置的calico相关的信息
kubectl get ippools default-ipv4-ippool -o yaml
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
  annotations:
    projectcalico.org/metadata: '{"uid":"321776f5-22ae-49cd-8924-1a9baef7edfb","creationTimestamp":"2025-05-07T05:03:11Z"}'
  creationTimestamp: "2025-05-07T05:03:11Z"
  generation: 1
  name: default-ipv4-ippool
  resourceVersion: "473394"
  uid: 3fb4a469-fe7e-44b0-8d0a-3b8ca079633a
spec:
  allowedUses:
  - Workload
  - Tunnel
  blockSize: 24
  cidr: 10.244.0.0/16
  ipipMode: Always
  natOutgoing: true
  nodeSelector: all()
  vxlanMode: Never
可以看到这里的calico采用的模型是 ipip模型(默认),分配的网段是使我们指定的 cidr网段,而且子网段也是我们指定的 24位掩码

环境创建完毕后,会生成一个tunl0的网卡,所有的流量会走这个tunl0网卡

ifconfig tunl0
tunl0: flags=193<UP,RUNNING,NOARP>  mtu 1480
        inet 10.244.0.1  netmask 255.255.255.255
        tunnel   txqueuelen 1000  (IPIP Tunnel)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 4  bytes 240 (240.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
calico模型中,默认使用的是tunl0虚拟网卡实现数据包的专线转发

calico结构

image-20250506172725723

  • Felix:每个节点都有,负责配置路由、ACL、向etcd宣告状态等
  • BIRD:每个节点都有,负责把 Felix 写入Kernel的路由信息 分发到整个 Calico网络,确保 workload 连通
  • etcd:存储calico自己的状态数据,可以结合kube-apiserver来工作
  • Route Reflector:路由反射器,用于集中式的动态生成所有主机的路由表,非必须选项

Calico模型

模式 原理 性能
IPIP(默认) underlay network - BGP(三层虚拟网络解决方案),pod与pod通过隧道封装原始数据包之后通信 中等
BGP overlay network - IPIP(双层IP实现跨网段效果)、VXLAN(数据包标识实现大二层上的跨网段通信)

IPIP模型

环境准备

kubectl get pod -o wide
busybox1-5c99cfb6f4-kv9vl           1/1     Running   5 (167m ago)   45h     10.244.2.5   node02   <none>           <none>
nginx-deployment-6b9d659f5f-9fxt9   1/1     Running   1 (167m ago)   3d12h   10.244.1.2   node01   <none>           <none>
可以看到两个pod在不同的节点上,busybox1(10.244.2.5)访问nginx(10.244.1.2)

图解

image-20250507151126313

实践访问流程

  • busybox1(10.244.2.5)发起请求,目标IP:10.244.1.2

    • busybox1(10.244.2.5)容器内部进行路由决策
    kubectl exec -it busybox1-5c99cfb6f4-kv9vl -- sh
    ip route
    default via 169.254.1.1 dev eth0     #目标IP为 10.244.1.2数据包将会走默认路由
    169.254.1.1 dev eth0 scope link
    
    为什么容器内的默认网关是 169.254.1.1?
    Calico 在默认使用 host-local IPAM 时,会配置一个虚拟的默认网关为 169.254.1.1,这不是实际存在的设备,而是 由 Calico CNI 插件在设置容器网络时写入的路由配置。
    
    • 数据包从 busybox1(10.244.2.5)容器内部通过veth pair 到达 Node02 的 calidb5654f5f5c@if3 接口

      kubectl exec -it busybox1-5c99cfb6f4-kv9vl  -- cat /sys/class/net/eth0/iflink
      7  #表名接口的序号为7,然后在node02节点查看接口号
      ip link show
      7: calidb5654f5f5c@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
        link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 3
      由此确认busybox1(10.244.2.5)容器的eth0对应的是calidb5654f5f5c@if3 接口
      
    • 数据包从calidb5654f5f5c@if3 接口通过路由到达tunl0网卡

    ip route
    default via 100.100.137.1 dev ens192 proto static metric 100
    10.244.0.0/24 via 100.100.137.200 dev tunl0 proto bird onlink   
    10.244.1.0/24 via 100.100.137.201 dev tunl0 proto bird onlink  #目标IP为 10.244.1.2数据包将会走这条路由到tunl0网卡
    blackhole 10.244.2.0/24 proto bird
    10.244.2.2 dev cali14f0fd09a5c scope link
    10.244.2.3 dev calie493c967aa8 scope link
    10.244.2.4 dev calia8666b46cd1 scope link
    10.244.2.5 dev calidb5654f5f5c scope link
    100.100.137.0/24 dev ens192 proto kernel scope link src 100.100.137.202 metric 100
    172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
    
  • 数据包在tunl0网卡进行IPIP隧道封装

    • 进行路由查询获取目标IP(10.244.1.2)对应的node IP (100.100.137.201)
    查看路由:
    ip route |grep 10.244.1.0
    10.244.1.0/24 via 100.100.137.201 dev tunl0 proto bird onlink
    
    • 封装数据包:
      • 外层 IP 头: 源 IP 100.100.137.202,目标 IP 100.100.137.201。
      • 内层原始包: 源IP 10.244.2.5, 目标IP 10.244.1.2 。
      • 隧道设备: tunl0(MTU=1480)
      • 协议:ipip-proto-4
  • 数据包经过tunl0封装之后路由到ens192网卡

ip route
default via 100.100.137.1 dev ens192 proto static metric 100
10.244.0.0/24 via 100.100.137.200 dev tunl0 proto bird onlink
10.244.1.0/24 via 100.100.137.201 dev tunl0 proto bird onlink
blackhole 10.244.2.0/24 proto bird
10.244.2.2 dev cali14f0fd09a5c scope link
10.244.2.3 dev calie493c967aa8 scope link
10.244.2.4 dev calia8666b46cd1 scope link
10.244.2.5 dev calidb5654f5f5c scope link
100.100.137.0/24 dev ens192 proto kernel scope link src 100.100.137.202 metric 100    #数据包经过Calico IPIP封装之后目标ip为100.100.137.1,走这条路由
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
  • 数据包从node02的ens192网卡传输到node01的ens192网卡
  • 数据包到达 node01 的ens192网卡自动送到 node01的tunl0网卡
为什么数据包会自动送到 tunl0?
IPIP 协议绑定:
tunl0 设备在内核注册为 IPIP 隧道的端点,所有协议号为 4 的 IP 包会自动递交给它。
类似 HTTP 流量交给 80 端口的服务。
  • 数据包到达 node01tunl0网卡进行解封装

    • 剥离外层封装,得到原始数据包。
    • 根据目标 IP 10.244.1.2 ,通过路由将数据包交给cali216bedde1dc@if4接口 。
    ip route
    default via 100.100.137.1 dev ens192 proto static metric 100
    10.244.0.0/24 via 100.100.137.200 dev tunl0 proto bird onlink
    blackhole 10.244.1.0/24 proto bird
    10.244.1.2 dev cali216bedde1dc scope link     #目标IP 10.244.1.2,走这个路由
    10.244.1.3 dev cali598209dffcf scope link
    10.244.1.4 dev calid80d6491883 scope link
    10.244.2.0/24 via 100.100.137.202 dev tunl0 proto bird onlink
    100.100.137.0/24 dev ens192 proto kernel scope link src 100.100.137.201 metric 100
    172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
    
  • 数据包从cali216bedde1dc@if4接口交给到nginx容器

为什么交给nginx容器?
kubectl exec -it nginx-deployment-6b9d659f5f-9fxt9  -- cat /sys/class/net/eth0/iflink
5 #表名接口的序号为5,然后在node01节点查看接口号
ip link show
5: cali216bedde1dc@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP mode DEFAULT group default
    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 0
由此确认nginx(10.244.1.2)容器的eth0对应的是cali216bedde1dc@if4 接口

抓包验证

由于在calico的默认网络模型是 IPIP,所以我们在进行数据包测试的时候,可以通过直接抓取宿主机数据包,来发现双层ip效果
busybox1容器发起请求:
kubectl exec -it busybox1-5c99cfb6f4-kv9vl -- sh
/ # ping 10.244.1.2 -c 1
PING 10.244.1.2 (10.244.1.2): 56 data bytes
64 bytes from 10.244.1.2: seq=0 ttl=62 time=2.153 ms


node01抓包:
tcpdump -i ens192 proto 4 -nn -vv
tcpdump: listening on ens192, link-type EN10MB (Ethernet), capture size 262144 bytes
15:21:27.581030 IP (tos 0x0, ttl 63, id 49359, offset 0, flags [DF], proto IPIP (4), length 104)
    100.100.137.202 > 100.100.137.201: IP (tos 0x0, ttl 63, id 27908, offset 0, flags [DF], proto ICMP (1), length 84)
    10.244.2.5 > 10.244.1.2: ICMP echo request, id 9216, seq 0, length 64
15:21:27.581491 IP (tos 0x0, ttl 63, id 2094, offset 0, flags [none], proto IPIP (4), length 104)
    100.100.137.201 > 100.100.137.202: IP (tos 0x0, ttl 63, id 44546, offset 0, flags [none], proto ICMP (1), length 84)
    10.244.1.2 > 10.244.2.5: ICMP echo reply, id 9216, seq 0, length 64

BGP模型

配置calico模型为BGP模型

kubectl calico get ipPools
NAME                  CIDR            SELECTOR
default-ipv4-ippool   10.244.0.0/16   all()

kubectl calico get ipPools default-ipv4-ippool -o yaml > default-ipv4-ippool.yaml
vim default-ipv4-ippool.yaml
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
  creationTimestamp: "2025-05-07T05:03:11Z"
  name: default-ipv4-ippool
  resourceVersion: "473394"
  uid: 3fb4a469-fe7e-44b0-8d0a-3b8ca079633a
spec:
  allowedUses:
  - Workload
  - Tunnel
  blockSize: 24
  cidr: 10.244.0.0/16
  ipipMode: CrossSubnet   #将原来的Always 更换成 CrossSubnet(跨节点子网) 模式即可
  natOutgoing: true
  nodeSelector: all()
  vxlanMode: Never

kubectl calico apply -f default-ipv4-ippool.yaml
Successfully applied 1 'IPPool' resource(s)

环境准备

kubectl get pod -o wide
busybox1-5c99cfb6f4-kv9vl           1/1     Running   5 (167m ago)   45h     10.244.2.5   node02   <none>           <none>
nginx-deployment-6b9d659f5f-9fxt9   1/1     Running   1 (167m ago)   3d12h   10.244.1.2   node01   <none>           <none>
可以看到两个pod在不同的节点上,busybox1(10.244.2.5)访问nginx(10.244.1.2)

图解

image-20250507155414024

实践访问流程

  • busybox1(10.244.2.5)发起请求,目标IP:10.244.1.2

    • busybox1(10.244.2.5)容器内部进行路由决策
    ip route
    default via 169.254.1.1 dev eth0     #目标IP为 10.244.1.2数据包将会走默认路由
    169.254.1.1 dev eth0 scope link
    
    为什么容器内的默认网关是 169.254.1.1?
    Calico 在默认使用 host-local IPAM 时,会配置一个虚拟的默认网关为 169.254.1.1,这不是实际存在的设备,而是 由 Calico CNI 插件在设置容器网络时写入的路由配置。
    
    • 数据包从 busybox1(10.244.2.5)容器内部通过veth pair 到达 Node02 的 calidb5654f5f5c@if3 接口

      kubectl exec -it busybox1-5c99cfb6f4-kv9vl  -- cat /sys/class/net/eth0/iflink
      7  #表名接口的序号为7,然后在node02节点查看接口号
      ip link show
      7: calidb5654f5f5c@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default
        link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 3
      由此确认busybox1(10.244.2.5)容器的eth0对应的是calidb5654f5f5c@if3 接口
      
    • 数据包从calidb5654f5f5c@if3 接口通过路由到达ens192网卡

    ip route
    default via 100.100.137.1 dev ens192 proto static metric 100
    10.244.0.0/24 via 100.100.137.200 dev ens192 proto bird
    10.244.1.0/24 via 100.100.137.201 dev ens192 proto bird  #目标IP为 10.244.1.2数据包将会走这条路由到ens192网卡
    blackhole 10.244.2.0/24 proto bird
    10.244.2.2 dev cali14f0fd09a5c scope link
    10.244.2.3 dev calie493c967aa8 scope link
    10.244.2.4 dev calia8666b46cd1 scope link
    10.244.2.5 dev calidb5654f5f5c scope link
    100.100.137.0/24 dev ens192 proto kernel scope link src 100.100.137.202 metric 100
    172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
    
  • 数据包从node02的ens192网卡传输到node01的ens192网卡

  • 数据包从node01的ens192网卡通过路由到达cali216bedde1dc@if4接口
ip route
default via 100.100.137.1 dev ens192 proto static metric 100
10.244.0.0/24 via 100.100.137.200 dev ens192 proto bird
blackhole 10.244.1.0/24 proto bird
10.244.1.2 dev cali216bedde1dc scope link   目标IP为10.244.1.2数据包将会走这条路由。
10.244.1.3 dev cali598209dffcf scope link
10.244.1.4 dev calid80d6491883 scope link
10.244.2.0/24 via 100.100.137.202 dev ens192 proto bird
100.100.137.0/24 dev ens192 proto kernel scope link src 100.100.137.201 metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1
  • 数据包从cali216bedde1dc@if4接口交给到nginx容器
为什么交给nginx容器?
kubectl exec -it nginx-deployment-6b9d659f5f-9fxt9  -- cat /sys/class/net/eth0/iflink
5 #表名接口的序号为5,然后在node01节点查看接口号
ip link show
5: cali216bedde1dc@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1480 qdisc noqueue state UP mode DEFAULT group default
    link/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netnsid 0
由此确认nginx(10.244.1.2)容器的eth0对应的是cali216bedde1dc@if4 接口

抓包验证

busybox1容器发起请求:
kubectl exec -it busybox1-5c99cfb6f4-kv9vl -- sh
/ # ping 10.244.1.2 -c 1
PING 10.244.1.2 (10.244.1.2): 56 data bytes
64 bytes from 10.244.1.2: seq=0 ttl=62 time=2.153 ms


node01抓包:
tcpdump -i ens192 -nn ip host 10.244.2.5
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ens192, link-type EN10MB (Ethernet), capture size 262144 bytes
15:55:41.025710 IP 10.244.2.5 > 10.244.1.2: ICMP echo request, id 14336, seq 0, length 64
15:55:41.026098 IP 10.244.1.2 > 10.244.2.5: ICMP echo reply, id 14336, seq 0, length 64

BGP模型的反射器模式实践

当前节点的网络效果

calicoctl node status  
Calico process is running.

IPv4 BGP status
+-----------------+-------------------+-------+----------+-------------+
|  PEER ADDRESS   |     PEER TYPE     | STATE |  SINCE   |    INFO     |
+-----------------+-------------------+-------+----------+-------------+
| 100.100.137.201 | node-to-node mesh | up    | 05:03:38 | Established |
| 100.100.137.202 | node-to-node mesh | up    | 05:03:38 | Established |
+-----------------+-------------------+-------+----------+-------------+

IPv6 BGP status
No IPv6 peers found.

当前的calico节点的网络状态是 BGP peer 的模型效果,也就是说 每个节点上都要维护 n-1 个路由配置信息整个集群中需要维护 n*(n-1) 个路由效果,这在节点量非常多的场景中,不是我们想要的。所以我们需要实现一种 BGP reflecter 的效果。

image-20250507160835264

使用反射器的网络效果:

如果我们要做 BGP reflecter 效果的话,需要对反射器角色做冗余,如果我们的集群是一个多主集群的话,可以将集群的master节点作为bgp的reflecter角色,从而实现反射器的冗余。

image-20250507161038295

创建反射器角色:

cat 01-calico-reflector-master.yaml
apiVersion: projectcalico.org/v3
kind: Node
metadata:
  labels:
    route-reflector: true   
  name: master01   #必须是通过 calicoctl get nodes 获取到的节点名称。
spec:
  bgp:
    ipv4Address: 100.100.137.200/24   #master01节点的ip地址
    ipv4IPIPTunnelAddr: 10.244.0.1    #master01节点的tunl0网卡的ip地址
    routeReflectorClusterID: 1.1.1.1   #是BGP网络中的唯一标识,所以这里的集群标识只要唯一就可以了

calicoctl apply -f 01-calico-reflector-master.yaml

更改节点的网络效果为 reflecter:

cat 02-calico-reflector-bgppeer.yaml
kind: BGPPeer
apiVersion: projectcalico.org/v3
metadata:
  name: bgppeer-demo
spec:
  nodeSelector: all()   #指定所有的后端节点
  peerSelector: route-reflector=="true"  #指定的是反射器的标签,标识所有的后端节点与这个反射器进行数据交流

calicoctl apply -f 02-calico-reflector-bgppeer.yaml

关闭默认的网络效果:

cat 03-calico-reflector-defaultconfig.yaml
apiVersion: projectcalico.org/v3
kind: BGPConfiguration
metadata:
  name: default   #在这里最好是default,因为我们要对BGP默认的网络效果进行关闭
spec:
  logSeverityScreen: Info
  nodeToNodeMeshEnabled: false  #关闭后端节点的BGP peer默认状态 -- 即点对点通信关闭
  asNumber: 63400   #指定的是后端节点间使用反射器的时候,我们要在一个标志号内,这里随意写一个

查看效果:

calicoctl node status
Calico process is running.

IPv4 BGP status
+-----------------+---------------+-------+----------+-------------+
|  PEER ADDRESS   |   PEER TYPE   | STATE |  SINCE   |    INFO     |
+-----------------+---------------+-------+----------+-------------+
| 100.100.137.201 | node specific | up    | 08:36:40 | Established |
| 100.100.137.202 | node specific | up    | 08:36:40 | Established |
+-----------------+---------------+-------+----------+-------------+

IPv6 BGP status
No IPv6 peers found.
默认的点对点通信方式就已经丢失了,剩下了反射器模式

其他

本章节的重点是calico底层的数据包通信流程,对于calico的策略管理(黑白名单)以及流量管控(流量限速)功能这里不做过多介绍,这两个功能生产环境一般不会使用,管理极其麻烦。之后用到我将会再做补充。。。

results matching ""

    No results matching ""