详解Docker负载均衡和服务发现
Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余项目,自开源后受到广泛的关注和讨论,以至于dotCloud 公司后来都改名为 Docker Inc。
Docker 是一个使用Go语言开发的开源的应用容器引擎,是PaaS提供商dotCloud开源的一个容器引擎。Docker 遵从 Apache 2.0 协议,项目代码在 GitHub 上进行维护。
简单讲,Docker就是一个可以分配资源的进程隔离模型。Docker 项目的目标是实现轻量级的操作系统虚拟化解决方案。
相关术语解释
- Dubbo:阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和Spring框架无缝集成。
- LVS:Linux Virtual Server的简写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统。
- Ipvs:IP虚拟服务器(IP Virtual Server,简写为IPVS)。是运行在LVS下的提供负载平衡功能的一种技术
- Nginx:一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器。
- HAProxy:一个使用C语言编写的自由及开放源代码软件[1],其提供高可用性、负载均衡,以及基于TCP和HTTP的应用程序代理。
- 南北通信:指的是整个容器集群入口的通信。南北通信的特点往往是通信量比较大,因此我们首先用SLB将流量分散到各个主机节点。
- 东西通信:指的是集群内部,容器和容器间的通信方式。
技术原理介绍
容器化带来的问题
在缺省网络模型中,容器每次重启后,IP会发生变动,在一个大的分布式系统保证IP地址不变是比较复杂的事情。
IP频繁发生变动,动态应用部署无法预知容器的IP地址,client端如何发现server端的访问端点?
问题解决方案
根据客户端是否有感知进行分类。
客户端的发现:client 订阅注册中心,有一个固定的注册中心地址,client订阅某个服务的注册中心,注册中心根据服务的运行状态推送某个服务的访问端点列表给client端。该方案的实现举例有Dubbo、DNS的解析等。
服务端的发现:服务端提供某个服务固定的访问端点,客户端直接访问该端点即可达到与服务端通信的目的,该访问端口对接后端具有动态IP的容器,作为请求的入口,负责请求转发到后端的容器。
该方案的实现举例就是各种对后端负载均衡的实现,包括LVS 、Nginx、HAProxy等。
我们可以认为如下公式基本成立:"服务端的发现" = "负载均衡器" + "路由配置自动更新"
对比客户端的发现,服务端发现对客户端无感知,由于很多已有的应用或者系统并不是按照类似Dubbo这种服务化的框架实现的,这些应用或者系统的客户端对服务发现都是无感知的,因此服务端的发现就表现出了独特的优势。
客户端的服务发现方法中,DNS是一个例外,几乎所有的客户端都支持DNS。下面介绍客户端DNS和服务端的负载均衡器做服务发现的几个方案。
微服务发现方案
DNS解析到多个IP
优点:Docker版本大于1.10即原生支持容器集群内部DNS的服务发现。
缺点:由于DNS TTL生效时间的存在,解析的结果不能做到实时,即使TTL设置为0,某些应用或者方法库会缓存DNS解析的结果,导致会解析到已经失效的地址上。
内核空间 LVS/IPVS
优点:IPVS的方案是Docker在未来会正式发布的1.12版本中采用的方案,主要是做到了4层的负载均衡,请求的转发实现在内核中,不需要二次拷贝请求和响应的内容,不需要解析和处理7层HTTP协议,效率高。
缺点:缺少7层负载均衡的支持,一个服务的负载均衡会占用主机的一个端口,服务与服务之间暴露的端口如果相同会产生冲突。
用户空间Nginx
优点:同时支持4层和7层负载均衡的代理,多进程模型方便利用多核性能,具有cache功能,亦可作为静态文件Web服务器,7层可以根据区分HOST字段来复用同一个80端口,解决端口冲突的问题。
7层负载会解析HTTP协议,支持多层路由,包括根据域名不同进行路由,根据路径不同进行路由,内部重定向等多种HTTP协议层的功能。
缺点:负载均衡调度算法较少,对后端进行健康检查的策略较少。
用户空间 HAProxy
优点:同时支持用户空间4层和7层负载均衡的代理,是一个纯粹的软负载均衡器,支持Round-Robin,static-rr,least-conn,source-IP等多种调度算法,对后端进行健康检查的策略完备,包括TCP端口检查,HTTP请求检查,可执行程序检查等带外检查。
7层可以根据区分HOST字段来复用同一个80端口,解决端口冲突的问题。7层负载会解析HTTP协议,支持多层路由,包括根据域名不同进行路由,根据路径不同进行路由,内部重定向等多种HTTP协议层的功能。
缺点:不能作为静态文件服务器,不支持Cache缓存功能。
容器服务的负载均衡方案
经过前面优缺点的分析和结合相关产品的优势,提供了以下解决方案。
Docker自带的DNS 服务发现,只要Docker版本大于1.1,就支持DNS的服务发现。其特点是为:
1)每个容器提供独立的DNS解析;
2)可以通过容器名与别名(Aliases)方式在整个网络作用域内解析到某个容器的IP;
3)通过链接别名的方式(Link Aliases)在容器的作用域内设置DNS解析,好处是不同容器的相同别名不会冲突。
4)对外部的DNS解析请求进行代理。如下图所示:
imageSLB做到动态绑定的原理:Swarm监管容器的状态,如果容器正常运行,则把容器加入到SLB的后端,如果容器发现异常,则把容器从SLB的后端摘下来。
HAProxy实现动态服务发现的原理:HAProxy容器内除了有HAProxy软件,还有脚本程序监管容器的状态,根据容器的健康状况重新生成负载均衡信息,然后重新加载(reload)HAProxy,使得新的负载均衡信息生效。
实现不停服rolling_update原理:平滑升级的关键在于每一时刻均有至少一个容器还能正常提供服务。
1)需要部署多个容器,将容器分为A、B两批更新。
2)更新容器时,先将A批容器的路由从SLB或者HAProxy上面摘下来。
- 更新A批容器
4)A批容器健康检查正常后,重新加入路由
5)摘下B批容器的路由
6)更新B批容器。
实现灰度发布原理:不通版本的服务可以共享同一路由信息,通过调整SLB或者HAProxy权重的方式来做到灰度发布。
根据场景提供服务形态
简单路由服务:基于HAProxy,我们加了一层Wrapper,做到动态发现处于运行状态的容器,加入到负载均衡中,我们称之为简单路由服务(Routing service),其公网IP通过一个SLB对外进行暴露。主要解决如下需求:
7层服务端点对公网暴露,即承接公网访问集群内使用7层协议的服务的流量。
7层服务端点对内网暴露,即容器集群内的负载均衡和服务发现:如下图所示,集群内的服务发现利用了Docker自带的DNS resolver配合了HAProxy的负载均衡和健康检查。图中的LB即为简单路由服务下的HAProxy容器。
1)首先通过Docker自带的DNSresolver将域名restserver.local解析到HAProxy容器的IP,此处会优先选择当前节点的HAProxy容器进行负载均衡;
2)RestClient请求域名restserver.local时,请求先走到代理容器LB;
3)LB根据从Discovery Service获取到的负载均衡信息代理到提供相应服务的容器后端RestServer Contaienr。
场景和对应的路由服务总结
如图表所示,我们将容器集群外进入容器集群内的入口通信称为南北通信,将集群内容器和容器之间的流量成为东西通信。
image我们根据不同的通信形式和协议层提供不同的服务来满足用户的需求,例如对应南北通信,如果是使用7层协议的服务,我们推荐用户使用集群的SLB进行流量转发,最终的流量会转发到每个主机的HAProxy容器上面,然后在分发到相应的处理请求的服务上。