[issue翻译]提议:为CNI支持虚拟机监控程序容器运行时

URL: https://github.com/containernetworking/cni/issues/251

feiskyer
Overview
CNI旨在为Linux上的应用程序容器提供基于通用插件的联网解决方案。今天有许多容器运行时实现。为了将容器网络与主机隔离,其中一些基于Linux网络 命名空间(例如docker,没有lkvm的rkt),而其他的则基于管理程序(例如rkt带有lkvmHyperContainer)。 当前的CNI设计已经支持基于网络名称空间的运行时,但不支持虚拟机管理程序。因此,该提案旨在为CNI添加虚拟机监控程序运行时支持。 当前的CNI设计已经支持基于网络命名空间的运行时,但不支持虚拟机管理程序。因此,该提案旨在为CNI添加虚拟机监控程序运行时支持。
Considerations
网络上的hypervisor和linux网络名称空间之间有很多区别:
- 虚拟机监控程序具有自己的网络栈,无需为虚拟机监控程序创建网络名称空间。相反,CNI应该为管理程序创建一个Tap设备。
- 由于网络是在系统管理程序内部配置的,因此CNI网络插件无法直接在外部配置IP地址。相反,CNI应该准备网络设备(例如,创建网桥并连接tap设备),然后将网络信息(包括设备,IP地址,网关,路由等)传递回运行时,并让运行时完成剩余工作配置工作。
Design
- 为了支持基于管理程序的运行时,应在CNI中进行一些更改:
- 让CNI知道运行时类型( hypervisor 或netns) 为基于 hypervisor 的运行时配置tap设备而非veth对
- 将网络信息传递回运行时

1) 知道运行时类型
在RuntimeConf中添加一个新字段UsesTapDevice, 例如:

type RuntimeConf struct {
    // The ID of the container
    ContainerID string
    // If set, a tap device instead of veth pair should be created
    UsesTapDevice bool
    // The network namespace path. 
    // Optional. Only need if IsHypervisor is not set.
    NetNS       string
    // Name of the interface inside the container. 
    IfName      string
    // Extra arguments
    Args        [][2]string
}

2) 配置tap设备,而不是veth对
这将是网络插件的工作。由于没有网络名称空间,因此网络插件应直接在主机上创建Tap设备。现有插件将被重构以创建用于虚拟机管理程序运行时的tap设备。
网络配置和IPAM可以保持不变,因此可以像以前一样分配容器的IP地址。

3) 将网络信息传递回运行时
Dan已经从#145开始了

feiskyer:CNI不在乎虚拟机监控程序的实现细节,它只是知道运行时基于虚拟机监控程序,并且希望使用Tap设备而不是veth对。容器运行时应将此Tap设备连接到虚拟机监控程序。

lukasredynk :我研究了 #145 ,它在接口更改方面显得更改量很大,添加像“ CNI_VIRT”这样的env并返回tap索引还不够吗?

feiskyer :添加环境可能有效,但不是那么明确。当前所有的cni插件都应该是基于netns的,它需要知道运行时明确是虚拟机管理程序。
仅返回tap索引是不够的,因为我们不希望每个插件都直接与hypervisor交互。取而代之的是,cni将创建的tap设备和相关的IP地址/掩码/网关/dns返回到运行时,并让运行时本身将其配置到虚拟机监控程序中。

lukasredynk : 抱歉,我不确切,是的,我同意。
我正在考虑的方案:

  1. Set CNI_VIRT (optional)
  2. Run plugin (from plugins/main)
  3. Plugin checks if CNI_VIRT set
    • CNI_VIRT not set: no changes in flow
    • CNI_VIRT set: creates tap instead of veth
  4. Plugin executes IPAM plugin (and passes CNI_VIRT if set)
    • CNI_VIRT not set: no changes in flow
    • CNI_VIRT set: only reserves IP and returns information about how to configure iface in VM
  5. Top-most plugin returns:
    • CNI_VIRT not set: no changes in flow
    • CNI_VIRT set: to the usual output would be added additional param "Iface *uint" in types.IPConfig

feiskyer: 确实需要一个新的环境变量(例如CNI_VIRT),但是仅此一个环境变量是不够的:
- 许多外部cni插件都在使用libcni,这当然需要知道运行时是否基于管理程序
- invoke.Args和CmdArgs还需要知道运行时是否基于虚拟机管理程序
顺便说一句,我对IsHypervisor bool不满意,您对此有何建议?也许 Virtualized bool?例如

type RuntimeConf struct {
    // If set, the runtime is based on hypervisor
    Virtualized bool
    ....
}

lukasredynk:我已经开始着手研究可以显示的代码,以进一步推动讨论,但是现在,我已经在pkg / skel / skel.go“ CNI_VIRT”中添加了放入CmdArgs的环境变量列表。 首先,我正在考虑提供ptp和bridge插件的基本实现,但我仍在努力,看来netlink库版本应该受到影响,因为所使用的版本不支持创建tun / tap设备。 感谢您指出invoke.Args,我完全错过了它,我本以为在pkg / skel / skel.go中添加文件就足够了。
关于IsHypervisor bool:也许TunTap bool,默认为false?因为可能会出现这种情况,tap优先于veth,而不与虚拟机管理程序字段相关?

jellonek : 要使用tap设备,您不必使用虚拟化,因此应该使用UsesTapDevice 而不是Virtualized 。

feiskyer: 谢谢。已将提案更新为UsesTapDevice而不是IsHypervisor。

lukasredynk:非常早期且非常WIP的分支,它为ptp插件创建和配置Tap设备:https://github.com/lukasredynk/cni/pull/1/files,这里的大部分更改与更新netlink库有关(供应商的版本不能处理 Tap设备 )。

lukasredynk:我已经使用rkt-kvm进行了测试,并且至少在默认受限网络中有效。

lukasredynk: RFC:应该如何命名接口?我认为它应该与“ veth_”不同,不要混用不同的设备类型,也许是“ tap_”,其中“ *”后缀的生成方式与veth_型接口相同。你怎么看待这件事?

lukasredynk:我更新了PR: 将CNI_VIRT更改为CNI_USE_TAP ;向libcni添加了代码路径;重构tap创建。
唯一缺少的是在netlink存储库中合并挂起的PR。

thockin :我不确定,但是对我来说,这似乎是一个错误的转折。并非所有的CNI插件都将使用veth。感觉就像是在获取内部信息并将其放在外部。您现在使每个网络插件负责了解tap设备吗?
为什么不能在CNI插件中完全完成此操作?我们是否需要在CNI API中提供一些能力以为插件提供线索,我也许可以买单。但总的来说,这对我来说很困难。只是感觉不对。

feiskyer: 是的,该建议将使每个cni插件负责理解tap设备。这是有道理的,因为CNI是所有容器(包括基于管理程序的运行时)的所有容器的通用网络接口。
可以在CNI插件中完全完成此操作 ,但是该插件不再通用。通常,所有内置网络插件(不确定这是否正确,我的意思是这里的插件)应在所有支持CNI的运行时中正常工作。

thockin : 我认为这是一个非常可疑的起点。你不能合理地 希望所有容器供应商都支持这一点。
有人对网络有图表或详尽的解释吗 为虚拟化运行时设置?我想看一个例子 设备链接在一起,以及如何配置IP路由。

lukasredynk : 遵循@jellonek的建议,我已经准备了带有更新的netlink(包括我的更改)的PR(#274),合并之后,我将使用PTP插件对原始PR进行重新设置。

lukasredynk : @thockin CNI插件无法在VM内部设置网络,因为它始终在主机上运行。对于基于systemd-nspawn的容器来说,在主机生成之前先建立网络连接并不是问题,因为主机和容器共享内核空间,但是在VM的情况下,没有直接的方法来创建虚拟接口(它将如何连接到主机上的网络?) ,请通过syscall分配IP地址并设置路由(有效执行CNI的操作),因为在生成VM之前无法执行此操作。系统管理程序也无法将veth设备附加到VM(当前由ptp,bridge和flannel插件创建)。

lukasredynk : 您可以看一下rkt中的网络(在github.com/coreos/rkt/networking:networking.go和kvm.go中):当前实现对nspawn容器使用CNI,对于基于VM的容器,使用自定义路径(使用CNI代码库)设置tap,获取路由,获取IP并将此信息传递回虚拟机监控程序(tap设备名称, 通过cli )。还创建了systemd单元文件,VM的systemd使用该文件来设置所有网络。实际上,所有插件均能通过这种“野路子”支持,但是会a)部分复制了CNI功能 b)使其难以维护。
集装箱供应商对此有何担忧?选择加入创建tap。在将特定的可选标志传递给CNI之前,它们将像以前一样工作。实际上,对于希望通过CNI处理网络配置并且还需要针对特定​​情况需要基于tap的网络的项目而言,这是很大的收获。

jellonek: 老实说-并非所有插件(ipvlan 不支持)。将这些代码从rkt移到cni 仍然可以解决很多问题-首先消除了提到项目上的代码重复,其次-为其他基于VM的运行时(例如HyperKube或vocker)提供了相同的功能。 而且,这还提供了在容器启动之前配置网络,并在结束后在其他网络上重用它的可能性。

thockin:明确地说-我理解为什么这对于超虚拟化运行时更好。我比较关注的是API的丑陋性以及现有驱动程序的想法:应该实现两种模式。只是感觉不对。
如果我有一个可以处理OVS的CNI插件-我可以将Tap设备链接到我的 OVS 设备中吗 ?
我可以在Calico上使用tap设备吗? Weave ? Contiv 呢?
为什么这比一组明确支持"仅tap设备"的新CNI插件更好 ?是“需要开放的FD”问题吗?

feiskyer : @thockin创建水龙头已启用。现有的驱动程序可以像今天一样选择仅支持基于netns的容器。你能解释为什么它很丑吗?
当然可以支持OVS 。只需稍作改动即可:连接到容器时,创建分接设备而不是veth对。
如果Calico / Weave / Contiv添加了对tap设备的支持,则也是如此。

feiskyer:
1) 减少代码重复 :
- 一个二进制和代码库都可以
- 网络插件可以执行许多步骤来设置网络,例如分配IP地址,创建网桥,设置iptables规则,设置隧道,将网桥连接到容器(通过veth对或Tap设备)。只有最后一步是不同的。两者的所有其他步骤完全相同。
2) 扩展CNI的范围,因此它可以支持所有类型的运行时,无论是netns还是hypervisor。 (否则,我们可能需要另一个专用于Tap设备的CNI)
3) 切换运行时时具有一致的用户体验,因此用户无需重写新插件即可与现有网络基础架构集成。

lukasredynk: 并且只有以下插件是受此变更集影响 :

  • ptp
  • bridge
  • macvlan
  • flannel (because internally wraps bridge plugin)

一些来自主要的CNI存储库。如果外部插件希望支持创建tap,则可以,但这不是强制性的。 AFAIK Calico仅使用ipam进行IP管理(单独管理veth),我不确定Weave,但快速浏览他们的文档后,情况似乎是这样。

thockin :您正在详细说明:
网络设备的特定类型并且使其成为API的一部分。一旦支持,就没有根据了 拒绝下一个不同的想法,而您的APi变成 混乱的条件和可选的if字段。有优雅和 简单到不透明。这感觉不是最简单的 可能的解决方案。
我们可以做一些管理程序存在于网络中的事情吗? 主机os)和net插件在所有情况下都是相同的,并委托 虚拟机管理程序的复杂性仅在管理程序中出现?
MacVLAN? IPVLAN?我的意思是您已经强迫用户 考虑他们的插件是否支持特定的操作模式 。 为什么将其作为API的一部分。
我的观点是,tap是一种非常特殊的技术。立刻 更好的东西来了,此API就过时。可能是一样的 说到netns(实际上,libnetwork甚至不公开netns,如果我 回想一下),但那是一个较低的水平,因此对我来说风险较小。
但实际上他们确实有不同的插件。它可能会使用80% 相同的代码,但是其行为和执行方式有所不同,因此它 是不同的。

thockin : 用户也可以针对不同的运行时使用相同的配置。

rosenhouse : 不要堆得太多,但我还会问我们如何计划在CI系统中测试这种虚拟机监控程序支持? Travis允许我们创建netns并在这些netns中执行插件。是否可以使用CI环境来行使管理程序代码路径?

squeed : 与其让“ ADD”命令具有两种相当不同的语义,不如在规范中添加一个附加命令?诸如“ CONFIGURE”之类的东西,它通过一个已经初始化的接口(例如,由管理程序创建tap)来由CNI插件配置?

jellonek:这将要求管理程序在调用CNI插件之前运行(这与kvm风格的rkt的当前实现的方式非常相似)。 使用当前的建议(以及  #271 中的初始实现),您可以从kubernetes网络准备过程中运行,然后将结果传递给任何基于虚拟机监控程序的运行时(这可能是kvm风格的rkt,hyperd或其他我现在正在工作的东西; ))。
仍然有可能通过网络设备(可以准备veth / tap / macv(lan | tap))作为附加的CNI“主”插件似乎很容易实现。现有的插件也可以重用它,就像它们已经在调用IPAM插件一样。
这样,我们将拥有像这样的链:
- 准备接口(可以通过插件或外部事物完成),该接口调用:
- 在调用特定设备的设备上分配ipv4 / ipv6所需的配置(地址/路由/ iptables) ,该接口调用:
- IPAM
实际上,前两个步骤在主要插件中组合在一起。 如果有人对此感兴趣-我们可以讨论这个问题,但是我看来在另外一个问题上。

翻译声明:并不保证翻译的质量,只是看都看了,顺便把零散的片段整理起来,也能够帮助同样需要的人罢了。