k8s阶段07 Pod网路模型及主流网络插件flannel, Calico, Cilium
Kubernetes网络: Node网络: Node间能正常建立TCP/IP通信连接 Node间隔了路由器: 隧道模型 Node共存于同一个二层网络: 隧道模型 路由模型 Service网络 Pod网络 CNI: 网络插件 NetPlugin插件 IPAM插件 生成虚拟网络接口: veth pair:#通用 if1.1@if1.2 MACVLAN/IPVLAN #用软件模拟直接使用物理网卡 SR-IOV #直接使用物理网卡 虚拟网络类型: overlay:隧道网络,叠加网络 #通用性好 vxlan, ipip, gre, ... #常见协议(vxlan不受限制,微软云限制ipip协议) underlay: MACVLAN/IPVLAN #要主机内核支持,要底层网卡和交换机支持(云环境本来就虚拟,很难支持);通常物理环境用,用得少,大部分主流插件不支持;性能好 Native Routing #把节点当路由器来用(不会在原生报文再添加多余报文封装,性能优于overlay),性能好 BGP #协议,如果云环境已经用BGP协议,这里就不能再支持了 将Pod接口接入到虚拟网络中的方法: veth pair: 在宿主机上创建虚拟交换机,该虚拟交换机也作为宿主机的接口使用;
2 主流的网络插件
通常来说,选择网络插件时应该通过底层系统环境限制、容器网络的功能需求和性能需求三个重要的评估标准来衡量插件的适用性 底层系统环境限制 ◼ 公有云环境多有专有的实现,例如Google GCE、Azure CNI、AWS VPC CNI和Aliyun Terway等,它们通常是相应环境上较佳的选择 ◼ 虚拟化环境限制较多,除叠加网络模型别无选择,可用有Flannel vxlan、Calico ipip、Weave和Antrea等 ◼ 物理机环境几乎支持任何类型的网络插件,此时一般应该选择性能较好的的Calico BGP、Flannel host-gw或DAMM IPVLAN等 容器网络功能需求 ◼ 支持NetworkPolicy的解决方案以Calico、WeaveNet和Cilium为代表,而且后两个支持节点到节点间的通信加密 ◼ 而大量Pod需要与集群外部资源互联互通时应该选择承载网络模型一类的解决方案 容器网络性能需求 ◼ Overlay网络中的协议报文有隧道开销,性能略差,而Underlay网络则几乎不存这方面的问题 ◼ 但Overlay或Underlay路由模型的网络插件支持较快的Pod创建速度,而Underlay模型中IPVLAN或MACVLAN模式中较慢
3 Flannel网络插件
Flannel简介
Flannel ◼ 由CoreOS研发,兼容CNI插件API,支持Kubernetes、OpenShift、Cloud Foundry、Mesos、Amazon ECS、 Singularity和OpenSVC等平台 ◼ 使用“虚拟网桥和veth设备”的方式为Pod创建虚拟网络接口,通过可配置的“后端”定义Pod间的通信网络, 支持基于VXLAN和UDP的Overlay网络,以及基于三层路由的Underlay网络 ◆虚拟网桥cni0 ◆隧道接口通常为flannel.1 ◼ 在IP地址分配方面,它将预留的一个专用网络(默认为10.244.0.0/16)切分成多个子网后作为每个节点的 podCIDR,而后由节点以IPAM插件host-local进行地址分配,并将子网分配信息保存于etcd之中 Flannel中可用的网络模型: VXLAN隧道模型 host-gw路由模型 #节点间不能隔路由器,因为当中路由器不会查etcd,不知道去哪找pod(不支持路由学习功能,靠查询中央数据库) vxlan with directrouting的混合模型 #混合路由和隧道(自动判定节点间是否跨路由,没跨用路由,跨了用隧道) 子网:10.244.0.0/16 使用24bits的掩码,为每个节点划分一个子网 #实例: 查看flannel默认vxlan通讯 #在节点查看路由表(vxlan通讯) [root@master01 ~]#route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 10.0.0.2 0.0.0.0 UG 0 0 0 ens33 10.244.0.0 0.0.0.0 255.255.255.0 U 0 0 0 cni0 #本地交换机 10.244.1.0 10.244.1.0 255.255.255.0 UG 0 0 0 flannel.1#通过本地flannel.1送出去 10.244.2.0 10.244.2.0 255.255.255.0 UG 0 0 0 flannel.1 10.244.3.0 10.244.3.0 255.255.255.0 UG 0 0 0 flannel.1 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0 [root@master01 ~]#kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE demoapp-8699b9895b-bljbb 1/1 Running 5 (9h ago) 4d8h 10.244.2.187 node02 demoapp-8699b9895b-n5vcb 1/1 Running 5 (9h ago) 4d8h 10.244.1.221 node01 #进入pod01请求pod02 [root@master01 ~]#kubectl exec -it demoapp-8699b9895b-n5vcb -- /bin/sh [root@demoapp-8699b9895b-n5vcb /]# curl 10.244.2.187 iKubernetes demoapp v1.0 !! ClientIP: 10.244.1.221, ServerName: demoapp-8699b9895b-bljbb, ServerIP: 10.244.2.187! #进行抓包,node1上 -i指向cni0 tcp 80端口 (pod01容器内部再次请求pod02) [root@node01 ~]#tcpdump -i cni0 -nn tcp port 80 #node01抓包返回 下面就像两个pod直接通讯一样 11:24:32.327042 IP 10.244.1.221.58816 > 10.244.2.187.80 11:24:32.327486 IP 10.244.2.187.80 > 10.244.1.221.58816 #在物理网卡上进行抓包,node1上 -i指向ens33网卡 tcp 80端口 [root@node01 ~]#tcpdump -i ens33 -nn tcp port 80 #pod01容器内部再次请求pod02,没有返回,因报文经过flannel.1封装外层首部(内层被隐藏),外层vxlan通讯(udp协议8472端口) #改抓udp8472端口通讯 (干扰项可能会很多,组件很多通过改接口通讯如arp,这里找与80端口通讯) [root@node01 ~]#tcpdump -i ens33 -nn udp port 8472 11:41:08.854785 IP 10.0.0.152.50784 > 10.0.0.153.8472: OTV, flags [I] (0x08), overlay 0, instance 1 IP 10.244.1.221.37374 > 10.244.2.187.80: Flags [S], seq 1979909046, win 64860, options [mss 1410,sackOK,TS val 235461296 ecr 0,nop,wscale 7], length 0 11:41:08.855206 IP 10.0.0.153.53041 > 10.0.0.152.8472: OTV, flags [I] (0x08), overlay 0, instance 1 IP 10.244.2.187.80 > 10.244.1.221.37374: Flags [S.], seq 75563753, ack 1979909047, win 64308, options [mss 1410,sackOK,TS val 2980163 ecr 235461296,nop,wscale 7], length 0 ... #配置flannel为host-gw通讯 (节点间不能隔路由器) #注意:在生产环境中,不要半道改网络插件模式(一开始部署就选好) #查看下kube-flannel-cfg下net-conf.json下Backend是什么模式 [root@master01 ~]#kubectl get cm kube-flannel-cfg -o yaml -n kube-flannel #修改配置文件 [root@master01 ~]#kubectl get cm kube-flannel-cfg -o yaml -n kube-flannel > kube-flanne-cfg.yaml #去掉底部metadata下的 annotations,creationTimestamp,resourceVersion,uid [root@master01 ~]#vim kube-flanne-cfg.yaml ... net-conf.json: | { "Network": "10.244.0.0/16", "Backend": { "Type": "host-gw" #vxlan改成路由模式,把整个集群节点当路由器,不再使用隧道封装 } } #配置 [root@master01 ~]#kubectl apply -f kube-flanne-cfg.yaml #确认下net-conf.json下Backend是不是host-gw模式 [root@master01 ~]#kubectl get cm kube-flannel-cfg -o yaml -n kube-flannel #注意可能没有立即生效,flannel可能也不会动态加载它,重启整个集群flannel #获取deamonset名称 [root@master01 ~]#kubectl get ds -n kube-flannel NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE kube-flannel-ds 4 4 4 4 4 <none> 22d [root@master01 ~]#kubectl rollout restart ds kube-flannel-ds -n kube-flannel #kube-flannel的pod都会被重启(看时间AGE) [root@master01 ~]#kubectl get pods -n kube-flannel NAME READY STATUS RESTARTS AGE kube-flannel-ds-dmcjh 1/1 Running 0 12s kube-flannel-ds-p8mtr 1/1 Running 0 26s kube-flannel-ds-rzsc8 1/1 Running 0 22s kube-flannel-ds-sq6xr 1/1 Running 0 17s #在节点查看路由表(host-gw通讯) [root@node01 ~]#route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 10.0.0.2 0.0.0.0 UG 0 0 0 ens33 10.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 ens33 10.244.0.0 10.0.0.151 255.255.255.0 UG 0 0 0 ens33 10.244.1.0 0.0.0.0 255.255.255.0 U 0 0 0 cni0 10.244.2.0 10.0.0.153 255.255.255.0 UG 0 0 0 ens33 10.244.3.0 10.0.0.154 255.255.255.0 UG 0 0 0 ens33 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0 #抓包,无论在哪个位置抓都行,不会经过flannel.1,看到都是内层通讯报文 #进入pod01请求pod02 [root@master01 ~]#kubectl exec -it demoapp-8699b9895b-n5vcb -- /bin/sh [root@demoapp-8699b9895b-n5vcb /]# curl 10.244.2.187 #进行抓包,node1上 -i指向cni0或ens33都行 tcp 80端口 (pod01容器内部再次请求pod02) [root@node01 ~]#tcpdump -i ens33 -nn tcp port 80 14:34:44.084978 IP 10.244.1.221.47746 > 10.244.2.187.80: Flags [S], seq 4211588754, win 64860, options [mss 1410,sackOK,TS val 245876527 ecr 0,nop,wscale 7], length 0 14:34:44.085365 IP 10.244.2.187.80 > 10.244.1.221.47746: Flags [S.], seq 359380265, ack 4211588755, win 64308, options [mss 1410,sackOK,TS val 13395394 ecr 245876527,nop,wscale 7], length 0 ... #如既要host-gw,当中隔路由器又要vxlan通讯 [root@master01 ~]#vim kube-flanne-cfg.yaml ... net-conf.json: | { "Network": "10.244.0.0/16", "Backend": { "Type": "vxlan", #改回vxlan "Directrouting": true #追加,表示节点没隔路由器用直接路由,隔了用vxlan隧道 } } [root@master01 ~]#kubectl apply -f kube-flanne-cfg.yaml #重启 [root@master01 ~]#kubectl rollout restart ds kube-flannel-ds -n kube-flannel #kube-flannel的pod都会被重启(确保重启完成) [root@master01 ~]#kubectl get pods -n kube-flannel #如果有节点当中隔路由器,查路由表,下面会有直接路由和flannel.1两种路由模式 [root@node01 ~]#route -n
Calico简介
#支持隧道封装,也支持路由模型(flannel支持的路由模型是查询中央数据库来建立路由表),calico支持基于路由协议通过自动进学习方式生成路由表,
并且calico支持网络策略,flannel不支持 ProjectCalico ◼ 三层的虚拟网络方案 #模本使用隧道模型 ◼ 它把每个节点都当作虚拟路由器(vRouter),把每个节点上的Pod都当作是“节点路由器”后的一个终端设备并为其分配一个IP地址 ◼ 各节点路由器通过BGP(Border Gateway Protocol)协议学习生成路由规则从而实现不同节点上Pod间的互联互通 Calico在每一个计算节点利用Linux内核实现了一个高效的vRouter(虚拟路由器)进行报文转发,而每个vRouter通过BGP协议负责把自身所属的节点上
运行的Pod资源的IP地址信息基于节点的agent程序(Felix)直接由vRouter生成路由规则向整个Calico网络内传播
Calico的网络
Calico的工作机制
◼ Calico把Kubernetes集群环境中的每个节点上的Pod所组成的网络视为一个自治系统,各节点也就是各自治系
统的边界网关,它们彼此间通过BGP协议交换路由信息生成路由规则
◼ 考虑到并非所有网络都能支持BGP,以及BGP路由模型要求所有节点必须要位于同一个二层网络,Calico还
支持基于IPIP和VXLAN的叠加网络模型
◼ 类似于Flannel在VXLAN后端中启用DirectRouting时的网络模型,Calico也支持混合使用路由和叠加网络模型,
BGP路由模型用于二层网络的高性能通信,IPIP或VXLAN用于跨子网的节点间(Cross-Subnet)报文转发
概括来说,Calico主要由Felix、Orchestrator Plugin、etcd、BIRD和BGP Router Reflector等组件组成 ◼ Felix:Calico Agent,运行于每个节点,主要负责维护虚拟接口设备和路由信息 ◼ Orchestrator Plugin:编排系统(例如Kubernetes、OpenStack等)用于将Calico整合进行系统中的插件,例如 Kubernetes的CNI ◼ etcd:持久存储Calico数据的存储管理系统 #可以使用独立的,也可以使用apiserver的(建议使用apiserver) ◼ BIRD:负责分发路由信息的BGP客户端 ◼ BGP Route Reflector:BGP路由反射器,可选组件,用于较大规模的网络场景 #中央路由器维持所有节点的连接
(中央路由器要有2个做冗余(所有节点都连接着两个中央路由器);如果小规模直接mesh网格两两节点互相维持连接即可)
#Felix需要通过Calico存储(可以用apiserver)读取资源定义相关信息(网络策略), 如果节点数量多, 需要经过Typha #如果整个集群节点数量小于等于50个, 可以不加Typha, 让节点与apiserver直接交互; 否则建议加上Typha, 以减轻他们与apiserver连接时的压力,
通过Typha再连接apiserver calico-node ◼ 运行于集群中的每个节点,负责路由编程(Felix)和路由分发(BIRD) ◼ Felix:负责生成路由规则和iptables规则,前者用于完成Pod报文路由,后者用于支撑NetworkPolicy ◼ BIRD:读取并分发由同一节点上的Felix生成的路由规则,支持多种分发拓扑#(mesh和路由反射器模式) calico-kube-controller ◼ 负责监视Kubernetes对象中(包括NetworkPolicy、Pod、Namespace、ServiceAccount和Node等)会影 响到路由的相关变更 ◼ 将变更带来的影响生成Calico配置,并保存于Calico Datastore中 Typha #一个Typha一般能支持200个实例连接 ◼ 各calico-node实例同Calico Datastore通信的中间层,由其负责将Calico Datastore中生成的更改信息分发给各 calico-node,以减轻50个节点以上规模集群中的Calico Datastore的负载 ◼ 具有缓存功能,且能够通过删除重复事件,降低系统负载 Calico Datastore ◼ 通用术语,是指存储的Calico配置、路由、策略及其它信息,它们通常表现为Calico CRD资源对象 ◼ 支持的CRD包括BGPConfiguration、BGPFilter、BGPPeer、BlockAffinity、CalicoNodeStatus、ClusterInformation、FelixConfiguration、
GlobalNetworkPolicy、GlobalNetworkSet、HostEndpoint、IPAMBlock、IPAMConfig、IPAMHandle、IPPool、IPReservation、NetworkPolicy、
NetworkSet和KubeControllersConfiguration等 ◼ 几个常用的CRD功能说明 ◆BGPConfiguration:全局BGP配置,用于设定AS(自治系统)编号、node mesh,以及用于通告ClusterIP的设置 ◆FelixConfiguration:Felix相关的低级别配置,包括iptables、MTU和路由协议等 ◆GlobalNetworkPolicy:全局网络策略,生效于整个集群级别; ◆GlobalNetworkSet:全局网络集,是指可由GlobalNetworkPolicy引用的外部网络IP列表或CIDR列表; ◆IPPool:IP地址池及相关选项,包括要使用的路由协议(IPIP、VXLAN或Native);一个集群支持使用多个Pool; Calico: 192.168.0.0/16子网: 划分子网:26bits 子网数量:2^10 每个节点上的可用地址数量:2^6-2
#Calico数据存储模式:Kubernetes 在几乎所有情况下,都建议使用Kubernetes数据存储而非独立的ectd ◼ 数据通过CRD存储于kube-apiserver ◼ 对calico资源的访问可由Kubernetes RBAC控制 ◼ 但成百上千个Felix实例同时与kube-apiserver交互,会带来负责影响,因而必须要使用typha中间层 #Calico数据存储模式:etcd 将Calico数据存储于独立的etcd中 ◼ 可减轻kube-apiserver的压力,但也会引入不必要的复杂性和安全风险 ◼ 切不可直接使用Kubernetes etcd作为Calico的数据存储
Calico网络插件如何为Pod配置网络接口
◼ 为每个Pod创建一组veth pair,一端注入Pod网络名称空间,另一端驻留在宿主机上
◆Pod内的一端,通常名称格式为“eth0@ifN”,其中的n是驻留在宿主机上的另一端的ip link编号
◆驻留宿主机的一端,名称格式为“caliXXXXXXXXXXX@ifN”,其中的11位X是经由函数计算生成,而N则是注入到Pod网络名称空间中的对端的ip link编号
◼ Pod网络名称空间中,会生成独特的默认路由,将网关指向169.254.1.1
◼ 宿主机为每个cali接口都开启了ARP Proxy功能,从而让宿主机扮演网关设备,并以自己的MAC地址代为应答 对端Pod中发来的所有ARP请求 ◆ARP Proxy的关键配置:/proc/sys/net/ipv4/conf/DEV/proxy_arp 同一节点上的Pod间通信依赖于为每个Pod单独配置的路由规则
不同节点上的Pod间通信,则由Calico的网络模式决定#(隧道网络还是路由网络)
Calico支持多种路由模式 ◼ Native:原生路由,无隧道封装 #类似flannel的host gw ◼ IP-in-IP:IPIP隧道模式,开销较小的隧道协议 ◼ VXLAN:VXLAN隧道模式 Calico: 网络模型: 隧道模型:VXLAN和IPIP 承载模型:Native Routing #路由模式 Native Routing模式 #路由模式 难以使用该模式的常见场景: ◆ 集群节点跨越多个子网或路由器,这些路由器依赖目标IP来确定目标主机 ◆ 对入站数据包强制执行源地址和目标地址核验; ◆ 阻止所有BGP报文的网络环境 IPIP隧道模式 ◼ 适用性好,尤其是跨多子网的网络环境 ◼ 存在额外开销,MTU一般要设置为1480 难以使用该模式的常见场景: ◆ 禁止使用IPIP协议的环境,例如Azure ◆ 阻止所有BGP报文的网络环境
VXLAN隧道模式 #默认使用UDP的4789端口 ◼ 完全不需要依赖于BGP,开销较之IPIP略大(MTU为1450),但功能也更为强大
部署前要关注的几项配置
选用的Pod CIDR及子网掩码长度 ◼ 默认为192.168.0.0/16 ◼ 可修改 env: - name: CALICO_IPV4POOL_CIDR value: "192.168.0.0/16" - name: CALICO_IPV4POOL_BLOCK_SIZE value: "24" #可以修改掩码数量 选用的路由模式 #默认为ipip ◼ Always, Never, Cross-Subnet env: #IPIP和VXLAN选一种,VXLAN看支不支持IPV6,可以同时IPV4和6 - name: CALICO_IPV4POOL_IPIP value: "Always" - name: CALICO_IPV4POOL_VXLAN value: "Never" - name: CALICO_IPV6POOL_VXLAN value: "Never" #Cross-Subnet表示混合模式,可以IPIP和VXLAN选一种,其他为Never,表示跨路由器用该模式,不跨就用路由模式 #如果3个都是Never就必须走BGP,当中不能跨路由器 IPIP和VXLAN的MTU ◼ 默认由configmap/calico-config提供配置 BGP要使用的IP地址 env: - name: IP value: "autodetect" #如果自动探测不对,也可以人为指定
#根据官方文档部署 https://docs.tigera.io/calico/latest/getting-started/kubernetes/self-managed-onprem/onpremises #左侧选self-managed-onprem本地管理, 还支持国外云self-managed public cloud #这里选择manitest清单安装 1.少于等于50个节点 2多余50个节点 3.独立的etcd存储(不推荐) #不建议k8s运行到一半把flannel拆了换成CALICO,这里直接重新搭建k8s #把当前k8s集群重置 [root@master01 ~]#cd learning-k8s-master/ansible-k8s-install/ ]#ansible-playbook reset-kubeadm.yaml #等待4个节点重启完成 #下载配置文件并修改 (测试小于50节点的配置,应用好像有问题) ]#curl https://raw.githubusercontent.com/projectcalico/calico/v3.29.1/manifests/calico-typha.yaml -o calico.yaml [root@master01 ~]#vim calico.yaml #下面网络模式保持默认值不改了 - name: CALICO_IPV4POOL_IPIP value: "Always" # Enable or Disable VXLAN on the default IP pool. - name: CALICO_IPV4POOL_VXLAN value: "Never" # Enable or Disable VXLAN on the default IPv6 IP pool. - name: CALICO_IPV6POOL_VXLAN value: "Never" ... # - name: CALICO_IPV4POOL_CIDR # value: "192.168.0.0/16" #默认值可以改 - name: CALICO_IPV4POOL_BLOCK_SIZE #追加,设定子网掩码长度 value: "24" #部署k8s集群 #创建控制平面的第一个节点: (pod子网地址要改为192.168.0.0/16,要和上面匹配) kubeadm init --image-repository=registry.aliyuncs.com/google_containers --control-plane-endpoint="kubeapi.magedu.com" --kubernetes-version=v1.31.2 --pod-network-cidr=192.168.0.0/16 --service-cidr=10.96.0.0/12 --token-ttl=0 --upload-certs #创建隐藏目录 [root@master01 ~]#mkdir .kube #复制管理员配置文件 [root@master01 ~]#cp /etc/kubernetes/admin.conf .kube/config #测试能不能用 [root@master01 ~]#kubectl get nodes NAME STATUS ROLES AGE VERSION master01 NotReady control-plane 2m v1.31.2 #部署网络插件 [root@master01 ~]#kubectl apply -f ./calico.yaml #查看pod(calico-typha也没什么用,50节点内也不会发挥什么作用) 这里下镜像不影响后面加入节点,可同时进行 #calico-kube-controllers不能部署在主节点,等工作节点加进来才会正常 [root@master01 ~]#kubectl get pods -n kube-system NAME READY STATUS RESTARTS AGE calico-kube-controllers-5d7d9cdfd8-xxf9x 0/1 Pending 0 99s calico-node-w2ddc 0/1 Init:0/3 0 99s calico-typha-676b744474-wjqrs 0/1 ContainerCreating 0 99s coredns-855c4dd65d-hs82g 0/1 Pending 0 11m coredns-855c4dd65d-kzkg6 0/1 Pending 0 11m etcd-master01 1/1 Running 31 11m kube-apiserver-master01 1/1 Running 0 11m kube-controller-manager-master01 1/1 Running 0 11m kube-proxy-jxkpv 1/1 Running 0 11m kube-scheduler-master01 1/1 Running 32 11m #添加节点(3个节点都执行) [root@node01 ~]#kubeadm join kubeapi.magedu.com:6443 --token b3ria0.sy500wxhqo5svmie \ --discovery-token-ca-cert-hash sha256:ecc15e5dd7eefcf140f1f4e9ff8afd52e2e29f8a14dcdf17d0f8c47504616fe4 #主节点查看节点状态 [root@master01 ~]#kubectl get nodes NAME STATUS ROLES AGE VERSION master01 Ready control-plane 21m v1.31.2 node01 Ready <none> 5m35s v1.31.2 node02 Ready <none> 3m1s v1.31.2 node03 Ready <none> 2m48s v1.31.2 #查看路由 [root@master01 ~]#route -n Kernel IP routing table Destination Gateway Genmask Flags Iface 0.0.0.0 10.0.0.2 0.0.0.0 UG ens33 10.0.0.0 0.0.0.0 255.255.255.0 U ens33 172.17.0.0 0.0.0.0 255.255.0.0 U docker0 192.168.99.0 10.0.0.154 255.255.255.0 UG tunl0 #通过隧道(ipip隧道) 192.168.170.0 0.0.0.0 255.255.255.0 U * 192.168.170.1 0.0.0.0 255.255.255.255 UH caliabf27cedc4d#同一节点pod通讯依赖这个 192.168.170.2 0.0.0.0 255.255.255.255 UH caliac09a82faf1 192.168.170.3 0.0.0.0 255.255.255.255 UH calic2e2386e4dc 192.168.220.0 10.0.0.152 255.255.255.0 UG tunl0 192.168.231.0 10.0.0.153 255.255.255.0 UG tunl0 #UH:目标地址为主机级的 UG:网关 #下面安装calicoctl(go语言研发,静态编译),来查看calico的自定义资源 https://docs.tigera.io/calico/latest/operations/calicoctl/install 1.部署2进制文件 #这里选择这个,方便 2.安装为kubectl的插件,通过kubectl命令直接ongoing 3.作为容器,进入容器使用 #下载二进制文件 [root@master01 ~]#cd /tmp [root@master01 tmp]#curl -L https://github.com/projectcalico/calico/releases/download/v3.29.1/calicoctl-linux-amd64 -o calicoctl [root@master01 tmp]#chmod +x calicoctl [root@master01 tmp]#mv calicoctl /usr/local/bin/ #calicoctl可以直接使用了 #查看节点状态信息(以该节点为中心,查看与其他节点的连接) [root@master01 ~]#calicoctl node status Calico process is running. IPv4 BGP status +--------------+-------------------+-------+----------+-------------+ | PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO | +--------------+-------------------+-------+----------+-------------+ | 10.0.0.153 | node-to-node mesh | up | 10:26:33 | Established | | 10.0.0.154 | node-to-node mesh | up | 10:26:58 | Established | | 10.0.0.152 | node-to-node mesh | up | 10:27:22 | Established | +--------------+-------------------+-------+----------+-------------+ #mesh表示当前节点与其他节点一对一连接(非路由反射器) #获取节点 [root@master01 ~]#calicoctl get node NAME master01 node01 node02 node03 #显示更多信息 [root@master01 ~]#calicoctl get node -o wide NAME ASN IPV4 IPV6 master01 (64512) 10.0.0.151/24 node01 (64512) 10.0.0.152/24 node02 (64512) 10.0.0.153/24 node03 (64512) 10.0.0.154/24 #ASN:自治系统号码 #显示节点1的具体信息 [root@master01 ~]#calicoctl get node node01 -o yaml ... spec: addresses: - address: 10.0.0.152/24 type: CalicoNodeIP - address: 10.0.0.152 type: InternalIP bgp: ipv4Address: 10.0.0.152/24 ipv4IPIPTunnelAddr: 192.168.220.0 #节点1真实地址 orchRefs: - nodeName: node01 orchestrator: k8s status: podCIDRs: - 192.168.1.0/24 #猜测地址,calico不会听k8s分配地址,而是自己随机给地址 #显示pod子网 [root@master01 ~]#calicoctl get ippool NAME CIDR SELECTOR default-ipv4-ippool 192.168.0.0/16 all() #查看子网分配具体配置 (现在也可以定制修改) [root@master01 ~]#calicoctl get ippool default-ipv4-ippool -o yaml apiVersion: projectcalico.org/v3 kind: IPPool metadata: creationTimestamp: "2024-12-09T10:24:42Z" name: default-ipv4-ippool resourceVersion: "2038" uid: 10b167e8-e474-4eb0-831a-5d1df388bb9d spec: allowedUses: - Workload - Tunnel blockSize: 24 #掩码长度 cidr: 192.168.0.0/16 ipipMode: Always #使用子网模式 natOutgoing: true nodeSelector: all() vxlanMode: Never #创建pod [root@master01 ~]#kubectl create deployment demoapp --image=ikubernetes/demoapp:v1.0 --replicas=3 #查看,可以看到各自获得的子网是什么 [root@master01 ~]#kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE demoapp-8699b9895b-c2rh7 1/1 Running 0 44s 192.168.231.1 node02 <none> demoapp-8699b9895b-fzlv5 1/1 Running 0 44s 192.168.220.1 node01 <none> demoapp-8699b9895b-kz8xm 1/1 Running 0 44s 192.168.99.1 node03 <none> #进入pod01容器内部 [root@master01 ~]#kubectl exec -it demoapp-8699b9895b-fzlv5 -- /bin/sh #连接node3 ]# curl 192.168.99.1 iKubernetes demoapp v1.0 !! ClientIP: 192.168.220.1, ServerName: demoapp-8699b9895b-kz8xm, ServerIP: 192.168.99.1! #node01 如果抓包,进入隧道前看到是内存ip,进入隧道后看到是外层ip [root@node01 ~]#ip link show 1: lo: ... 2: ens33: ... link/ether 00:0c:29:a8:ab:2e brd ff:ff:ff:ff:ff:ff altname enp2s1 3: docker0: ... 4: tunl0@NONE: ... link/ipip 0.0.0.0 brd 0.0.0.0 7: calid7f10d9ed36@if3: ... #报文离开pod先达到对端接口calid7f10d9ed36@if3, 然后到达tunl0@NONE(进行封装), 然后到达物理接口ens33 #在对端抓包得到内层ip相关信息 [root@node01 ~]#tcpdump -i calid7f10d9ed36 -nn tcp port 80 13:54:16.501756 IP 192.168.220.1.54720 > 192.168.99.1.80: Flags [S] ... 13:54:16.502410 IP 192.168.99.1.80 > 192.168.220.1.54720: Flags [S.]... ... #在物理接口上抓包(外层封装了ip报文,这样抓取不到) [root@node01 ~]#tcpdump -i ens33 -nn tcp port 80 #在物理接口上直接抓ip报文,抓取与10.0.0.153的通讯 [root@node01 ~]#tcpdump -i ens33 -nn host 10.0.0.153 14:13:42.095035 IP 10.0.0.152 > 10.0.0.154: IP 192.168.220.1.57798 > 192.168.99.1.80: Flags [S], seq 690814493, win 64800, options [mss 1440,sackOK,TS val 1563621029 ecr 0,nop,wscale 7], length 0 14:13:42.095573 IP 10.0.0.154 > 10.0.0.152: IP 192.168.99.1.80 > 192.168.220.1.57798: Flags [S.], seq 565224245, ack 690814494, win 64260, options [mss 1440,sackOK,TS val 2051671588 ecr 1563621029,nop,wscale 7], length 0 #网络模式从ipip改为VXLAN [root@master01 ~]#kubectl get ippool NAME AGE default-ipv4-ippool 4h [root@master01 ~]#kubectl get ippool default-ipv4-ippool -o yaml > default-ipv4-ippool.yaml [root@master01 ~]#vim default-ipv4-ippool.yaml #删掉metadata下的annotations,creationTimestamp,generation,resourceVersion,uid(只保留最简单的信息) spec: ... ipipMode: Never #改为Never vxlanMode: Always #改为Always [root@master01 ~]#kubectl apply -f default-ipv4-ippool.yaml #再次查看 [root@master01 ~]#kubectl get ippool default-ipv4-ippool -o yaml #在节点上查看路由(tunl0变成了vxlan.calico) [root@node02 ~]#route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 10.0.0.2 0.0.0.0 UG 0 0 0 ens33 10.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 ens33 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0 192.168.99.0 192.168.99.2 255.255.255.0 UG 0 0 0 vxlan.calico 192.168.170.0 192.168.170.4 255.255.255.0 UG 0 0 0 vxlan.calico 192.168.220.0 192.168.220.2 255.255.255.0 UG 0 0 0 vxlan.calico 192.168.231.0 0.0.0.0 255.255.255.0 U 0 0 0 * 192.168.231.1 0.0.0.0 255.255.255.255 UH 0 0 0 calibf51ed21cf8 #网络模式 使用纯BGP通讯(要求节点支持BGP通讯,所有节点没有跨路由器,没有跨子网) [root@master01 ~]#vim default-ipv4-ippool.yaml spec: ... ipipMode: Never #改为Never vxlanMode: Never #改为Never #最为妥当的做法是设定为Cross-Subnet混合模式(自动判断节点跨节点用指定隧道协议,没跨用BGP) [root@master01 ~]#vim default-ipv4-ippool.yaml spec: ... ipipMode: Never #用了vxlan模式,ipip得是Never vxlanMode: CrossSubnet #使用vxlan的跨子网模式,当中不能加- [root@master01 ~]#kubectl apply -f default-ipv4-ippool.yaml #在节点上查看路由(vxlan.calico变成了ens33) [root@node02 ~]#route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 0.0.0.0 10.0.0.2 0.0.0.0 UG 0 0 0 ens33 10.0.0.0 0.0.0.0 255.255.255.0 U 0 0 0 ens33 172.17.0.0 0.0.0.0 255.255.0.0 U 0 0 0 docker0 192.168.99.0 192.168.99.2 255.255.255.0 UG 0 0 0 ens33 192.168.170.0 192.168.170.4 255.255.255.0 UG 0 0 0 ens33 192.168.220.0 192.168.220.2 255.255.255.0 UG 0 0 0 ens33 192.168.231.0 0.0.0.0 255.255.255.0 U 0 0 0 * 192.168.231.1 0.0.0.0 255.255.255.255 UH 0 0 0 calibf51ed21cf8 #在calid接口和物理接口抓包,都在 tcp 80端口 抓到,没有隧道封装了
Calico在各节点间基于BGP传播路由信息 ◼ BGP是路由器间交换路由信息的标准路由协议 ◆iBGP:Interior Border Gateway Protocol,负责在同一AS内的BGP路由器间传播路由,它通过递归方式进行路径选择 ◆eBGP: Exterior Border Gateway Protocol,用于在不同AS间传播BGP路由,它基于hop-by-hop机制进行路径选择 ◼ 每个路由器都存在一到多个BGP Peer(对等端) ◼ Calico Node能够基于BGP协议将物理路由器作为BGP Peer BGP的常用拓扑 ◼ Full-mesh ◆启用BGP时,Calico默认在所有节点间建立一个AS(64512),并基于iBGP为它们创建full-mesh连接 ◆该模式较适用于集群规模较小(100个节点以内)的场景 ◼ Route Reflectors #路由反射器 ◆在大规模的iBGP场景中, BGP route reflectors能显著降低每个节点需要维护的BGP Peer的数量 ◆可选择几个节点作为BGP RR,并在这些RR之间建立full mesh拓扑 ◆其它节点只需要同这些RR之间建立Peer连接即可 ◼ ToR( Top of Rack ) #栈顶交换机(需要专业网络工程师参与) ◆节点直接同机柜栈顶的L3交换机建立Peer连接,并禁用默认的full-mesh模式 ◆ Calico也能够完全支持eBGP机制,从而允许用户灵活构建出需要的网络拓扑
在100个节点规模以上的Calico集群环境中,为提升iBGP的效率,通常应该建立Router Reflector 配置步骤 ◼ 禁用默认的full-mesh拓扑 ◼ 在选定的RR节点上添加专用的节点标签 ◼ 配置集群节点同RR节点建立BGP会话 #创建BGPConfiguration对象,禁用默认的full-mesh拓扑 apiVersion: crd.projectcalico.org/v1 kind: BGPConfiguration metadata: name: default #必须叫default,表示改默认配置 spec: logSeverityScreen: Info #日志级别 # 是否启用full-mesh模式,默认为true nodeToNodeMeshEnabled: false nodeMeshMaxRestartTime: 120s #默认重启时间,已禁用,不用关注 # 使用的自治系统号,默认为64512 asNumber: 65009 # BGP要对外通告的Service CIDR(pod间通信实际上先访问pod中的service) serviceClusterIPs: - cidr: 10.96.0.0/12 #创建BGPPeer对象,配置所有节点同选定的RR节点建立BGP Peer apiVersion: crd.projectcalico.org/v1 kind: BGPPeer metadata: name: bpgpeer-rr spec: # 节点标签选择器,定义当前配置要生效到的目标节点 nodeSelector: all() #适用于所有节点 # 该节点要请求与之建立BGP Peer的节点标签选择器,用于过滤和选定远端节点 peerSelector: route-reflector == 'true' #只与有这个标签的建立对等连接 #实际操作示例 #可以把节点上的calicoctl复制到其他节点做测试 [root@master01 ~]#scp /usr/local/bin/calicoctl 10.0.0.152:/usr/local/bin/calicoctl [root@master01 ~]#scp /usr/local/bin/calicoctl 10.0.0.153:/usr/local/bin/calicoctl #node1上查看以自己为中心,与其他节点的连接 [root@node01 ~]#calicoctl node status Calico process is running. IPv4 BGP status +--------------+-------------------+-------+----------+-------------+ | PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO | +--------------+-------------------+-------+----------+-------------+ | 10.0.0.151 | node-to-node mesh | up | 02:09:06 | Established | | 10.0.0.153 | node-to-node mesh | up | 02:09:07 | Established | | 10.0.0.154 | node-to-node mesh | up | 02:09:07 | Established | +--------------+-------------------+-------+----------+-------------+ #集群中的任意Calico Node都可以配置为Route Reflector。不过要作为Route Reflector必须要有一个配置好的Cluster ID,
这个ID通常是一个未被使用的IPV4地址,例如224.0.99.1。 #这里选定master01节点作为集群中的第一个RR (node后跟主节点名称) 加入注解信息 [root@master01 ~]#kubectl annotate node master01 projectcalico.org/RouteReflectorClusterID=224.0.99.1 node/master01 annotated #可以查看到加入的注释 [root@master01 ~]#kubectl get nodes master01 -o yaml #给主节点打上路由器反射器的标签 [root@master01 ~]#kubectl label node master01 route-reflector='true' #创建BGPPeer资源对象 (所有主机只能与route-reflector标签建立连接) [root@master01 ProjectCalico]#vim bgppeer-with-rr.yaml # 选定集群中的RR节点 apiVersion: crd.projectcalico.org/v1 kind: BGPPeer metadata: name: peer-with-route-reflector spec: # 节点标签选择器,定义当前配置要生效到的目标节点 nodeSelector: all() # 该节点要请求与之建立BGP Peer的节点标签选择器,用于过滤和选定远端节点 peerSelector: route-reflector == 'true' [root@master01 ProjectCalico]#kubectl apply -f bgppeer-with-rr.yaml #禁用Full-mesh 编辑应用BGPConfiguration [root@master01 ~]#cd learning-k8s-master/ProjectCalico/ [root@master01 ProjectCalico]#vim bgpconfiguration-default.yaml apiVersion: crd.projectcalico.org/v1 kind: BGPConfiguration metadata: name: default spec: logSeverityScreen: Info # 是否启用full-mesh模式,默认为true nodeToNodeMeshEnabled: false nodeMeshMaxRestartTime: 120s # 使用的自治系统号,默认为64512 asNumber: 65009 serviceClusterIPs: - cidr: 10.96.0.0/12 #listenPort: 179 #bindMode: NodeIP [root@master01 ProjectCalico]#kubectl apply -f bgpconfiguration-default.yaml #查看连接状态,mesh已经被关闭,改为特殊连接方式 [root@master01 ProjectCalico]#calicoctl node status #主节点看到 IPv4 BGP status +--------------+---------------+-------+----------+-------------+ | PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO | +--------------+---------------+-------+----------+-------------+ | 10.0.0.152 | node specific | up | 07:39:57 | Established | | 10.0.0.153 | node specific | up | 07:39:57 | Established | | 10.0.0.154 | node specific | up | 07:39:57 | Established | +--------------+---------------+-------+----------+-------------+ #配置冗余的RR #再找一个节点,给他打上注解和标签 [root@master01 ~]#kubectl annotate node node01 projectcalico.org/RouteReflectorClusterID=224.0.99.1 node/node01 annotated [root@master01 ~]#kubectl label node node01 route-reflector='true' #node01上查看连接状态 [root@node01 ~]#calicoctl node status +--------------+---------------+-------+----------+-------------+ | PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO | +--------------+---------------+-------+----------+-------------+ | 10.0.0.151 | node specific | up | 07:46:59 | Established | | 10.0.0.153 | node specific | up | 07:47:11 | Established | | 10.0.0.154 | node specific | up | 07:47:11 | Established | +--------------+---------------+-------+----------+-------------+
什么是Network Policy?
◼ 标准的API资源类型
◼ 由网络插件负责转换为节点上的iptables filter规则,以定义Pod间的通信许可
◼ 主要针对TCP、UDP和SCTP协议,实现在IP地址或Port层面进行流量控制
Network Policy的功能 ◼ 针对一组Pod,定义其同对端实体通信时,在入向(Ingress)或/和 出向(Egress)流量上的控制规则 ◼ 描述对端实体的方法有如下几种 ◆一组Pod对象,通常基于标签选择器定义筛选条件 ◆单个或一组名称空间 ◆IP地址块(但Pod同其所在的节点间的通信不受限制) ◼ Network Policy的具体实现依赖于Network Plugin Network Policy的生效机制 #只要许可机制,没规则就是允许,一旦一个方向有规则,那么这个方向只有允许的可通过 ◼ 默认情况下,一组Pod上的出向和入向流量均被允许 ◼ 同一方向上,适用于一组Pod的多个规则的生效遵循加法机制 #满足任何一个规则就能通过 ◆一组Pod上,多个Ingress策略相加所生成的集合(并集)是为最终生效的结果 ◆一组Pod上,多个Egress策略相同所生成的集合是为最终生效的结果策略 ◼ 若同时定义了Ingress和Egress规则,针对一个对端,双向策略都为“许可”,通信才能真正实现 网络策略: 对某组Pod来说: 若不存在任何netpol的定义,则默认策略为允许; 存在任何一个netpol的定义后,其默认策略将转为拒绝; 这意味着,仅策略允许的对端,才能同该组Pod建立通信;
NetworkPolicy资源规范 #NetworkPolicy资源规范 apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: … namespace: … spec: egress: # 出向流量控制规则,默认为开放 to: # 出向流量控制规则,默认为开放;流量目标的表示方式与ingress.from相同; ports: # 出向流量的目标端口;流量目标端口的表示方式与ingress.ports相同 ingress: # 入向流量控制规则,默认为开放 from: # 入向流量源,多个列表项之间为“或”逻辑;未给定任何值或未定义该字段,将匹配所有 # 流量源;定义了该字段且至少存在一个item,则表示仅允许指定的流量源; ipBlock: # 源IP地址段,通常是网络地址;不支持同namespaceSelector或podSelector同时使用; cidr: # CIDR格式的网络地址 except: # 要排除的地址列表,可以是CIDR格式的子网地址; namespaceSelector: # namespace标签选择器,用于表示源自匹配到的namespace内的所有流量 podSelector: # pod标签选择器,用于表示源自匹配到的Pod上的所有流量;可以同nameSelector # 同时使用,用于匹配选定namespace中选定的pod; ports: # 入向流量的目标端口,即流量源要访问的目标端口,生效机制同from; port: # 端口,同endPort字段同时使用时,表示端口范围的起始端口号 endPoint: # 端口号,同port字段同时使用时,表示端口范围的结束端口号 protocol: # 协议,仅支持TCP、UDP和SCTP,默认为TCP; podSelector: # Pod标签选择器,用于选定流量规则适用的对象,必选字段(上面名称空间下控制哪些pod来执行规则) policyTypes: # 规则类型,支持Ingress、Egress,默认在两个方向上同时生效(这里要指明哪个生效)
#NetworkPolicy资源示例 apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-selected-ingresses namespace: default spec: podSelector: {} #default名称空间下的所有pod ingress: - from: - namespaceSelector: matchExpressions: - key: kubernetes.io/metadata.name #如果有该键,指名称空间的名字 operator: In #名称空间的名字在下面列表中 values: ["default", "kube-system", "monitor"] - ipBlock: #如果是这个子网内的客户端也行(和上面名称空间判断是或的关系) cidr: 192.168.10.0/24 ports: [] #所有端口 - from: - namespaceSelector: matchLabels: kubernetes.io/metadata.name: demo #得是demo名称空间 podSelector: matchExpressions: #和上面的demo是与的关系,必须在demo名称空间的基础上满足下面条件 - key: app #标签为app,并且值为"demoapp"或"nginx" operator: In values: ["demoapp", "nginx"] ports: - port: 80 #只允许访问80端口 protocol: TCP policyTypes: - Ingress #Ingress生效
实际操作示例
#把之前的资源删掉,防止干扰 [root@master01 network-policy-examples]#kubectl delete deployment demoapp #准备环境 [root@master01 network-policy-examples]#kubectl apply -f network-policy-examples/ namespace/demo created #创建demo名称空间 namespace/dev created #创建dev名称空间 deployment.apps/demoapp created #demo名称空间 deployment.apps/demoapp created #default名称空间,每个pod有2个容器1.0和1.1,一个监听80,一个监听8080 deployment.apps/sleep created #demo名称空间 deployment.apps/demoapp created #dev名称空间 #每个pod有2个容器1.0和1.1,一个监听80,一个监听8080 [root@master01 network-policy-examples]#kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE demoapp-757b5b67d6-2m55p 2/2 Running 0 5m17s 192.168.220.5 node01 demoapp-757b5b67d6-hpbj7 2/2 Running 0 5m17s 192.168.99.5 node03 demoapp-757b5b67d6-mm9cg 2/2 Running 0 5m17s 192.168.231.5 node02 [root@master01 network-policy-examples]#curl 192.168.220.5 iKubernetes demoapp v1.0 !! ClientIP: 10.0.0.151, ServerName: demoapp-757b5b67d6-2m55p, ServerIP: 192.168.220.5! [root@master01 network-policy-examples]#curl 192.168.220.5:8080 iKubernetes demoapp v1.1 !! ClientIP: 10.0.0.151, ServerName: demoapp-757b5b67d6-2m55p, ServerIP: 192.168.220.5! #查看 [root@master01 network-policy-examples]#kubectl get pods -n demo NAME READY STATUS RESTARTS AGE demoapp-8699b9895b-b8ffn 1/1 Running 0 12m sleep-79f6f95c6-7fsnw 1/1 Running 0 8m2s [root@master01 network-policy-examples]#kubectl get pods -n dev NAME READY STATUS RESTARTS AGE demoapp-8699b9895b-b8gc4 1/1 Running 0 12m #规则 [root@master01 network-policy-examples]#vim allow-selected-ingress-traffic.yaml --- apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-selected-ingresses namespace: default spec: podSelector: {} ingress: - from: - namespaceSelector: matchExpressions: #只允许"default","kube-system","monitor"才能访问 - key: kubernetes.io/metadata.name operator: In values: ["default", "kube-system", "monitor"] - ipBlock: cidr: 192.168.10.0/24 ports: [] - from: #demo名称空间下,demoapp和nginx才能访问80 - namespaceSelector: matchLabels: kubernetes.io/metadata.name: demo #demo名称空间下 podSelector: matchExpressions: #demo名称空间下,app标签值在["demoapp", "nginx"]来访问 - key: app operator: In values: ["demoapp", "nginx"] ports: - port: 80 protocol: TCP policyTypes: - Ingress [root@master01 network-policy-examples]#kubectl apply -f allow-selected-ingress-traffic.yaml #查看网络策略 [root@master01 network-policy-examples]#kubectl get netpol NAME POD-SELECTOR AGE allow-selected-ingresses <none> 35s #描述网络策略 [root@master01 network-policy-examples]#kubectl describe netpol allow-selected-ingresses #测试 #被拒绝 ,demo空间下只允许app标签值为demoapp来访问 [root@master01 network-policy-examples]#kubectl exec sleep-79f6f95c6-7fsnw -n demo -- curl -s 192.168.220.5:80 [root@master01 network-policy-examples]#kubectl exec sleep-79f6f95c6-7fsnw -n demo -- curl -s 192.168.220.5:8080 #demoapp可以访问80端口,但是不能访问8080端口,没被开放 [root@master01 network-policy-examples]#kubectl exec demoapp-8699b9895b-b8ffn -n demo -- curl -s 192.168.220.5:80 iKubernetes demoapp v1.0 !! ClientIP: 192.168.231.4, ServerName: demoapp-757b5b67d6-2m55p, ServerIP: 192.168.220.5! #dev名称空间下的pod都是不被允许访问default下的pod的 #default名称空间下的pod可以访问default下的pod
全称为“Berkeley Packet Filter”,于1997年自Linux 2.1.75版本的内核引入
基于寄存器的虚拟机,运行于内核空间
◼ 负责运行从用户空间注入的代码而无须对内核进行编程(开发内核模块)
◼ 使用自定义的64位RISC指令集
◼ 能够在Linux内核内部运行即时本地编译的 “BPF 程序”,并能访问内核功能和内存的子集
最初因tcpdump而知名,如今各个类Unix系统几乎都在使用BPF作为网络数据包过滤技术
#到今天为止,eBPF已经完全取代BPF eBPF,即“extended BPF”,由Alexei Straovoitov于2014年实现,最早出现在Linux 3.15版本的内核中 ◼ 针对现代硬件进行了优化,较之BPF执行速度更快 ◼ 2014年6月,eBPF扩展到用户空间,从而演进成了一个通用执行引擎,应用场景不再局限于数据包过滤,而是逐步扩展到内核各子模块,随后被广泛应用在观测、网络和安全等领域 ◼ 目前已然发展成为Linux内核的顶级子系统 “Makes the Linux kernel programmable in a secure and efficient way.” ◼ eBPF之于内核,就类似于JavaScript之于浏览器 #ePBF为用户提供了通过简单程序扩展或定制内核功能的接口。用户可编写eBPF程序与内核中的目标事件相关联,并由这些事件触发执行。
eBPF不止是一个包过滤系统,bpf() syscall允许在内核中直接运行在用户空间编写的代码
XDP ◼ XDP的全称为“eXpress Data Path”,在内核级提供可编程数据包处理机制 ◼ Linux内核中基于eBPF提供的高性能数据路径,支持绕过内核的网络栈直接在网络驱动程序的级别处理数据包 ◼ 无须处理上下文切换、中断等,可大大减少内核开销 #在驱动层级,接近网卡硬件实现报文处理功能,不用再到内核去处理
关于Cilium
Cilium ◼ 基于eBPF和XDP的Kubernetes高性能网络插件 ◼ 支持可观测性和安全机制 ◼ ServiceMesh的数据平面 #Cilium的功能 容器网络解决方案和负载均衡器 ◼ CNI, Kubernetes Services, Multi-Cluster 网络安全 ◼ Network Policy, Identity-based, Encryption 可观测性 ◼ Metrics, Flow Visibility, Service Dependency #Cilium: Kubernetes Networking 节点级Agent 支持隧道和直接路由 eBPF原生数据平面 可替换kube-proxy #可以完全取代kube-proxy #cilium取代kube-proxy的好处 报文无须遍历iptables表 #eBPF用hash表查规则,效率提升很多
Cilium 较之传统容器网络的优势
Component Overview
Cilium ◼ Agent ◼ Client(CLI) ◼ Operator ◼ CNI Plugin Hubble #可观测工具 ◼ Server ◼ Relay ◼ Client (CLI) ◼ Graphical UI eBPF Data Store ◼ Kubernetes CRDs (Default) #推荐 ◼ Key-Value Store
Cilium Agent ◼ 由DaemonSet编排运行集群中的每个节点上 ◼ 从Kubernetes或API接口接受配置:网络、LB、Network Policy等 ◼ 基于SDK调用节点上的eBPF实现相关的功能 Cilium Client #Cilium Agent的客户端工具 ◼ 命令行工具,通过Rest API同当前节点上的Cilium Agent进行交互 ◼ 常用于检查本地Cilium Agent的状态 ◼ 也可用于直接访问eBPF Map ◼ 另外还有一个客户端工具称为Cilium CLI,负责管理整个Cilium集群,而非当前Cilium Agent Cilium Operator ◼ 负责监视和维护整个集群 ◼ 同Cilium的网络功能和网络策略机制不相关 ◆其故障并不会影响报文转发和网络策略的进行 ◆但会影响IPAM和kvstore的数据存取 Cilium Plugin ◼ Cilium自身即为Kubernetes CNI插件,同时还可以完全取代Kube Proxy的功能
3 部署Kubernetes使用Cilium插件
Cilium高级特性依赖到的内核版本
Encapsulation(隧道封装) ◼ VXLAN:默认模式,8472/UDP ◼ Geneve:6081/UDP #用的不多 Native-Routing(原生路由,或直接路由) ◼ 将所有非本地目标地址报文都交由内核的路由子系统,处理逻辑等同于本地进程生成的报文 ◼ 节点上的路由表将决定如何路由Pod报文 ◼ 开启方式:--set tunnel=disabled --set routing-mode=native
Cilium部署三种方案 只作为CNI网络插件,Service的功能仍由kube-proxy实现; 作为CNI网络插件,同时也能够取代kube-proxy部分功能,余下功能部分仍由kube-proxy实现; 完全取代kube-proxy #这里采用这种 部署Kubernetes集群 ◼ 在kubeadm init命令上使用“--skip-phases=addon/kube-proxy”选项,以跳过Kube Proxy的部署 # 部署Kubernetes集群 kubeadm init --control-plane-endpoint kubeapi.magedu.com \ --kubernetes-version=v1.31.2 \ --pod-network-cidr=10.244.0.0/16 \ --service-cidr=10.96.0.0/12 \ --upload-certs \ --image-repository=registry.aliyuncs.com/google_containers \ --skip-phases=addon/kube-proxy # 部署cilium工具: helm cilium CLI #这里采用这种部署方式 #vxlan隧道模式(默认使用隧道vxlan网络模型) # 使用默认的隧道模式,以及VXLAN协议,但自定义了地址分配模式和PodCIDR cilium install \ --set kubeProxyReplacement=strict \ #完全取代kubeProxy --set ipam.mode=kubernetes \ #地址分配通过kubernetes分配方式 --set ipam.operator.clusterPoolIPv4PodCIDRList=10.244.0.0/16 \ #所用子网要与pod子网保持一致 --set ipam.Operator.ClusterPoolIPv4MaskSize=24 #子网分割时子网掩码长度 # 功能同上,但明确指明了路由模式和使用的协议,将vxlan换成geneve即可换用隧道协议 cilium install \ --set kubeProxyReplacement=strict \ --set ipam.mode=kubernetes \ --set routingMode=tunnel \ --set tunnelProtocol=vxlan \ --set ipam.operator.clusterPoolIPv4PodCIDRList=10.244.0.0/16 \ --set ipam.Operator.ClusterPoolIPv4MaskSize=24 #注:不指定版本,会用当前新的稳定版本 #native routing模式 (使用原生路由的方式) cilium install \ --set kubeProxyReplacement=strict \ --set ipam.mode=kubernetes \ --set routingMode=native \ --set ipam.operator.clusterPoolIPv4PodCIDRList=10.244.0.0/16 \ --set ipam.Operator.ClusterPoolIPv4MaskSize=24 \ --set ipv4NativeRoutingCIDR=10.244.0.0/16 \ #要与子网保持一致 --set autoDirectNodeRoutes=true #如果要修改cilium的属性,把cilium插件卸载再重新安装。非生产环境哪怕卸载了,在运行的pod也不会受到影响 #卸载 cilium uninstall #实际操作示例 准备新环境,1主,3从 #安装cilium cli,谷歌搜索cilium cli https://github.com/cilium/cilium-cli #官网查看版本说明,点击下载右侧最新的 Release v0.16.21 #选择程序版本 cilium-linux-amd64.tar.gz ,复制链接地址,下载到本地 [root@master01 ~]#curl -LO https://github.com/cilium/cilium-cli/releases/download/v0.16.21/cilium-linux-amd64.tar.gz [root@master01 ~]#tar xf cilium-linux-amd64.tar.gz [root@master01 ~]#mv cilium /usr/local/bin #直接可以使用cilium命令 #查看cilium可以安装的版本 [root@master01 ~]#cilium install --list-versions # 部署Kubernetes集群 [root@master01 ~]#kubeadm init --control-plane-endpoint kubeapi.magedu.com \ --kubernetes-version=v1.31.2 \ --pod-network-cidr=10.244.0.0/16 \ --service-cidr=10.96.0.0/12 \ --upload-certs \ --image-repository=registry.aliyuncs.com/google_containers \ --skip-phases=addon/kube-proxy [root@master01 ~]#mkdir .kube [root@master01 ~]#cp /etc/kubernetes/admin.conf .kube/config #部署网络插件(使用vxlan协议) [root@master01 ~]#cilium install \ --set kubeProxyReplacement=strict \ #这里报错,改成true就可以了 --set ipam.mode=kubernetes \ --set routingMode=tunnel \ --set tunnelProtocol=vxlan \ --set ipam.operator.clusterPoolIPv4PodCIDRList=10.244.0.0/16 \ --set ipam.Operator.ClusterPoolIPv4MaskSize=24 #查看 [root@master01 ~]#kubectl get pods -n kube-system #了解整个集群的部署状况 [root@master01 ~]#cilium status #加入3个节点 [root@node01 ~]#kubeadm join kubeapi.magedu.com:6443 --token 0h7u7b.pppm0p2w0bb3oxhq \ --discovery-token-ca-cert-hash sha256:d2cb2ca9249aa83a0095f971cccfe7a3e50097373e755d0326984d6df2da5a70 #创建pod测试 [root@master01 ~]#kubectl create deployment demoapp --image=ikubernetes/demoapp:v1.0 --replicas=3
Cilium会在宿主机上创建四个虚拟网络接口 ◼ cilium_host和cilium_net ◆一组veth pair,由Cilium Agent创建 ◆cilium_host会被配置为该主机分配到的PodCIDR中的第一个地址,并作为该子网的默认网关 ◆CNI插件负责创建BPF规则,以便于在内核接通veth pair的两端 ◼ cilium_vxlan ◆负责在vxlan模式下,封装或解封vxlan报文 ◼ lxc_health ◆节点健康状态检测 Pod网络接口 ◼ Cilium会为每个Pod创建一组veth pair ◆一端作为Pod中的网络接口存在,网关指向cilium_host的地址 ◆另一端表现为宿主机上名字形如“lxcXXXX”的虚拟接口 ◼ LXC接口的MAC地址用于响应ARP请求 #进入pod容器内 #下面用的是cilium client工具查询 #可以查看已当前节点对应基于bpf所构建出的vxlan隧道信息 ~# cilium bpf tunnel list TUNNEL VALUE 10.244.1.0 192.168.10.12:0 #要到10.244.1.0所要经过的隧道地址192.168.10.12 10.244.0.0 192.168.10.6:0 10.244.3.0 192.168.10.13:0 #显示当前cilium agent相关信息(查看各种功能是否开启,集群健康信息) ~# cilium status #进行抓包,在物理网卡抓包,容器内往另一个pod发请求,获得返回外层宿主机,内层pod地址,vxlan隧道进行了封装 #创建service,查看cilium是否实现service功能(当前每个节点并没有kube proxy) [root@master01 ~]#kubectl create service clusterip demoapp --tcp=80:80 [root@master01 ~]#kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE demoapp ClusterIP 10.109.248.138 <none> 80/TCP 26s kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 42m #查看是否匹配到 [root@master01 ~]#kubectl get ep NAME ENDPOINTS AGE demoapp 10.244.1.79:80,10.244.2.151:80,10.244.3.166:80 46s kubernetes 10.0.0.151:6443 42m #连接测试service是否可以连接,连接可行,负载均衡功能可以使用 [root@master01 ~]#kubectl exec -it demoapp-8699b9895b-7rc94 -- /bin/sh [root@demoapp-8699b9895b-7rc94 /]#curl 10.109.248.138 #名称也可以解析 [root@demoapp-8699b9895b-7rc94 /]# curl demoapp iKubernetes demoapp v1.0 !! ClientIP: 10.244.1.79, ServerName: demoapp-8699b9895b-f9lc2, ServerIP: 10.244.3.166! #nodeport类型也可以使用,cilium完全取代了kube proxy #下面cilium插件换成native routing模式(路由模式) #先卸载之前的cilium插件 [root@master01 ~]#cilium uninstall #native routing模式部署 [root@master01 ~]#cilium install \ --set kubeProxyReplacement=strict \ #这里报错了,替换成true --set ipam.mode=kubernetes \ --set routingMode=native \ --set ipam.operator.clusterPoolIPv4PodCIDRList=10.244.0.0/16 \ --set ipam.Operator.ClusterPoolIPv4MaskSize=24 \ --set ipv4NativeRoutingCIDR=10.244.0.0/16 \ --set autoDirectNodeRoutes=true #进行抓包,在物理网卡抓包,容器内往另一个pod发请求,获得返回pod地址通讯,没有隧道封装
最后对Cilium网络插件配置做下总结
Cilium在Kubernetes上的运行模式 ◼ 与kube-proxy共存:通常在内核版本较低时采用的模式,替换kube-proxy的部分功能 ◆kubeProxyReplacement=probe:自动探测内核中支持BPF特性,并自行决定如何替换 ◆kubeProxyReplacement=partial:由用户手动指定由BPF替换的特性 ◼ 完全替换kube-proxy ◆kubeProxyReplacement=strict Masquerading(地址伪装) ◼ 将来自集群外部流量转发至集群上的Pod时依赖的功能,传统方式是使用iptables nat机制(称为iptables mode) ◼ eBPF提供了更加高效的masquerading机制(eBPF mode),但仅在4.19及以上版本的Linux内核上支持 ◼ 启用方式:“bpf.masquerade=true” #例: ]#cilium install \ ... --set bpf.masquerade=true #使用eBPF地址转换而不是使用iptables地址转换 Host-Routing(主机路由) ◼ eBPF模式下,网络报文仍会通过常规网络栈中的某些部分,例如netfilter框架,这会增加网络开销 ◼ Cilium自v1.9版本引入的基于eBPF的Host-Routing机制,可让报文完全绕过netfilter和上层网络栈 ◼ 该功能存在Legacy Mode和BPF Mode两种模式,后一种依赖于多个条件 ◆Kernel >= 5.10 ◆eBPF-based kube-proxy replacement ◆eBPF-based masquerading
4 Hubble及Cilium指标
分布式的网络可观测性平台,建立在Cilium和eBPF之上 存储、展示从Cilium获取的相关数据 #hubble ui可以实时显示各个pod的通讯路径
配置Cilium启用Hubble
通过cilium命令启用 ◼ cilium hubble enable --ui 部署cilium时直接启用,可以在“cilium install”命令上使用如下选项 # 启用Hubble cilium install \ … --set hubble.enabled="true" \ --set hubble.listenAddress=":4244" \ #默认4244 --set hubble.relay.enabled="true" \ #中继,每个节点都能收集信息并发送给hubble端(否则监控不到) --set hubble.ui.enabled="true" #打开ui功能 若需要暴露指标给Prometheus,还需要通过如下选项进行 --set prometheus.enabled=true \ #启动prometheus格式指标 --set operator.prometheus.enabled=true \ #把cilium operator指标也启用暴露给prometheus --set hubble.metrics.port=9665 \ --set hubble.metrics.enableOpenMetrics=true \ #启用开放Metrics指标 --set hubble.metrics.enabled="{dns,drop,tcp,flow,port-distribution,icmp,httpV2:exemplars=true;labelsContext=source_ip\,source_namespace\,source_workload\,destination_ip\,destination_namespace\,destination_workload\,traffic_direction}" #启用哪些指标(这里是hubble启用的所有指标了) #例:启动hubble [root@master01 ~]#cilium hubble enable --ui #查看 Hubble Relay为ok就说明启动起来了 [root@master01 ~]#cilium status [root@master01 ~]#kubectl get svc -n kube-system NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ... hubble-ui ClusterIP 10.108.158.206 <none> 80/TCP 3m37s #这里为了让hubble-ui能外部访问,部署metallb(如果失败了,或者懒得装,用nodeport也行) [root@master01 ~]#kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.8/config/manifests/metallb-native.yaml #等待部署完成 [root@master01 ~]#kubectl get pods -n metallb-system [root@master01 ~]#cd learning-k8s-master/MetalLB/ #创建地址池和公告方式 [root@master01 MetalLB]#kubectl apply -f metallb-ipaddresspool.yaml [root@master01 MetalLB]#kubectl apply -f metallb-l2advertisement.yaml #编辑服务 [root@master01 MetalLB]#kubectl edit svc hubble-ui -n kube-system ... type: LoadBalancer #如果上面metallb安装失败了,这里就改成NodePort [root@master01 MetalLB]#kubectl get svc -n kube-system NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ... hubble-ui LoadBalancer 10.108.158.206 10.0.0.51 80:30430/TCP 20m #浏览器输入10.0.0.51访问 #选择名称空间,如果有内部pod通讯,就会显示通讯的拓扑逻辑 #实例: 部署cilium(vxlan隧道模式),同时启用Hubble,暴露指标给Prometheus cilium install \ --set kubeProxyReplacement=strict \ --set ipam.mode=kubernetes \ --set routingMode=tunnel \ --set tunnelProtocol=vxlan \ --set ipam.operator.clusterPoolIPv4PodCIDRList=10.244.0.0/16 \ --set ipam.Operator.ClusterPoolIPv4MaskSize=24 \ --set hubble.enabled="true" \ --set hubble.listenAddress=":4244" \ --set hubble.relay.enabled="true" \ --set hubble.ui.enabled="true" \ --set prometheus.enabled=true \ --set operator.prometheus.enabled=true \ --set hubble.metrics.port=9665 \ --set hubble.metrics.enableOpenMetrics=true \ --set hubble.metrics.enabled="{dns,drop,tcp,flow,port-distribution,icmp,httpV2:exemplars=true;labelsContext=source_ip\,source_namespace\,source_workload\,destination_ip\,destination_namespace\,destination_workload\,traffic_direction}"
Cilium Ingress Controller
Cilium内置支持用于Kubernetes Cluster的Ingress Controller ◼ 支持通过将ingressClassName的值设定为“cilium”来解析Ingress资源 ◼ 也兼容 “kubernetes.io/ingress.class”注解方式 ◼ 支持各类Ingress,包括TLS类型 ◼ 依赖的环境 ◆启用Kube Proxy时,设置了“nodePort.enabled=true” ◆未启用Kube Proxy,由Cilium代替其功能 Cilium Ingress的负载均衡器模式 ◼ dedicated:独占模式,即Cilium会为每个Ingress资源在同一个namespace下创建一个专用的LoadBalancer类型的 Service资源 #特有的高级功能 ◼ shared:共享模式,多个Ingress资源共享使用Cilium默认创建的LoadBalancer类型的Service资源(位于Cilium所 在的名称空间,默认为kube-system)#就是之前ingress-nginx的使用方式 #注:ingress只能使用一个统一的流量入口,当暴露的服务很多的情况下,很容易成为流量瓶颈。Cilium通过独占模式这种高级功能规避了这个问题
部署Cilium时,通过特定的选项即可启用其Ingress Controller功能 # 启用Ingress Controller cilium install \ … --set kubeProxyReplacement=true \ --set ingressController.enabled=true \ # 启用Ingress Controller --set ingressController.loadbalancerMode=dedicated \ # 设定默认的LoadBalancer模式为dedicated --set ingressController.default=true # 可选项,设定Cilium作为默认使用的Ingress Controller 配置Ingress ◼ 在ingress资源上,将“spec.ingressClassName”字段或者“kubernetes.io/ingress.class”注解的值设置为“cilium” ◼ ingress资源的LoadBalancer类型 ◆默认继承Cilium的全局设定 ◆也可通过注解“ingress.cilium.io/loadbalancer-mode”注解单独配置 ◆dedicated模式下,还可通过注解“ingress.cilium.io/service-type”指定专用Service的类型,可用值有“LoadBalancer”和“NodePort” ◼ 其它可用的注解请参考Cilium文档
#Dedicated模式的Ingress示例 apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: prometheus-ingress namespace: prom annotations: #声明,指定模式,service类型 ingress.cilium.io/loadbalancer-mode: 'dedicated' ingress.cilium.io/service-type: 'NodePort' spec: ingressClassName: 'cilium' #指定ingressclass名字 rules: - host: prometheus.magedu.com http: paths: - path: / pathType: Prefix backend: service: name: prometheus port: number: 9090 #Shared模式的Ingress示例 apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: prometheus-ingress namespace: prom annotations: ingress.cilium.io/loadbalancer-mode: 'shared' ingress.cilium.io/service-type: 'Loadbalancer' spec: ingressClassName: 'cilium' rules: - host: prometheus.magedu.com http: paths: - path: / pathType: Prefix backend: service: name: prometheus port: number: 9090 #实际操作示例 #重置k8s #部署k8s [root@master01 ~]#kubeadm init --control-plane-endpoint kubeapi.magedu.com \ --kubernetes-version=v1.31.2 \ --pod-network-cidr=10.244.0.0/16 \ --service-cidr=10.96.0.0/12 \ --upload-certs \ --image-repository=registry.aliyuncs.com/google_containers \ --skip-phases=addon/kube-proxy [root@master01 ~]#mkdir .kube [root@master01 ~]#cp /etc/kubernetes/admin.conf .kube/config #部署网络插件(使用vxlan协议) [root@master01 ~]#cilium install \ --set kubeProxyReplacement=strict \ #这里报错,改成true就可以了 --set ipam.mode=kubernetes \ --set routingMode=tunnel \ --set tunnelProtocol=vxlan \ --set ipam.operator.clusterPoolIPv4PodCIDRList=10.244.0.0/16 \ --set ipam.Operator.ClusterPoolIPv4MaskSize=24 \ --set ingressController.enabled=true \ --set ingressController.loadbalancerMode=shared #默认为dedicated,这里做修改 #查看cilium是否已经running [root@master01 ~]#kubectl get pods -n kube-system #加入3个节点 [root@node01 ~]#kubeadm join kubeapi.magedu.com:6443 --token wllmq5.lh8vu3qraahkcjtn \ --discovery-token-ca-cert-hash sha256:aee94a61a63e747fbfde7c75da91332894d59cca347d88346c8a0b7c723a59d5 #service多了cilium-ingress,这个就是shared模式作为ingress接入客户端流量的 [root@master01 ~]#kubectl get svc -n kube-system NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE cilium-ingress LoadBalancer 10.97.199.168 <pending> 80:31680/TCP,443:30280/TCP 2m38s #部署metallb(如果失败了,或者懒得装,用nodeport也行) [root@master01 ~]#kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.8/config/manifests/metallb-native.yaml #等待部署完成 [root@master01 ~]#kubectl get pods -n metallb-system [root@master01 ~]#cd learning-k8s-master/MetalLB/ #创建地址池和公告方式 [root@master01 MetalLB]#kubectl apply -f metallb-ipaddresspool.yaml [root@master01 MetalLB]#kubectl apply -f metallb-l2advertisement.yaml #获取ip地址池地址范围 [root@master01 MetalLB]#kubectl get ipaddresspool -n metallb-system NAME AUTO ASSIGN AVOID BUGGY IPS ADDRESSES localip-pool true true ["10.0.0.51-10.0.0.80"] [root@master01 MetalLB]#kubectl get svc -n kube-system NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ... cilium-ingress LoadBalancer 10.97.199.168 10.0.0.51 80:31680/TCP,443:30280/TCP 11h #例:启动hubble [root@master01 ~]#cilium hubble enable --ui #查看 Hubble Relay为ok就说明启动起来了 [root@master01 ~]#cilium status [root@master01 ~]#kubectl get svc -n kube-system NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ... hubble-ui ClusterIP 10.102.96.112 <none> 80/TCP 4m21s #通过cilium-ingress把hubble-ui的service开放出去 #获取ingressclass,是cilium,不是ingress-nginx了 [root@master01 ~]#kubectl get ingressclass NAME CONTROLLER PARAMETERS AGE cilium cilium.io/ingress-controller <none> 11h #创建ingress,上面创建时配置的ingress默认是shared模式(共享cilium-ingress的服务,通过用它来对外发布) [root@master01 ~]#kubectl create ingress hubble-ui --rule='hubble.magedu.com/*=hubble-ui:80' --class='cilium' -n kube-system #获取ingress [root@master01 MetalLB]#kubectl get ingress -n kube-system NAME CLASS HOSTS ADDRESS PORTS AGE hubble-ui cilium hubble.magedu.com 10.0.0.51 80 32s #windows的host文件配置 10.0.0.51 hubble.magedu.com #浏览器输入 hubble.magedu.com [root@master01 ~]#kubectl create deployment demoapp --image=ikubernetes/demoapp:v1.0 --replicas=3 [root@master01 ~]#kubectl create service clusterip demoapp --tcp=80:80 #查看匹配 [root@master01 ~]#kubectl get ep NAME ENDPOINTS AGE demoapp 10.244.1.6:80,10.244.2.138:80,10.244.3.243:80 3m12s #创建ingress开放出去 [root@master01 ~]#kubectl create ingress demoapp --rule='demoapp.magedu.com/*=demoapp:80' --class='cilium' [root@master01 ~]#kubectl get ingress NAME CLASS HOSTS ADDRESS PORTS AGE demoapp cilium demoapp.magedu.com 10.0.0.51 80 21s #windows配置host文件,进行访问 demoapp.magedu.com 内部实现了负载均衡 #创建dedicated的ingress #删除上面shared的ingress [root@master01 ~]#kubectl delete ingress demoapp [root@master01 ~]#kubectl create ingress demoapp --rule='demoapp.magedu.com/*=demoapp:80' --class='cilium' -o yaml --dry-run=client > demoapp.yaml [root@master01 ~]#vim demoapp.yaml ... metadata: name: demoapp annotations: ingress.cilium.io/loadbalancer-mode: 'dedicated' #明确使用dedicated模式 ingress.cilium.io/service-type: 'Loadbalancer' #自动为ingress创建service服务,是Loadbalancer类型 ... [root@master01 ~]#kubectl apply -f demoapp.yaml #查看ingress [root@master01 ~]#kubectl get ingress NAME CLASS HOSTS ADDRESS PORTS AGE demoapp cilium demoapp.magedu.com 10.0.0.52 80 19s #自动创建了cilium-ingress-demoapp的服务,专门通过ingress开放demoapp的服务(不能通过ip访问) [root@master01 ~]#kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) cilium-ingress-demoapp LoadBalancer 10.103.248.133 10.0.0.52 80:30826/TCP,443:31955/TCP demoapp ClusterIP 10.97.244.14 <none> 80/TCP #windows配置host文件,进行访问 demoapp.magedu.com 10.0.0.52 demoapp.magedu.com
6 Cilium高级特性
Kubernetes Without kube-proxy
Cilium既可作为CNI插件,亦可同时取代Kube Proxy的功能 ◼ 取代Kube Proxy的功能称为“eBPF kube-proxy replacement” ◼ 可代替Kube Proxy支持ClusterIP、NodePort和LoadBalancer类型的Service,以及在Service使用ExternalIP ◼ 支持在容器支持hostPort进行端口映射 ◼ 需要注意的是,节点上具有多个网络接口时,需要在各节点的kubelet上基于“--node-ip”明确指定集群内部使用的IP地址,否则,Cilium可能会工作异常 “eBPF kube-proxy replacement”模式下,Cilium支持多种高级功能 ◼ Client Source IP Preservation ◼ Maglev Consistent Hashing ◼ Direct Server Return (DSR) ◼ Hybrid DSR and SNAT Mode ◼ Socket LoadBalancer Bypass ◼ LoadBalancer & NodePort XDP Acceleration
保留客户端IP ◼ 客户端请求NodePort类型的Service时,Kubernetes可能会执行SNAT,Cilium能够帮助规避这种SNAT操作以保 留真实客户端IP ◼ 外部流量策略(externalTrafficPolicy)下的外部请求报文的SNAT机制 #Cluster(默认):流量可以转发给其他节点上的Pod, Local流量只能发给本机的Pod ◆Local策略:无须对请求报文进行SNAT,Cilium和Kube Proxy都能支持 ◆Cluster策略:需要对请求报文进行SNAT,但Cilium工作于DSR或Hybrid模式时,支持保持TCP流量报文的原地址 ◼ 内部流量策略(internalTrafficPolicy)仅影响东西向流量(E-W),这类流量的请求报文默认都不需要进行SNAT 两类流量策略下,流量调度至Service后端Pod上的生效逻辑
磁悬浮算法一致性hash逻辑
Cilium支持在南北向流量(N-S)上,对外部请求实施Maglev调度策略 ◼ 默认的负载均衡算法是Random ◼ Maglev是一种特殊的一致性哈希策略,能更好地应对故障 ◼ 该算法基于5个后端节点完成哈希映射,并且无须同其它节点同步状态 ◼ 节点故障后再均衡的影响范围较小 配置Cilium使用Maglve算法 ◼ Maglev算法允许用户自定义查找表的大小 ◼ 表的大小要显著大于Service后端Pod的平均数量 ◼ 实践中,表大小至少应该是后端Pod实例数量最大值的100倍 # 启用Maglev一致性哈希算法 cilium install \ … --set kubeProxyReplacement=true \ --set loadBalancer.algorithm=maglev \ # 启用Maglev负载均衡算法 --set maglev.tableSize=65521 \ #设定Hash查找表大小,表的大小要显著大于Service后端Pod的数量(hash环大小) --set k8sServiceHost=${API_SERVER_IP} \ --set k8sServicePort=${API_SERVER_PORT}
直接服务器返回
DSR在南北向流量上支撑起了“Client Source IP Preservation”机制的实现 ◼ 外部请求报文从A主机转发至B主机时,无须进行SNAT ◼ 而且,响应报文将直接由B主机响应给外部客户端 ◆避免了报文的转发操作,降低了传输延迟 ◆响应报文的源地址和源端口将使用Service的IP和端口 #启用DSR cilium install \ … --set kubeProxyReplacement=true \ --set loadBalancer.mode=dsr #在Native Routing模式下启用DSR及IPv4 Option cilium install \ … --set kubeProxyReplacement=true \ --set routingMode=native \ --set loadBalancer.mode=dsr \ --set loadBalancer.dsrDispatch=opt #某些情况下,某后端很可能会同时隶属于多个Service。DSR模式下,Service IP和Port可附加在IPv4报文的options段,
或者IPv6报文的 Destination Option extension header中,以告诉后端该响应给哪个Service。这避免了添加更多的字段导致影响到原始MTU的设定,
且仅会影响SYN报文。但该功能仅Native Routing可用,Encapsulation模式不支持。
Cilium还支持混合使用DSR和SNAT模式 ◼ DSR功能应用于TCP流量上,而SNAT应用于UDP流量之上 ◼ 于是,loadBalancer.mode参数实际上存在以下三个可用值 ◆snat:默认配置 ◆dsr ◆hybrid #推荐使用,会自动适配,能用dsr用dsr,不能用就用snat # 启用混合模式的负载均衡机制 cilium install \ … --set kubeProxyReplacement=true \ --set routingMode=native \ --set loadBalancer.mode=hybrid \ # 启用混合模式的外部流量负载均衡机制 --set k8sServiceHost=${API_SERVER_IP} \ --set k8sServicePort=${API_SERVER_PORT}
Socket级的负载均衡器 ◼ Socket级的负载均衡器对Cilium的下层数据路径透明 ◼ 尽管应用程序认为自身连接到了Service,但对应的内核中的Socket却是直接连接到了后端地址,而且该功能并不需要NAT的参与 Cilium支持绕过Socket LB ◼ 绕过Socket LB,意味着针对客户端请求,内核中的Socket使用的是ClusterIP及相应的Port ◆有些场景中要实现的目标会依赖于客户原始请求的ClusterIP,例如Istio Sidecar ◆也有些场景中并不支持Socket LB,例如KubeVirt、Kata Containers和gVisor几种容器运行时 ◼ 该功能要通过“socketLB.hostNamespaceOnly”参数进行配置 # 启用Socket LB Bypass cilium install \ … --set kubeProxyReplacement=true \ --set routingMode=native \ --set socketLB.hostNamespaceOnly=true # 启用Socket LB Bypass功能
XDP Acceleration ◼ 外部请求报文发往NodePort、LoadBalancer类型的Service,或者发往具有externalIP的Service,且需要跨节点转 发时,Cilium支持基于XDP对请求进行加速 ◼ 默认为禁用状态,可将参数“loadBalancer.acceleration”的值设定为“native”进行启用 ◼ 该功能能够同Kubernetes系统上的LoadBalancer服务的实现结合使用,例如MetalLB ◼ 该功能自Cilium 1.8版推出 ◼ 仅能用于启用了direct routing的单个设备上 ◼ 支持同dsr、snat或hybrid中的任何一种负载均衡模式配合使用 # 启用XDP加速,负载均衡机制为混合模式 cilium install \ … --set kubeProxyReplacement=true \ --set routingMode=native \ --set loadBalancer.acceleration=native \ --set loadBalancer.mode=hybrid \ --set k8sServiceHost=${API_SERVER_IP} \ --set k8sServicePort=${API_SERVER_PORT}