OpenStack容器网络项目Kuryr的示例分析

71次阅读
没有评论

共计 5893 个字符,预计需要花费 15 分钟才能阅读完成。

这篇文章给大家分享的是有关 OpenStack 容器网络项目 Kuryr 的示例分析的内容。丸趣 TV 小编觉得挺实用的,因此分享给大家做个参考,一起跟随丸趣 TV 小编过来看看吧。

容器近几年非常流行,有很多项目都考虑将容器与 SDN 结合。Kuryr 就是其中一个项目。Kuryr 项目在 OpenStack big tent 下,目的是将容器网络与 openstack Neutron 对接。Kuryr 给人的第一印象是:这又是一个在 Neutron 框架下的项目,能够通过 Neutron 统一的北向接口来控制容器网络的 SDN 项目。但是实际上,Kuryr 是将 Neutron 作为南向接口,来与容器网络对接。Kuryr 的北向是容器网络接口,南向是 OpenStack Neutron。

Kuryr 背景介绍

正式介绍前,先说下 Kuryr 这个单词。Kuryr 是一个捷克语单词 kurýr,对应英语里面是 courier,对应的中文意思就是信使,送信的人。从这个名字能看出来,Kuryr 不生产信息,只是网络世界的搬运工。这个从项目的图标也可以看出来。另外,由于都是拉丁语系,所以可以不负责任的说,Kuryr 的发音应该是与 courier 类似。

Kuryr 最开始创立的时候,其目的是为了提供 Docker 与 Neutron 的连接。将 Neutron 的网络服务带给 docker。随着容器的发展,容器网络的发展也出现了分歧。主要分为两派,一个是 Docker 原生的 CNM(Container Network Model),另一个是兼容性更好的 CNI(Container Network Interface)。Kuryr 相应的也出现了两个分支,一个是 kuryr-libnetwork(CNM),另一个是 kuryr-kubernetes(CNI)。

Kuryr 在 Libnetwork 中如何工作

kuryr-libnetwork 是运行在 Libnetwork 框架下的一个 plugin。要理解 kuryr-libnetwork 如何工作,首先要看一下 Libnetwork。Libnetwork 是从 Docker Engine 和 libcontainer 中将网络逻辑模块化之后独立出来的的项目,并且替代了原有的 Docker Engine 网络子系统。Libnetwork 定义了一个灵活的模型,使用 local 或者 remote driver 来向 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])
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 中呢?这个问题应该这样看,Libnetwork 是怎样发现 Kuryr 的?这要依赖于 Docker 的 plugin discovery 机制。当用户或者容器需要使用 Docker 的 plugin 的时候,他 / 它只需要指定 plugin 的名字。Docker 会在相应的目录中查找与 plugin 名字相同的文件,文件中定义了如何连接该 plugin。

如果用 devstack 安装 kuryr-libnetwork,devstack 的脚本会在 /usr/lib/docker/plugins/kuryr 创建一个文件夹,里面的文件内容也很简单,默认是:http://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 连接

上面讲的 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 的集成,并没有太多需要注意的地方,有机会可以专门说说这个。

接下来看看 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 所在(否则跟一个代理有什么区别)。最后来看看这部分吧。

当 Docker 创建一个容器,并且需要创建 Endpoint 的时候,请求发送到了作为 Libnetwork 的 remote driver—Kuryr 上。Kuryr 接到这个请求首先会创建 Neutron port:

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

之后会根据配置文件的内容,调用相应的 driver,目前支持的 driver 有 veth,用来连接主机容器网络,另一个是 nested,用来连接虚机内的容器网络。当然,这里的主机,虚机都是相对 OpenStack 来说的,严格的说,OpenStack 的主机也可以是一个虚机,例如我的开发环境。接下来以 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 网络中的 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 中的 host interface 接入到 OpenStack Neutron 的 L2 拓扑中。接入之后,容器实际上与虚机处于一个 L2 网络,自然能与虚机通讯。另一方面,也可以使用 Neutron 提供的各种服务,Security group,QoS 等等。

目前 kuryr 支持的 L2 网络类型有:bridge iovisor midonet ovs tap unbound

等等,这跟 OpenStack Nova 使用 Neutron 的方式是不是很像。Nova 调用 Neutron API 创建 port,Nova 实际的创建网卡,绑定到虚机中。

感谢各位的阅读!关于“OpenStack 容器网络项目 Kuryr 的示例分析”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-16发表,共计5893字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)