详解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解析请求进行代理。如下图所示:
SLB做到动态绑定的原理:Swarm监管容器的状态,如果容器正常运行,则把容器加入到SLB的后端,如果容器发现异常,则把容器从SLB的后端摘下来。
HAProxy实现动态服务发现的原理:HAProxy容器内除了有HAProxy软件,还有脚本程序监管容器的状态,根据容器的健康状况重新生成负载均衡信息,然后重新加载(reload)HAProxy,使得新的负载均衡信息生效。
实现不停服rolling_update原理:平滑升级的关键在于每一时刻均有至少一个容器还能正常提供服务。
1)需要部署多个容器,将容器分为A、B两批更新。
2)更新容器时,先将A批容器的路由从SLB或者HAProxy上面摘下来。
3) 更新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。
场景和对应的路由服务总结
如图表所示,我们将容器集群外进入容器集群内的入口通信称为南北通信,将集群内容器和容器之间的流量成为东西通信。