Pod的特点:容器
1、有自己的IP地址
2、有自己的hostname
3、有自己的端口
Pod实际上可以理解为就是k8s云平台中的虚拟机,而这个pod内部封装的是由docker引擎所创建的容器,也可以理解为pod就是一个虚拟化分组,pod内部可以存储一个或者多个容器。
所谓Pod网络,就是能够保证K8s集群中的所有Pods(包括同一节点上的,也包括不同节点上的Pods),逻辑上看起来都在同一个平面网络内,能够相互做IP寻址和通信的网络,下图是Pod网络的简化概念模型:
网络通讯模式的方式:
1、同一个Pod内的多个容器之间:localhost
2、各Pod之间的通讯:Overlay Network
3、Pod与Service之间的通讯:各节点的Iptables规则
思考:pod内部的容器是如何通信的?
思考:同一个节点中的pod是如何通信的?
思考:不同节点中的pod是如何通信的?
思考:pod如何对外网提供服务???
是否可以通过外网直接访问pod的ip:port ???
当然不可以的,pod只是一个进程,没有与之对应的物理实体。要在现实的网络中进行通信,必须要有与之对应的物理实体。也就是说pod要想和外部进行通信,必须借助于物理网卡。
上图节点上展示了Pod网络所依赖的3个网络设备,eth0是节点主机上的网卡,这个是支持该节点流量出入的设备,也是支持集群节点间IP寻址和互通的设备。docker0是一个虚拟网桥,可以简单理解为一个虚拟交换机,它是支持该节点上的Pod之间进行IP寻址和互通的设备。veth0则是Pod1的虚拟网卡,是支持该Pod内容器互通和对外访问的虚拟设备。docker0网桥和veth0网卡,都是linux支持和创建的虚拟网络设备。
上图Pod1内部住了3个容器,它们都共享一个虚拟网卡veth0。内部的这些容器可以通过localhost相互访问,但是它们不能在同一端口上同时开启服务,否则会有端口冲突,这就是共享网络栈的意思。Pod1中还有一个比较特殊的叫pause的容器,这个容器运行的唯一目的是为Pod建立共享的veth0网络接口。如果你SSH到K8s集群中一个有Pod运行的节点上去,然后运行docker ps,可以看到通过pause命令运行的容器。
即pod内部容器是通过共享一个虚拟网卡相互通信的,可以直接通过localhost相互访问,而这个虚拟网卡是通过一个特殊的容器pause创建的。
docker0是一个虚拟网桥,可以简单理解为一个虚拟交换机,它是支持该节点上的Pod之间进行IP寻址和互通的设备。Pod的IP是由docker0网桥分配的,例如上图docker0网桥的IP是172.17.0.1,它给第一个Pod1分配IP为172.17.0.2。如果该节点上再启一个Pod2,那么相应的分配IP为172.17.0.3,如果再启动Pod可依次类推。因为这些Pods都连在同一个网桥上,在同一个网段内,它们可以进行IP寻址和互通。
即相同节点的pod通信是通过当前节点的虚拟网桥相互通信的
实际上不同节点间的Pod网络互通,有很多技术实现方案,底层的技术细节也很复杂。为了简化描述,我把这些方案大体分为两类,一类是路由方案,另外一类是覆盖(Overlay)网络方案。
这个方案简单理解,就是通过路由设备为K8s集群的Pod网络单独划分网段,并配置路由器支持Pod网络的转发。例如上图中,对于目标为172.17.1.0/24这个范围内的包,转发到10.100.0.3这个主机上,同样,对于目标为172.17.0.0/24这个范围内的包,转发到10.100.0.2这个主机上。当主机的eth0接口接收到来自Pod网络的包,就会向内部网桥转发,这样不同节点间的Pod就可以相互IP寻址和通信。这种方案依赖于底层的网络设备,但是不引入额外性能开销。
如果底层的网络是你无法控制的,比如说公有云网络,或者企业的运维团队不支持路由方案,可以采用覆盖(Overlay)网络方案,如下图所示:
Flannel是CoreOS团队针对Kubernetes设计的一个网络规划服务,简单来说,它的功能是让集群中的不同节点主机创建的Docker容器都具有全集群唯一的虚拟IP地址。而且它还能在这些IP地址之间建立一个覆盖网络(Overlay Network),通过这个覆盖网络,将数据包原封不动地传递到目标容器内。
所谓覆盖网络,就是在现有网络之上再建立一个虚拟网络,实现技术有很多,例如flannel/weavenet等等,这些方案大都采用隧道封包技术。简单理解,Pod网络的数据包,在出节点之前,会先被封装成节点网络的数据包,当数据包到达目标节点,包内的Pod网络数据包会被解封出来,再转发给节点内部的Pod网络。这种方案对底层网络没有特别依赖,但是封包解包会引入额外性能开销。
Kubernetes-基于Flannel的网络原理
flannel组建一个大二层扁平网络,pod的ip分配由flannel统一分配,通讯过程也是走flannel的网桥。每个node上面都会创建一个flannel0虚拟网卡,用于跨node之间通讯。所以容器直接可以直接使用pod id进行通讯。
跨节点通讯时,发送端数据会从docker0路由到flannel0虚拟网卡,接收端数据会从flannel0路由到docker0。
外网要和内部的pod进行通信,必须在物理机上开辟一个端口,通过物理机的端口对数据包(请求)进行转发,把请求转发给pod。实现pod内部服务外网访问。
将物理机的端口 和 内部pod(进程)做映射,访问物理机ip 和 端口,可以转发到对应的pod(内部进程的互连)
可以使用iptabels的配置规则实现数据包转发
有了Pod网络,K8s集群内的所有Pods在逻辑上都可以看作在一个平面网络内,可以正常IP寻址和互通。但是Pod仅仅是K8s云平台中的虚拟机抽象,最终,我们需要在K8s集群中运行的是应用或者说服务(Service),而一个Service背后一般由多个Pods组成集群,这时候就引入了服务发现(Service Discovery)和负载均衡(Load Balancing)等问题
同一个Pod内部通讯
同一个Pod共享同一个网络命名空间,共享同一个 Linux协议栈
pod1和pod2通讯--不在同一台机器:
Pod1与Pod2不在同一台主机,Pod的地址是与 docker0在同一个网段的,但doke0网段与宿主机网卡是两个完全不同的IP网段,并且不同Node之间的通信只能通过宿主机的物理网卡进行。将Pod的IP和所在Node的IP关联起来,通过这个关联让Pod可以互相访问
pod1和pod2通讯–在同一台机器:
Pod1与Pod2在同一台机器,由 Docker0网桥直接转发请求至Pod2,不需要经过 Flanne1
Pod至 Service的网络
目前基于性能考虑,全部为 iptables(现在版本都是通过LVS)维护和转发
Pod到外网
Pod向外网发送请求,查找路由表,转发数据包到宿主机的网卡,宿主网卡完成路由选择后, iptables执行 Masquerade,把源IP更改为宿主网卡的IP,然后向外网服务器发送请求
外网访问Pod
外网访问Pod通过Service