在实际的业务场景中,业务组件之间的关系十分复杂,特别是随着微服务理念逐步深入人心,应用部署的颗粒度更加细小和灵活,为了支持业务应用组件的通信联系,Kubernets网络的设计主要致力于解决以下场景。
在同一个Pod内的容器共享同一个网络命名空间,共享同一个Linux协议栈。所以对于网络的各类操作,就和它们在同一台机器上一样,它们甚至可以用localhost地址来访问彼此的端口。
这么做的结果就是很简单、安全和高效,也能减少将已经存在的程序从物理机或者虚拟机移植到容器下运行的难度。

图1 Kubernets的Pod网络模型
先来看下Kubernets是如何利用docker网络模型的。图1的阴影部分就是在Node1上运行着的一个Pod实例。容器1和容器2共享了一个网络名称空间,这也就意味着它们就好像在同一台机器上运行似的,它们打开的端口不会有冲突,可以直接使用Linux本地的IPC进行通信。其实这和传统一组普通程序运行的环境是完全一样的,传统的程序不需要针对网络做特别的修改就可以移植了,它们之间的互相访问只需要使用localhost就可以了。
我们知道每一个Pod都有一个真实的全局IP地址,同一个Node内的不同Pod之间可以采用对方Pod的IP地址通信,而且不需要使用其他发现机制,例如DNS、Consul或者etcd。
Pod容器既有可能在同一个Node上运行,也有可能在不同的Node上运行,所以通信也分为两类:同一个Node内的Pod之间的通信和不同Node上的Pod之间的通信。
1)同一个Node内的Pod之间的通信

图2 同一个Node内Pod的关系
我们看一下同一个Node上两个Pod之间的关系。从图2可以看出,Pod1和Pod2都是通过Veth连接在同一个Docker0网桥上的,它们的IP地址IP1和IP2都是从docker0的网段上动图获取的,它们和网桥本身的IP3是同一个网段的。
另外,在Pod1和Pod2的Linux协议栈上,默认路由都是docker0的地址,也就是说所有非本地地址的网络数据,都会默认发送到docker0网桥上,由docker0直接转发。
综上所述。由于它们都是关联在同一个docker0网桥上,地址段相同,所以它们之间是能直接通信的。
2)不同Node上的Pod之间的通信
Pod的地址是与docker0在同一网段内的,我们知道docker0网段与宿主机网卡是两个完全不同的IP网段,并且不同Node之间的通信只能通过访问宿主机的物理网卡进行,因此想要时间位于不同Node上的Pod容器之间的通信,就必须想办法通过主机的这个IP地址来寻址和通信。
另一方面,这些动态分配且藏在docker0之后的所谓“私有”IP地址也是可以找到的。Kubernets会记录所有正在运行Pod的IP分配信息,并将这些信息保存在etcd中(作为Service的Endpoint)。这些私有IP信息对于Pod到Pod的通信也是十分重要的,因为我们的网络模型要求Pod到Pod使用私有IP进行通信。所以首先要知道这些IP是什么。
之前提到,Kubernets的网络对Pod的地址是平面和直达的,所以这些Pod的IP规划也是很重要的,不能有冲突。只要没有冲突,我们就可以想办法在整个Kubernets的集群中找到它。
综上所述,想要支持不同的Node上的Pod之间的通信,就要满足两个条件:
(1)在整个Kubernets集群中对Pod的IP地址分配进行规划,不能有冲突;
(2)找到一种办法,将Pod的IP地址和所在的Node的IP关联起来,通过这个关联让Pod可以互相访问。

图3 跨Node的Pod通信
在图3中IP1对应Pod1,IP2对应Pod2。Pod1在访问Pod2时,首先要将数据从源Node1的eth0发送出去,找到并到达Node2的eth0,也就是说要从IP3到IP4之后才是IP4到IP2的递送。
在私有环境中跨Node的Pod之间的网络主要是通过一些网络插件(如Flannel、Calico等)来实现的,由它们管理资源池的分配,做到这些,Pod和Pod就能无差别地透明通信了。在后面的文章会介绍常用的组件和它们的组网原理。