Kubernetes的网络模型
Pod IP
Kubernetes的网络模型主要分为三层。第一层是Pod的多个容器之间的互通,这层实现起来比较简单,因为所有的容器都共享一个网卡,所以可以直接通信。第二层是同个宿主机中多个Pod的互通,这方面其实也很好解决,可以通过Docker默认服务创建的Docker 0的网桥进行通信,其他的像Calico、Flannel这样的网络插件也会创建类似的网桥来方便通信。第三层是跨主机的容器互通,关于这层kubernetes只是定义了规范并没有真正的实现,包括IP分配、IP通信,IP是否固定这些kubernetes都不会管,仅仅是规定了所有的部分都需要有一个IP才能互通。
Kubernetes网络模型的缺陷
动态IP or 固定IP
过去Kubernetes网络模型的动态IP模式是我们在与客户交流过程中遇到的最大难题,一般很难有客户能够接受非固定IP的形式,大多是强烈要求采用固定IP。
从传统角度来看,运维是管控所有的计算网络资源的,有精细化的IP管理。因此完全的IP随机分配对他们来说在理念上就是一个有很大的冲击,除此之外很多的传统工作也离不开基于IP的技术,比如基于IP的监控、安全策略。
从实际角度来看,传统的服务部署也有很多是基于IP的实现,如果IP无法固定会带来很多问题。最典型的就是Etcd的部署,传统的模式中这很容易实现,但是在kubernetes中就需要仔细考量下,在理念上会有很大的转变,要理解些新的概念。
从以上分析来看,其实客户想要固定IP的需求并非是因为理念上的固执,而是有一定的合理的依据。
反过来看如果kubernetes有了固定IP,又会有哪些影响。细细想想其实整体来看并不会有太大的影响,原先的kubernetes的服务发现机制依然可用,甚至还多了一种方式,传统的基于IP的监控运维也没什么问题。
目前之所以很少看到固定IP的模式,个人认为主要有三方面的原因。首先Docker最初就是基于单机的设计,没有想过大规模集群的应用,所以不会涉及到对IP的管控。第二可能是理念之争。到底服务应该是有状态还是无状态?现在业界都在追求微服务、无状态,不关心网络、存储只关注计算,但是其实很难做到真正的无状态,即使自身的服务是无状态的也还是会调用第三方的服务留下状态信息。第三是因为实现起来难度很高,固定的IP所带来的是有状态,而有状态的服务往往都很难去做。
固定IP是不是无法实现呢?其实使用CNI就能很好的解决。
CNI简介
CNI是CNCS的子项目,它为容器提供了一套标准的网络接口,具体分为两部分,一部分是CNI标准,包括如何实现CNI以及一些接口和库,另一部分是官方的实现,会提供一些CNI网络插件。
CNI and kubernetes
这里我们看下CNI和kubernetes之间是如何交互的。
通过kubernetes命令行和CNI关联会有两个选项,分别是CNI配置地址和CNI二进制文件目录。一般程序之间的交互,大多是RPC、HTTP之类的通信方式,但是CNI和kubernetes的交互是通过二进制文件的方式。
CNI的配置中比较重要的是type和IPAM,type表示网卡的生成方式,IPAM指定IP分配的方式。创建Pod的时候,CNI会根据这个文件先调用type指定的方式,创建一个网卡,然后调用IPAM指定的方式,获取IP。销毁Pod的时候,反过来调用,先释放IP再删除网卡。
不同的CNI之间也是通过Config来进行通信。具体的结构中首先interface中会有一个MAC地址,这在一般的网络插件中是没有,而这里提供了设置。配置中的IP是列表的形式,多IP的形式方便了传统功能的实现。另外每台Pod的网络路由和DNS都可以自行设置。
固定IP
针对前面提到的固定IP的实现,只需要用到CNI关于IP控制的插件,目前已有的分别是DHCP和host-local,不过他们都不能很好的满足我们的需求。DHCP的随机分配模式在生产环境中很少得到应用,和容器网络也很难结合起来;host-local会限定每台机器的固定网络范围,增减机器的时候重新分配IP很困难。因此我们最后将IP给提取出来作为第一级的资源,进行单独的管控。我们专门实现了衣蛾IPAM的插件,它包含网段添加和删除、网关路由设置、权限控制和配合等功能。
坑和期望
CNI本质上是事件驱动的模型,因此很有可能会遇到丢事件的问题,这里具体表现为执行失败、kubernetes挂了、机器挂了等等,从而导致IP泄露、IP冲突。所以如果要做固定IP就一定要加入垃圾处理回收的机制,通过补偿机制将不同步的状态改为同步。
未来我们可能会做一些更灵活的网络,通过插件在容器的生命周期内改变网络配置,包括固定MAC、动态路由、dns。另外还想要和现有系统解耦以及支持更多的网络模式。