在k8s中calico部署和使用
calico是k8s中常见的网络插件,非常灵活,支持ipip,vxlan隧道bgp路由以及ebpf,虚机k8s均可使用
架构
组件 | 作用 |
---|---|
felix | 状态报告,路由规划,接口管理,acl |
bird | 负责路由宣告,以及路由反射 |
confd | 监控bgp变更,配置和更新bird的配置 |
储存插件 | 存储配置,有etcd和k8s两种选择 |
CNI插件 | 为pod配置网络 |
typha | 为confd和felix和后端存储之间提供缓存等增加性能服务 |
calicoctl | 命令行工具 |
dikastes | 配合istio |
术语
ippool
- ip池子,默认calico将会从池子中分配给podip
ipamblocks
- 从ippool里分割出来的ip段,为了减少路由数量,calico宣告路由时是以块为单位在pod所在的节点进行宣告的
部署calico cni
1 | curl https://raw.githubusercontent.com/projectcalico/calico/v3.25.1/manifests/calico.yaml -O |
安装calicoctl
- calicoctl使用calic的命令行客户端攻击可以用来查看一些信息,有三种安装方法选一种即可
用容器的方式运行calicoctl
1 | curl https://raw.githubusercontent.com/projectcalico/calico/v3.25.1/manifests/calicoctl.yaml -o calicoctl.yaml |
使用方法:calicoctl version
二进制文件
1 | curl -L https://github.com/projectcalico/calico/releases/latest/download/calicoctl-linux-amd64 -o calicoctl |
使用方法:calicoctl version
kubectl插件
1 | curl -L https://github.com/projectcalico/calico/releases/latest/download/calicoctl-linux-amd64 -o kubectl-calico |
使用方法: kubectl calico version
calicoctl常用命令
- 查看ipam分配情况
1 | calicoctl ipam show |
- 查看blocks分配情况
1 | calicoctl ipam show --show-blocks |
- 查看ippool
1 | calicoctl get ippool -o wide |
IPIP模式
calico的网络模式默认
是IPIP模式
- 通过calicoctl查看ippool的
IPIPMODE
字段,如下
1 | calicoctl get ippool -o wide |
- 启动ipipmode分为两种,一种是
Always
,还有一种CrossSubnet
- always,无论是否跨子网都通过ipip来通讯
- CrossSubnet,只有在跨子网时使用ipip模式
- Never,关闭从不使用ipip模式
ipip路径分析
- 部署一个nginx
1 | k get po -l app=nginx -o wide |
- 进入一个nginx的pod去ping另一个容器
1 | kubectl exec -it nginx-7fc57c59f7-4nxhh -- ping 10.244.205.195 |
ipip-pod到pod所在的node
- 查看容器的网卡和路由信息
1 | kubectl exec -it nginx-7fc57c59f7-4nxhh -- sh -c "ip addr;ip r" |
从上面的信息比较难以理解的是路由的网关是169.254.1.1,169.254.0.0/16为保留地址一般用于dhcp获取,而calico则将容器的默认路由设置为此,当容器发现目标地址不是本ip段时,会将流量发送给网关,这时需要知道网关的mac地址
这里calico将网关设置为169.254.1.1而没有任何一个网卡是169.254.1.1,其实是因为开了arp_proxy代答,具体使用了pod的veth的外面的网卡,这样流量就通过二层到达主机
官方的FAQ
1 | tcpdump -i cali1143a22bb0c host 10.244.120.68 -ennnvv |
- veth抓包
1 | tcpdump -i cali1143a22bb0c host 10.244.120.68 -ennnvv |
ipip-pod所在的node到目标所在的node的pod
- 查看minikube上的路由
1 | ip r |
- 隧道抓包
1 | tcpdump -i tunl0 host 10.244.120.68 -ennnvv |
到此pod的流量通过ipip封装发送到目标pod所在的node上,目标node将ipip包解封包,然后查找路由表发送到目标pod的veth网卡中
登录另一个node
1 | minikube ssh --node="minikube-m02" |
- 通过ip找到对应的网卡
1 | ip r |grep 10.244.205.195 |
- 抓包对应的网卡
1 | tcpdump -i cali614e1c7b24e |
- 抓包ipip隧道网卡
1 | tcpdump -i tunl0 |
- 通过以上抓包可以确定流量的路径
2个pod在同一个node上
当目标pod和源pod在同一个node上执行通过node上的路由到对应的veth网卡,不经过隧道
这次ping一个在相同node上的pod的ip
1 | kubectl get po whoami-7c88bd4c6f-7tc5b -o wide |
- 直接抓包隧道发现没有流量
1 | minikube ssh |
- 通过路由表找到对应的网卡
1 | ip r |grep 10.244.120.67 |
- 抓包目标的网卡
1 | tcpdump -i cali00c313c8253 |
- 通过以上抓包可以发现并没有经过隧道,而是直接路由到了目标的网卡
小结
VXLAN模式
开启vxlan模式
修改ippool中
VXLANMODE
字段为Always
,IPIPMODE
改为Never
,其中VXLANMODE
中的也有CrossSubnet
只在跨子网时使用修改calico-node的环境变量
1 | 修改环境变量 |
- 关闭关闭bird
1 | 将calico_backend修改为vxlan |
- 关闭bird的健康检查
1 | kubectl -n kube-system edit ds calico-node |
1 | livenessProbe: |
vxlan路径分析
依然使用之前的nginx来做测试
节点路由
1 | ip r |
- 查看vxlan.calico网卡信息
1 | ip -s addr show vxlan.calico |
- 搜下这个ip发现他作用为vxlan的网卡ip
1 | calicoctl ipam show --ip=10.244.120.70 |
vxlan-pod到node
- 从上面的网卡和路由信息可以看到calico只是修改了到其他节点pod的通讯方式,从ipip隧道改为vlxan隧道然后修改路由
- 所以后pod到node的方式其实没有变化和ipip模式是一样的
vxlan-pod到另一个node的pod
依然开始长ping
抓包vxlan网卡
1 | tcpdump -i vxlan.calico -eenn |
- 可以看到到vxlan网卡有我们长ping的数据包,可以确定不同node上的pod是通过vxlan来通讯
66:66:b0:7b:5c:e1
为源头pod所在node的vxlan网卡mac66:9f:82:63:75:c1
为目标pod所在node的vxlan网卡mac
vxlan-小结
- vxlan模式下知识变换了从一个node到另一个node的方式,从之前的ipip变为vxlan
- pod到node没有变化
BGP模式(full mesh)
BGP是一个使用广泛的路由协议,分为2种一种是不同as号的ebgp已经同as号的ibgp,这里使用的是ibgp
开启BGP模式
开启bgp模式基本思路就是将
ipip
和vxlan
模式都给关闭了
修改ippool中
VXLANMODE
字段为Never
,IPIPMODE
改为Never
修改calico-node的环境变量
1 | 修改环境变量 |
- 如果关闭bird需要开启bird
1 | 将calico_backend修改为bird |
- 如果关闭了bird的健康检查则需要开启bird的健康检查
1 | kubectl -n kube-system edit ds calico-node |
1 | livenessProbe: |
BGP模式路径分析
- 查看路由,可以发现跨节点通讯使用路由
1 | ip r |
- 查看网卡,可以发现没有vxlan的网卡
1 | ip addr |
- 通过birdcl查看路由表
1 | kubectl -n kube-system exec -it calico-node-9pwnj -c calico-node -- /bin/bash |
- 通过birdcl查bgp邻居状态
1 | birdcl show protocols |
- 查看bgp详细信息
1 | birdcl show protocols all Mesh_192_168_49_2 |
- 总体而言比较简单,直接通过路由到目标pod对应的节点
bgp小结
- 每个节点之间通过bgp宣告路由
BGP-RR模式(Route reflectors)
- 从节点中选取一部分节点作为bgp路由反射器以减少bgp对等体数量
路由反射部署方案
测试环境为4个节点的minikube集群
部署路由反射器有很多方法:
- 在集群外的机器中部署bird
- 在k8s中选择专门的节点
- 部署专门的calico-node容器作为反射器
很显然直接在集群中部署专门的节点作为反射器比较方便且容易管理
选择2个节点作为反射器,这里我选择后2个
确认现在的为bgp的mesh模式
需要注意calicoctl node status
这个需要再部署calico的节点上执行。。。。有点唐突
- 查看当前bgp邻居,可以发现已经与另外三个节点建立了邻居关系
1 | calicoctl node status |
1 | calicoctl get nodes -o wide |
指定节点作为反射器
- 导出
calico/node
资源
1 | calicoctl get nodes minikube-m03 -o yaml > minikube-m03.yaml |
- 在导出的文件中添加下面的字段
routeReflectorClusterID应该是为了防止路由环路
1 | # ... |
- 应用修改
1 | calicoctl replace -f minikube-m03.yaml |
- 在指定的节点打上标签
1 | kubectl label node minikube-m04 minikube-m03 route-reflector=true |
配置bgp对等体
1 | kind: BGPPeer |
- 修改bgp配置,关闭mesh模式
1 | apiVersion: projectcalico.org/v3 |
查看节点bgp状态
1 | rr模式的节点上 |
BGP-TOR路由
Top of Rack
机架上面的交换机
- 这个方案中所有的节点将bgp信息宣告给tor交换机由交换机负责bgp宣告
- 需要硬件交换机和路由器中整体部署bgp网络,然后宣告给这个网络
IPIP/VXLAN跨子网模式
- 当跨子网时使用
ipip/vxlan
来进行通讯 - 将ippool中的
IPIPMODE
或VXLANMODE
修改为CrossSubnet
即可
环境说明
1 | kubectl get no |
10.72.137.177
为一个子网,10.72.164.144,10.72.164.145
为同一个子网
IPIP
- 修改ipip为
CrossSubnet
1 | 关闭vxlan如果开启了 |
- 查看路由
1 | ip r |
- 确认是ipip网卡
1 | ip addr show tunl0 |
- 通过路由可以看到ipip在CrossSubnet下同子网直接通过bgp(bird)获取的路由发送出去,跨子网则使用了ipip隧道
VXLAN
- 修改VXLANMODE为CrossSubnet
1 | calicoctl patch ippool default-ipv4-ippool -p '{"spec":{"ipipMode": "Never"}}' |
- 查看路由
1 | ip r |
- 查看vxlan网卡
1 | ip addr show vxlan.calico |
- 结论通过路由可以看到vxlan在CrossSubnet下同子网直接通过eth0发送出去,跨子网则使用了vxlan
跨子网bgp full mesh模式下路由
- 查看路由
1 | [root@10-72-164-144 ~]# calicoctl get ippool default-ipv4-ippool -o wide |
- 可以看到在跨子网时,路由指向了当前节点的网关,但我们的网关却不知道10.244.90.128/26路由到哪里,所以10.244.90.128/26的地址不通
1 | k get po -o wide |
- 符合预期
将ipip和vxlan全部设置为CrossSubnet
- 不行
1 | [root@10-72-164-144 ~]# calicoctl patch ippool default-ipv4-ippool -p '{"spec":{"vxlanMode": "CrossSubnet"}}' |
EBPF
epbf是一个内核虚拟机
EBPF部署
环境验证
1 | uname -rv |
- ebpf对内核版本要求比较高,内核版本最高5.0往上,红帽4.8往上也行
修改api-server地址
- calico默认使用的是kube-proxy提供的api-server的svc地址需要改为api-server负载均衡的地址
- 可以通过
kubelet
的配置文件来查看, - 创建配置文件
1 | kind: ConfigMap |
- 重启calico组件
1 | kubectl -n kube-system rollout restart deployment calico-kube-controllers |
开启ebpf
- 配置kube-proxy
1 | kubectl patch ds -n kube-system kube-proxy -p '{"spec":{"template":{"spec":{"nodeSelector":{"non-calico": "true"}}}}}' |
- 开启ebpf
1 | calicoctl patch felixconfiguration default --patch='{"spec": {"bpfEnabled": true}}' |
开启dsr
dsr可以保留客户端ip
主要是修改
BPFExternalServiceMode
这个变量开启dsr
1 | calicoctl patch felixconfiguration default --patch='{"spec": {"bpfExternalServiceMode": "DSR"}}' |
- 回滚
1 | calicoctl patch felixconfiguration default --patch='{"spec": {"bpfExternalServiceMode": "Tunnel"}}' |
IP地址管理
静态ip
一般来说pod不需要pod的ip是静态的,而是已通过service来访问,但是在安全等领域可能需要pod的ip是静态或者一小段范围
使用ipam里面的ip
1 | annotations: |
不使用ipam里面的ip
此功能需要cni开启特性才行
1 | kubectl -n kube-system edit cm calico-config |
1 | { |
- 然后重启calico的agent
1 | kubectl -n kube-system rollout restart ds calico-node |
- 在pod上添加下面的注释
1 | annotations: |
- 此时pod的ip则会设置为
10.0.0.1
,但是这个ip只能在你在pod所在的node上ping通过,路由等需要自己手动处理
Floating IP(浮动ip)
Floating IP(浮动IP)是一种IP地址分配技术,它能够将一个IP地址从一台主机转移到另一台主机。 浮动IP通常用于云计算环境中,因为在这种环境下,虚拟资源(如虚拟机)可能随时在不同的物理主机之间移动。
只能在BGP模式下使用
- 也需要打开特性和上面noipam的类似方法不做赘述
1 | { |
1 | annotations: |
IP预留
- 顾名思义保留的ip不会分配给pod
1 | apiVersion: crd.projectcalico.org/v1 |
优先级
pod的注释>pod所在的ns的注释>ippool
带宽限制
- cni配置文件需要设置如下参数
1 | { |
- 在pod上配置带宽
1 | apiVersion: v1 |
- 实际是调用了
bandwidth
这个cni插件
指定MAC地址
1 | annotations: |
- 重启pod生效
开启ipv6支持
ipv6需要k8s同步开启双栈
修改cni配置文件
1 | kubectl -n kube-system edit cm calico-config |
1 | "ipam": { |
修改DS环境变量
环境变量 | 值 |
---|---|
IP6 | autodetect |
FELIX_IPV6SUPPORT | true |
1 | 修改环境变量 |