关于负载均衡和服务发现,Google的经验在这里

云天羽翊 7年前
   <p>今天主要分享三个议题,首先是Google数据中心的简单介绍——Google数据中心约有200万台X86PC服务,没有买任何大、小型机,完全使用廉价的PC服务器搭建,因规模庞大,所以对网络要求非常高,交换机都是自行设计后定制的。服务发现、负载均衡的问题,对于Google的量级来说非常复杂,此次跟大家分享下Google内部如何实现服务发现和负载均衡。</p>    <h2>经典的服务发现与负载均衡</h2>    <p><img src="https://simg.open-open.com/show/54e219cdc0af673391a99eb69caee557.png"></p>    <p>静态的服务发现方式其实很好理解,即基于IP地址和端口做服务发现,应用绑定了服务器的IP地址和端口后,当有请求时,应用就可以接收。</p>    <p>经典的负载均衡器也是绑定某个特定的IP地址和端口,同时负载均衡器将需要做负载均衡的应用实例预先配置好,当负载均衡器收到请求后即可分发给后台的应用实例。</p>    <p>用IP+端口的方式做服务发现对人不友好,因为IP地址不好记忆,所以人们又发明了DNS作为非常经典的服务发现方式。</p>    <p>DNS实现的是域名解析,比较常用的方式是A记录向:DNS查询某个域名的A纪录会返回该域名对应的一个或多个IP地址,图上图所示,给定一个域名,通过查询DNS服务器返回来这个域名所对应的IP地址。</p>    <p>另外一种DNS解析方式是SRV记录,这是DNS里实现更高级服务发现的一种方式,向DNS查询某个域名的SRV记录要返回该域名对应的一个或多哥地址及端口,如上图所示,向DNS查询一个域名地址,DNS返回了该域名对应的一系列地址和端口。</p>    <p><img src="https://simg.open-open.com/show/771625c3776038286be9ba4d712f17bb.png"></p>    <p>DNS除了具有服务发现功能,也可以实现负载均衡,如上图所示,DNS可以根据用户请求,动态的返回某域名的A纪录,如,DNS返回的A纪录是目前最不繁忙的实例IP地址,这样DNS就可以实现负载均衡的功能。</p>    <p><img src="https://simg.open-open.com/show/0c0dfea103a2731af87831fcc2452e49.png"></p>    <p>静态环境下的负载均衡是最常见的负载均衡器使用场景,如上图所示,用户的请求发给负载均衡器,其根据一定的策略,如轮转策略或暗转一定的权重把收到的请求分发给后面具体的应用实例,应用实例在处理完请求后把响应返回给负载均衡器,之后负载均衡器再把请求响应返回给最终用户。</p>    <p><img src="https://simg.open-open.com/show/6562f4137b95d1d3213c4c8db0513c2f.png" alt="关于负载均衡和服务发现,Google的经验在这里" width="550" height="309"></p>    <p>常见的负载均衡器支持四层和七层协议,具体来说就是TCP协议和HTTP协议。</p>    <p>四层负载均衡器,按照TCP协议来说是实现了一种路由转发——一个TCP请求数据包经过四层负载均衡器时,负载均衡器只修改这个TCP请求数据包的目的地址然后转给后面的应用实例;当负载均衡器收到应用实例返回的TCP响应数据包时,会修改这个TCP响应数据包的目的地址然后返回给用户。</p>    <p>七层负载均衡器和四层负载均衡器的工作原理不一样,当七层负载均衡器收到一个用户的HTTP请求数据包会把该请求包拆掉,然后封装成一个新的HTTP请求数据包传给后面的应用实例;当负载均衡器收到应用实例返回的HTTP响应数据包时,会把HTTP响应数据包拆掉然后重新封装一个新的HTTP响应数据包返回给用户,所以四层和七层负载均衡器的工作原理不同,四层类似于路由转发,七层则是完全重新封装的包。</p>    <p><img src="https://simg.open-open.com/show/263079bab930a452bb9044d44d967e7d.png"></p>    <p>常见的服务发现方式有三种:</p>    <p>第一种是用IP地址+端口或域名+端口的方式做服务发现,比如,“website.com:8080”代表一个应用,“website.com:8081”代表另一个应用,虽然这两个应用的域名相同。这种方式适用于四层和七层协议,即TCP及HTTP协议都可以用。</p>    <p>第二种是子域名的方式,仅适用于七层协议,子域名的方式是指不同的应用可能有共同的根源,但是有不同的子域名,比如 <a href="/misc/goto?guid=4959754459258752538" rel="nofollow,noindex">http://service1.zone1.website.com</a> 和 <a href="/misc/goto?guid=4959754459344373171" rel="nofollow,noindex">http://service2.zone1.website.com</a> ,这两个不同的域名(访问端口都是80),有共同的根域名website.com,但子域名不同,因此七层协议如HTTP协议会通过不同的子域名解析到不同的应用。</p>    <p>第三种是子路径的方式,也仅适用于七层协议,如 <a href="/misc/goto?guid=4959754459426179298" rel="nofollow,noindex">http://zone1.website.com/service1</a> 和 <a href="/misc/goto?guid=4959754459510964690" rel="nofollow,noindex">http://zone1.website.com/service2</a> ,两个路径的域名完全一样,但子路径不同,可以用于区分不同的应用服务。</p>    <p>这三种服务发现方式其实总结下来只有IP地址或者域名+端口同时适用于四层、七层,其他如子域名、子路径的方式只适用于七层服务发现。</p>    <h2>动态环境</h2>    <p><img src="https://simg.open-open.com/show/49fa3783bd748e7b62fc802a66456242.png"></p>    <p>上述都是非常经典的负载均衡、服务发现的基本概念和做法,但当数据中心规模达到一定程度时应用和服务器之间更确切的说应用和具体IP地址+端口之间就不再是静态的绑定关系。</p>    <p>如Google的数据中心大约有200万台服务器,若Google的应用和服务器之间是一一对应的静态绑定关系:即某个应用必须要绑在某一个服务武器上,或者绑定某个服务器的IP地址+端口上,那么对Google来说,每时每刻大概会有几百万到上千万个应用运行在200万台服务器中,静态管理应用和服务器非常复杂,因此对于Google这样庞大规模的数据中心来说,必须用动态管理,即要求应用不能以静态绑定在服务器的IP地址+端口上,它可以在不同的服务器之间动态迁移来实现故障自愈,应用运行在某一个服务器上,这个服务器宕机或有问题,会被自动迁移到别的服务器上回复运行,动态的应用调度管理方式可以使应用的管理即服务器的管理进行解耦——应用和服务器之间不再是静态的绑定关系。</p>    <p><img src="https://simg.open-open.com/show/245c50fab7ebfd442fd703c04a23d58b.png"></p>    <p>动态环境下如何做负载均衡和服务发现?首先要明确问题,动态环境最根本的一点是要把服务发现实现,客户端要找到服务的后台,它从哪里找?这就是服务发现,在动态的环境下其实并不复杂,每个服务的后台实例绑定的IP地址和端口注册侧到一个服务注册中心,注册的方式可以是被动注册也可以是主动注册,被动注册是指负责应用调度的调度器来完成应用实例的IP地址+端口注册;主动注册是指每一个服务的实例要主动地上报自己目前所绑定的IP地址+端口。</p>    <p><img src="https://simg.open-open.com/show/e9eebc944d59017a9a4326f0e13f2ea2.png" alt="关于负载均衡和服务发现,Google的经验在这里" width="550" height="309"> <img src="https://simg.open-open.com/show/d90c4c52ef7a69ad18b4403a96a5f75b.png"></p>    <p>有了动态服务注册机制后,动态环境下的负载均衡也就好实现了,在动态环境下,当负载均衡器收到一个请求后,会去服务注册中心进行查询响应的应用实例地址,然后把请求路由到该应用的后台实例上。</p>    <h2>实例:数人云Swan</h2>    <p><img src="https://simg.open-open.com/show/3442e9e17fc881d68e71bf3534da54bb.png"></p>    <p>Google内部的服务发现和负载均衡外面看不到,数人云借鉴Google的理念实现了Swan(Github地址: <a href="/misc/goto?guid=4959754459585552500" rel="nofollow,noindex">https://github.com/Dataman-Cloud/swan</a> ),Swan基于Mesos来做容器化应用的动态调度,同时Swan实现了DNS和Proxy支持服务发现和负载均衡,跟Google的方式几乎一模一样,因此后面用Swan作例子给大家分享下Google怎么做服务发现和负载均衡。</p>    <p>首先,如何给应用命名,在动态的应用调度和运行的环境下非常重要,因为经典的应用发现偶读是按照IP和端口,没有对应用有统一的命名,但Google对于每个应用、实例都会有相应的命名,要明确几个概念:</p>    <ol>     <li>一个实例,是应用的某个Task,运行在一个容器里,应用会包含多个Task,都是运行同样的二进制程序;</li>     <li>一个应用,是一组运行同样二进制程序的实例集合,每个实例是这个应用的某个Task;</li>     <li>一组服务可以是一组应用程序;</li>     <li>一个服务会由一个用户在某个集群上发起运行。</li>    </ol>    <p>Swan给每个实例用五个标签去命名,task-app-service-user-cluster ,Task是从0开始的连续整数,用于标识不同实例;相应地Swan给每个应用四个标签来命名,app-service-user-cluster;进而,Swan给每个服务用是三个标签来命名,service-user-cluster。</p>    <p>Swan实现了DNS用于服务发现,即Swan DNS把Swan调度的每一个实例所绑定的IP地址+端口的信息都记录下来,或是A记录或是SRV记录。</p>    <p>对于每个应用,Swan的DNS也生成一个相应的域名用于四层服务发现,即app.service.user.cluster.swan.com 。另外七层的应用SwanProx会解析应用的另外一个域名 <a href="/misc/goto?guid=4959754459670766234" rel="nofollow,noindex">http://app.service.user.cluster.gateway.swan.com</a> ,用于七层应用的服务发现和负载均衡。</p>    <p><img src="https://simg.open-open.com/show/c48eca2fbb0e610b04447486be1394e2.png"></p>    <p>上图是Swan架构的示意图,简单解释了Swan、DNS、Proxy之间的关系:如何通过Swan对应用动态调度后实现服务发现和负载均衡,举例说明:首先,Swan发布一个应用app-app-service-user-cluster ,包含三个实例分别是:0-app-service-user-cluster,1-app-service-user-cluster,2-app-service-user-cluster;当Swan把三个实例都运行起来后,会将三个实例目前运行时所绑定的IP+端口信息提交给Swan DNS。</p>    <p>比如可以访问Swan DNS去解析app.service.user.cluster.swan.com这个域名,会解析出来三个容器的实例;当用户的请求访问app.service.user.cluster.gateway.swan.com,该请求会送达到Swan Proxy上,因为Swan Proxy地址是gateway.swan.com ,Swan Proxy采用子域名的方式解析app.service.user.cluster,在解析此地址时会查Swan DNS,查此应用所对应的实例,每个实例分别在哪个IP+端口上,Swan Proxy查询了Swan DNS后,发现它后面有三个实例,分别在不同的IP和端口上,当Swan Proxy收到对这个应用请求时回分别往后面三个实例上进行分发。</p>    <p><img src="https://simg.open-open.com/show/bdcbf2d4c6d17bc22bee8aa99f954e24.png"></p>    <p>上图详细地解析了Swan DNS如何做服务发现,展示了Swan DNS里面的A纪录,图里对应的A纪录是nginx-demo.default.xcm.beijing.swan.com应用,应用名称为nginx-demo,属于default服务,用户为是:xcm,default 服务目前运行在beijing数据中心中,swan.com作为一个后缀去表示是Swan的内网域名,A纪录展现出应用有6个实例,都在192.168.1.196的IP地址上。</p>    <p><img src="https://simg.open-open.com/show/4348da563f6fe0dfef4d74eafa4266ae.png"></p>    <p>上图是Swan DNS的SRV记录,SRV记录和A记录不同之处是A记录只返回域名的IP地址,SRV记录要返回域名的IP地址+端口。上图所示的SRV查询结果包含了6个不同的应用实例,分别在不同的端口上,6个不同的实例又在同一个IP地址上:192.168.1.196,但它们绑定的端口不一样:31000、31001、301002、301003、301004、301005、31006。</p>    <p><img src="https://simg.open-open.com/show/63fd1584bc7ba134a832b36ab5a4c296.png"></p>    <p>Swan实现了Proxy用于负载均衡。</p>    <p>七层负载均衡:通过Swan Proxy支持子域名方式实现前面提到,用户的HTTP请求发往app.service.user.cluster.gateway.swan.com这个域名地址时,先是.gateway.swan.com解析到Swan Proxy的IP地址上,然后因为Swan Proxy针对HTTP协议做解析的时候它会解析HTTP协议里面的域名,这个域名的子域名就是app.service.user.cluster,也就是这个域名里面的前缀。按照这个前缀,Swan Proxy可以区分出该HTTP请求是要访问哪个具体的应用。Swan Proxy在做HTTP这个服务发现负载均衡的时候会支持会话保持,也会支持HTTPS。</p>    <p>但是Swan Proxy不支持HTTP子路径方式,因为子路径的方式本质上讲不是一种负载均衡的方式,子路径其实和应用所提供的不同服务相关的,所以具体的子路径服务的注册方式需要用额外的,比如微服务自身的服务发现支持,比如SpringCloud里面的Eurake或者阿里的Dubbo这些服务注册中心来做子路径方式的服务注册。</p>    <p>四层负载均衡:因为四层协议,比如TCP协议,的特殊性,Swan Proxy支持的TCP协议只能是端口方式,根据一个Swan Proxy的IP或者Swan Proxy的域名,加上不同的端口来区分不同的应用。Swan Proxy在对TCP进行负载均衡的时候也会支持会话保持。</p>    <p><img src="https://simg.open-open.com/show/05b8ef32ff4e405cf69fd7ee5e342585.png"></p>    <p>最后汇总下Swan的服务发现、负载均衡方式。结合容器目前的几种网络模式:Bridge方式、Host方式还有固定IP的方式,上图给出Swan在不同容器的网络模式下如何做服务发现、负载均衡。</p>    <p>王璞本次分享介绍了经典的四层、七层负载均衡以及服务发现的三种方式,又以Google数据中心入手,讲述了大体量动态环境的负载均衡及服务发现方式,最后以数人云Swan为例,进行了详细讲解,小数希望大家看后有所收获。</p>    <p> </p>    <p>来自:http://dockone.io/article/2688</p>    <p> </p>