沃趣技術(shù)干貨丨cilium cni 流程分析

/01

cni注冊(cè)流程

cilium啟動(dòng)時(shí),會(huì)在主機(jī):/etc/cni/net.d目錄下生成配置,containerd會(huì)定期掃描目錄下的文件,當(dāng)發(fā)現(xiàn)有配置時(shí),表示cni已經(jīng)完成初始化。

/02

cni 調(diào)用流程分析

pod創(chuàng)建到cni調(diào)用流程如下:

wechat-1761807949265.png

在調(diào)用cni時(shí),基于CNI規(guī)范,將傳入如下參數(shù):

type CmdArgs struct { ContainerID string  // 容器ID Netns       string  // 網(wǎng)絡(luò)命名空間 IfName      string  // 網(wǎng)卡名稱 Args        string  // cni 擴(kuò)展參數(shù) Path        string  // CNIPATH 路徑 StdinData   []byte  // 其它自定義參數(shù):來自 /etc/cni/net.d下的配置文件數(shù)據(jù)}

樣例如下:

&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、整體流程圖

wechat-1761807951578.png

上面的流程,忽略了些特定場(chǎng)景才會(huì)用到的功能:

下面將針對(duì)調(diào)用cilium-agent的三個(gè)接口:申請(qǐng)IP,創(chuàng)建endpoint,釋放IP深入分析。

2、ip申請(qǐng)流程

在PostIpam函數(shù)中,可明確看到,ip申請(qǐng)調(diào)用的是POST /ipam接口,請(qǐng)求參數(shù)為pod名稱與namespace。

在cilium-agent中,相關(guān)的定義入口如下:

o.handlers["POST"]["/ipam"] = ipam.NewPostIpam(o.context, o.IpamPostIpamHandler)restAPI.IpamPostIpamHandler = NewPostIPAMHandler(d)

收到請(qǐng)求后,將基于family:ip類型,owner:pod信息,從內(nèi)存中申請(qǐng)ip。

在申請(qǐng)時(shí),needSyncUpstream是設(shè)置成true的,表示在申請(qǐng)完ip后,需要將ip信息更新到cilium-node中,用于信息持久化。

ip的分配相對(duì)比較簡(jiǎn)單。在啟動(dòng)時(shí),cilium會(huì)加載所有已分配的ip信息到內(nèi)存中,使用bigint按位標(biāo)志使用的ip情況,分配時(shí),只需要取未分配的標(biāo)志就可以。這里不做深入展開。

重點(diǎn)在于如何完成內(nèi)存的初始化與在分配完成后,如何保證ip分配信息完成上報(bào)。

ip內(nèi)存初始化

由于ipam支持很多種,這里分析兩種典型:crd和cluster-pool。crd需要將相關(guān)信息保存在cilium-node中,cluster-pool為純內(nèi)存操作。

在NewIPAM中定義的ipam初始化流程

1、cluster-pool初始化流程

newHostScopeAllocator比較簡(jiǎn)單,只是初始化結(jié)構(gòu)體

2、crd初始化流程

newCRDAllocator相比cluster-pool場(chǎng)景,多傳入了一個(gè)參數(shù):k8sEventReg,這就是重點(diǎn)所在。

相關(guān)初始化都在newNodeStore中:

wechat-1761807953913.png

3、restore流程

在allocator初始化后,cilium將加載本機(jī)已分配的ip信息:

restoreCiliumHostIPs 加載cilium-host的ip信息

restoreOldEndpoints 加載本機(jī)在支持的endpoint信息:掃描/var/run/cilium/state/下的endpoint信息,并加載到內(nèi)存中

AnnotateNode 更新node annotation信息。默認(rèn)未開啟,可通過AnnotateK8sNode開關(guān)控制。開啟后會(huì)將節(jié)點(diǎn)子網(wǎng),cilium內(nèi)部ip信息記錄在node上

調(diào)用RestoreFinished,crd模式下,為關(guān)閉store.restoreFinished。從上面2.1.2可知,會(huì)觸發(fā)一次refreshTrigger

到此,crd初始化與restore流程結(jié)束。

crd與cluster-pool都需要經(jīng)過restore流程,但唯一的區(qū)別在于crd需要在初始化時(shí),等待node完成加載,且crd多了一個(gè)refreshTrigger。

crd時(shí),加載了兩遍,因?yàn)閏rd的更新是異步的,極端情況crd不存在,而本地存在。本地是最完整信息。而初始化store與trigger也是為了后續(xù)運(yùn)行作為準(zhǔn)備。

refreshTrigger ip信息上報(bào)

在初始化trigger時(shí),注冊(cè)了refreshNodeTrigger回調(diào)函數(shù)。在收到事件時(shí),就調(diào)用該函數(shù)

在回調(diào)函數(shù)失敗時(shí),通過調(diào)用n.refreshTrigger.TriggerWithReason("retry after error")保證繼續(xù)再重試。

刷新就是每次調(diào)用:n.refreshNode(),將本地內(nèi)存中的分配信息更新到ciliumNode中。

3、endpoint創(chuàng)建流程

在cni流程最后,會(huì)調(diào)用endpoointCreate,用于觸發(fā)endpoint創(chuàng)建:

// 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端,注冊(cè)的處理函數(shù)如下:

o.handlers["PUT"]["/endpoint/{id}"] = endpoint.NewPutEndpointID(o.context, o.EndpointPutEndpointIDHandler)restAPI.EndpointPutEndpointIDHandler = NewPutEndpointIDHandler(d)

endpoint創(chuàng)建處理流程如下:

wechat-1761807956198.png

m.requests[podName] = &endpointCreationRequest{ cancel:   cancel, endpoint: ep, started:  time.Now(),}2. eventQueue為cilium的整個(gè)內(nèi)部消息事件隊(duì)列,而ep的流程也使用了。后續(xù)將單獨(dú)分析eventQueue的管理流程:epEvent := eventqueue.NewEvent(&EndpointRegenerationEvent{        regenContext: regenContext,        ep:           e,    })/03

cni 卸載流程

卸載流程比較簡(jiǎn)單,流程圖如下:

wechat-1761807958461.png

在cni卸載中,調(diào)用了cilium的endpoint刪除接口,用于清理endpoint相關(guān)資源:

o.handlers["DELETE"]["/endpoint/{id}"] = endpoint.NewDeleteEndpointID(o.context, o.EndpointDeleteEndpointIDHandler)restAPI.EndpointDeleteEndpointIDHandler = NewDeleteEndpointIDHandler(d)

在endpoint清理流程中,主要是兩個(gè)資源回收:

清理endpointCreations中的資源m.requests[podName]。回收可能殘留的endpoint管理器。按cilium注釋中說明:Cancel any ongoing endpoint creation,由于cni中加載ebpf為異步,且可能存在之前未完成的cni流程,在這里統(tǒng)一進(jìn)行清理。

調(diào)用d.ipam.ReleaseIP回收ip

本期作者丨沃趣科技產(chǎn)品研發(fā)部

版權(quán)作品,未經(jīng)許可禁止轉(zhuǎn)載

往期作品快速瀏覽:

wechat-1761807960823.png
wechat-1761807963188.png
wechat-1761807965531.png