为什么Kubernetes不用libnetwork

jopen 8年前

【编者的话】libnetwork是2015年5月1日Docker发布的自家的容器网络管理项目。libnetwork使用Go语言编写,目标是定义一个容器网络模型(CNM),并为应用程序提供一致的编程接口以及网络抽象。为什么后来发布的Kubernetes在网络管理方面,没有使用Docker中得libnetwork呢?且听google工程师的一些看法。

Kubernetes 1.0版本之前就已经具有基本的网络插件体系。与此同时,Docker也将 libnetworkCNM (容器网络模型)引入系统中使用。与之不同的是,Kubernetes网络插件系统却还停留在Alpha阶段。既然Docker的网络插件libnetwork已经发布并支持,那问题来了,为什么Kubernetes没有使用libnetwork呢?毕竟大部分的供应商肯定会支持Docker的网络插件,为什么不选择使用相同的组件呢?

深入讨论之前,首先我们要知道:Kubernetes是一个支持多容器的运行环境,而Docker只是其中一个容器而已。每一个运行环境都会去配置网络环境,所以当人们问“Kubernetes将会支持CNM吗?”时,他们真正的意思是“Kubernetes是否在Docker运行时下支持CNM?”。如果我们能使用用相同的网络组件支持多运行时的话,这当然很好。但是,这并不是明确的目标。

Kubernetes确实没有在Docker中采用CNM/libnetwork。事实上,我们一直在研究能不能用CoreOS提出的APP Container( appc )标准中得容器网络接口(Container Network Interface, CNI )替代它。为什么呢?这里有一些技术和非技术上的原因。

首先,在Docker的网络驱动设计中,预先做了一些兼容的基本假设,这给我们带来不少的问题。

在Docker中有一个“本地”驱动和“全局”驱动的概念。本地驱动是以一台机器为中心(例如“bridge”),不能实现跨机器节点的协作。全局驱动依赖 libkv (一个抽象的键值对存数对象)可以实现跨机器节点的协作(例如:overlay)。libkv是一个非常底层(语义层面的)的键值对存储接口。为了能让Docker中类似overlay的全局驱动在Kubernetes集群运行,我们还需要集群系统管理员运行 consuletcd 或者 zookeeper 实例(参考 multi-host networking ,或者我们不得不在Kubernetes中提供我们自己对于libkv的实现。

听起来后者更有吸引力一些,而且我们也正在尝试实现它。但是,libkv的接口太过于底层,而且它的架构模式也是Docker内部的量身定制版,我们要么直接暴露底层的key-value存储,要么提供key-value语义接口(在一个key-value系统上实现结构存储的API)。就起性能、可扩展性和安全性而言,以上两种方案都不是太合适。我们使用Docker网络的目标是简化操作,如果那样做的话,很明显会使整个系统将变得更加复杂。

对于那些想运行 Docker 的全局驱动并能有能力配置 Docker 的用户来说,Docker 的网络应该是“可行的”。对 Kubernetes 来说,我们不希望介入或影响 Docker 的配置步骤,并且不论 Kubernetes 这个项目今后如何发展,这一点应该都不会改变。Docker 的全局驱动是对用户和开发者来说增加了多余的负担,并且我们不会用它作为默认的网络选项,这也意味着使用 Docker 插件再没什么价值。

Docker 的网络模型在设计上还做了很多和 Kubernetes 不兼容的假设。在 Docker 1.8 和 1.9 的版本中,它在实现“Discovery”时有一个根本性的设计缺陷,结果导致了容器中的 /etc/hosts 文件被随意改写甚至破坏( docker #17190 ) ,而且我们还不能轻易关闭“服务发现”这个功能。Docker在 1.10 的版本中,还计划增加 捆绑一个新 DNS 服务器 的功能 ,而我们现在还不不清楚这个功能能否被关闭。对 Kubernetes 来说,把命名寻址绑定在容器层面,并不是一个正确的设计—我们已经自己定义了一套 Service 命名、寻址、绑定的概念和规则,并且我们也有了我们自己的 DNS 架构和服务(构建在很成熟的 SkyDNS 上)。所以,捆绑一个 DNS 服务器这样的方案并不能满足我们的需求,而且它还有可能无法被关闭。

除了“本地/全局”驱动这样的区分,Docker 还定义了“进程内”和“进程外”(“远程”)插件。我们还研究了下是否可以绕过 libnework 本身(这样就能避开之前所述的那些问题)来直接使用”远程“插件。不幸的是,这意味着我们同时也失去了使用 Docker 的那些“进程内”插件的可能,特别是”bridge”和”overlay”这样的全局插件。这又令使用 libnetwork 失去了很大一部分意义。

另一方面来说,CNI 和 Kubernetes 在设计哲学上非常一致。它远比 CNM 简单,不需要守护进程,并且至少是跨平台的(CoreOS 的 rkt 容器支持 CNI)。跨平台意味着同一个网络配置可以在多个运行环境下使用(例如 Docker, rkt 和 Hyper)。这也非常符合 Unix 的设计哲学:做好一件事。

另外,包装 CNI 模块来实现一个自定义的模块是非常简单的-通过一个简单的 shell 脚本就可以完成。相反 CNM 就复杂多了。因此我们认为 CNI 更适合快速开发和迭代。早期的实验尝试证明我们可以利用 CNI 插件来替代几乎所有 kubelet 中硬编码的网络逻辑。

我们也尝试为 Docker 实现了一个 “bridge”CNM 驱动来使用 CNI 的驱动。结果表明这更加复杂了问题。首先 CNM 和 CNI 的模型非常不同,没有一个“methods”能把它们很好的兼容。其次,我们还是有之前提到的“全局”和“本地”以及 key-value 的问题存在。假设这是个本地驱动,那么我们仍旧要从 Kubernetes 中去得到相应的逻辑网络的信息。

不幸的是,Docker驱动很难映射接入到类似Kubernetes这样的管理平台。特别是这些驱动使用了 Docker 内部分配的一个 ID 而不是一个通用的网络名字来指向一个容器所属的网络。这样的设计导致一个被定义在外部系统中(例如 Kubernetes )的网络很难被驱动所理解识别。

我们和其它网络提供商将这些问题和其它一些相关问题都汇报给了 Docker 的开发人员。尽管这些问题给非 Docker 的第三方系统带来了很多困扰,它们通常被以“设计就是如此”的理由所关闭( libnetwork #139 , libnetwork #486 , libnetwork #514 , libnetwork #865 , docker #18864 )。通过这些举动,我们观察到 Docker 清楚的表明了他们对于有些建议的态度不够开放,因为这些建议可能会分散其一些主要精力、或者降低其对项目的控制。这一点让我们很担忧,因为 Kubernetes 一直支持 Docker,并且为其增加了如此多的功能,但同时 Kubernetes 也是一个独立于 Docker 之外的项目。

种种原因致使我们选择了 CNI 作为 Kubernetes 的网络模型。这将会带来一些令人遗憾的副作用,虽然绝大部分都是一些小问题,比如 Docker 的 inspect 命令显示不了网络地址。不过也会有一些显著的问题,特别是被 Docker 所启动的容器可能不能和被 Kubernetes 启动的容器沟通,以及网络整合工程师必须提供 CNI 驱动来把网络完全整合到 Kubernetes 中。但重要的是,Kubernetes 会变得更简单、灵活并且不需要进行提前配置(比如配置 Docker 使用我们的网桥)。

随着我们越走越远,我们会毋庸置疑地汲取更多更好的整合、简化的方法。如果您对此有啥想法,我们洗耳恭听——可以通过 slack ( http://slack.k8s.io /) 和 network SIG 邮件列表联系我们。

原文链接: why-Kubernetes-doesnt-use-libnetwork (整理:ylzhang )

参考链接: http://www.infoq.com/cn/articl ... twork

来自: http://dockone.io/article/973