Flannel 源码走读
文章目录
Flannel 源码走读~~最简单的CNI网络插件
1. 关键Interface
- backend.Backend:每个Backend代表一种后端机制,如UDP、host-gw、vxlan等等
- RegisterNetwork:通过配置文件创建backend.Network对象
- backend.Network:flannel 要管理的网络地址范围,表示整个PODIP的网段
- Run:一个独立的线程,监听来自subnet.Manager.WatchLeases的消息进行处理
- subnet.Manager:当前节点Flannel管理的一小段PodIP
- 有 ETCDV2 和 kube 俩种实现
- 用来监听子网的变更消息
2. 入口函数
- 根据配置决定ExternalInterface,即Flannel使用的出口网卡
- 创建SubnetManager
- 调用 GetBackend创建Backend对象,该对象包含了SubnetManager和Network
- 构造函数中传递了SubnetManager
- 调用backend.RegisterNetwork进行注册 –> 创建了backend.Network
- 根据配置同步IPTable规则
- MASQ规则
- Forward规则
- 生成Subnet文件
- 启动后端网络:Network.Run
3. 子网管理接口
Manager接口当前包括:
|
|
Manager接口包括两种实现:
- etcdv2:基于etcd原生API工作
- Kubernetes:基于Kubernetes的API Server的Informer进行工作
1. 创建KubeSubnetManager
基于Kubernetes的subnet.Manager的工作流程如下:
-
创建接口:subnet.kube.NewSubnetManager
- 创建kubeclient
- 获取当前节点的NodeName
- 读取Flannel的配置文件
- 调用 newKubeSubnetManager 接口,该接口会返回kubeSubnetManager对象
- 运行kubeSubnetManager对象的Run方法,即启动对应nodeController
- 等待nodeController初始化完成,最终返回kubeSubnetManager对象
-
newKubeSubnetManager 接口,初始化kubeSubnetManager对象
1 2 3 4 5 6 7 8 9
type kubeSubnetManager struct { annotations annotations // Flannel 在对应节点上添加的注解 client clientset.Interface // go-client客户端 nodeName string // nodeName nodeStore listers.NodeLister nodeController cache.Controller subnetConf *subnet.Config events chan subnet.Event // 包括:EventAdded、EventRemoved 两种事件 }
该接口核心工作就是调用go-client的NewIndexerInformer,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
indexer, controller := cache.NewIndexerInformer( // 每个flannel所要list和watch的资源,这里只关心Node &cache.ListWatch{ ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { return ksm.client.CoreV1().Nodes().List(ctx, options) }, WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { return ksm.client.CoreV1().Nodes().Watch(ctx, options) }, }, // 指定k8s的资源类型 &v1.Node{}, // 指定re-list的周期,当指定了一个非0值时会周期性的收到OnUpdate消息, resyncPeriod, // 指定event的回调函数 cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { ksm.handleAddLeaseEvent(subnet.EventAdded, obj) }, UpdateFunc: ksm.handleUpdateLeaseEvent, DeleteFunc: func(obj interface{}) { node, isNode := obj.(*v1.Node) // 类型断言,判断Event是否是*v1.Node // We can get DeletedFinalStateUnknown instead of *api.Node here and we need to handle that correctly. if !isNode { deletedState, ok := obj.(cache.DeletedFinalStateUnknown) if !ok { log.Infof("Error received unexpected object: %v", obj) return } node, ok = deletedState.Obj.(*v1.Node) if !ok { log.Infof("Error deletedFinalStateUnknown contained non-Node object: %v", deletedState.Obj) return } obj = node } ksm.handleAddLeaseEvent(subnet.EventRemoved, obj) }, }, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, )
从上面看到处理Node消息的核心接口包括:
- handleUpdateLeaseEvent:节点变更flannel相关Annotations的消息,生成subnet.EventAdded消息
- handleAddLeaseEvent:Node节点添加和删除相关的消息
上述两个接口均通过nodeToLease接口生成subnet.Lease对象,然后将消息再次转发到kubeSubnetManager.events管道。
1 2 3 4 5 6 7 8 9 10 11 12
type LeaseAttrs struct { PublicIP ip.IP4 // 取自flannel.alpha.coreos.com/public-ip BackendType string `json:",omitempty"` // 取自flannel.alpha.coreos.com/backend-type表示后端类型 BackendData json.RawMessage `json:",omitempty"` // 取自flannel.alpha.coreos.com/backend-data是一个JSON,包括:VtepMAC等 } type Lease struct { Subnet ip.IP4Net // 取自节点的 Spec.PodCIDR ??这个是谁分配的?? Attrs LeaseAttrs Expiration time.Time Asof uint64 }
PS:当节点flannel.alpha.coreos.com/kube-subnet-manager=false,flanneld不会管理该节点的Pod网络。
4. VXLan Backend接口
1. 创建Network对象
当Flanneld在Main中调用backend.vxlan.New创建VXLan的Backend对象后,会紧接着调用RegisterNetwork接口来创建对应该节点的子网:
-
根据参数创建VXLAN设备
-
根据实际设备的Mac地址等参数,生成subnet.LeaseAttrs,并去调用SubnetManager.AcquireLease。
- SubnetManager.AcquireLease主要作用是获取kube-controller分配给该节点cidr,即子网网段,而KubeSubnetManager的实现中就是从Node对象的Spec.PodCIDR字段去获取。
- SubnetManager.AcquireLease还会Patch节点状态的信息,并且将节点的NetworkUnavailable状态设置为False,最后返回一个SubnetLease对象
-
结构体network
1 2 3 4 5 6 7 8
nw := &network{ SimpleNetwork: backend.SimpleNetwork{ SubnetLease: lease, ExtIface: extIface, }, subnetMgr: subnetMgr, dev: dev, }
2. 节点消息
创建Network对象之后,Main函数会调用Network对象的Run接口,开始接收KubeSubnetManager监听到的Subnet.Event。
每个flanneld中都会维护整个集群的Lease对象在以下结构体中,并且包括update、remove等接口:
|
|
用于处理消息的接口是handleSubnetEvents,该接口进行以下工作:
- 添加ARP规则
- 添加FDB
- 管理路由表
5. podCIDR的分配
使用KubeSubnetManager作为管理服务时,Flannel通过node节点的podCIDR判断当前节点的子网网段。这个值是kube-controller分配的,并且随意更改。
默认情况下,kube-controller不会自动分配podCIDR,用户需要添加启动参数:–allocate-node-cidrs=true 和 –cluster-cidr=${POD_IP}。
需要注意:Service-CIDR的范围应该小于Pod-Cluster-CIDR,否则可能会Controller分配地址段会失败。
PS:kubeadm 部署时使用–pod-network-cidr参数和podSubnet字段能达成等价的效果。
文章作者 yoaz
上次更新 2021-02-01
许可协议 MIT