正如在前文所说,托管在不同机器上的Pod之间的通信可能有点棘手。默认情况下,Docker在主机上创建一个名为“docker0”的虚拟桥接器,并为其分配一个专用范围的网络。
图1:超级网桥(172.17.0.1/16)
对于创建的每个容器,都将一个虚拟以太网设备附加到这个桥接器上,然后将这个桥接器映射到容器内的eth0,并在前面提到的网络范围内使用一个ip。请注意,对于每个运行Docker的主机都将发生这种情况,主机之间是没有任何协调的。因此,网络范围可能会发生冲突。
因此,每个容器只能与连接到同一虚拟网桥的容器通信。为了与其他主机上的其他容器通信,则必须依赖端口映射。这意味着需要在主机上为每个容器分配一个端口,然后以某种方式将该端口上的所有通信转发到该容器。
设想一个场景,如果某个容器中的应用程序需要向托管在另一个节点上的容器发布自己的IP地址,该怎么办?它实际上并不知道它的真实IP,因为它的本地IP被翻译成主机上的另一个IP和端口。可以通过某些自动化方式来实现端口映射,但在遵循这个模型时,事情会变得有点复杂。
这就是为什么Kubernetes选择了简单性,而跳过了动态端口分配协议。它假设:1)所有容器都可以彼此通信,而不需要网络地址转换(NAT);2)所有容器都可以与每个节点通信(反之亦然),并且容器自己看到的IP与其他容器看到的IP是相同的。除了更简单之外,它还使应用程序可以很容易地从虚拟机迁移到容器中,因为它们不需要改变应用程序在网络上的工作方式。
有许多不同的网络方案与组件,可以为Kubernetes提供这些功能,常见的包括:Contiv, Flannel, NuageNetworks, OpenVSwitch, OVN, Project Calico,Romana和 Weave Net。在我们的案例中将使用这两种最佳组合:Calico和Flannel。
接下来谈谈Calico和Flannel。Flannel通过提供一个封装好的软件定义网络(SDN),允许不同主机之间的Pod通信。这就解决了Docker网络模型的主要问题。如前所述,在使用Docker时,每个容器都有一个IP地址,允许它与同一主机上的其他容器通信。
但是当Pod被部署在不同的主机上时,它们依赖于主机的IP地址。因此,它们之间的通信会通过端口映射实现。这在容器级别上没什么问题,但是如果这些容器中运行的应用程序需要向其他所有调用方告知其外部IP和端口,那么问题就来了。
Flannel是为了解决这一问题而出现的,它负责给每个主机分配一个不同的IP子网段。然后Docker守护进程负责将这个范围内的IP分配给容器。然后容器可以通过数据包来封装使用这些唯一的IP地址,以此与其他外部节点通信。举个例子,假设有两个容器分别为容器A和容器B。容器A部署在主机A上,容器B部署在主机B上。
当容器A与容器B通信时,它将使用容器B的IP地址作为数据包的目标地址(如图2,目标地址为:10.1.20.3)。然后这个数据包将被封装为主机A和主机B之间的一个UDP数据包,由主机A发送,并将主机B的IP地址作为UDP数据包的目标地址。
当UDP数据包到达主机B后,会解封装该数据包,并使用包中内部IP地址,并将包路由到主机B上的容器B。整个过程中Flannel配置,特别是涉及容器和主机之间的关系映射会存储在etcd中。路由是由一个称为flanneld的Flannel守护进程来实现。
图2:主机级别Pod通信
Calico则负责保护这个网络,根据细粒度的网络策略限制Pod之间的通信。如前所述,Kubernetes默认情况下是允许来自集群内外部的所有源流量触达集群内的任意Pod。以下是关于Kubernetes网络模型的说明:
1. 所有容器都可以与其他容器通信并且不需要网络地址转换(NAT)。
2. 所有节点都可以与任意容器通信并且不需要网络地址转换(NAT),反之亦然。
3. 容器将本身的IP与其他容器观察到的IP是一致的。
出于对安全因素和多租户的考虑,通常会需要限制Kubernetes集群上的Pod集合间的相干性通信,即仅允许关联方进行通信,不相干节点或Pod间的通信会被隔离。Calico支持Kubernetes的v1 alpha1网络策略API,它能够实现网络隔离,限制从一组可选源到另一组可选目标TPC/UDP端口的连接。当然并不会限制主机本身对Pod的访问,因为对于Kubernetes的健康检查仍然是必要的。
考虑到这一点,Pod之间的通信可以限制在名称空间(namespace)级别,或者使用特定的网络策略,使用选择器来选择相关的节点。
这个案例中之所以选择Flannel作为SDN(软件定义网络),部分是由于它是CoreOS (Container Linux)的标准SDN工具,并且随发行版一起发布,配置起来相当容易,而且文档也很棒。而选择Calico是因为计划在Kubernetes上使用基于测试策略的安全管理,也由于它与Flannel的紧密集成。除此以外,它们两者都依赖于etcd。(注:Calico可以在没有etcd集群的情况下使用,使用Kubernetes API作为数据存储)。
顺便说一下,Calico还可以作为一个独立的组件使用,它独立处理SDN和基于网络策略的安全管理。此外,Flannel还有一些额外的网络方案选项,比如udp、vxlan,甚至AWS VPC路由编程等。
原文作者:Sebastian Caceres 译者:江玮