27.7 网络设计
网络对于集群来说是十分关键的功能。Kubernetes在设计上考虑了对网络的需求和模型设计,但自身并没有重新实现,而是可以另外嵌入现有的网络管理方案。同时,Kubernetes试图通过插件化的形式来采用AppC提出的Container Networking Interface(CNI)规范。这意味着,将来所有支持Kubernetes的网络插件都要遵循该规范。
实际上,CNI的模型十分简洁,Kubernetes只需要告诉插件,把某个Pod挂载到某个网络、或者从某个网络卸载,其他工作都由插件来完成。Kubernetes自身不需要了解网络的具体细节。
对于Kubernetes集群来说,一般要考虑如下四种通信场景:
·Pod内(容器之间):因为容器共享了网络命名空间,可以通过lo直接通信,无需额外支持;
·Pod之间:又分在同一个节点上和在不同节点上,前者通过本地网桥通信即可,后者需要在各自绑定的网桥之间打通;
·Pod和服务之间:因为服务是虚拟的ClusterIP,因此,需要节点上配置代理机制(例如基于iptables)来映射到后端的Pod。
·外部访问服务:要从外面访问服务,必须经过负载均衡器,通过外部可用的地址映射到内部的服务上。
其实Docker默认采用iptables实现NAT的方式(后来也支持overlay模式,但所提出的CNM规范未被Kubernetes接纳)已经通过借用主机地址组成了简单的网络。但Kubernetes认为NAT方式实现跨节点通信就需要占用本地端口映射,这会给服务层面的访问带来麻烦。
Kubernetes在网络方面的设计理念包括如下几点:
·所有容器之间不使用NAT就可以互相通信;
·所有节点跟容器之间不使用NAT就可以互相通信;
·容器自己看到的地址,跟其他人访问自己使用的地址应该是一样的(其实还是在说不要有NAT)。
可以看到,这个设计理念跟云平台里面的虚拟机网络十分类似,这意味着一些基于虚拟机云的项目可以很方便地迁移到Kubernetes平台上。
要实现这几点需求,可以有两种设计思路:直接路由和Overlay网络,这也是现在云计算领域常见的网络实现方式。
1.直接路由
这种思路最简单,所有Pod直接暴露在物理网络上,大家彼此地址可见,不能有地址冲突,不同子网之间通过路由机制进行三层转发。
此时,各个Node上会创建cbr0网桥,并且需要在开启本地转发支持:
- $ sysctl net.ipv4.ip_forward=1
另外,配置Docker服务的默认网桥,并且取消Docker对iptables的自动修改:
- DOCKER_OPTS="--bridge=cbr0 --iptables=false --ip-masq=false"
为了让Pod可以通过Node地址来访问外网(因为Pod的私有数据地址是无法路由到外部的),可以配置SNAT:
- $ iptables -t nat -A POSTROUTING ! -d 10.0.0.0/8 -o eth0 -j MASQUERADE
这种实现的最大优势是简洁,可以直接复用底层的物理设备。目前,包括Google的GCE和微软的容器云在内的许多实现都支持这种模式。
2.Overlay网络
Overlay网络相对要复杂一些,支持底层更灵活的转发。目前包括Flannel、Open-VSwitch、Weave、Calico等一系列方案都能实现用Overlay网络来联通不同节点上的Pod。
以Flannel网络为例,提前为各个Node分配互不重合的子网,例如将完整的172.16.0.0/16私有网段划分为多个子网172.16.10.0/24、172.16.11.0/24……,各个Node上的Pod分配网络地址的时候只能从这个子网范围内分配,避免了地址的冲突,如图27-5所示。

图27-5 Kubernetes使用Flannel网络
所有Pod都挂载到docker0网桥上,网桥的内部接口(docker0)作为子网网关接口。
同时,在各个Node上创建网络接口(如图中的flannel.1),该接口分配子网的0号地址,负责处理往其他节点发送的网包。当docker0收到目标地址为其他节点上Pod的网包时,根据路由表会扔到flannel.1接口,由flanneld进程进行封装,通过隧道发送到对应节点上进行解封装。
以左边节点为例,本地路由表为:
- $ route -en
- Kernel IP routing table
- Destination Gateway Genmask Flags MSS Window irtt Iface
- 0.0.0.0 192.168.122.1 0.0.0.0 UG 0 0 0 eth0
- 172.16.0.0 0.0.0.0 255.255.0.0 U 0 0 0 flannel.1
- 172.16.10.0 0.0.0.0 255.255.255.0 U 0 0 0 docker0
- 191.167.121.1 0.0.0.0 255.255.255.0 U 0 0 0 eth0
