/01
cni注册流程
cilium启动时,会在主机:/etc/cni/net.d目录下生成配置,containerd会定期扫描目录下的文件,当发现有配置时,表示cni已经完成初始化。
/02
cni 调用流程分析
pod创建到cni调用流程如下:

在调用cni时,基于CNI规范,将传入如下参数:
type CmdArgs struct { ContainerID string // 容器ID Netns string // 网络命名空间 IfName string // 网卡名称 Args string // cni 扩展参数 Path string // CNIPATH 路径 StdinData []byte // 其它自定义参数:来自 /etc/cni/net.d下的配置文件数据}
样例如下:
&skel.CmdArgs{ContainerID:"26e73994457474ab3520a9b2f9ada2600378ac58280934aaf8d1bd2ec2abd089",Netns:"/var/run/netns/cni-bf264779-8c1d-7d7f-9d6d-7a29c3a3d243",IfName:"eth0",Args:"IgnoreUnknown=1;K8S_POD_NAMESPACE=default;K8S_POD_NAME=nettest-qmfnz;K8S_POD_INFRA_CONTAINER_ID=26e73994457474ab3520a9b2f9ada2600378ac58280934aaf8d1bd2ec2abd089;K8S_POD_UID=45602948-01f7-4062-b786-6d381bc2afd5",Path:"/opt/cni/bin"}
1、整体流程图

上面的流程,忽略了些特定场景才会用到的功能:
下面将针对调用cilium-agent的三个接口:申请IP,创建endpoint,释放IP深入分析。
2、ip申请流程
在PostIpam函数中,可明确看到,ip申请调用的是POST /ipam接口,请求参数为pod名称与namespace。
在cilium-agent中,相关的定义入口如下:
o.handlers["POST"]["/ipam"] = ipam.NewPostIpam(o.context, o.IpamPostIpamHandler)restAPI.IpamPostIpamHandler = NewPostIPAMHandler(d)
收到请求后,将基于family:ip类型,owner:pod信息,从内存中申请ip。
在申请时,needSyncUpstream是设置成true的,表示在申请完ip后,需要将ip信息更新到cilium-node中,用于信息持久化。
ip的分配相对比较简单。在启动时,cilium会加载所有已分配的ip信息到内存中,使用bigint按位标志使用的ip情况,分配时,只需要取未分配的标志就可以。这里不做深入展开。
重点在于如何完成内存的初始化与在分配完成后,如何保证ip分配信息完成上报。
ip内存初始化
由于ipam支持很多种,这里分析两种典型:crd和cluster-pool。crd需要将相关信息保存在cilium-node中,cluster-pool为纯内存操作。
在NewIPAM中定义的ipam初始化流程
1、cluster-pool初始化流程
newHostScopeAllocator比较简单,只是初始化结构体
2、crd初始化流程
newCRDAllocator相比cluster-pool场景,多传入了一个参数:k8sEventReg,这就是重点所在。
相关初始化都在newNodeStore中:

3、restore流程
在allocator初始化后,cilium将加载本机已分配的ip信息:
restoreCiliumHostIPs 加载cilium-host的ip信息
restoreOldEndpoints 加载本机在支持的endpoint信息:扫描/var/run/cilium/state/下的endpoint信息,并加载到内存中
AnnotateNode 更新node annotation信息。默认未开启,可通过AnnotateK8sNode开关控制。开启后会将节点子网,cilium内部ip信息记录在node上
调用RestoreFinished,crd模式下,为关闭store.restoreFinished。从上面2.1.2可知,会触发一次refreshTrigger
到此,crd初始化与restore流程结束。
crd与cluster-pool都需要经过restore流程,但唯一的区别在于crd需要在初始化时,等待node完成加载,且crd多了一个refreshTrigger。
crd时,加载了两遍,因为crd的更新是异步的,极端情况crd不存在,而本地存在。本地是最完整信息。而初始化store与trigger也是为了后续运行作为准备。
refreshTrigger ip信息上报
在初始化trigger时,注册了refreshNodeTrigger回调函数。在收到事件时,就调用该函数
在回调函数失败时,通过调用n.refreshTrigger.TriggerWithReason("retry after error")保证继续再重试。
刷新就是每次调用:n.refreshNode(),将本地内存中的分配信息更新到ciliumNode中。
3、endpoint创建流程
在cni流程最后,会调用endpoointCreate,用于触发endpoint创建:
// Specify that endpoint must be regenerated synchronously. See GH-4409. ep.SyncBuildEndpoint = true// EndpointCreate creates a new endpointfunc (c *Client) EndpointCreate(ep *models.EndpointChangeRequest) error { id := pkgEndpointID.NewCiliumID(ep.ID) params := endpoint.NewPutEndpointIDParams().WithID(id).WithEndpoint(ep).WithTimeout(api.ClientTimeout) _, err := c.Endpoint.PutEndpointID(params) return Hint(err)}
而在server端,注册的处理函数如下:
o.handlers["PUT"]["/endpoint/{id}"] = endpoint.NewPutEndpointID(o.context, o.EndpointPutEndpointIDHandler)restAPI.EndpointPutEndpointIDHandler = NewPutEndpointIDHandler(d)
endpoint创建处理流程如下:

m.requests[podName] = &endpointCreationRequest{ cancel: cancel, endpoint: ep, started: time.Now(),}2. eventQueue为cilium的整个内部消息事件队列,而ep的流程也使用了。后续将单独分析eventQueue的管理流程:epEvent := eventqueue.NewEvent(&EndpointRegenerationEvent{ regenContext: regenContext, ep: e, })/03
cni 卸载流程
卸载流程比较简单,流程图如下:

在cni卸载中,调用了cilium的endpoint删除接口,用于清理endpoint相关资源:
o.handlers["DELETE"]["/endpoint/{id}"] = endpoint.NewDeleteEndpointID(o.context, o.EndpointDeleteEndpointIDHandler)restAPI.EndpointDeleteEndpointIDHandler = NewDeleteEndpointIDHandler(d)
在endpoint清理流程中,主要是两个资源回收:
清理endpointCreations中的资源m.requests[podName]。回收可能残留的endpoint管理器。按cilium注释中说明:Cancel any ongoing endpoint creation,由于cni中加载ebpf为异步,且可能存在之前未完成的cni流程,在这里统一进行清理。
调用d.ipam.ReleaseIP回收ip
本期作者丨沃趣科技产品研发部
版权作品,未经许可禁止转载
往期作品快速浏览:


