Kubernetes网络一直是一个非常复杂的主题。本文将介绍Kubernetes实际如何创建网络以及如何为Kubernetes集群设置网络。
本文不包括如何设置Kubernetes集群。这篇文章中的所有例子都将使用Rancher 2.0集群(其他平台也同样适用)。即使你打算使用其他的公有云管理Kubernetes服务,也希望你对Kubernetes网络的工作原理有更好的了解。
如何使用Kubernetes网络
许多Kubernetes部署指南中包含了在K8S部署中部署Kubernetes网络CNI的说明。但是如果你的K8S集群已经运行,并且尚未部署任何网络,那么部署网络就像在K8S上运行其提供的配置文件一样简单(对于大多数网络和基本用例而言)。例如,要部署flannel:
这样,从网络的角度来看,K8S已经可以使用。为了测试一切是否正常,我们创建了两个Pod。
这将创建两个pod,它们正在使用我们的驱动器。查看其中一个容器,我们发现网络的IP地址范围为10.42.0.0/24。
在另一个Pod进行的快速ping测试表明,网络运行正常。
与Docker网络相比,Kubernetes网络如何工作?
Kubernetes通过Docker之上的CNI管理网络,并将设备附加到Docker。尽管有Docker Swarm的Docker也具有自己的联网功能(例如overlay、macvlan、bridging等),但CNI也提供了类似类型的功能。
还有一点十分重要,K8S并不使用docker0(这是Docker的默认网桥),而是创建自己的网桥,名为cbr0,该网桥需要与docker0区分开来。
为什么我们需要Overlay网络?
诸如vxlan或ipsec之类的overlay网络(如果你设置过安全***,你应该对此比较熟悉)可以将数据包封装到另一个数据包中。这使得实体在另一台计算机的范围之外依旧可以寻址。Overlay网络的替代方案包括如macvtap(lan)之类的L3解决方案,甚至包括ivtap(lan)之类的L2解决方案,但是这些方案具有一定的局限性。
L2或L3上的任何解决方案都可以让pod在网络上寻址。这意味着pod不仅在Docker网络内部访问,还能直接从Docker网络外部访问。这些是公共IP地址或私有IP地址。
然而,在L2上进行通信比较麻烦,并且你的经验会因为网络设备而异。某些交换机需要一些时间来注册你的Mac地址,然后才能将其实际连接到网络的其余部分。你还可能会遇到一些麻烦,因为系统中其他主机的neighbor(ARP) table仍在过时的缓存上运行,并且始终需要使用dhcp运行而不是host-local,这样可以避免主机之间的ip冲突。Mac地址和neighbor table问题是诸如ipvlan之类的解决方案存在的原因。这些解决方案不会注册新的mac地址,而是在现有地址上路由流量(尽管它们也有自己的问题)。
因此,我的建议是,对于大多数用户而言,将overlay网络作为默认解决方案应该足够了。但是,一旦工作负载变得更加高级并提出了更具体的要求,你将需要考虑其他的解决方案,如BGP和直接路由。
Kubernetes网络如何工作?
在Kubernetes中首先要了解的是,pod实际上并不等同于容器,而是容器的集合。在同一集合的容器*享一个网络堆栈。Kubernetes通过在暂停容器上设置网络来进行管理,你可以在你所创建的每个pod中找到这些暂停容器。所有其他pod都连接到暂停容器的网络,该容器本身除了提供网络外不执行任何操作。因此,也可以使一个容器通过localhost与不同容器中的服务进行通信,此时该容器具有相同pod的相同定义。
除了本地通信之外,pod之间的通信看起来与Docker网络中的container-to-container通信几乎相同。
Kubernetes流量路由
我将以两种场景为例,详细地说明如何在Pod之间路由流量。
1、 在同一主机上路由流量:
在两种情况下,流量不会离开主机。一是当调用的服务在同一节点上运行,一是单个pod中的同一个容器集合。
如果从第一个pod中的容器1调用localhost:80并在容器2中运行服务,则流量将通过网络设备并将数据包转发到其他目的地。在这种情况下,路由流量的路线很短。
如果我们想要与其他pod进行通信,时间会更长一些。首先,流量将传递到cbr0,接下来cbr0将会注意到我们在同一个子网通信,因此它会将流量转发到目标Pod,过程如下图所示:
2、 跨主机路由流量:
当我们离开节点时,这将变得更加复杂。现在,cbr0会将流量传递到下一个节点,该节点的配置由CNI管理。这些基本上只是以目标主机为网关的子网路由。然后,目标主机可以继续使用自己的cbr0并将流量转发到目标容器,如下所示:
究竟什么是CNI?
CNI是Container Networking Interface(容器网络接口)的缩写,基本上是一个具有定义明确的外部接口,Kubernetes可以调用它来提供网络功能。
你可以在以下链接中找到维护的参考插件,其中包括容器网络官方repo中的大多数重要插件:
https://github.com/containernetworking/plugins
CNI 3.1版不是很复杂。它包含三个必需的功能,ADD、DEL和VERSION,这些功能可以尽其所能管理网络。有关每个函数应返回和传递的内容的更详细说明,您可以在此处阅读规范:
https://github.com/containernetworking/cni/blob/master/SPEC.md
CNI之间的区别
以下我们将介绍一些最受欢迎的CNI:
Flannel
Flannel是一个简单的网络,并且是overlay网络最简单的设置选项。它的功能包括原生网络,但在多个网络中使用时会受到限制。对于大多数用户来说,Flannel是Canal下面的默认网络,部署起来非常简单,甚至还有本地网络功能,如主机网关。但是Flannel有一些限制,包括缺乏对网络安全策略的支持以及没有多网络的功能。
Calico
Calico与Flannel采用不同的方法,从技术的角度来说,它不是overlay网络,而是在所有相关系统之间配置路由的系统。为此,Calico利用边界网关协议(BGP),它在名为peering的过程中用于Internet。其中每方peering交换流量并参与BGP网络。BGP协议本身会在其ASN下传播路由,不同之处在于它们是私有的,不需要再RIPE中注册它们。
但是,在某些情况下,Calico可与overlay网络配合使用,如IPINIP。当节点位于不同网络上时使用,以便启动两个主机之间的流量交换。
Canal
Canal基于Flannel,但有一些Calico自己的组件,例如felix(主机代理),它可以利用网络安全策略。这些通常在Flannel中不存在。因此,它基本上通过添加安全策略来扩展Flannel。
Multus
Multus是一个CNI,但实际上它本身并不是网络接口。只是它编排了多个接口,并且没有配置实际的网络,因而Pod无法单独与Multus通信。实际上,Multus是多设备和多子网网络的推动者。下图显示了它是如何工作的,Multus本身基本上调用了真正的CNI而不是kubelet,并将结果传递回kubelet。
Kube-Router
同样值得一提的是kube-router,与Calico一样,它可以与BGP和路由而不是overlay网络一起使用。就像Calico一样,它在必要的时候可以使用IPINIP。它还能利用ipvs进行负载均衡。
设置多网络K8S集群
如果您需要使用多个网络,则可能需要Multus。
设置Multus
我们需要做的第一件事是设置Multus。我们使用的几乎是Multus仓库示例中的配置,但进行了一些重要的调整。请参阅下面的示例。
首先是调整configmap。因为我们计划使用Flannel创建默认网络,所以我们在Multus配置的delegates数组中定义配置。这里用红色标记的一些重要设置是“ masterplugin”:true,用于定义Flannel网络本身的网桥。你将在接下来的步骤中了解为什么我们需要这样做。除此之外,还需要添加配置映射的安装定义,其他则不需要调整,因为由于某些原因,此示例未完成。
关于此configmap的另一件重要事情是,这一configmap中定义的所有内容都是默认网络,这些默认网络会自动安装到容器,而无需进一步说明。另外,如果要编辑此文件,请注意,你要么需要终止并重新运行守护进程的容器,要么重新启动节点才能使更改生效。
示例yaml文件:
设置主要的Flannel Overlay网络
对于主要的Flannel网络,设置非常简单。我们可以从Multus仓库中获取示例,然后进行部署。此处所做的调整是CNI安装、容差的调整以及对Flannel的CNI设置所做的一些调整。例如,添加“ forceAddress”:true并删除“ hairpinMode”:true。
这已在使用RKE设置的集群上进行了测试,但是只要您从主机正确安装CNI(在本例中为/ opt / cni / bin),它就可以在其他集群上工作。
Multus本身并没有太大的改变。他们只注释了initcontainer配置,你可以删除它。之所以如此,是因为Multus将建立其delegates,并充当主要的“ CNI”。
这是修改后的Flannel daemonset:
部署了这些样本之后,我们已经完成了很多工作,现在应该为pod分配一个IP地址。让我们测试一下:
如你所见,我们已经成功部署了Pod,并在eth0接口(默认接口)上为其分配了IP 10.42.2.43。所有其他接口都将显示为netX,即net1。
设置辅助网络
辅助网络还需要进行一些调整,这些调整的前提是假设你要部署vxlan。为了实际服务于辅助overlay,我们需要更改VXLAN标识符“ VIN”,默认情况下将其设置为1,并且我们的第一个overlay网络现在已经使用了它。因此,我们可以通过在etcd服务器上配置网络来更改此设置。我们使用自己的集群etcd,此处标记为绿色(并且假设job在运行etcd客户端的主机上运行),然后从本地主机(在我们的情况下,将其存储在本地主机)中装入密钥(此处标记为红色),存储在/ etc / kubernetes / ssl文件夹中。
完整的YAML文件示例:
接下来,我们可以实际部署辅助网络。此设置几乎与主要网络设置相同,但有一些关键区别。最明显的是,我们更改了子网,但是我们还需要更改其他一些内容。
首先,我们需要设置一个不同的dataDir,即/ var / lib / cni / flannel2,以及一个不同的subnetFile,即/run/flannel/flannel2.env。这十分必要,因为它们已经被我们的主要网络占用。接下来,我们需要调整网桥,因为主要的Flannel overlay网络已经使用了kbr0。
其余还需更改的配置包括将其更改为实际针对我们之前配置的etcd服务器。在主网络中,这是通过–kube-subnet-mgr flag直接连接到K8S API来完成的。但是我们不能这样做,因为我们还需要修改要读取的前缀。你可以在下面看到橙色标记的内容,而集群etcd连接的设置则显示为红色。最后一个设置是再次指定子网文件,在示例中以绿色标记。最后一点是,我们添加了一个网络定义。其余部分与我们的主要网络配置相同。
有关上述步骤,请参见示例配置文件:
完成此操作后,我们便准备好了辅助网络。
分配额外的网络
既然我们已经准备好辅助网络,那么我们现在需要分配他。为此,我们需要先定义一个NetworkAttachmentDefinition,之后我们可以使用它将网络分配给容器。基本上,这是在初始化Multus之前,我们设置的configmap的动态替代方案。这样,我们可以按需安装所需的网络。在此定义中,我们需要指定网络类型(本例中是Flannel)以及必要的配置。这包括前面提到的subnetFile、dataDir和网桥名称。
我们需要确定的最后一件事是网络的名称,我们将其命名为flannel2。
现在,我们终于可以使用辅助网络生成第一个pod。
现在应该使用辅助网络创建新的Pod,并且我们将那些附加网络视为额外添加的网络接口。
成功啦,辅助网络分配10.5.22.4作为其IP地址。
Troubleshooting
如果该示例没有正常工作,你需要查看kubelet的日志。
一个常见的问题的是缺少CNI。我第一次测试的时候,遗漏了CNI网桥,因为RKE没有部署它。但是这个问题十分容易解决。
外部连接和负载均衡
现在我们已经建立并运行网络,接下来我们要做的是使我们的应用程序可以访问并将其配置为高可用和可扩展。高可用性和可伸缩性不仅可以通过负载均衡来实现,它还我们需要具备的关键组件。
Kubernetes有四个概念,可以使应用程序在外部可用。
使用负载均衡器
Ingress
Ingress基本上就是具有Layer7功能的负载均衡器,特别是HTTP(s)。最常用的ingress controller是NGINX ingress。但这主要取决于你的需求以及你的使用场景。例如,你还可以选择traefik或HA Proxy。
配置一个ingress十分简单。在以下例子中,你将了解一个链接服务的例子。蓝色标注的是指向服务的基本配置。绿色标注的是链接SSL证书所需的配置(需要在此之前安装这一证书)。最后,你会看到调整了NGINX ingress的一些详细设置。
Layer 4 负载均衡器
在Kubernetes中,使用type: LoadBalancer定义Layer 4 负载均衡器,这是一个依赖于负载均衡解决方案的服务提供程序。对于本地计算机,大概率会使用HA代理或一个路由解决方案。云提供商会使用自己的解决方案以及专用硬件,也可以使用HA代理或路由解决方案。
最大的区别是第4层负载平衡器不了解高级应用程序协议(layer 7),并且仅能够转发流量。此级别上的大多数负载均衡器还支持SSL终止。这通常需要通过注释进行配置,并且尚未标准化。
使用 {host,node} 端口
{host,node} Port基本上等同于docker -p port:port,尤其是hostPort。与hostPort不同,nodePort在所有节点上可用,而不是仅在运行pod的节点上可用。对于nodePort,Kubernetes首先创建一个clusterIP,然后通过该端口负载均衡流量。nodePort本身只是将端口上的流量转发到clusterIP的iptable规则。
除了快速测试外,很少使用nodePort,只有在你希望每个节点公开端口(即用于监视)时才会在生产中使用nodePort。大多数时候,你需要使用Layer 4负载均衡器。hostPort仅用于测试,或者少数时候,将pod粘贴到特定节点并在指向该节点的特定IP地址下发布。
例如,在容器规范中定义了hostPort,如下所示:
什么是ClusterIP ?
clusterIP是Kubernetes集群及其中所有服务的内部可访问IP。该IP本身将负载均衡流量到与其selector规则匹配的所有Pod。在很多情况下,例如在指定类型:LoadBalancer服务或设置nodePort时,也会自动生成clusterIP。其背后的原因是所有负载均衡都是通过clusterIP进行的。
clusterIP作为一个概念是为了解决多个可寻址主机以及这些主机的有效更新的问题。具有不变的单个IP比始终通过服务发现针对服务的所有性质重新获取数据要容易得多。尽管有时在某些情况下更适合使用服务发现,但如果你想要explicit control,那么还是建议使用clusterIP,如在某些微服务环境中。
常见的故障
如果您使用公有云环境并手动设置主机,则您的集群可能缺少防火墙规则。例如,在AWS中,您将需要调整安全组,以允许集群间通信以及ingress和egress。如果不这样做,将导致集群无法运行。确保始终打开主节点和worker节点之间的必要端口。直接在主机上打开的端口(即hostPort或nodePort)也是如此。
网络安全
既然我们已经设置了所有Kubernetes网络,我们还需要确保它们具备一定的安全性。保证安全性的最低原则是为应用程序提供其运行所需的最少访问量。这可以在一定程度上确保即使在发生安全漏洞的情况下,***者也将难以深入挖掘你的网络。虽然它不能完全确保你的安全,但无疑会使***者进行***时变得更加困难和耗时。这很重要,因为它会使你有更多的时间做出反应并防止进一步的破坏。这里有一个典型的例子,不同应用程序的不同exploits/漏洞的组合,这使得***者只有从多个维度(例如,网络、容器、主机)到达任何***面的情况下,才能进行***。
这里的选择要么是利用网络策略,要么是寻求第三方安全解决方案以实现容器网络安全。有了网络策略,我们有坚实的基础来确保流量仅在流量应流的地方进行,但这仅适用于少数几个CNI。例如,它们可与Calico和Kube-router一起使用。Flannel不支持它,但是幸运的是,你可以移至Canal,这使得Flannel可以使用Calico的网络策略功能。对于大多数其他CNI,则没有支持,目前尚未有支持的计划。
但这不是唯一的问题。问题在于,网络策略规则只是针对特定端口的防火墙规则,它十分简单。这意味着你无法应用任何高级设置。例如,如果你发现某个容器可疑,就不能按需阻止它。进一步来说,网络规则无法理解流量,因此你不知道流量的流向,并且仅限于在第3层和第4层上创建规则。最后,它还无法检测到基于网络的威胁或***,例如DDoS,DNS,SQL注入以及即使在受信任的IP地址和端口上也可能发生的其他破坏性网络***。
因此,我们需要专用的容器网络安全解决方案,它可为关键应用程序(例如财务或合规性驱动的应用程序)提供所需的安全性。我个人喜欢NeuVector。它具有我曾在Arvato / Bertelsmann进行部署的容器防火墙解决方案,并提供了我们所需的Layer7可见性和保护。
应该注意的是,任何网络安全解决方案都必须是云原生的,并且可以自动扩展和调整。部署新应用程序或扩展Pod时,你无需检查iptable规则或更新任何内容。也许对于几个节点上的简单应用程序堆栈,你可以手动进行管理,但是对于任何企业而言,部署安全不能减慢CI / CD流水线的速度。
除了安全性和可见性之外,我还发现拥有连接和数据包级容器网络工具有助于在测试和staging期间调试应用程序。借助Kubernetes网络,除非您能看到流量,否则您将永远无法真正确定所有数据包的去向以及将哪些Pod路由到其中。
选择网络CNI的一些建议
现在已经介绍了Kubernetes网络和CNI,始终会出现一个大问题:应该选择哪种CNI解决方案?我将尝试提供一些有关如何做出此决定的建议。
首先,定义问题
每个项目的第一件事是尽可能详细地定义你需要首先解决的问题。你也许想知道要部署哪种应用程序以及它们将产生什么样的负载。你可能会问自己的一些问题:
我的应用程序:
网络是否繁忙?
是否对延迟敏感?
是单体架构吗?
还是微服务架构服务?
需要在多个网络上吗?
我可以承受宕机时间吗,甚至是最小的宕机时间?
这是一个十分重要的问题,因为你需要事先确定好。如果你现在选择一种解决方案,以后再进行切换,则需要重新设置网络并重新部署所有容器。除非你已经拥有Multus之类的东西并且可以使用多个网络,否则这将意味着您的服务会停机。在大多数情况下,如果你有计划的维护时段,那么事情会没那么严重,但是随着应用程序的不断迭代,零停机时间变得更加重要!
我的应用程序在多个网络上
这一情况在本地安装中十分常见,实际上,如果你只想将通过专用网络和公用网络的流量分开,那么这需要你设置多个网络或者有智能的路由。
我是否需要CNI中的某些特定功能?
影响你做决定的另一件事是,你需要一些特定的功能,在某些CNI中可用,而其他CNI中不可用。例如,你想使用Weave或希望通过ipvs进行更为成熟的负载均衡。
需要什么网络性能?
如果你的应用程序对延迟敏感或网络繁忙,那么你需要避免使用任何overlay网络。Overlay在性能上并不划算,规模上也是如此。这这种情况下,提高网络性能的唯一方法是避免overlay并改用路由之类的网络实用程序。寻找网络性能时,你有几种选择,例如:
Ipvlan:它有良好的性能,但需要注意,你不能在同一主机上同时使用macv{tap,lan}。
Calico:这个CNI不是对用户最友好的,但于vxlan相比,它可以为你提供更好的性能,并且可以进行扩展而无需担心。
Kube-Router:它通过使用BGP和路由,以及支持LVS/IPVS,来提供更好的性能(这与Calico类似)。但Calico比它更为成熟。
云提供商解决方案:一些云提供商提供了自己的网络解决方案,这些方案的好坏需要根据具体情况来确定,这里无法一概而论。值得一提的是,Rancher的一个开源项目Submariner。它支持多个Kubernetes集群之间的跨集群网络连接,并且创建了必要的隧道和路径,能为部署在需要相互通信的多个Kubernetes集群中的微服务提供网络连接。
我只是想要一些可行的方法!
在这样的情况下,推荐使用canal或带有vxlan的flannel,因为它们十分容易且有效。但是正如我之前所提到的,vxlan速度很慢,随着应用程序的不断发展,它将耗费大量资源。但是对于刚刚起步的项目而言,这绝对是最简单的方法。
做出决定
这实际上是做出决定而不是根本不做出决定的问题。如果你没有特定的功能要求,则可以从Flannel和vxlan开始。如果您已部署到生产环境,稍后需要一些工作以进行迁移,但是从长远来看,做出错误的决定总比完全不做出决定要好。
有了所有这些信息,我希望您对Kubernetes网络的工作方式有一些相关的背景和更好的了解。