跟唐老师学习云网络 - CloudFoundry网络实现
在K8s独霸天下之前,CloudFoundry才是那时的PaaS平台一哥呢。虽然你之前可能没了解过CloudFoundry是什么,不过今天,我们可以来回顾一下当年老PaaS是如何实现容器集群中网络分发的,也许还能顺道看下这家伙是如何被后浪拍死在沙滩上的。
1 简单介绍下CloudFoundry
Docker解决了单机上面的容器管理,当在大规模集群上面管理容器集群时,则需要依赖PaaS平台。CloudFoundry就是业界首个开源的PaaS平台,那在当年(大概2013~2015)可是叱咤风云,一时风光无限。
后来的故事大家基本也看到了,几个比较有名的PaaS平台起来,然后就是K8s一家独大,然后就没有然后了。(可参考唐老师的《K8S前世今生》文章)
这里提一下,当年的CF用的容器技术,还不是Docker,而是自创的一个叫做Warden的容器技术(原理和Docker差不多,但是没有镜像管理这个功能,所以后面被Docker碾压了)。后来Docker实在太火,CF把底层的容器换成了Docker,不过也来不及了。
2 集群中容器网络怎么打通
要访问容器,有2种场景: (1)容器间互相访问。(2)PaaS平台外部访问容器。
CF基本没怎么考虑第(1)种场景,只是对第(2)种场景做了较好的处理。所以我们详细介绍下第(2)种场景:PaaS外部怎么访问容器。
为了从外部可以访问容器,CF新增了一个叫做GoRouter的组件,你就理解成一个自己实现的定制版Nginx。
PaaS集群外部,访问集群里面的容器,都需要一个这样的LB的。定制也好,取第三方也罢。 比如作为后来者,K8s为了偷懒,实现弯道超车CF,直接只做控制逻辑(还取个好听的名字叫Ingress),具体转发动作交给Nginx这种第三方工具去实现了。
回到CF,那我们就详细看下报文是怎么经过GoRouter,到达容器的。
(1) 首先,GoRouter 和 容器所在的节点(DEA),是局域网的VM,互相连通。
(2) GoRouter记录URL和容器endpoint的映射关系。
(3) 收到请求,将报文转给对应的容器Host节点。这个和K8s是一样的。
2.1 Host节点的容器网络
Warden容器出现的比Docker容器早,所以网络模型,也更简单。具体来讲,就是没Bridge网桥,报文直接靠Host路由导入容器。
l 在DEA上运行ifconfig看到的结果:
w-开头的 就是 Veth-pair 网线,没有Bridge网桥。
l 在容器内运行ifconfig看到的结果:
可以看到,warden容器内只有一个接口,就是Veth的另一头。在warden容器中运行的app也只能看到这一个接口。
所以CF为每个容器,创建了一根Veth网线,一头在Host主机(DEA)中,一头在Warden容器中,如下图:
l DEA如何把app请求交给Warden容器内
前面提到GoRouter转发过来的请求,目的IP都是DEA的,那么DEA是怎么区分请求是给哪个warden容器的呢? 答案是端口映射(目的IP都一样,不是还有端口不同么)。是的,DEA通过端口来区分不同的warden容器,从而交给不同的app。
DEA做的端口转warden容器IP工作是交给Host的iptables规则来完成的(跟Docker一样):
所以,之前提到的路由组件(GoRouter)不关心容器(Warden)里面的实际IP,只记录容器所在主机(DEA)的IP就够了。Warden容器之间网络彼此隔离,所以这个Warden容器内部的IP其实并不重要,随机都可以,只要保证所有虚拟Veth网线的IP不重复就能区分不同的Warden容器。
2.2 容器内部网络
从容器角度来看,它自己肯定认为自己是完整的世界,所以我们只需要把容器当作普通的主机就行。(容器还是为了模拟出一个“真实”的运行环境)。
对于warden容器内部来说,与外界通信的唯一途径就那根Veth网线,网线的对端就是它的网关(即容器所在的主机),再外部的网络它就一点都不知道了。在这个虚拟世界中(warden容器中)运行的app所看到的网络也是这么的简单:我的世界只有一个网卡,路由也只有一条:
2.3 报文从客户端到达容器
这一章节,是上述章节的细化版,有兴趣的看看就行了。
2.3.1 客户端到CF的GoRouter
1. 首先客户端知道app的URL网址。so会先去查询DNS,DNS返回的IP是GoRouter的外部IP(即EIP啦)。
2. 接着客户端访问刚查到的GoRouter的外部EIP(实际就是Openstack的网络节点,IaaS报文都是通过网络节点,转给内部的VM的。)。
3. Openstack网络节点将外部IP通过NAT转换成内部的IP,交给对应的VM。这里也就是GoRouter所在的节点了。
Ps:因为NAT对客户端用户来说是不感知的,客户端会认为用EIP就是接与GoRouter通信了。
2.3.2 Router到DEA(App所在的VM)
4. 路由组件GoRouter执行L7层终结模式。即先和客户端正常TCP建链,这时还不跟任何后端容器连接通信。等到客户端发起GET的时候,会根据客户端请求的URL,找到“url-app”的关联映射记录,找出是访问哪个app,然后才向目标app发起syn建链。注意这里向容器中的app发起请求时的目的端口已经换了,不再是http默认端口80。
5. “url-app”记录中app的地址是容器所在的节点(即:目的IP是DEA)。所以GoRouter把报文丢给DEA,其实也是一个VM。
2.3.3 DEA收到的报文
6. 可以看到GoRouter发其建链报文到达容器所在的Host节点。
2.3.4 DEA转发进入容器(App所在的运行环境)
dea执行nat,把报文转入到容器里面。
7. DEA根据报文的目的端口,注意是端口,执行NAT动作,将目的IP改成内部容器的IP,交给APP。
2.3.5 最终App看的请求
app看到的抓包
8 还是一样,NAT行为对客户端来说是不感知的,于是GoRouter路由组件以为自己直接跟容器App通信了。它继续开心的认为“url-app”的映射关系是正确的:
2.3.6 整体图
3 总结
CF的容器网络实现,和K8s主要3个地方稍有区别。
(1) K8s用Service概念搞定容器间如何互访问题,CF未考虑容器如何互访。
(2) 外部路由转发的实现,K8s借助第三方组件,CF自己上阵
(3) 容器所在Host组网,Docker带Bridge,CF不带Bridge
第一点:K8s精明的地方,也是一把直接站在了CF的肩膀上。
第二点:App网络主要考虑了集群外部,如何访问容器,即K8s中的Ingress部分。跟K8s-Ingress中只定义“URL->容器”映射规则,转发则交给Nginx不一样的是。CF搞了个GoRouter路由组件,除了定义映射关系,自己上手做了转发动作。
第三点:容器有没有接入Bridge网桥,其实没太大区别,这个本身还是Docker和Warden的差异。Docker比Warden更通用一些,场景考虑的更周到,所以和K8s时代盖住了CF一样,Warden这种容器,也被Docker盖的死死的。
- 点赞
- 收藏
- 关注作者
评论(0)