容器近幾年非常流行,有很多項目都考慮將容器與SDN結(jié)合。Kuryr就是其中一個項目。Kuryr項目在OpenStackbig tent下,目的是將容器網(wǎng)絡(luò)與OpenStack Neutron對接。Kuryr給人的第一印象是: 這又是一個在Neutron框架下的項目,能夠通過Neutron統(tǒng)一的北向接口來控制容器網(wǎng)絡(luò)的SDN項目。但是實際上,Kuryr是將Neutron作為南向接口,來與容器網(wǎng)絡(luò)對接。Kuryr的北向是容器網(wǎng)絡(luò)接口,南向是OpenStack Neutron。
正式介紹前,先說下Kuryr這個單詞。Kuryr是一個捷克語單詞kuryr,對應(yīng)英語里面是courier,對應(yīng)的中文意思就是信使,送信的人。從這個名字能看出來,Kuryr不生產(chǎn)信息,只是網(wǎng)絡(luò)世界的搬運工。這個從項目的圖標(biāo)也可以看出來。另外,由于都是拉丁語系,所以可以不負(fù)責(zé)任的說,Kuryr的發(fā)音應(yīng)該是與courier類似。
Kuryr最開始創(chuàng)立的時候,其目的是為了提供Docker與Neutron的連接。將Neutron的網(wǎng)絡(luò)服務(wù)帶給Docker。隨著容器的發(fā)展,容器網(wǎng)絡(luò)的發(fā)展也出現(xiàn)了分歧。主要分為兩派,一個是Docker原生的CNM(ContainerNetwork Model),另一個是兼容性更好的CNI(Container Network Interface)。Kuryr相應(yīng)的也出現(xiàn)了兩個分支,一個是kuryr-libnetwork(CNM),另一個是kuryr-kubernetes(CNI)。
以上是Kuryr項目的背景介紹,下面看一下kuryr-libnetwork。
kuryr-libnetwork是運行在Libnetwork框架下的一個plugin。要理解kuryr-libnetwork如何工作,首先要看一下Libnetwork。Libnetwork是從Docker Engine和libcontainer中將網(wǎng)絡(luò)邏輯模塊化之后獨立出來的的項目,并且替代了原有的Docker Engine網(wǎng)絡(luò)子系統(tǒng)。Libnetwork定義了一個靈活的模型,使用local或者remote driver來向container提供網(wǎng)絡(luò)服務(wù)。kuryr-libnetwork就是Libnetwork的一個remote driver實現(xiàn),現(xiàn)在已經(jīng)成為Docker官網(wǎng)推薦的一個remote driver。
Libnetwork的driver可以看是Docker的一個plugin,與Docker的其他plugin共用一套plugin管理框架。也就是說,Libnetwork的remote driver與Docker Engine中的其他plugin用一樣的方式激活,并使用同樣的協(xié)議。有關(guān)Libnetwork remote driver需要實現(xiàn)的接口在Libnetwork的Git上都有詳細(xì)的描述。
kuryr-libnetwork需要做的就是實現(xiàn)這些接口??梢詮膋uryr-libnetwork的代碼中看出來。Libnetwork通過調(diào)用remote driver的Plugin.Activate接口,來查看remote driver實現(xiàn)了什么內(nèi)容。從kuryr-libnetwork的代碼中能看到,它實現(xiàn)了兩個功能:NetworkDriver, IPAMDriver.
@app.route('/Plugin.Activate', methods=['POST'])def plugin_activate():? ? """Returns the list of the implemented drivers.? ? This function returns the list of the implemented drivers defaults to? ? ``[NetworkDriver, IpamDriver]`` in the handshake of the remote driver,? ? which happens right before the first request against Kuryr.? ? See the following link for more details about the spec:? ? docker/libnetwork? # noqa? ? """? ? app.logger.debug("Received /Plugin.Activate")? ? return flask.jsonify(const.SCHEMA['PLUGIN_ACTIVATE'])
Kuryr是怎么作為remote driver注冊到Libnetwork中呢?這個問題應(yīng)該這樣看,Libnetwork是怎樣發(fā)現(xiàn)Kuryr的?這要依賴于Docker的plugin discovery機制。當(dāng)用戶或者容器需要使用Docker的plugin的時候,他/它只需要指定plugin的名字。Docker會在相應(yīng)的目錄中查找與plugin名字相同的文件,文件中定義了如何連接該plugin。
如果用devstack安裝kuryr-libnetwork,devstack的腳本會在/usr/lib/docker/plugins/kuryr創(chuàng)建一個文件夾,里面的文件內(nèi)容也很簡單,默認(rèn)是:http://127.0.0.1:23750。也就是說,kuryr-libnetwork實際上就起了一個http server,這個http server提供了Libnetwork所需的所有接口。Docker找到有這樣的文件之后,就通過文件的內(nèi)容與Kuryr進(jìn)行通信。
所以Libnetwork與Kuryr的交互是這樣:
Libnetwork:有人要用一個叫Kuryr的plugin,讓我找找看。哦,Kuryr你好,你有什么功能?
Kuryr:我有NetworkDriver, IpamDriver這些功能,怎樣,開心嗎?
上面講的Kuryr如何與Docker Libnetwork連接。再來看看Kuryr如何與OpenStack Neutron對接。由于同是OpenStack陣營下的項目,并且都是Python語言開發(fā)的,所以,沒有懸念,Kuryr用neutronclient與Neutron連接。所以總體來看,Kuryr的工作方式如下:

由于Kuryr跟下面實際的L2實現(xiàn)中間還隔了個Neutron,所以Kuryr不是太依賴L2的實現(xiàn)。上圖是Gal Sagie列出的Kuryr支持的一些Neutron L2實現(xiàn)方式。在此之外,我試過kuryr-libnetwork和Dragonflow的集成,并沒有太多需要注意的地方,有機會可以專門說說這個。
接下來看看Kuryr-libnetwork如何在Neutron和Docker中間做一個courier。由于北向是Libnetwork,南向是Neutron,所以可以想象,kuryr-libnetwork做的事情就是接收Libnetwork的資源模型,轉(zhuǎn)化成Neutron的資源模型。先來看看Libnetwork的資源模型,也就前面說過的容器網(wǎng)絡(luò)兩派之一CNM。CNM由三個數(shù)據(jù)模型組成:

Network Sandbox:定義了容器的網(wǎng)絡(luò)配置
Endpoint:容器用來接入網(wǎng)絡(luò)的網(wǎng)卡,存在于Sandbox中,一個Sandbox中可以有多個Endpoint
Network:相當(dāng)于一個Switch,Endpoint接入在Network上。不同的Network之間是隔離的。
對應(yīng)Neutron,Endpoint是Neutron中的Port,而Network是Neutron中的Subnet。為什么Network對應(yīng)的不是Neutron中的Network?可能是因為Libnetwork與Neutron的網(wǎng)絡(luò)定義的區(qū)別的,不過至少在一個Neutron Network中只有一個Subnet時,兩者在名字上是能對應(yīng)的。
除此之外,Kuryr還依賴OpenStack Neutron中的另一個特性:subnetpool。Subnetpool是Neutron里面的一個純邏輯概念,它能夠保證所有在subnetpool中的subnet,IP地址段不重合。Kuryr借助這個特性保證了由其提供的Docker Network,IP地址是唯一的。
Kuryr將Libnetwork發(fā)來的請求轉(zhuǎn)換成相應(yīng)的Neutron的請求,發(fā)送給Neutron。
Kuryr連通容器網(wǎng)絡(luò)與虛機網(wǎng)絡(luò)
但是實際網(wǎng)絡(luò)的連通,沒法通過Neutron的API來告訴Neutron怎么做,Neutron不知道容器的網(wǎng)絡(luò)怎么接出來,也不提供這樣的API。這部分需要Kuryr自己來完成,這也就是Kuryr的Magic所在(否則跟一個代理有什么區(qū)別)。最后來看看這部分吧。
當(dāng)Docker創(chuàng)建一個容器,并且需要創(chuàng)建Endpoint的時候,請求發(fā)送到了作為Libnetwork的remote driver---Kuryr上。Kuryr接到這個請求首先會創(chuàng)建Neutron port:
neutron_port,subnets=_create_or_update_port(neutron_network_id,endpoint_id,interface_cidrv4,interface_cidrv6,interface_mac)
之后會根據(jù)配置文件的內(nèi)容,調(diào)用相應(yīng)的driver,目前支持的driver有veth,用來連接主機容器網(wǎng)絡(luò),另一個是nested,用來連接虛機內(nèi)的容器網(wǎng)絡(luò)。當(dāng)然,這里的主機,虛機都是相對OpenStack來說的,嚴(yán)格的說,OpenStack的主機也可以是一個虛機,例如我的開發(fā)環(huán)境。接下來以veth driver為例來說明。先看代碼吧:
try:with ip.create(ifname=host_ifname, kind=KIND,reuse=True, peer=container_ifname) as host_veth:if not utils.is_up(host_veth):host_veth.up()with ip.interfaces[container_ifname] as container_veth:utils._configure_container_iface(container_veth, subnets,fixed_ips=port.get(utils.FIXED_IP_KEY),mtu=mtu, hwaddr=port[utils.MAC_ADDRESS_KEY].lower())except pyroute2.CreateException:raise exceptions.VethCreationFailure('Virtual device creation failed.')except pyroute2.CommitException:raise exceptions.VethCreationFailure('Could not configure the container virtual device networking.')try:stdout, stderr = _configure_host_iface(host_ifname, endpoint_id, port_id,port['network_id'], port.get('project_id') or port['tenant_id'],port[utils.MAC_ADDRESS_KEY],kind=port.get(constants.VIF_TYPE_KEY),details=port.get(constants.VIF_DETAILS_KEY))except Exception:with excutils.save_and_reraise_exception():utils.remove_device(host_ifname)
與Docker網(wǎng)絡(luò)中的bridge模式類似,Driver首先創(chuàng)建了一個veth pair對,兩個網(wǎng)卡,其中一塊是container interface,用來接在容器的network namespace,并通過調(diào)用_configure_container_iface來進(jìn)行配置;另一塊是host interface,通過調(diào)用_configure_host_iface接入到Neutron的L2拓?fù)渲小?/p>
Host interface的處理方式是專門為OpenStack Neutron定制的。這里需要注意的是,不同的SDN底層的L2拓?fù)涫遣灰粯拥?,OpenVswitch,LinuxBridge,Midonet等等。Kuryr是怎么來支持不同的L2底層?首先,注意看OpenStack Neutron的port信息,可以發(fā)現(xiàn)有這么一個屬性:binding:vif_type。這個屬性表示了該port是處于什么樣的L2底層。Kuryr針對不同的L2實現(xiàn)了一些shell腳本,用來將指定的網(wǎng)卡接入到Neutron的L2拓?fù)渲校@些腳本位于/usr/libexec/kuryr目錄下,它們與binding:vif_type的值一一對應(yīng)。所以,Kuryr要做的就是讀取Neutron port信息,找到對應(yīng)的shell腳本,通過調(diào)用shell,將veth pair中的host interface接入到OpenStack Neutron的L2 拓?fù)渲?。接入之后,容器實際上與虛機處于一個L2網(wǎng)絡(luò),自然能與虛機通訊。另一方面,也可以使用Neutron提供的各種服務(wù),Security group,QoS等等。
目前kuryr支持的L2網(wǎng)絡(luò)類型有:bridge iovisor midonet ovs tap unbound

等等,這跟OpenStack Nova使用Neutron的方式是不是很像。Nova調(diào)用Neutron API創(chuàng)建port,Nova實際的創(chuàng)建網(wǎng)卡,綁定到虛機中。
下次再接著介紹一下kuryr-kubernetes吧。
本文轉(zhuǎn)載自:https://zhuanlan.zhihu.com/p/24554386