一、Kubernetes基本网络模型
Kubernetes对一个容器网络是否合格做出了限制,也就是Kubernetes等容器网络模型,大致可以分为约法三章和四大目标:
- 约法三章指的是在评价一个容器网络或者设计容器网络的时候,它需要满足三个准入条件,这样的网络才能认为它是一个合格的网络。
- 四大目标的意思是在设计网络具体功能实现的时候,要想清楚,能不能达成连通性这四大指标。
约法三章:
- 第一条:任意两个pod之间是可以直接通信的,无需经过显式地使用NAT来接收数据和地址的转换;
- 第二条:node和pod之间是可以直接通信的,无需使用显式地址转换;
- 第三条:pod看到自己的IP和别人看到它所用的IP是一样的,中间不能经过转换。
四大目标:
- 外部世界和service之间是怎么通信的?比如说:一个互联网或者公司外部的一个用户,如何用到service?注意:这里的service是k8s中的服务概念。
- service如何与它后端的pod通信?
- pod和pod之间调用是如何通信的?
- 最后是一个pod内的容器与容器之间是如何通信的?
对基本约束的解释
因为容器的网络发展的复杂性就在于它是寄生于Host网络之上的,从这个角度上讲,可以把容器网络方案大体分为Underlay / Overlay两大派别:
- Underlay 的标准是它与Host网络是同层的,从外在可见的一个特征就是它是不是使用了Host网络同样的网段、输入输出、基础设备、容器的IP地址是不是需要与Host网络取得协同(来自同一个中心分配或统一划分);
- Overlay 不一样的地方就在于它并不需要从 Host 网络的 IPM 管理的组件去申请IP,一般来说,它只需要跟 Host 网络不冲突,这个 IP 可以自由分配的。
这两者的主要差别在于是否与Host网络同层,这样对于微服务发现及治理,容器可访问方式都造成很大的差异,所以社区的同学以perPodperIP这种简单的模型,摈弃了显示端口等NAT配置,统一了容器网络对外服务的视角。
二、Network Namespace(网络命名空间)
作用:
Network Namespace(Netns)是实现网络虚拟化的内核基础,创建了隔离的网络空间
- 拥有独立的附属网络设备(Io、veth等虚设备/物理网卡)
- 独立的协议栈,IP地址和路由表
- iptables规则
- ipvs等
pod与Network Namespace的关系
上图可以清晰表明 pod 里 Network Namespace 的关系,每个 pod 都有着独立的网络空间,pod net container 会共享这个网络空间。一般 K8s 会推荐选用 Loopback 接口,在 pod net container 之间进行通信,而所有的 container 通过 pod 的 IP 对外提供服务。另外对于宿主机上的 Root Netns,可以把它看做一个特殊的网络空间,只不过它的 Pid 是1。
三、典型的容器网络实现方案
容器网络方案可能是 K8s 里最为百花齐放的一个领域,它有着各种各样的实现。容器网络的复杂性,其实在于它需要跟底层 Iass 层的网络做协调、需要在性能跟 IP 分配的灵活性上做一些选择,方案是多种多样的。
下面简单介绍几个比较主要的方案:分别是 Flannel、Calico、Canal ,最后是 WeaveNet,中间的大多数方案都是采用了跟 Calico 类似的策略路由的方法。
- Flannel,最为普遍的实现,提供多种网络 backend实现。不同的 backend 实现了不同的拓扑,覆盖多种场景。
- Calico,主要是采用了策略路由,节点之间采用 BGP 的协议,去进行路由的同步。它的特点是功能比较丰富,尤其是对 Network Point 支持比较好,大家都知道 Calico 对底层网络的要求,一般是需要 mac 地址能够直通,不能跨二层域;
- Canal (Flannel for network + Calico for firewalling),嫁接型创新项目。
- Cilium,基于eBPF和XDP的高性能Overlay 网络方案。
- Kube-router,同样采用BGP提供网络直连,集成基于LVS的负载均衡能力。
- Romana,采用BGP or OSPF提供网络直连能力的方案。
- WeaveNet,采用UDP封装实现L2Overlay,支持用户态(慢,可加密)/内核态(快,不能加密) 两种实现。
Flannel方案
Flannel 方案是目前使用最为普遍的。如上图所示,可以看到一个典型的容器网方案。它首先要解决的是 container 的包如何到达 Host,这里采用的是加一个 Bridge 的方式。它的 backend 其实是独立的,也就是说这个包如何离开 Host,是采用哪种封装方式,还是不需要封装,都是可选择的。
现在来介绍三种主要的 backend:
- 一种是用户态的 udp,这种是最早期的实现;
- 然后是内核的 Vxlan,这两种都算是 overlay 的方案。Vxlan 的性能会比较好一点,但是它对内核的版本是有要求的,需要内核支持 Vxlan 的特性功能;
- 如果你的集群规模不够大,又处于同一个二层域,也可以选择采用 host-gw 的方式。这种方式的 backend 基本上是由一段广播路由规则来启动的,性能比较高。
四、Network Policy 基本概念
刚才提到了 Kubernetes 网络的基本模型是需要 pod 之间全互联,这个将带来一些问题:可能在一个 K8s 集群里,有一些调用链之间是不会直接调用的。比如说两个部门之间,那么我希望 A 部门不要去探视到 B 部门的服务,这个时候就可以用到策略的概念。
基本上它的想法是这样的:它采用各种选择器(标签或 namespace),找到一组
pod,或者找到相当于通讯的两端,然后通过流的特征描述来决定它们之间是不是可以联通,可以理解为一个白名单的机制。
在使用 Network Policy 之前,如上图所示要注意 apiserver 需要打开一下这几个开关。
另一个更重要的是我们选用的网络插件需要支持 Network Policy 的落地。
大家要知道,Network Policy 只是 K8s 提供的一种对象,并没有内置组件做落地实施,需要取决于你选择的容器网络方案对这个标准的支持与否及完备程度,如果你选择 Flannel 之类,它并没有真正去落地这个 Policy,那么你试了这个也没有什么用。
配置实例
接下来讲一个配置的实例,或者说在设计一个 Network Policy 的时候要做哪些事情?我个人觉得需要决定三件事:
- 第一件事是控制对象,就像这个实例里面 spec 的部分。spec 里面通过 podSelector 或者 namespace 的 selector,可以选择做特定的一组 pod 来接受我们的控制;
- 第二个就是对流向考虑清楚,需要控制入方向还是出方向?还是两个方向都要控制?
- 最重要的就是第三部分,如果要对选择出来的方向加上控制对象来对它流进行描述,具体哪一些 stream 可以放进来,或者放出去?类比这个流特征的五元组,可以通过一些选择器来决定哪一些可以作为我的远端,这是对象的选择;也可以通过 IPBlock 这种机制来得到对哪些 IP 是可以放行的;最后就是哪些协议或哪些端口。其实流特征综合起来就是一个五元组,会把特定的能够接受的流选择出来 。
五、总结
- 在 pod 的容器网络中核心概念就是 IP,IP 就是每个 pod 对外通讯的地址基础,必须内外一致,符合 K8s 的模型特征;
- 影响容器网络性能最关键的就是拓扑。要能够理解你的包端到端是怎么联通的,中间怎么从 container 到达 Host,Host 出了 container 是要封装还是解封装?还是通过策略路由?最终到达对端是怎么解出来的?
- 容器网络选择和设计选择。如果你并不清楚你的外部网络,或者你需要一个普适性最强的方案,假设说你对 mac 是否直连不太清楚、对外部路由器的路由表能否控制也不太清楚,那么你可以选择 Flannel 利用 Vxlan 作为 backend 的这种方案。如果你确信你的网络是 2 层可直连的,你可以进行选用 Calico 或者 Flannel-Hostgw 作为一个 backend;
- 最后就是对 Network Policy,在运维和使用的时候,它是一个很强大的工具,可以实现对进出流的精确控制。实现的方法我们也介绍了,要想清楚你要控制谁,然后你的流要怎么去定义。