Kubelet 對(duì)CNI的實(shí)現(xiàn)

不管是 docker 還是 kubernetes,在網(wǎng)絡(luò)方面目前都沒(méi)有一個(gè)完美的、終極的、普適性的解決方案,不同的用戶和企業(yè)因?yàn)楦鞣N原因會(huì)使用不同的網(wǎng)絡(luò)方案。目前存在網(wǎng)絡(luò)方案 flannel、calico、openvswitch、weave、ipvlan等,而且以后一定會(huì)有其他的網(wǎng)絡(luò)方案,這些方案接口和使用方法都不相同,而不同的容器平臺(tái)都需要網(wǎng)絡(luò)功能,它們之間的適配如果沒(méi)有統(tǒng)一的標(biāo)準(zhǔn),會(huì)有很大的工作量和重復(fù)勞動(dòng)。

CNI 就是這樣一個(gè)標(biāo)準(zhǔn),它旨在為容器平臺(tái)提供網(wǎng)絡(luò)的標(biāo)準(zhǔn)化。不同的容器平臺(tái)(比如目前的 kubernetes、mesos 和 rkt)能夠通過(guò)相同的接口調(diào)用不同的網(wǎng)絡(luò)組件。

CNI(Conteinre Network Interface) 是 google 和 CoreOS 主導(dǎo)制定的容器網(wǎng)絡(luò)標(biāo)準(zhǔn),它 本身并不是實(shí)現(xiàn)或者代碼,可以理解成一個(gè)協(xié)議。這個(gè)標(biāo)準(zhǔn)是在?rkt 網(wǎng)絡(luò)提議?的基礎(chǔ)上發(fā)展起來(lái)的,綜合考慮了靈活性、擴(kuò)展性、ip 分配、多網(wǎng)卡等因素。

這個(gè)協(xié)議連接了兩個(gè)組件:容器管理系統(tǒng)和網(wǎng)絡(luò)插件。它們之間通過(guò) JSON 格式的文件進(jìn)行通信,實(shí)現(xiàn)容器的網(wǎng)絡(luò)功能。具體的事情都是插件來(lái)實(shí)現(xiàn)的,包括:創(chuàng)建容器網(wǎng)絡(luò)空間(network namespace)、把網(wǎng)絡(luò)接口(interface)放到對(duì)應(yīng)的網(wǎng)絡(luò)空間、給網(wǎng)絡(luò)接口分配 IP 等等。

關(guān)于網(wǎng)絡(luò),docker 也提出了 CNM 標(biāo)準(zhǔn),它要解決的問(wèn)題和 CNI 是重合的,也就是說(shuō)目前兩者是競(jìng)爭(zhēng)關(guān)系。目前 CNM 只能使用在 docker 中,而 CNI 可以使用在任何容器運(yùn)行時(shí)。CNM 主要用來(lái)實(shí)現(xiàn) docker 自身的網(wǎng)絡(luò)問(wèn)題,也就是?docker network?子命令提供的功能。

官方網(wǎng)絡(luò)插件

所有的標(biāo)準(zhǔn)和協(xié)議都要有具體的實(shí)現(xiàn),才能夠被大家使用。CNI 也不例外,目前官方在 github 上維護(hù)了同名的?CNI?代碼庫(kù),里面已經(jīng)有很多可以直接拿來(lái)使用的 CNI 插件。

官方提供的插件目前分成三類:main、meta 和 ipam。main 是主要的實(shí)現(xiàn)了某種特定網(wǎng)絡(luò)功能的插件;meta 本身并不會(huì)提供具體的網(wǎng)絡(luò)功能,它會(huì)調(diào)用其他插件,或者單純是為了測(cè)試;ipam 是分配 IP 地址的插件。

ipam 并不提供某種網(wǎng)絡(luò)功能,只是為了靈活性把它單獨(dú)抽象出來(lái),這樣不同的網(wǎng)絡(luò)插件可以根據(jù)需求選擇 ipam,或者實(shí)現(xiàn)自己的 ipam。

這些插件的功能說(shuō)明如下:

main

????????loopback:這個(gè)插件很簡(jiǎn)單,負(fù)責(zé)生成?lo?網(wǎng)卡,并配置上?127.0.0.1/8?地址

????????bridge:和 docker 默認(rèn)的網(wǎng)絡(luò)模型很像,把所有的容器連接到虛擬交換機(jī)上

????????macvlan:使用 macvlan 技術(shù),從某個(gè)物理網(wǎng)卡虛擬出多個(gè)虛擬網(wǎng)卡,它們有獨(dú)立的 ip 和 mac 地址

????????ipvlan:和 macvlan 類似,區(qū)別是虛擬網(wǎng)卡有著相同的 mac 地址

????????ptp:通過(guò) veth pair 在容器和主機(jī)之間建立通道

meta

????????flannel:結(jié)合 bridge 插件使用,根據(jù) flannel 分配的網(wǎng)段信息,調(diào)用 bridge 插件,保證多主機(jī)情況下容器

????????tuning:調(diào)整現(xiàn)有接口的sysctl參數(shù)

????????portmap:一個(gè)基于iptables的portmapping插件。將端口從主機(jī)的地址空間映射到容器。

ipam

????????host-local:基于本地文件的 ip 分配和管理,把分配的 IP 地址保存在文件中

????????dhcp:從已經(jīng)運(yùn)行的 DHCP 服務(wù)器中獲取 ip 地址


接口參數(shù)

網(wǎng)絡(luò)插件是獨(dú)立的可執(zhí)行文件,被上層的容器管理平臺(tái)調(diào)用。網(wǎng)絡(luò)插件只有兩件事情要做:把容器加入到網(wǎng)絡(luò)以及把容器從網(wǎng)絡(luò)中刪除。調(diào)用插件的數(shù)據(jù)通過(guò)兩種方式傳遞:環(huán)境變量和標(biāo)準(zhǔn)輸入。一般插件需要三種類型的數(shù)據(jù):容器相關(guān)的信息,比如 ns 的文件、容器 id 等;網(wǎng)絡(luò)配置的信息,包括網(wǎng)段、網(wǎng)關(guān)、DNS 以及插件額外的信息等;還有就是 CNI 本身的信息,比如 CNI 插件的位置、添加網(wǎng)絡(luò)還是刪除網(wǎng)絡(luò)。

我們來(lái)看一下為容器添加網(wǎng)絡(luò)是怎么工作的,刪除網(wǎng)絡(luò)和它過(guò)程一樣。

* 把容器加入到網(wǎng)絡(luò)

調(diào)用插件的時(shí)候,這些參數(shù)會(huì)通過(guò)環(huán)境變量進(jìn)行傳遞:

CNI_COMMAND:要執(zhí)行的操作,可以是?ADD(把容器加入到某個(gè)網(wǎng)絡(luò))、DEL(把容器從某個(gè)網(wǎng)絡(luò)中刪除)

CNI_CONTAINERID:容器的 ID,比如 ipam 會(huì)把容器 ID 和分配的 IP 地址保存下來(lái)??蛇x的參數(shù),但是推薦傳遞過(guò)去。需要保證在管理平臺(tái)上是唯一的,如果容器被刪除后可以循環(huán)使用

CNI_NETNS:容器的 network namespace 文件,訪問(wèn)這個(gè)文件可以在容器的網(wǎng)絡(luò) namespace 中操作

CNI_IFNAME:要配置的 interface 名字,比如?eth0

CNI_ARGS:額外的參數(shù),是由分號(hào);分割的鍵值對(duì),比如 “FOO=BAR;hello=world”

CNI_PATH:CNI 二進(jìn)制查找的路徑列表,多個(gè)路徑用分隔符?:?分隔

網(wǎng)絡(luò)信息主要通過(guò)標(biāo)準(zhǔn)輸入,作為 JSON 字符串傳遞給插件,必須的參數(shù)包括:

cniVersion:CNI 標(biāo)準(zhǔn)的版本號(hào)。因?yàn)?CNI 在演化過(guò)程中,不同的版本有不同的要求

name:網(wǎng)絡(luò)的名字,在集群中應(yīng)該保持唯一

type:網(wǎng)絡(luò)插件的類型,也就是 CNI 可執(zhí)行文件的名稱

args:額外的信息,類型為字典

ipMasq:是否在主機(jī)上為該網(wǎng)絡(luò)配置 IP masquerade

ipam:IP 分配相關(guān)的信息,類型為字典

dns:DNS 相關(guān)的信息,類型為字典

插件接到這些數(shù)據(jù),從輸入和環(huán)境變量解析到需要的信息,根據(jù)這些信息執(zhí)行程序邏輯,然后把結(jié)果返回給調(diào)用者,返回的結(jié)果中一般包括這些參數(shù):

IPs assigned to the interface:網(wǎng)絡(luò)接口被分配的 ip,可以是 IPv4、IPv6 或者都有

DNS 信息:包含 nameservers、domain、search domains 和其他選項(xiàng)的字典

CNI 協(xié)議的內(nèi)容還在不斷更新,請(qǐng)到官方文檔獲取當(dāng)前的信息。


CNI 的特性

CNI 作為一個(gè)協(xié)議/標(biāo)準(zhǔn),它有很強(qiáng)的擴(kuò)展性和靈活性。如果用戶對(duì)某個(gè)插件有額外的需求,可以通過(guò)輸入中的?args?和環(huán)境變量?CNI_ARGS?傳輸,然后在插件中實(shí)現(xiàn)自定義的功能,這大大增加了它的擴(kuò)展性;CNI 插件把 main 和 ipam 分開,用戶可以自由組合它們,而且一個(gè) CNI 插件也可以直接調(diào)用另外一個(gè) CNI 插件,使用起來(lái)非常靈活。

如果要實(shí)現(xiàn)一個(gè)繼承性的 CNI 插件也不復(fù)雜,可以編寫自己的 CNI 插件,根據(jù)傳入的配置調(diào)用 main 中已經(jīng)有的插件,就能讓用戶自由選擇容器的網(wǎng)絡(luò)。

在 kubernetes 中的使用

CNI 目前已經(jīng)在 kubernetes 中開始使用,也是目前官方推薦的網(wǎng)絡(luò)方案,具體的配置方法可以參考kubernetes 官方文檔。

CNI插件必須實(shí)現(xiàn)一個(gè)可執(zhí)行文件,這個(gè)文件可以被容器管理系統(tǒng)(例如rkt或Kubernetes)調(diào)用。

CNI插件負(fù)責(zé)將網(wǎng)絡(luò)接口插入容器網(wǎng)絡(luò)命名空間(例如,veth對(duì)的一端),并在主機(jī)上進(jìn)行任何必要的改變(例如將veth的另一端連接到網(wǎng)橋)。然后將IP分配給接口,并通過(guò)調(diào)用適當(dāng)?shù)腎PAM插件來(lái)設(shè)置與“IP地址管理”部分一致的路由。

kubernetes 使用了 CNI 網(wǎng)絡(luò)插件之后,工作過(guò)程是這樣的:

kubernetes 先創(chuàng)建 pause 容器生成對(duì)應(yīng)的 network namespace

調(diào)用網(wǎng)絡(luò) driver(因?yàn)榕渲玫氖?CNI,所以會(huì)調(diào)用 CNI 相關(guān)代碼)

CNI driver 根據(jù)配置調(diào)用具體的 cni 插件

cni 插件給 pause 容器配置正確的網(wǎng)絡(luò)

pod 中其他的容器都是用 pause 的網(wǎng)絡(luò)

CNI接口

CNI接口定義在:containernetworking/cni/libcni/api.go,CNI接口包括如下幾個(gè)方法:

CNI設(shè)計(jì)的時(shí)候考慮了以下問(wèn)題:

容器運(yùn)行時(shí)必須在調(diào)用任何插件之前為容器創(chuàng)建一個(gè)新的網(wǎng)絡(luò)命名空間。

然后,運(yùn)行時(shí)必須確定這個(gè)容器應(yīng)屬于哪個(gè)網(wǎng)絡(luò),并為每個(gè)網(wǎng)絡(luò)確定哪些插件必須被執(zhí)行。

網(wǎng)絡(luò)配置采用JSON格式,可以很容易地存儲(chǔ)在文件中。網(wǎng)絡(luò)配置包括必填字段,如name和type以及插件(類型)。網(wǎng)絡(luò)配置允許字段在調(diào)用之間改變值。為此,有一個(gè)可選的字段args,必須包含不同的信息。

容器運(yùn)行時(shí)必須按順序?yàn)槊總€(gè)網(wǎng)絡(luò)執(zhí)行相應(yīng)的插件,將容器添加到每個(gè)網(wǎng)絡(luò)中。

在完成容器生命周期后,運(yùn)行時(shí)必須以相反的順序執(zhí)行插件(相對(duì)于執(zhí)行添加容器的順序)以將容器與網(wǎng)絡(luò)斷開連接。

容器運(yùn)行時(shí)不能為同一容器調(diào)用并行操作,但可以為不同的容器調(diào)用并行操作。

容器運(yùn)行時(shí)必須為容器訂閱ADD和DEL操作,這樣ADD后面總是跟著相應(yīng)的DEL。 DEL可能跟著額外的DEL,但是,插件應(yīng)該允許處理多個(gè)DEL(即插件DEL應(yīng)該是冪等的)。

容器必須由ContainerID唯一標(biāo)識(shí)。存儲(chǔ)狀態(tài)的插件應(yīng)該使用(網(wǎng)絡(luò)名稱,容器ID)的主鍵來(lái)完成。

運(yùn)行時(shí)不能調(diào)用同一個(gè)網(wǎng)絡(luò)名稱或容器ID執(zhí)行兩次ADD(沒(méi)有相應(yīng)的DEL)。換句話說(shuō),給定的容器ID必須只能添加到特定的網(wǎng)絡(luò)一次。


同時(shí),在cni/libcni/api.go中,除了CNI接口定義,還定義了如下數(shù)據(jù)結(jié)構(gòu):CNIConfig、NetworkConfig、NetworkConfigList、RuntimeConf;

CNIConfig::主要數(shù)據(jù)成員是plugin的路徑,并實(shí)現(xiàn)了CNI接口;

NetworkConfig和NetworkConfigList:包括在/etc/cni/net.d下面的配置;

RuntimeConf:定義了runtimeConf配置;

對(duì)于CNI接口,以AddNetwork()為例:先從pluginPath獲得plugin的binary,然后injectRuntimeConfig()將網(wǎng)絡(luò)配置注入到networkconfig中,并作為最后plugin執(zhí)行的輸入,然后還會(huì)將network的操作(ADD或者DEL)以及RuntimeConf,作為plugin執(zhí)行時(shí)的變量;

Kubelet對(duì)CNI的實(shí)現(xiàn)

kubelet對(duì)CNI的實(shí)現(xiàn)的主要代碼在:pkg/kubelet/network/cni/cni.go SetUpPod/TearDownPod(創(chuàng)建Pod和銷毀Pod)

CNI插件是可執(zhí)行文件,會(huì)被kubelet調(diào)用。啟動(dòng)kubelet時(shí),指定--network-plugin=cni,同時(shí)--cni-conf-dir 指定networkconfig配置,默認(rèn)路徑是:/etc/cni/net.d,并且,--cni-bin-dir 指定plugin可執(zhí)行文件路徑,默認(rèn)路徑是:/opt/cni/bin;

kubeDeps對(duì)象的數(shù)據(jù)結(jié)構(gòu)NetworkPlugins是調(diào)用方法ProbeNetworkPlugins收集所有Network Plugin;

network.InitNetworkPlugin(pkg/kubelet/network/plugins.go)

InitNetworkPlugin時(shí),會(huì)根據(jù)NetworkPluginName來(lái)選擇需要的Network Plugin,然后調(diào)用plugin的Init進(jìn)行初始化

networkPlugin初始化好后,會(huì)傳遞到containerRuntime,在Pod的生命周期中負(fù)責(zé)管理網(wǎng)絡(luò)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容