OpenStack容器網路項目Kuryr(libnetwork)

容器近幾年非常流行,有很多項目都考慮將容器與SDN結合。Kuryr就是其中一個項目。Kuryr項目在OpenStacknbig tent下,目的是將容器網路與OpenStacknNeutron對接。Kuryr給人的第一印象是:n這又是一個在Neutron框架下的項目,能夠通過Neutron統一的北向介面來控制容器網路的SDN項目。但是實際上,Kuryr是將Neutron作為南向介面,來與容器網路對接。Kuryr的北向是容器網路介面,南向是OpenStacknNeutron。

Kuryr背景介紹

正式介紹前,先說下Kuryr這個單詞。Kuryr是一個捷克語單詞kuryr,對應英語裡面是courier,對應的中文意思就是信使,送信的人。從這個名字能看出來,Kuryr不生產信息,只是網路世界的搬運工。這個從項目的圖標也可以看出來。另外,由於都是拉丁語系,所以可以不負責任的說,Kuryr的發音應該是與courier類似。

Kuryr最開始創立的時候,其目的是為了提供Docker與Neutron的連接。將Neutron的網路服務帶給Docker。隨著容器的發展,容器網路的發展也出現了分歧。主要分為兩派,一個是Docker原生的CNM(Container Network Model),另一個是兼容性更好的CNI(ContainernNetwork Interface)。Kuryr相應的也出現了兩個分支,一個是kuryr-libnetwork(CNM),另一個是kuryr-kubernetes(CNI)。

以上是Kuryr項目的背景介紹,下面看一下kuryr-libnetwork。

Kuryr 在Libnetwork中如何工作

kuryr-libnetwork是運行在Libnetwork框架下的一個plugin。要理解kuryr-libnetwork如何工作,首先要看一下Libnetwork。Libnetwork是從DockernEngine和libcontainer中將網路邏輯模塊化之後獨立出來的的項目,並且替代了原有的Docker Engine網路子系統。Libnetwork定義了一個靈活的模型,使用local或者remotendriver來向container提供網路服務。kuryr-libnetwork就是Libnetwork的一個remote driver實現,現在已經成為Docker官網推薦的一個remote driver。

Libnetwork的driver可以看是Docker的一個plugin,與Docker的其他plugin共用一套plugin管理框架。也就是說,Libnetwork的remote driver與Docker Engine中的其他plugin用一樣的方式激活,並使用同樣的協議。有關Libnetwork remote driver需要實現的介面在Libnetwork的git上都有詳細的描述。

kuryr-libnetwork需要做的就是實現這些介面。可以從kuryr-libnetwork的代碼中看出來。Libnetwork通過調用remote driver的Plugin.Activate介面,來查看remote driver實現了什麼內容。從kuryr-libnetwork的代碼中能看到,它實現了兩個功能:NetworkDriver, IPAMDriver.

@app.route(/Plugin.Activate, methods=[POST])ndef plugin_activate():n """Returns the list of the implemented drivers.n This function returns the list of the implemented drivers defaults ton ``[NetworkDriver, IpamDriver]`` in the handshake of the remote driver,n which happens right before the first request against Kuryr.n See the following link for more details about the spec:n docker/libnetwork # noqan """n app.logger.debug("Received /Plugin.Activate")n return flask.jsonify(const.SCHEMA[PLUGIN_ACTIVATE])n

Kuryr是怎麼作為remote driver註冊到Libnetwork中呢?這個問題應該這樣看,Libnetwork是怎樣發現Kuryr的?這要依賴於Docker的plugin discovery機制。當用戶或者容器需要使用Docker的plugin的時候,他/它只需要指定plugin的名字。Docker會在相應的目錄中查找與plugin名字相同的文件,文件中定義了如何連接該plugin。

如果用devstack安裝kuryr-libnetwork,devstack的腳本會在/usr/lib/docker/plugins/kuryr創建一個文件夾,裡面的文件內容也很簡單,默認是:127.0.0.1:23750。也就是說,kuryr-libnetwork實際上就起了一個http server,這個http server提供了Libnetwork所需的所有介面。Docker找到有這樣的文件之後,就通過文件的內容與Kuryr進行通信。

所以Libnetwork與Kuryr的交互是這樣:

  • Libnetwork:有人要用一個叫Kuryr的plugin,讓我找找看。哦,Kuryr你好,你有什麼功能?
  • Kuryr:我有NetworkDriver, IpamDriver這些功能,怎樣,開心嗎?

Kuryr如何與Neutron連接

nnnnnnnnnn上面講的Kuryr如何與Docker Libnetwork連接。再來看看Kuryr如何與OpenStack Neutron對接。由於同是OpenStack陣營下的項目,並且都是python語言開發的,所以,沒有懸念,Kuryr用neutronclient與Neutron連接。所以總體來看,Kuryr的工作方式如下:

由於Kuryr跟下面實際的L2實現中間還隔了個Neutron,所以Kuryr不是太依賴L2的實現。上圖是Gal Sagie列出的Kuryr支持的一些Neutron L2實現方式。在此之外,我試過kuryr-libnetwork和Dragonflow的集成,並沒有太多需要注意的地方,有機會可以專門說說這個。

nnnnnnnnnn接下來看看Kuryr-libnetwork如何在Neutron和Docker中間做一個courier。由於北向是Libnetwork,南向是Neutron,所以可以想像,kuryr-libnetwork做的事情就是接收Libnetwork的資源模型,轉化成Neutron的資源模型。先來看看Libnetwork的資源模型,也就前面說過的容器網路兩派之一CNM。CNM由三個數據模型組成:

  • Network Sandbox:定義了容器的網路配置

  • Endpoint:容器用來接入網路的網卡,存在於Sandbox中,一個Sandbox中可以有多個Endpoint

  • Network:相當於一個Switch,Endpoint接入在Network上。不同的Network之間是隔離的。

對應Neutron,Endpoint是Neutron中的Port,而Network是Neutron中的Subnet。為什麼Network對應的不是Neutron中的Network?可能是因為Libnetwork與Neutron的網路定義的區別的,不過至少在一個Neutron Network中只有一個Subnet時,兩者在名字上是能對應的。

除此之外,Kuryr還依賴OpenStack Neutron中的另一個特性:subnetpool。Subnetpool是Neutron裡面的一個純邏輯概念,它能夠保證所有在subnetpool中的subnet,IP地址段不重合。Kuryr藉助這個特性保證了由其提供的Docker Network,IP地址是唯一的。

Kuryr將Libnetwork發來的請求轉換成相應的Neutron的請求,發送給Neutron。

Kuryr連通容器網路與虛機網路

但是實際網路的連通,沒法通過Neutron的API來告訴Neutron怎麼做,Neutron不知道容器的網路怎麼接出來,也不提供這樣的API。這部分需要Kuryr自己來完成,這也就是Kuryr的Magic所在(否則跟一個代理有什麼區別)。最後來看看這部分吧。

nnnnnnnnnnnnnn當Docker創建一個容器,並且需要創建Endpoint的時候,請求發送到了作為Libnetwork的remote driver---Kuryr上。Kuryr接到這個請求首先會創建Neutron port:

neutron_port, subnets = _create_or_update_port(n neutron_network_id, endpoint_id, interface_cidrv4,n interface_cidrv6, interface_mac)n

nnnnnnnnnn之後會根據配置文件的內容,調用相應的driver,目前支持的driver有veth,用來連接主機容器網路,另一個是nested,用來連接虛機內的容器網路。當然,這裡的主機,虛機都是相對OpenStack來說的,嚴格的說,OpenStack的主機也可以是一個虛機,例如我的開發環境。接下來以vethndriver為例來說明。先看代碼吧:

try:n with ip.create(ifname=host_ifname, kind=KIND,n reuse=True, peer=container_ifname) as host_veth:n if not utils.is_up(host_veth):n host_veth.up()n with ip.interfaces[container_ifname] as container_veth:n utils._configure_container_iface(n container_veth, subnets,n fixed_ips=port.get(utils.FIXED_IP_KEY),n mtu=mtu, hwaddr=port[utils.MAC_ADDRESS_KEY].lower())n except pyroute2.CreateException:n raise exceptions.VethCreationFailure(n Virtual device creation failed.)n except pyroute2.CommitException:n raise exceptions.VethCreationFailure(n Could not configure the container virtual device networking.)n try:n stdout, stderr = _configure_host_iface(n host_ifname, endpoint_id, port_id,n port[network_id], port.get(project_id) or port[tenant_id],n port[utils.MAC_ADDRESS_KEY],n kind=port.get(constants.VIF_TYPE_KEY),n details=port.get(constants.VIF_DETAILS_KEY))n except Exception:n with excutils.save_and_reraise_exception():n utils.remove_device(host_ifname)n

與Docker網路中的bridge模式類似,Driver首先創建了一個veth pair對,兩個網卡,其中一塊是container interface,用來接在容器的network namespace,並通過調用_configure_container_iface來進行配置;另一塊是host interface,通過調用_configure_host_iface接入到Neutron的L2拓撲中。

Host interface的處理方式是專門為OpenStack Neutron定製的。這裡需要注意的是,不同的SDN底層的L2拓撲是不一樣的,OpenVswitch,LinuxBridge,Midonet等等。Kuryr是怎麼來支持不同的L2底層?首先,注意看OpenStack Neutron的port信息,可以發現有這麼一個屬性:binding:vif_type。這個屬性表示了該port是處於什麼樣的L2底層。Kuryr針對不同的L2實現了一些shell腳本,用來將指定的網卡接入到Neutron的L2拓撲中,這些腳本位於/usr/libexec/kuryr目錄下,它們與binding:vif_type的值一一對應。所以,Kuryr要做的就是讀取Neutron port信息,找到對應的shell腳本,通過調用shell,將veth pair中的hostninterface接入到OpenStack Neutron的L2n拓撲中。接入之後,容器實際上與虛機處於一個L2網路,自然能與虛機通訊。另一方面,也可以使用Neutron提供的各種服務,Security group,QoS等等。

目前kuryr支持的L2網路類型有:bridge iovisor nmidonet ovs tap nunbound

等等,這跟OpenStacknNova使用Neutron的方式是不是很像。Nova調用Neutron API創建port,Nova實際的創建網卡,綁定到虛機中。

下次再接著介紹一下kuryr-kubernetes吧。


推薦閱讀:

SDN再談
NFV再談(應用現狀篇)
VXLAN with EVPN as Control Plane
Traceroute in OpenFlow based SDN
NFV 閑聊(基礎技術篇)

TAG:容器 | SDN | 计算机网络 |